FFmpeg 视频格式解码方法



流程

下面附上一张FFmpeg编码视频的流程图。通过该流程,不仅可以编码H.264/H.265的码流,而且可以编码MPEG4/MPEG2/VP9/VP8等多种码流。实际上使用FFmpeg编码视频的方式都是一样的。图中蓝色背景的函数是实际输出数据的函数。浅绿色的函数是视频编码的函数。



测试环境:VS2010+FFmpeg


重要函数介绍:

av_register_all():注册FFmpeg所有编解码器。
avformat_alloc_output_context2():初始化输出码流的AVFormatContext。
avio_open():打开输出文件。
av_new_stream():创建输出码流的AVStream。
avcodec_find_encoder():查找编码器。
avcodec_open2():打开编码器。
avformat_write_header():写文件头(对于某些没有文件头的封装格式,不需要此函数。比如说MPEG2TS)。
avcodec_encode_video2():编码一帧视频。即将AVFrame(存储YUV像素数据)编码为AVPacket(存储H.264等格式的码流数据)。
av_write_frame():将编码后的视频码流写入文件。
flush_encoder():输入的像素数据读取完成后调用此函数。用于输出编码器中剩余的AVPacket。
av_write_trailer():写文件尾(对于某些没有文件头的封装格式,不需要此函数。比如说MPEG2TS)。


实例代码:

extern "C"  

{  

#include "libavutil\opt.h"  

#include "libavcodec\avcodec.h"  

#include "libavformat\avformat.h"  

#include "libswscale\swscale.h"  

};  

#define MAX_BUF_SIZE 256*1024

int main(int argc,char *argv[]){
 AVFormatContext *pFormatCtx; 
    int             i, videoindex; 
    AVCodecContext  *pCodecCtx; 
    AVCodec         *pCodec; 

    av_register_all();     //注册所有编解码器
    avformat_network_init();   //允许从网络上直接获取


// 分配一个AVFormatContext结构

// 引入头文件:#include "libavformat/avformat.h"


// 其中负责申请一个AVFormatContext结构的内存,并进行简单初始化


    pFormatCtx = avformat_alloc_context();   


//初始化本机硬件设备,

    avdevice_register_all(); 


 //dshow
 AVInputFormat *ifmt=av_find_input_format("dshow"); 
    //Set own video device's name 
    if(avformat_open_input(&pFormatCtx,"video=Integrated Camera",ifmt,NULL)!=0){ 
        printf("Couldn't open input stream.(无法打开输入流)\n"); 
        return -1; 
    } 

 if(avformat_find_stream_info(pFormatCtx,NULL)<0) 
    { 
        printf("Couldn't find stream information.(无法获取流信息)\n"); 
        return -1; 
    } 
    videoindex=-1; 

 for(i=0; i < pFormatCtx->nb_streams; i++)
        if(pFormatCtx->streams[i]->codec->codec_type==AVMEDIA_TYPE_VIDEO) 
        { 
            videoindex=i; 
            break; 
        } 
 
    if(videoindex==-1) 
    { 
        printf("Couldn't find a video stream.(没有找到视频流)\n"); 
        return -1; 
    } 

 pCodecCtx=pFormatCtx->streams[videoindex]->codec; 
    pCodec=avcodec_find_decoder(pCodecCtx->codec_id); 
    if(pCodec==NULL) 
    { 
        printf("Codec not found.(没有找到解码器)\n"); 
        return -1; 
    } 
    if(avcodec_open2(pCodecCtx, pCodec,NULL)<0) 
    { 
        printf("Could not open codec.(无法打开解码器)\n"); 
        return -1; 
    }

 //为帧分配空间用以保存图片
 AVFrame *pFrame,*pFrameYUV; 
    pFrame=avcodec_alloc_frame(); 
    pFrameYUV=avcodec_alloc_frame();

    uint8_t *out_buffer=(uint8_t *)av_malloc(avpicture_get_size(PIX_FMT_YUV420P, pCodecCtx->width, pCodecCtx->height)); 
    avpicture_fill((AVPicture *)pFrameYUV, out_buffer, PIX_FMT_YUV420P, pCodecCtx->width, pCodecCtx->height); 

 int ret, got_picture;
 //为一针分配内存
    AVPacket *packet=(AVPacket *)av_malloc(sizeof(AVPacket)); 
    //Output Info----------------------------- 
    printf("File Information(文件信息)---------------------\n"); 
    //控制台打印信息
 av_dump_format(pFormatCtx,0,NULL,0); 
    printf("-------------------------------------------------\n"); 
   
 //输出的文件
 FILE *fp_yuv=fopen("G:\output.yuv","wb+"); 

 struct SwsContext *img_convert_ctx; 

 /*前三個參數分別代表 source 的寬、高及PixelFormat;
 四到六個參數分別代表 destination 的寬、高及PixelFormat;
 第七個參數則代表要使用哪種scale的方法;此參數可用的方法可在 libswscale/swscale.h 內找到。
 最後三個參數,如無使用,可以都填上NULL。*/

    img_convert_ctx = sws_getContext(pCodecCtx->width, pCodecCtx->height, pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height, PIX_FMT_YUV420P, SWS_BICUBIC, NULL, NULL, NULL);  
   
 while(av_read_frame(pFormatCtx, packet)>=0) 
    {
  if(packet->stream_index==videoindex) 
        {
   //解码一帧视频数据
   ret = avcodec_decode_video2(pCodecCtx, pFrame, &got_picture, packet); 
            if(ret < 0) 
            { 
                printf("Decode Error.(解码错误)\n"); 
                return -1; 
            } 
            if(got_picture) 
            {  

    /*第一個參數即是由 sws_getContext 所取得的參數。
    第二個 src 及第六個 dst 分別指向input 和 output 的 buffer。
    第三個 srcStride 及第七個 dstStride 分別指向 input 及 output 的 stride;如果不知道什麼是 stride,姑且可以先把它看成是每一列的 byte 數。
    第四個 srcSliceY,就註解的意思來看,是指第一列要處理的位置;這裡我是從頭處理,所以直接填0。想知道更詳細說明的人,可以參考 swscale.h 的註解。
    第五個srcSliceH指的是 source slice 的高度。
    */
                sws_scale(img_convert_ctx, (const uint8_t* const*)pFrame->data, pFrame->linesize, 0, pCodecCtx->height, pFrameYUV->data, pFrameYUV->linesize); 
    
    /*注意:这个函数以二进制形式对文件进行操作,不局限于文本文件

    返回值:返回实际写入的数据块数目

    (1)buffer:是一个指针,对fwrite来说,是要获取数据的地址;

    (2)size:要写入内容的单字节数;

    (3)count:要进行写入size字节的数据项的个数;

    (4)stream:目标文件指针;

    (5)返回实际写入的数据项个数count。
    */
    int y_size=pCodecCtx->width*pCodecCtx->height;   
                fwrite(pFrameYUV->data[0],1,y_size,fp_yuv); //Y  
                fwrite(pFrameYUV->data[1],1,y_size/4,fp_yuv);  //U 
                fwrite(pFrameYUV->data[2],1,y_size/4,fp_yuv);  //V 

   }
  }
        av_free_packet(packet);
 }
 sws_freeContext(img_convert_ctx);
 //close File
 fclose(fp_yuv);

    av_free(out_buffer); 
    av_free(pFrameYUV); 
    avcodec_close(pCodecCtx); 
    avformat_close_input(&pFormatCtx); 
 
 return 0;
}


代码测试后,会生成"G:\output.yuv"

感谢雷霄骅同学的博客,从中学到了太多!

转载:http://blog.csdn.net/leixiaohua1020/article/details/39770947

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值