示例1:打开视频流并生成PPM文件

基本音视频流的处理顺序:

1).打开video.avi文件,读取video_stream.

2).从video_stream读取packet,分离frame

3).判断frame是否完整,若不完整,转到2

4).处理frame

5).转到2

 

1.初始化ffmpeg

       创建main.cpp,包含头文件并初始化ffmpeg。

extern "C" //main.cpp文件包含C头文件,否则查找不到函数的定义
{
#include <libavformat/avformat.h>
}

...

//register all all the muxers, demuxers and protocols
av_register_all();

...

av_register_all注册所有格式的音视频文件的编解码器,这个函数只需要在主函数开始时调用一次。当然,如果你只想注册单一格式的编解码器,你可以单独调用av_register_input_format/av_register_output_format.

 2.打开文件

AVFormatContext *pFormatContext = nullptr;
char szFilePath[] = "input.mp4";
if(avformat_open_input(&pFormatContext, szFilePath, nullptr, nullptr) != 0)
{
     return -1;
}

avformat_open_input读取视频文件的头,并把文件格式信息保存到AVFormatContext中。第三、四个参数设为null,表示函数自动检测文件格式及操作。

 

3.查找流信息

//retrieve stream information
if(avformat_find_stream_info(pFormatContext, nullptr) < 0)
{
     return -1;
}

4.输出打印信息

avformat_find_stream_info填充pFormatContext->streams数据,可以用av_dump_format(pFormatContext, 0, szFilePath, 0)打印文件及流信息。

pFormatContext->streams是一组指针数组,数组大小为pFormatContext->nb_streams。

5.查找视频流数据

int videoStream = -1;
for(int i = 0; i < pFormatContext->nb_streams; ++i)
{
    if(pFormatContext->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO)
    {
        videoStream = i;
        break;
    }
}

if(videoStream == -1)
{
    return -1;
}

AVCodecContext *pCodecCtxOrig = pFormatContext->streams[videoStream]->codec;

6.打开解码器

AVCodec *pCodec = avcodec_find_decoder(pCodecCtx->codec_id);
if(pCodec == nullptr)
{
    return -1;
}

AVCodecContext *pCodecCtx = avcodec_alloc_context3(pCodec);
AVCodecContext *pCodecCtxOrig = nullptr;
if(avcodec_copy_context(pCodecCtx, pCodecCtxOrig) != 0)
{
    return -1;
}

if(avcodec_open2(pCodecCtx, pCodec, nullptr) < 0)
{
    return -1;
}

注:不能直接操作AVCodecContext的视频流数据,我们必须copy一份。

7.存储数据

AVFrame *pFrame = av_frame_alloc();
AVFrame *pFrameRGB = av_frame_alloc();
if(pFrame == nullptr || pFrameRGB == nullptr)
{
    return -1;
}
uint8_t *buffer = nullptr;
int numBytes = -1;
numBytes = avpicture_get_size(AV_PIX_FMT_RGB24, pCodecCtx->width, pCodecCtx->height);
buffer = (uint8_t *)av_malloc(numBytes * sizeof(uint8_t));
if(buffer == nullptr)
{
    return -1;
}

avpicture_fill((AVPicture *)pFrameRGB, buffer, AV_PIX_FMT_RGB24,
               pCodecCtx->width, pCodecCtx->height);

PPM文件数据格式是24b-RGB格式,必须将视频帧格式转换成24b-RGB格式。

 

8.读取数据

struct SwsContext *sws_ctx = nullptr;
int frameFinished = -1;
sws_ctx = sws_getContext(pCodecCtx->width,
                         pCodecCtx->height,
                         pCodecCtx->pix_fmt,
                         pCodecCtx->width,
                         pCodecCtx->height,
                         AV_PIX_FMT_RGB24,
                         SWS_BILINEAR,
                         nullptr,
                         nullptr,
                         nullptr);

int i = 0;
AVPacket packet;
while(av_read_frame(pFormatContext, &packet) >= 0)
{
    if(packet.stream_index == videoStream)
    {
         avcodec_decode_video2(pCodecCtx, pFrame, &frameFinished, &packet);
         if(frameFinished)
         {
             sws_scale(sws_ctx, (const uint8_t *const*)pFrame->data,
                       pFrame->linesize, 0, pCodecCtx->height,
                       pFrameRGB->data, pFrameRGB->linesize);
             if(++i <= 5)
             {
                 //save the frame to disk
                 saveFrame(pFrameRGB, pCodecCtx->width, pCodecCtx->height, i);
             }
         }
   }
}

9.保存数据

void saveFrame(AVFrame *pFrame, int width, int height, int iFrame)
{
    FILE *pFile = nullptr;
    char szFilename[32];
    sprintf(szFilename, "frame%d.ppm", iFrame);
    pFile = fopen(szFilename, "wb");
    if(pFile == nullptr)
    {
        return;
    }

    fprintf(pFile, "P6\n%d %d\n255\n", width, height);
    for(int y = 0; y < height; ++y)
    {
        fwrite(pFrame->data[0] + y * pFrame->linesize[0], 1, width * 3, pFile);
    }

    fclose(pFile);
}

10.释放内存数据

av_free_packet(&packet);
av_free(buffer);
av_free(pFrameRGB);
av_free(pFrame);
avcodec_close(pCodecCtx);
avcodec_close(pCodecCtxOrig);
avformat_close_input(&pFormatContext);

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值