libavformat/libavcodec提供了无效的容器头 [英] libavformat/libavcodec providing invalid container header

查看:87
本文介绍了libavformat/libavcodec提供了无效的容器头的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用libavcodec将流编码为h264,并使用libavformat将其存储在mp4中.产生的容器具有无效的标头,可以在VLC中播放,但不能在任何其他播放器上播放.

I'm using libavcodec to encode a stream to h264 and libavformat to store it in an mp4. The resulting container has an invalid header that can be played in VLC, but not any other player.

我发现使用mp4容器和"mpeg4"编解码器会生成有效的mp4文件,但是使用libx265(HEVC)或libx264编解码器会生成无效的mp4.

I've found that using the mp4 container and the "mpeg4" codec produces a valid mp4 file, but using libx265 (HEVC) or the libx264 codec produces invalid mp4s.

我可以使用 ffmpeg -i invalid.mp4 -vcodec复制valid.mp4 ,我得到一个几乎完全相同大小的文件,但是在一个有效的容器中.

I can use ffmpeg -i invalid.mp4 -vcodec copy valid.mp4 and I get a file of almost the exact same size, but in a valid container.

以下是这些文件的示例:损坏的文件已拒绝文件 [使用右上角的下载链接进行检查]

Examples of these files are here: Broken file and Repaied file [use the download links in the upper right to examine]

我使用了十六进制编辑器来查看两个文件的标题之间的差异,无效的文件比有效文件小1个字节.

I used a hex editor to see the differences in the headers of the two files and the invalid one is 1 byte smaller than the valid one.

我用来打开容器和编解码器并编写标题的代码在这里:

The code I'm using to open the container and codec and to write the header is here:

AVOutputFormat *container_format;
AVFormatContext *container_format_context;
AVStream *video_stream;
int ret;

/* allocate the output media context */
avformat_alloc_output_context2(&container_format_context, NULL, NULL, out_file);
if (!container_format_context) {
    log(INFO, "Unable to determine container format from filename, exiting\n");
    exit(1);
}
else {
    log(INFO, "Using container %s\n", container_format_context->oformat->name);
}

if (!container_format_context) {
    log(ERROR, "Could not build container format context. Encoding failed.");
    exit(1);
}

container_format = container_format_context->oformat;

/* Pull codec based on name */
AVCodec* codec = avcodec_find_encoder_by_name(codec_name);
if (codec == NULL) {
    log(ERROR, "Failed to locate codec \"%s\".",
            codec_name);
    exit(1);
}

/* create stream */
video_stream = NULL;
video_stream = avformat_new_stream(container_format_context, codec);
if (!video_stream) {
    log(ERROR, "Could not allocate encoder stream. Cannot continue.\n");
    exit(1);
}
video_stream->id = container_format_context->nb_streams - 1;

video_stream->time_base = video_stream->codec->time_base = (AVRational) { 1, 25};

av_dump_format(container_format_context, 0, out_file, 1);



/* Retrieve encoding context */
AVCodecContext* avcodec_context = video_stream->codec;
if (avcodec_context == NULL) {
    log(ERROR, "Failed to allocate context for "
            "codec \"%s\".", codec_name);
    exit(1);
}


/* Init context with encoding parameters */
avcodec_context->bit_rate = bitrate;
avcodec_context->width = width;
avcodec_context->height = height;
avcodec_context->gop_size = 10;
avcodec_context->max_b_frames = 1;
avcodec_context->qmax = 31;
avcodec_context->qmin = 2;
avcodec_context->pix_fmt = AV_PIX_FMT_YUV420P;

av_dump_format(container_format_context, 0, out_file, 1);

/* Open codec for use */
if (avcodec_open2(avcodec_context, codec, NULL) < 0) {
    log(ERROR, "Failed to open codec \"%s\".", codec_name);
    exit(1);
}

/* Allocate corresponding frame */
AVFrame* frame = av_frame_alloc();
if (frame == NULL) {
    exit(1);
}

/* Copy necessary data for frame from avcodec_context */
frame->format = avcodec_context->pix_fmt;
frame->width = avcodec_context->width;
frame->height = avcodec_context->height;

/* Allocate actual backing data for frame */
if (av_image_alloc(frame->data, frame->linesize, frame->width,
            frame->height, frame->format, 32) < 0) {
    exit(1);
}

/* open the output file, if the container needs it */
if (!(container_format->flags & AVFMT_NOFILE)) {
    ret = avio_open(&container_format_context->pb, out_file, AVIO_FLAG_WRITE);
    if (ret < 0) {
        log(ERROR, "Error occurred while opening output file: %s\n",
                av_err2str(ret));
        exit(1);
    }
}

/* write the stream header, if needed */
ret = avformat_write_header(container_format_context, NULL);
if (ret < 0) {
    log(ERROR, "Error occurred while writing output file header: %s\n",
                av_err2str(ret));
}

编码帧的代码在这里:

/* Init video packet */
AVPacket packet;
av_init_packet(&packet);

/* Request that encoder allocate data for packet */
packet.data = NULL;
packet.size = 0;

/* Write frame to video */
int got_data;
if (avcodec_encode_video2(avcontext, &packet, frame, &got_data) < 0) {
    log(WARNING, "Error encoding frame #%" PRId64,
            video_struct->next_pts);
    return -1;
}

/* Write corresponding data to file */
if (got_data) {
    if (packet.pts != AV_NOPTS_VALUE) {
        packet.pts = av_rescale_q(packet.pts, video_struct->output_stream->codec->time_base, video_struct->output_stream->time_base);
    }
    if (packet.dts != AV_NOPTS_VALUE) {
        packet.dts = av_rescale_q(packet.dts, video_struct->output_stream->codec->time_base, video_struct->output_stream->time_base);
    }
    write_packet(video_struct, &packet, packet.size);
    av_packet_unref(&packet);
}

以及将数据包写入视频流的代码:

And the code to write the packet to the video stream:

static int write_packet(video_struct* video, void* data, int size) {

int ret;

/* use AVStream is not null, otherwise write to output fd */
AVPacket *pkt = (AVPacket*) data;
pkt->stream_index = video->output_stream->index;
ret = av_interleaved_write_frame(video->container_format_context, pkt);
if (ret != 0) {
    return -1;
}

/* Data was written successfully */
return ret;
}

推荐答案

解决了此问题.问题是如果容器需要的话,我没有为容器分配全局头.在为 avcodec_context 分配高度,宽度,比特率等属性时,我添加了

Solved this issue. The problem was that I wasn't assigning global headers to the container if the container required it. While assigning properties like height, width, bit rate and so forth to the avcodec_context, I added

if (container_format_context->oformat->flags & AVFMT_GLOBALHEADER) {
    avcodec_context->flags |= CODEC_FLAG_GLOBAL_HEADER;
}

这似乎已解决了该问题.

which seems to have fixed the issue.

这篇关于libavformat/libavcodec提供了无效的容器头的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆