ffmpeg MP4转YUV格式保存

#include 
#include 
#include "libavcodec/avcodec.h"
#include "libavfilter/avfilter.h"
#include "libavformat/avformat.h"
#include "libavutil/avutil.h"
#include "libavutil/ffversion.h"
#include "libswresample/swresample.h"
#include "libswscale/swscale.h"
#include "libpostproc/postprocess.h"
#include 


#define SAVEFF_VIDEO_YUV
int main() {
    FILE *fp=fopen("save_video.yuv","w+b");
    if(fp==NULL)
    {
    	printf("cannot open file\n");
    	return -1;
    }
    char filePath[] = "/home/niko/work/compile/niko_cat.mp4";//文件地址

    int  videoStreamIndex = -1;//视频流所在流序列中的索引
    int ret=0;//默认返回值

    //需要的变量名并初始化
    AVFormatContext *fmtCtx=NULL;  //AVFormatContext usually be used
    AVPacket *pkt =NULL;
    AVCodecContext *codecCtx=NULL;
    AVCodecParameters *avCodecPara=NULL;
    AVCodec *codec=NULL;
    AVFrame *yuvFrame = av_frame_alloc();
    AVFrame *pFrameYUV = av_frame_alloc();

   do{
        //=========================== 创建AVFormatContext结构体 //
        //分配一个AVFormatContext,FFMPEG所有的操作都要通过这个AVFormatContext来进行
        fmtCtx = avformat_alloc_context();
        //==================================== 打开文件 
        if ((ret=avformat_open_input(&fmtCtx, filePath, NULL, NULL)) != 0) {
            printf("cannot open video file\n");
            break;
        }

        //===================================查找流信息 音频流和视频流================== //
        if ((ret=avformat_find_stream_info(fmtCtx, NULL)) < 0) {
            printf("cannot retrive video info\n");
            break;
        }

        //循环查找视频中包含的流信息,直到找到视频类型的流
        //便将其记录下来 保存到videoStreamIndex变量中

        for (unsigned int i = 0; i < fmtCtx->nb_streams; i++) { //*AVFormatContext.streams中元素的个数,有几路流

        printf("%s has %dstreams\n",filePath, fmtCtx->nb_streams) ;
        
            if (fmtCtx->streams[ i ]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
                videoStreamIndex = i;
                break;//找到视频流就退出
            }
        }

        //如果videoStream为-1 说明没有找到视频流
        if (videoStreamIndex == -1) {
            printf("cannot find video stream\n");
            break;
        }

        //打印输入和输出信息:长度 比特率 流格式等
        av_dump_format(fmtCtx, 0, filePath, 0);

        // 查找解码器 
        avCodecPara = fmtCtx->streams[ videoStreamIndex ]->codecpar;
        codec = avcodec_find_decoder(avCodecPara->codec_id);

        if (codec == NULL) {
            printf("cannot find decoder\n");
            break;
        }
        //根据解码器参数来创建解码器内容
        codecCtx = avcodec_alloc_context3(codec);
        avcodec_parameters_to_context(codecCtx, avCodecPara); //  将AVCodecParameters结构体中码流参数拷贝到AVCodecContext结构体中,format, width, height, codec_type等
        if (codecCtx == NULL) {
            printf("Cannot alloc context.");
            break;
        }
        //================================  打开解码器 
        if ((ret=avcodec_open2(codecCtx, codec, NULL)) < 0) { // 具体采用什么解码器ffmpeg经过封装 我们无须知道
            printf("cannot open decoder\n");
            break;
        }
        
        int w=codecCtx->width;//视频宽度
        int h=codecCtx->height;//视频高度
     printf("decode from %s,width is%d\n",filePath,w);
     printf("decode from %s,height is%d\n",filePath,h);
    struct SwsContext* sws_context = sws_getContext( w,h, codecCtx->pix_fmt, //源地址长宽以及数据格式
     w,h,AV_PIX_FMT_YUV420P, SWS_BICUBIC, NULL, NULL, NULL);//算法类型  AV_PIX_FMT_YUVJ420P 
          
            //==================================== 分配空间
           int numBytes = av_image_get_buffer_size(AV_PIX_FMT_YUV420P, w,h, 1);//使用av_image_get_buffer_size来获得我们需要的大小,并手动分配空间:
        unsigned char *out_buffer = (unsigned char *)av_malloc(numBytes * sizeof(unsigned char));

    
        //=========================== 分配AVPacket结构体
        int i= 0;//用于帧计数
        pkt = av_packet_alloc();                      //分配一个packet
        av_new_packet(pkt, w *h); //调整packet的数据    
    av_image_fill_arrays(pFrameYUV->data, pFrameYUV->linesize, out_buffer,AV_PIX_FMT_YUV420P, w, h,1);
        //===========================  读取视频信息 
 #ifdef SAVEFF_VIDEO_YUV       
         while (av_read_frame(fmtCtx, pkt) >= 0) //读取的是一帧视频  数据存入一个AVPacket的结构中
         {
            if(pkt->stream_index==videoStreamIndex)
           {  
        	 if (avcodec_send_packet(codecCtx, pkt) == 0) //将包发送到解码器
        	 {
                    while (avcodec_receive_frame(codecCtx, yuvFrame) == 0)  //接收解码帧
                    {
   				i++;//只计算视频帧
   				    sws_scale(sws_context,
                                      (const uint8_t* const*)yuvFrame->data,
                                      yuvFrame->linesize,
                                      0,
                                      codecCtx->height,
                                      pFrameYUV->data,
                                      pFrameYUV->linesize);//将帧数据转为yuv420p
                         fwrite(pFrameYUV->data[0],1,w*h,fp);//将y数据写入文件中
                        fwrite(pFrameYUV->data[1],1,w*h/4,fp);//将u数据写入文件中
                        fwrite(pFrameYUV->data[2],1,w*h/4,fp);//将v数据写入文件中
                    }
         	  }
         	  else
         	  {
         	     break;
         	  }
             }
         av_packet_unref(pkt);//重置pkt的内容
       }
        printf("There are %d frames int total.\n", i);
  #endif      
    }while(0);
    
    //===========================释放所有指针
    av_packet_free(&pkt); //释放av_packet_alloc
    avcodec_close(codecCtx);
    avcodec_parameters_free(&avCodecPara); 
   av_frame_free(&yuvFrame);
       av_frame_free(&pFrameYUV);
    fclose(fp);

    return ret;
}


主要参考这位博主的demo和雷神的博客,https://blog.csdn.net/qq_26056015/article/details/124926967?spm=1001.2014.3001.5502 这里是引用

测试./ffplay -video_size 720x1280 save_video.yuv

你可能感兴趣的:(学习小记录,LINUX学习,视频编解码)