• 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;
    }
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157

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

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

  • 相关阅读:
    解锁学习新方式——助您迈向成功之路
    用户组的概念(linux篇)
    LM1875L-TB5-T 音频功率放大器 PDF中文资料_参数_引脚图
    【GoWeb项目-个人Blog】个人Blog开篇
    C/C++高频面经-秋招篇
    pdf怎么转换成jpg图片或png图片
    编程语言如何推动DeFi成为主流?
    ModuleNotFoundError: No module named ‘pyLDAvis.gensim‘解决方案
    MySQL数据库的MVCC详解
    红包雨中:Redis 和 Lua 的邂逅
  • 原文地址:https://blog.csdn.net/jiaqi_ge/article/details/125915548