FFMPEG--裸码流解析AVCodecParser

来源:互联网 发布:网络维护公司 编辑:程序博客网 时间:2024/05/19 22:59

介绍

FFMPEG解码音视频的一般来讲,都是直接从媒体容器文件(网络码流或者封装文件)中,读取出AVPaket传个解码器。但一般音视频解码并不是在这样的场景下,而是直接给解码器传送裸码流(AAC、h264等),此时我们需要知道每次传给解码器的音视频数据大小,即每帧音频/视频大小。AVCodecParser可通过音视频裸码流解析出每帧的大小等信息。


AVCodecParser

解析器通过音视频标准解析出每帧音视频信息包括长度信息等。其核心函数如下:

/** * Parse a packet. * * @param s             parser context. * @param avctx         codec context. * @param poutbuf       set to pointer to parsed buffer or NULL if not yet finished. * @param poutbuf_size  set to size of parsed buffer or zero if not yet finished. * @param buf           input buffer. * @param buf_size      input length, to signal EOF, this should be 0 (so that the last frame can be output). * @param pts           input presentation timestamp. * @param dts           input decoding timestamp. * @param pos           input byte position in stream. * @return the number of bytes of the input bitstream used. * * Example: * @code *   while(in_len){ *       len = av_parser_parse2(myparser, AVCodecContext, &data, &size, *                                        in_data, in_len, *                                        pts, dts, pos); *       in_data += len; *       in_len  -= len; * *       if(size) *          decode_frame(data, size); *   } * @endcode */int av_parser_parse2(AVCodecParserContext *s,                     AVCodecContext *avctx,                     uint8_t **poutbuf, int *poutbuf_size,                     const uint8_t *buf, int buf_size,                     int64_t pts, int64_t dts,                     int64_t pos);

其中poutbuf指向解析后输出的压缩编码数据帧,buf指向输入的压缩编码数据。如果函数执行完后输出数据为空(poutbuf_size为0),则代表解析还没有完成,还需要再次调用av_parser_parse2()解析一部分数据才可以得到解析后的数据帧。当函数执行完后输出数据不为空的时候,代表解析完成,可以将poutbuf中的这帧数据取出来做后续处理。

从上边的参数中我们发现,解析码流帧信息,主要用到AVCodecParserContext 、AVCodecContext 等,这些参数的获取和使用可以参考以下例程:

示例

本示例主要是通过读取音视频裸码流文件,然后使用av_parser_parse2解析出每帧的信息,并将解析出来的每帧长度保存文件。已验证通过的码流类型有H264、Mpeg4、MP3,但aac解析出的长度异常、g719直接崩溃,有待经一步研究。

#include <stdio.h>extern "C"{#include "libavcodec/avcodec.h"};#define ASSERT(X) if(!(X)){printf("####[%s:%d]assert failed:%s\n", __FUNCTION__, __LINE__, #X);}#define _READ_BUF_SIZE (4<<10)int main(){    int dwRet = 0;//各个接口返回值    AVCodec* ptCodec = NULL;    AVCodecContext* ptCodecCtx = NULL;    AVCodecParserContext* ptCodecParserCtx = NULL;    AVPacket tavPacket;    av_init_packet(&tavPacket);    enum AVCodecID emVidDecId = AV_CODEC_ID_MP3;//AV_CODEC_ID_MPEG4;//AV_CODEC_ID_H264;    uint8_t* pbyReadBuf = new uint8_t[_READ_BUF_SIZE];//每次从文件中读取数据的缓冲    memset(pbyReadBuf, 0, _READ_BUF_SIZE);    int dwCurBufSize = 0;           //记录当前缓冲中剩余有效数据长度    uint8_t* pbyCurBuf = NULL;              //记录缓冲读指针位置    const char* pcbyInputFileName = "mp3_3.dat";//"704x576.mpeg4";//"1080P.h264";    const char* pcbyOutPutFileName = "mp3_3.len";//"704x566.mpeg4.len";//"1080P.len"; //这里通过解析裸码流得到每帧长度    FILE* pfInput = fopen(pcbyInputFileName, "rb");    if (!pfInput)    {        ASSERT(pfInput != NULL);        return -1;    }    FILE* pfOutPut = fopen(pcbyOutPutFileName, "wb");    if (!pfOutPut)    {        ASSERT(pfOutPut != NULL);        return -1;    }    avcodec_register_all();//注册解码器    ptCodec = avcodec_find_decoder(emVidDecId);//获取指定类型的解码器    if(!ptCodec)    {        ASSERT(ptCodec != NULL);        return -1;    }    ptCodecParserCtx = av_parser_init(emVidDecId);//根据解码器类型获取解析器上下文    if (!ptCodecParserCtx)    {        ASSERT(ptCodecParserCtx != NULL);        return -1;    }    ptCodecCtx = avcodec_alloc_context3(ptCodec);//根据解码器获取解码器上下文    if (!ptCodecCtx)    {        ASSERT(ptCodecCtx != NULL);        return -1;    }    for (;;)    {        dwCurBufSize = fread(pbyReadBuf, 1, _READ_BUF_SIZE, pfInput);        if (feof(pfInput))        {            printf("end of file\n");            break;        }        if (dwCurBufSize == 0)        {            ASSERT(dwCurBufSize != 0);            return -1;        }        pbyCurBuf = pbyReadBuf;        while (dwCurBufSize > 0)        {            //解析处理,tavPacket.size不为0,说明解析出完整的一帧数据            int len = av_parser_parse2(ptCodecParserCtx, ptCodecCtx,                 &tavPacket.data, &tavPacket.size,      //输出                pbyCurBuf, dwCurBufSize,                //输入                AV_NOPTS_VALUE, AV_NOPTS_VALUE, AV_NOPTS_VALUE);            pbyCurBuf += len; //移动缓冲读指针            dwCurBufSize -= len;//剩余缓冲数据长度            if (tavPacket.size == 0)            {                //如果输出size为0 说明输入长度不够解析为完整的一帧,需要继续输入数据                continue;            }            //打印输出帧信息            printf("Packet Seq Num:%d\t", ptCodecParserCtx->output_picture_number);            printf("KeyFrame:%d pts:%d, dts:%d, duration:%d\t", ptCodecParserCtx->key_frame,                 ptCodecParserCtx->pts, ptCodecParserCtx->dts, ptCodecParserCtx->duration);            switch(ptCodecParserCtx->pict_type)            {            case AV_PICTURE_TYPE_I:                printf("\tPacket Type:I\t");                break;            case AV_PICTURE_TYPE_P:                printf("\tPacket Type:P\t");                break;            case AV_PICTURE_TYPE_B:                printf("\tPacket Type:B\t");                break;            default:                printf("\tPacket Type:error:%d\t", ptCodecParserCtx->pict_type);                break;            }            printf("\tPacket Size:%d\n", tavPacket.size);            //将长度信息保存到文件中            fprintf(pfOutPut, "%d\n", tavPacket.size);        }    }   }

以上解析出来的AvPacket就是完整的一帧码流,可以直接传给解码器解码。

参考

本文主要参考雷神博客:
参考原文点击这里

0 0