FFmpeg 视频解码流程及常用结构体解析 [转]

FFmpeg 视频解码流程及常用结构体解析  [转]

目录

FFmpeg 视频解码流程及常用结构体解析  [转]

ffmpeg解码流程

ffmpeg旧接口的解码流程

新接口解码流程

使用到的ffmpeg结构体及API说明

AVFormatContext结构体

AVCodec结构体

AVCodecContext结构体

AVStream结构体

AVIOContext结构体

AVPacket结构体

AVFrame结构体

解码Demo

ffmpeg解码流程

ffmpeg旧接口的解码流程

新接口解码流程

  • 注意 在新接口流程中使用avcodec_parameters_to_context函数来初始解码器参数,在未加入该步骤之前解析avi封装的mpeg4视频没问题但是解析MP4封装的mpeg4视频会报如下错误
Picture size is 0x00
  • 加上该步骤后解决(解析wmv格式视频也必须加入这一步)

使用到的ffmpeg结构体及API说明

AVFormatContext结构体

  • 该结构体描述了一个媒体文件或媒体流的构成和基本信息
  • 它是一个贯穿始终的数据结构,很多函数调用需要使用到它。
  • 它也是FFMPEG解封装(flv,avi,mp4)功能的结构体。
  • 其主要的几个变量(主要考虑解码情况):
struct AVInputFormat *iformat;   //输入数据的封装格式。仅解封装用,由avformat_open_input()设置。
struct AVOutputFormat *oformat;  //输出数据的封装格式。仅封装用,调用者在avformat_write_header()之前设置。
AVIOContext *pb;                 // I/O上下文。

解封装:由用户在avformat_open_input()之前设置(然后用户必须手动关闭它)或通过avformat_open_input()设置。
封装:由用户在avformat_write_header()之前设置。 调用者必须注意关闭/释放IO上下文。

unsigned int nb_streams;        //AVFormatContext.streams中元素的个数。
AVStream **streams;             //文件中所有流的列表。
char filename[1024];            //输入输出文件名。

int64_t start_time;             //第一帧的位置。
int64_t duration;               //流的持续时间
int64_t bit_rate;               //总流比特率(bit / s),如果不可用则为0。 
int64_t probesize;              //从输入读取的用于确定输入容器格式的数据的最大大小。仅封装用,由调用者在avformat_open_input()之前设置。
AVDictionary *metadata;         //元数据
AVCodec *video_codec;           //视频编解码器
AVCodec *audio_codec;           //音频编解码器
AVCodec *subtitle_codec;        //字母编解码器
AVCodec *data_codec;            //数据编解码器

int (*io_open)(struct AVFormatContext *s, AVIOContext **pb, const char *url, int flags, AVDictionary **options);        //打开IO stream的回调函数。
void (*io_close)(struct AVFormatContext *s, AVIOContext *pb);  //关闭使用AVFormatContext.io_open()打开的流的回调函数。
  • 【使用时可以通过avformat_alloc_context分配后使用,也可以直接avformat_open_input】
  • 方法一
AVFormatContext *fmt_ctx = NULL;
string filename = "test.avi" ;

fmt_ctx = avformat_alloc_context();
avformat_open_input(&fmt_ctx, ilename.c_str(), NULL, NULL);

avformat_close_input(&fmt_ctx);
  • 方法二
AVFormatContext *fmt_ctx = NULL;
string filename = "test.avi" ;

int ret = avformat_open_input(&fmt_ctx, filename.c_str(), NULL, NULL);

avformat_close_input(&fmt_ctx);
  • 推荐使用方法2,因为若传进avformat_open_input fmt_ctx 为NULL,该函数内部会调用avformat_alloc_context函数。
  • 相应的avformat_close_input内部会调用avformat_free_context

AVCodec结构体

  • ffmpeg中的解码器及编码器都用AVCodec结构体保存一些编解码的配置信息
  • 对解码来说可以按照下面方式使用 avcodec_find_decoder  或者 avcodec_find_decoder_by_name
//解码H264流  
AVCodec*   Vcodec = NULL;
Vcodec = avcodec_find_decoder(AV_CODEC_ID_H264);
  
//或者直接通过解码器名字找到解码器   
Vcodec = avcodec_find_decoder_by_name("h264_mediacodec");

AVCodecContext结构体

  • 该结构体用于存储编解码器上下文的数据结构,包含了众多编解码需要的参数信息
  • 这些信息参数需要进行初始化,使用avcodec_parameters_to_context进行初始化。
    • 不初始化解析一些格式的封装视频会导致编解码失败。
    • 该结构体内很多参数是编码时使用的,解码用不上。
  • 几个主要的成员:
enum AVMediaType codec_type:     编解码器的类型(视频,音频...)
struct AVCodec  *codec:          采用的解码器AVCodec(H.264,MPEG2...)
int bit_rate:                    平均比特率
uint8_t *extradata; int extradata_size:针对特定编码器包含的附加信息(例如对于H.264解码器来说,存储SPS,PPS等)
AVRational time_base:            根据该参数,可以把PTS转化为实际的时间(单位为秒s)
int width, height:               如果是视频的话,代表宽和高
int refs:                        运动估计参考帧的个数(H.264的话会有多帧,MPEG2这类的一般就没有了)
int sample_rate:                 采样率(音频)
int channels:                    声道数(音频)
enum AVSampleFormat sample_fmt:  采样格式
int profile:                     型(H.264里面就有,其他编码标准应该也有)
int level:                       级(和profile差不太多)
  • 使用:
AVCodec*   Vcodec             = NULL;
Vcodec                        = avcodec_find_decoder(AV_CODEC_ID_H264);

AVCodecContext*     AvContext = NULL;
AvContext                     = avcodec_alloc_context3(mVcodec);

avcodec_parameters_to_context(mAvContext, fmt_ctx->streams[mVideoStreamIdx]->codecpar);

AVStream结构体

  • 该结构体用于描述一个流媒体:
    • 该结构体中大部分值域可以由avformat_open_input函数根据文件头的信息确定,缺少的信息需要通过调用av_find_stream_info进一步获得。
    • av_find_stream_info函数读取一部分音视频来获取有关视频文件的一些信息,如编码宽高、视频时长等。
      • 对于一些没有头部信息的视频文件(如mpeg编码的文件)调用该函数是必须的。
      • 调用该函数可能会带了很大的延迟。
      • 延迟优化方法参考:https://jiya.io/archives/vlc_optimize_1.html
  • 主要的成员域:
index/id:      index 对应流的索引,这个数字是自动生成的,根据index可以从AVFormatContext::streams表中索引到该流;而id则是流的标识,依赖于具体的容器格式。比如对于MPEG TS格式,id就是pid。
time_base:     流的时间基准,是一个实数,该流中媒体数据的pts和dts都将以这个时间基准为粒度。通常,使用av_rescale/av_rescale_q可以实现不同时间基准的转换。
start_time:    流的起始时间,以流的时间基准为单位,通常是该流中第一个帧的pts。
duration:      流的总时间,以流的时间基准为单位。
need_parsing:  对该流parsing过程的控制域。
nb_frames:     流内的帧数目。
avg_frame_rate:帧率相关。
codec:         指向该流对应的AVCodecContext结构,调用avformat_open_input时生成。
parser:        指向该流对应的AVCodecParserContext结构,调用av_find_stream_info时生成。

AVIOContext结构体

  • 用于管理FFMPEG输入输出数据的结构体。
  • 主要成员:
unsigned char *buffer: 缓存开始位置
int buffer_size:       缓存大小(默认32768)
unsigned char *buf_ptr:当前指针读取到的位置
unsigned char *buf_end:缓存结束的位置
void *opaque:          URLContext结构体
  • 解码的情况下,buffer用于存储ffmpeg读入的数据。
    • 如打开一个视频文件时,先把数据从硬盘读入buffer,然后在送给解码器解码。
  • opaque按照 这篇博客 说是指向URLContext,找源码没有找到相关的赋值操作但是在 aviobuf.c 的下面这个函数找到佐证。
/*
typedef struct AVIOInternal {
    URLContext *h;
} AVIOInternal;
*/
static void *ff_avio_child_next(void *obj, void *prev)
{
    AVIOContext *s         = obj;
    AVIOInternal *internal = s->opaque;
    return                prev ? NULL : internal->h;
}
  • URLContext 结构体中有一个 URLProtocol
    • 每种协议(rtp,rtmp,file,udp等)都有一个对应的URLProtocol。

AVPacket结构体

  • 该结构体是ffmpeg中很重要的一个结构体,它保存了解码后或编码前的数据(仍然是压缩数据)和这些数据的一些附加信息
    • 如显示时间戳(pts)、数据时长、所在媒体的索引等。
  • 对于视频来说,一个AVPacket通常包含一帧压缩数据,而音频则有可能包含多个压缩的Frame
  • 重要的成员变量:
uint8_t *data:  压缩编码的数据
  • 例如对于H.264来说,1个AVPacket的data通常对应一个NAL
    • 注意:在这里只是对应,而不是一模一样。
    • 他们之间有微小的差别:使用FFMPEG类库分离出多媒体文件中的H.264码流。
    • 因此在使用FFMPEG进行视音频处理的时候,常常可以将得到的AVPacket的data数据直接写成文件,从而得到视音频的码流文件。
int size:        data的大小
int64_t pts:     显示时间戳
int64_t dts:     解码时间戳
int stream_index:标识该AVPacket所属的视频/音频流。
  • avpacket.h 内有API说明,常用的几个API
av_packet_ref,
av_packet_unref
av_new_packet, 
av_packet_alloc, 
av_init_packet, 
av_packet_unref,
av_packet_free(free这个API为旧接口)
av_packet_clone:拷贝packet

AVFrame结构体

  • AVFrame结构体一般用于存储原始数据(非压缩的YUV,RGB数据等)
    • 此外还包含一些相关信息,比如解码的时候存储宏块类型表,QP表,运动矢量等数据。
  • AVFrame必须用 av_frame_alloc 分配,用 av_frame_free 释放。
    • 注意av_frame_alloc函数只创建实例但是该实例存储数据的buffer则需要通过另外的操作进行分配,如av_image_fill_arrays
  • AVFrame内部几个常用的成员:
uint8_t *data[AV_NUM_DATA_POINTERS]:解码后原始数据(对视频来说是YUV,RGB,对音频来说是PCM)
int linesize[AV_NUM_DATA_POINTERS]: data中“一行”数据的大小。注意:未必等于图像的宽,一般大于图像的宽。
int width, height:                  视频帧宽和高(1920x1080,1280x720…)
int nb_samples:                     音频的一个AVFrame中可能包含多个音频帧,在此标记包含了几个
int format:                         解码后原始数据类型(YUV420,YUV422,RGB24…)
int key_frame:                      是否是关键帧
enum AVPictureType pict_type:       帧类型(I,B,P…)
AVRational sample_aspect_ratio:     宽高比(16:9,4:3…)
int64_t pts:                        显示时间戳
int coded_picture_number:           编码帧序号
int display_picture_number:         显示帧序号
int interlaced_frame:               是否是隔行扫描
uint8_t motion_subsample_log2:      一个宏块中的运动矢量采样个数,取log的

解码Demo

#include "ffmpeg_test.h"
#include <stdio.h>
#include <iostream>
#include <string>

using namespace  std;

#define INBUF_SIZE    (300000)

AVCodecID strm_to_av_map_coding_type[STRM_CODEC_MAX] =
{
    AV_CODEC_ID_NONE,          //0
    AV_CODEC_ID_H264,          //1
    AV_CODEC_ID_H265,          //2
    AV_CODEC_ID_MPEG1VIDEO,    //3
    AV_CODEC_ID_MPEG2VIDEO,    //4
    AV_CODEC_ID_MJPEG,         //5
    AV_CODEC_ID_MPEG4          //6
};

AVPixelFormat strm_to_av_map_pixel_format[STRM_PIXFMT_MAX] =
{
    AV_PIX_FMT_NONE,
    AV_PIX_FMT_RGB24,
    AV_PIX_FMT_RGBA,
    AV_PIX_FMT_RGB565,
    AV_PIX_FMT_ARGB,
    AV_PIX_FMT_YUV420P,
    AV_PIX_FMT_YUV422P,
    AV_PIX_FMT_YUV444P,
    AV_PIX_FMT_GRAY8,
    AV_PIX_FMT_NV21,
    AV_PIX_FMT_UYVY422,
    AV_PIX_FMT_YUYV422
};

static AVFrame*    mVideoFrame = NULL;
static AVFrame*      mFrameYUV = NULL;

// 颜色转换上下文
static struct SwsContext*  mImgConvertCtx = NULL;

///< 解码器
static AVCodec*            mVcodec = NULL;
///< 解码上下文
static AVCodecContext*     mAvContext = NULL;

static AVFormatContext*    mFormatCtx = NULL;  // 用于读取文件   

static int mVideoStreamIdx = 0;


typedef struct DecodeFrameParams 
{
    unsigned int mInputCodecType;
    unsigned int mOutputPixelFormat;
}DecodeFrameParams;

bool GetDecodedFrame(MediaFrame& rawFrame, DecodeFrameParams& params)
{
    
    int frameSize = av_image_get_buffer_size(strm_to_av_map_pixel_format[params.mOutputPixelFormat], mVideoFrame->width, mVideoFrame->height, 1);

    // 分配内存
    void* buf_ptr = NULL;
    buf_ptr = (void*)new(std::nothrow) char[frameSize];
    if (buf_ptr == NULL)
    {
        printf("decode memory allocation error! [buf_size = %d]\n", frameSize);
        return false;
    }

    if (mImgConvertCtx)
    {
        sws_freeContext(mImgConvertCtx);
    }

    av_image_fill_arrays(mFrameYUV->data, mFrameYUV->linesize, (uint8_t*)buf_ptr,
        strm_to_av_map_pixel_format[params.mOutputPixelFormat], mVideoFrame->width, mVideoFrame->height, 1);
    mImgConvertCtx = sws_getContext(mVideoFrame->width, mVideoFrame->height, mAvContext->pix_fmt,
        mVideoFrame->width, mVideoFrame->height, strm_to_av_map_pixel_format[params.mOutputPixelFormat], SWS_BICUBIC, NULL, NULL, NULL);

    sws_scale(mImgConvertCtx, (const unsigned char* const*)mVideoFrame->data,
        mVideoFrame->linesize, 0, mVideoFrame->height,
        mFrameYUV->data, mFrameYUV->linesize);

    //mWidth  = mVideoFrame->width;
    //mHeight = mVideoFrame->height;

    // 配置输出信息
    rawFrame.frameBuf = (unsigned char*)buf_ptr;
    rawFrame.frameSize = frameSize;

    return true;
}

int test_decode(unsigned int mInputCodecType, unsigned int mOutputPixelFormat, 
    string &filename, string &error)
{
    FILE* fp_YUV = fopen("decode_out.yuv", "wb+");
    if (!fp_YUV)
    {
        perror("open out.yuv :");
        return -1;
    }

    av_register_all();

    mVideoFrame = av_frame_alloc();
    mFrameYUV   = av_frame_alloc();

    bool                mHasKeyFrame = false;

    AVPacket            mPktPacket;

    long long    mDecodedBytes = 0;

    int ret = avformat_open_input(&mFormatCtx, filename.c_str(), NULL, NULL);
    if (ret < 0)
    {
        printf("avformat_open_input fail \n");
        error = "avformat_open_input fail";

        av_log(NULL, AV_LOG_ERROR, "Cannot open file: %s.\n", filename.c_str());
        return false;
    }
    if (avformat_find_stream_info(mFormatCtx, NULL) < 0)
    {
        return false;
    }

    av_dump_format(mFormatCtx, 0, filename.c_str(), 0);

    //获取视频的编码信息   
    for (uint32_t i = 0; i < mFormatCtx->nb_streams; ++i)
    {
        if (mFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO)
        {
            mVideoStreamIdx = i;
            break;
        }
    }

    // 寻找解码器
    mVcodec = avcodec_find_decoder(strm_to_av_map_coding_type[mInputCodecType]);
    mAvContext = avcodec_alloc_context3(mVcodec);
    if (!mVcodec || !mAvContext)
    {
        //DecoderDeinit();
        return false;
    }

    ///不初始化解码器context会导致MP4封装的mpeg4码流解码失败    
    ret = avcodec_parameters_to_context(mAvContext, mFormatCtx->streams[mVideoStreamIdx]->codecpar);
    if (ret < 0) {
        av_log(NULL, AV_LOG_ERROR, "Error initializing the decoder context.\n");
        //exit_program(1);
    }

    // 打开解码器
    mAvContext->pix_fmt = strm_to_av_map_pixel_format[mOutputPixelFormat];
    if (avcodec_open2(mAvContext, mVcodec, NULL) != 0)
    {
        //DecoderDeinit();
        return false;
    }

    //循环读入H264数据  
    AVPacket h264Pack;
    
    while (1)
    {
        av_init_packet(&h264Pack);
        int ret = av_read_frame(mFormatCtx, &h264Pack);
        if (ret != 0)
        {
            error = "Read a frame failed";
            av_packet_unref(&h264Pack);
            return false;
        }
        else if (h264Pack.stream_index != mVideoStreamIdx)
        {
            // not a video packet, skip it in this version
            av_packet_unref(&h264Pack);
            continue;
        }

        mHasKeyFrame = true;
        if (mHasKeyFrame)
        {

            // 初始化待解码包
            av_init_packet(&mPktPacket);
            mPktPacket.data = (uint8_t*)h264Pack.data;     // 用于解码的压缩视频帧数据,mBitStreamBuf中包含pps数据
            mPktPacket.size = h264Pack.size;
            mPktPacket.pts = h264Pack.pts;
            mPktPacket.dts = h264Pack.dts;

            mDecodedBytes += mPktPacket.size;

            // 发送待解码包
            if (avcodec_send_packet(mAvContext, &mPktPacket))
            {
                error = "send packet failed";
                mHasKeyFrame = false;
                av_packet_unref(&mPktPacket);
                return false;
            }

            av_packet_unref(&mPktPacket);

            // 接收解码数据
            int ret = avcodec_receive_frame(mAvContext, mVideoFrame);
            if (ret != 0)
            {
                if (ret == AVERROR(EAGAIN))
                {
                    // 暂时没有输出,需要更多输入
                    error = "need more data";
                    //return false;
                    continue;
                }
                //error = "receive frame failed";
                // 输出数据接收失败
                //mHasKeyFrame = false;
                //return false;
            }

            DecodeFrameParams params;
            params.mInputCodecType = mInputCodecType;
            params.mOutputPixelFormat = mOutputPixelFormat;

            MediaFrame rawFrame;
            // 获取解码后的YUV数据
            GetDecodedFrame(rawFrame, params);
            //int mFrameRate = mAvContext->framerate.num;

            // 写文件保存视频数据
            fwrite(rawFrame.frameBuf, rawFrame.frameSize, 1, fp_YUV);
            fflush(fp_YUV);

            if (rawFrame.frameBuf)
            {
                delete[] rawFrame.frameBuf;
                rawFrame.frameBuf = NULL;
            }
        }
    }

    fclose(fp_YUV);
    avformat_close_input(&mFormatCtx);
    //printf("[strmsdk] decoder[%s] open successful.\n", mVcodec->name);
}


int flush_encoder(AVFormatContext *fmt_ctx, unsigned int stream_index) 
{
    int ret;
    int got_frame;
    AVPacket enc_pkt;
    if (!(fmt_ctx->streams[stream_index]->codec->codec->capabilities & AV_CODEC_CAP_DELAY))
    {
        return 0;
    }

    while (1) 
    {
        enc_pkt.data = NULL;
        enc_pkt.size = 0;
        av_init_packet(&enc_pkt);
        ret = avcodec_encode_video2(fmt_ctx->streams[stream_index]->codec, &enc_pkt,
            NULL, &got_frame);
        av_frame_free(NULL);
        if (ret < 0)
            break;
        if (!got_frame) {
            ret = 0;
            break;
        }
        printf("Flush Encoder: Succeed to encode 1 frame!\tsize:%5d\n", enc_pkt.size);
        /* mux encoded frame */
        ret = av_write_frame(fmt_ctx, &enc_pkt);
        if (ret < 0)
            break;
    }
    return ret;
}

int test_encode(int argc, char* argv[])
{
    AVFormatContext* pFormatCtx;
    AVOutputFormat* fmt;
    AVStream* video_st;
    AVCodecContext* pCodecCtx;
    AVCodec* pCodec;
    AVPacket pkt;
    uint8_t* picture_buf;
    AVFrame* pFrame;
    int picture_size;
    int y_size;
    int framecnt = 0;
    //FILE *in_file = fopen("src01_480x272.yuv", "rb"); //Input raw YUV data 
    FILE *in_file = fopen("ds_352x288.yuv", "rb");   //Input raw YUV data
    int in_w = 352, in_h = 288;                      //Input data's width and height
    int framenum = 50;                                   //Frames to encode
    //const char* out_file = "src01.h264";              //Output Filepath 
    //const char* out_file = "src01.ts";
    //const char* out_file = "src01.hevc";
    const char* out_file = "ds.h264";

    av_register_all();
    //Method1.
    pFormatCtx = avformat_alloc_context();
    //Guess Format
    fmt = av_guess_format(NULL, out_file, NULL);
    pFormatCtx->oformat = fmt;

    //Method 2.
    //avformat_alloc_output_context2(&pFormatCtx, NULL, NULL, out_file);
    //fmt = pFormatCtx->oformat;

    //Open output URL
    if (avio_open(&pFormatCtx->pb, out_file, AVIO_FLAG_READ_WRITE) < 0) {
        printf("Failed to open output file! \n");
        return -1;
    }

    video_st = avformat_new_stream(pFormatCtx, 0);
    //video_st->time_base.num = 1; 
    //video_st->time_base.den = 25;  

    if (video_st == NULL) {
        return -1;
    }
    //Param that must set
    pCodecCtx = video_st->codec;
    //pCodecCtx->codec_id =AV_CODEC_ID_HEVC;
    pCodecCtx->codec_id = fmt->video_codec;
    pCodecCtx->codec_type = AVMEDIA_TYPE_VIDEO;
    pCodecCtx->pix_fmt = AV_PIX_FMT_YUV420P;
    pCodecCtx->width = in_w;
    pCodecCtx->height = in_h;
    pCodecCtx->bit_rate = 400000;
    pCodecCtx->gop_size = 25;   //I帧间隔   

    pCodecCtx->time_base.num = 1;
    pCodecCtx->time_base.den = 25;  //time_base一般是帧率的倒数,但不总是  
    pCodecCtx->framerate.num = 25;  //帧率  
    pCodecCtx->framerate.den = 1;

    ///AVFormatContext* mFormatCtx 
    ///mBitRate   = mFormatCtx->bit_rate;   ///码率存储位置  
    ///mFrameRate = mFormatCtx->streams[stream_id]->avg_frame_rate.num;
    
    
    //H264
    //pCodecCtx->me_range = 16;
    //pCodecCtx->max_qdiff = 4;
    //pCodecCtx->qcompress = 0.6;
    pCodecCtx->qmin = 10;
    pCodecCtx->qmax = 51;

    //Optional Param
    pCodecCtx->max_b_frames = 0;  //不要B帧   

    // Set Option
    AVDictionary *param = 0;
    //H.264
    if (pCodecCtx->codec_id == AV_CODEC_ID_H264) {
        av_dict_set(&param, "preset", "slow", 0);
        av_dict_set(&param, "tune", "zerolatency", 0);
        //av_dict_set(&param, "profile", "main", 0);
    }
    //H.265
    if (pCodecCtx->codec_id == AV_CODEC_ID_H265) {
        av_dict_set(&param, "preset", "ultrafast", 0);
        av_dict_set(&param, "tune", "zero-latency", 0);
    }

    //Show some Information
    av_dump_format(pFormatCtx, 0, out_file, 1);

    pCodec = avcodec_find_encoder(pCodecCtx->codec_id);
    if (!pCodec) {
        printf("Can not find encoder! \n");
        return -1;
    }
    if (avcodec_open2(pCodecCtx, pCodec, &param) < 0) {
        printf("Failed to open encoder! \n");
        return -1;
    }


    pFrame = av_frame_alloc();
    picture_size = av_image_get_buffer_size(pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height, 1);
    picture_buf = (uint8_t *)av_malloc(picture_size);
    //avpicture_fill((AVPicture *)pFrame, picture_buf, pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height);
    av_image_fill_arrays(pFrame->data, pFrame->linesize, picture_buf, pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height, 1);

    //Write File Header
    avformat_write_header(pFormatCtx, NULL);

    av_new_packet(&pkt, picture_size);

    y_size = pCodecCtx->width * pCodecCtx->height;

    for (int i = 0; i < framenum; i++) 
    {
        int ret = 0;
        //Read raw YUV data
        if (fread(picture_buf, 1, y_size * 3 / 2, in_file) <= 0) 
        {
            printf("Failed to read raw data! \n");
            return -1;
        }
        else if (feof(in_file)) {
            break;
        }

        int got_picture = 0;

        pFrame->data[0] = picture_buf;              // Y
        pFrame->data[1] = picture_buf + y_size;      // U 
        pFrame->data[2] = picture_buf + y_size * 5 / 4;  // V
                                                         //PTS
                                                         //pFrame->pts=i;
        pFrame->pts = i;
        pFrame->width = pCodecCtx->width;
        pFrame->height = pCodecCtx->height;
        pFrame->format = AV_PIX_FMT_YUV420P;

        //Encode   旧版接口   
        /*ret = avcodec_encode_video2(pCodecCtx, &pkt, pFrame, &got_picture);
        if (ret < 0) {
            printf("Failed to encode! \n");
            return -1;
        }
        if (got_picture == 1) {
            printf("Succeed to encode frame: %5d\tsize:%5d\n", framecnt, pkt.size);
            framecnt++;
            pkt.stream_index = video_st->index;
            ret = av_write_frame(pFormatCtx, &pkt);
            av_free_packet(&pkt);
        }*/

        ret = avcodec_send_frame(pCodecCtx, pFrame);
        if (ret < 0)
        {
            fprintf(stderr, "Error sending a frame for encoding\n");
            return -1;
        }

        while (ret == 0)
        {
            //framecnt++;
            ret = avcodec_receive_packet(pCodecCtx, &pkt);
            if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
            {
                framecnt++;
                break;
            }
            //framecnt++;
            pkt.stream_index = video_st->index;
            ret = av_write_frame(pFormatCtx, &pkt);
            av_packet_unref(&pkt);
        }

    }

    //Flush Encoder
    int ret = flush_encoder(pFormatCtx, 0);
    if (ret < 0) {
        printf("Flushing encoder failed\n");
        return -1;
    }

    //Write file trailer
    av_write_trailer(pFormatCtx);

    //Clean
    if (video_st) {
        avcodec_close(video_st->codec);
        av_free(pFrame);
        av_free(picture_buf);
    }
    avio_close(pFormatCtx->pb);
    avformat_free_context(pFormatCtx);

    fclose(in_file);

    return 0;
}

猜你喜欢

转载自blog.csdn.net/baidu_41388533/article/details/115050338
今日推荐