You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于FFMPEG官方转码示例的代码输出损坏视频问题求助

我编写了如下视频转码代码,逻辑参考了FFMPEG官方转码示例。

但运行后输出的视频存在损坏,效果如下:
Big Buck Bunny损坏截图1
Big Buck Bunny损坏截图2

目前观察到I帧解码正常,但P帧、B帧看起来顺序错乱,我原本以为av_interleaved_write_frame()会自动处理帧顺序问题。同时还发现libx264没有生成预期的extradata数据。

恳请各位帮忙定位问题原因,非常感谢!

#include <stdbool.h>
#include <stdio.h>

#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libavutil/avutil.h>
#include <libavutil/mathematics.h>
#include <libavutil/opt.h>

#define DEBUG(level, format, ...) fprintf(stderr, format "\n", ##__VA_ARGS__)

#define stringify2(var) #var
#define stringify(var) stringify2(var)

#define RASSERT(cond, ...) do { \
    if (!(cond)) { \
        DEBUG(LOG_FATAL, __FILE__ ":" stringify(__LINE__) " " "Assertion failed! " #cond ". " __VA_ARGS__); \
        abort(); \
    } \
} while (0)

#define FFCHECK(ret, func) RASSERT(ret == 0, #func " failed: %s (%d)", av_err2str(ret), ret)

typedef struct decode_context {
    AVFormatContext *format;
    AVCodecContext *videoCodec;
    AVCodecContext *audioCodec;
    AVStream *videoStream;
    AVStream *audioStream;
} decode_context_t;

typedef struct encode_context {
    AVFormatContext *format;
    AVCodecContext *videoCodec;
    AVCodecContext *audioCodec;
    AVStream *videoStream;
    AVStream *audioStream;
} encode_context_t;

void open_input(decode_context_t *dec, const char *file) {
    int ret;

    ret = avformat_open_input(&dec->format, file, NULL, NULL);
    FFCHECK(ret, "avformat_open_input()");

    ret = avformat_find_stream_info(dec->format, NULL);
    FFCHECK(ret, "avformat_find_stream_info()");

    for (unsigned int i = 0; i < dec->format->nb_streams; ++i) {
        AVStream * stream = dec->format->streams[i];
        enum AVMediaType type = stream->codecpar->codec_type;
        switch (type) {
        case AVMEDIA_TYPE_VIDEO:
            if (dec->videoStream)
                break;

            dec->videoStream = stream;
            break;

        case AVMEDIA_TYPE_AUDIO:
            dec->audioStream = stream;

            if (dec->audioStream)
                break;
            
            break;

        default:
            break;
        }
    }

    RASSERT(dec->videoStream != NULL, "Didn't find video stream");

    const AVCodec * codec = avcodec_find_decoder(dec->videoStream->codecpar->codec_id);

    RASSERT(codec, "Failed to find decoder");

    dec->videoCodec = avcodec_alloc_context3(codec);
    RASSERT(dec->videoCodec, "avcodec_alloc_context3() failed");

    ret = avcodec_parameters_to_context(dec->videoCodec, dec->videoStream->codecpar);
    FFCHECK(ret, "avcodec_parameters_to_context()");

    dec->videoCodec->framerate = av_guess_frame_rate(dec->format, dec->videoStream, NULL);

    ret = avcodec_open2(dec->videoCodec, codec, NULL);
    FFCHECK(ret, "avcodec_open2()");
}

void open_output(encode_context_t *enc, const char *file, decode_context_t *dec) {
    int ret;

    ret = avformat_alloc_output_context2(&enc->format, NULL, NULL, file);
    FFCHECK(ret, "avformat_alloc_output_context2()");
    
    enc->videoStream = avformat_new_stream(enc->format, NULL);
    RASSERT(enc->videoStream, "avformat_new_stream() failed");

    enc->videoStream->id = enc->format->nb_streams - 1;

    const AVCodec *codec = avcodec_find_encoder_by_name("libx264");
    RASSERT(codec, "Failed to find encoder");

    enc->videoCodec = avcodec_alloc_context3(codec);
    RASSERT(enc->videoCodec, "avcodec_alloc_context3() failed");

    enc->videoCodec->bit_rate = 400000;
    enc->videoCodec->width = dec->videoCodec->width;
    enc->videoCodec->height = dec->videoCodec->height;
    enc->videoCodec->sample_aspect_ratio = dec->videoCodec->sample_aspect_ratio;
    enc->videoCodec->time_base = av_inv_q(dec->videoCodec->framerate);
    //enc->videoCodec->gop_size = 12;
    //enc->videoCodec->max_b_frames = 2;
    enc->videoCodec->pix_fmt = dec->videoCodec->pix_fmt;
    enc->videoCodec->framerate = dec->videoCodec->framerate;

    if (codec->id == AV_CODEC_ID_H264) {
        av_opt_set(enc->videoCodec->priv_data, "preset", "slow", 0);
        av_opt_set(enc->videoCodec->priv_data, "profile", "high", 0);
        av_opt_set(enc->videoCodec->priv_data, "level", "4.1", 0);
    }

    if (enc->format->flags & AVFMT_GLOBALHEADER)
        enc->videoCodec->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;

    ret = avcodec_open2(enc->videoCodec, codec, NULL);
    FFCHECK(ret, "avcodec_open2()");

    ret = avcodec_parameters_from_context(enc->videoStream->codecpar, enc->videoCodec);
    FFCHECK(ret, "avcodec_parameters_from_context()");
    //enc->videoStream->time_base = enc->videoCodec->time_base;
    //enc->videoStream->codecpar->extradata = enc->videoCodec->extradata;
    //enc->videoStream->codecpar->extradata_size = enc->videoCodec->extradata_size;

    av_dump_format(enc->format, 0, file, 1);

    ret = avio_open(&enc->format->pb, file, AVIO_FLAG_WRITE);
    FFCHECK(ret, "avio_open()");

    ret = avformat_write_header(enc->format, NULL);
    FFCHECK(ret, "avformat_write_header()");
}

int main(int argc, const char * argv[]) {
    int ret;

    if (argc < 3) {
        fprintf(stderr, "%s input output\n", argv[0]);
        return 1;
    }

    decode_context_t dec_ctx = {};
    decode_context_t *dec = &dec_ctx;
    encode_context_t enc_ctx = {};
    encode_context_t *enc = &enc_ctx;
    
    open_input(dec, argv[1]);

    open_output(enc, argv[2], dec);

    while (true) {
        AVPacket *packet = av_packet_alloc();
        ret = av_read_frame(dec->format, packet);
        if (ret < 0)
            break;

        if (packet->stream_index == dec->videoStream->index) {
            ret = avcodec_send_packet(dec->videoCodec, packet);
            if (ret == AVERROR(EAGAIN)) {
                AVFrame * frame = av_frame_alloc();
                while (true) {
                    ret = avcodec_receive_frame(dec->videoCodec, frame);
                    if (ret == AVERROR(EAGAIN))
                        break;
                    FFCHECK(ret, "avcodec_receive_frame()");

                    ret = avcodec_send_frame(enc->videoCodec, frame);
                    if (ret == AVERROR(EAGAIN)) {
                        AVPacket *pkt = av_packet_alloc();
                        while (true) {
                            ret = avcodec_receive_packet(enc->videoCodec, pkt);
                            if (ret == AVERROR(EAGAIN))
                                break;
                            FFCHECK(ret, "avcodec_receive_packet()");

                            pkt->stream_index = enc->videoStream->id;
                            av_packet_rescale_ts(pkt, dec->videoStream->time_base, enc->videoStream->time_base);

                            ret = av_interleaved_write_frame(enc->format, pkt);
                            FFCHECK(ret, "av_interleaved_write_frame()");
                        }
                        av_packet_free(&pkt);
                    }
                }
                av_frame_free(&frame);
            } else {
                FFCHECK(ret, "avcodec_send_packet()");
            }
        } else if (packet->stream_index == dec->audioStream->index) {
            // Deal with audio
        }

        av_packet_free(&packet);
    }

    ret = av_write_trailer(enc->format);
    FFCHECK(ret, "av_write_trailer()");

    ret = avio_close(enc->format->pb);
    FFCHECK(ret, "avio_close()");

    // Close some more stuff

    return 0;
}

内容的提问来源于stack exchange,提问作者Jonathan Hatchett

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.01 12:36:05