zoukankan      html  css  js  c++  java
  • Android 音视频深入 九 FFmpeg解码视频生成yuv文件(附源码下载)

    项目地址,求star

    https://github.com/979451341/Audio-and-video-learning-materials/tree/master/FFmpeg(MP4%E8%BD%ACyuv%EF%BC%89

    这一次是将MP4解码出yuv文件出来,先介绍一波yuv文件


    YUV是指亮度参量和色度参量分开表示的像素格式,而这样分开的好处就是不但可以避免相互干扰,还可以降低色度的采样率而不会对图像质量影响太大。YUV是一个比较笼统地说法,针对它的具体排列方式,可以分为很多种具体的格式。

    直接上主菜,如何将MP4解码出yuv文件

    首先在java使用NDK,调用解码函数,输入MP4文件的路径和输出yuv文件的路径
    decode(inputurl,outputurl);


    接下来就是正式的解说FFmpeg相关函数的作用了

    先来一张图片,理清代码运行顺序

    接下来看注释,。。。。要是每一个函数详细的讲,我做不到,也写不完,重在熟悉流程

    //1.注册所有组件
        av_register_all();
        //初始网络流
        avformat_network_init();
        //封装格式上下文,统领全局的结构体,保存了视频文件封装格式的相关信息
        pFormatCtx = avformat_alloc_context();
        //2.打开输入视频文件
        if(avformat_open_input(&pFormatCtx,input_str,NULL,NULL)!=0){
            LOGE("Couldn't open input stream.
    ");
            return -1;
        }
        //3.获取视频文件信息
        if(avformat_find_stream_info(pFormatCtx,NULL)<0){
            LOGE("Couldn't find stream information.
    ");
            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){
            LOGE("Couldn't find a video stream.
    ");
            return -1;
        }
        //只有知道视频的编码方式,才能够根据编码方式去找到解码器
        //获取视频流中的编解码上下文
        pCodecCtx=pFormatCtx->streams[videoindex]->codec;
        //4.根据编解码上下文中的编码id查找对应的解码
        pCodec=avcodec_find_decoder(pCodecCtx->codec_id);
        if(pCodec==NULL){
            LOGE("Couldn't find Codec.
    ");
            return -1;
        }
        if(avcodec_open2(pCodecCtx, pCodec,NULL)<0){
            LOGE("Couldn't open codec.
    ");
            return -1;
        }
    
        pFrame=av_frame_alloc();
        pFrameYUV=av_frame_alloc();
        out_buffer=(unsigned char *)av_malloc(av_image_get_buffer_size(AV_PIX_FMT_YUV420P,  pCodecCtx->width, pCodecCtx->height,1));
        av_image_fill_arrays(pFrameYUV->data, pFrameYUV->linesize,out_buffer,
                             AV_PIX_FMT_YUV420P,pCodecCtx->width, pCodecCtx->height,1);
    
        //准备读取
        //AVPacket用于存储一帧一帧的压缩数据(H264)
        //缓冲区,开辟空间
        packet=(AVPacket *)av_malloc(sizeof(AVPacket));
    //用于转码(缩放)的参数,转之前的宽高,转之后的宽高,格式等
        img_convert_ctx = sws_getContext(pCodecCtx->width, pCodecCtx->height, pCodecCtx->pix_fmt,
                                         pCodecCtx->width, pCodecCtx->height, AV_PIX_FMT_YUV420P, SWS_BICUBIC, NULL, NULL, NULL);
    
    //输出视频信息
        sprintf(info,   "[Input     ]%s
    ", input_str);
        sprintf(info, "%s[Output    ]%s
    ",info,output_str);
        sprintf(info, "%s[Format    ]%s
    ",info, pFormatCtx->iformat->name);
        sprintf(info, "%s[Codec     ]%s
    ",info, pCodecCtx->codec->name);
        sprintf(info, "%s[Resolution]%dx%d
    ",info, pCodecCtx->width,pCodecCtx->height);
    
    
        fp_yuv=fopen(output_str,"wb+");
        if(fp_yuv==NULL){
            printf("Cannot open output file.
    ");
            return -1;
        }
    
        frame_cnt=0;
        time_start = clock();
    //6.一帧一帧的读取压缩数据
        while(av_read_frame(pFormatCtx, packet)>=0){
            //只要视频压缩数据(根据流的索引位置判断)
            if(packet->stream_index==videoindex){
                //7.解码一帧视频压缩数据,得到视频像素数据
                ret = avcodec_decode_video2(pCodecCtx, pFrame, &got_picture, packet);
                if(ret < 0){
                    LOGE("Decode Error.
    ");
                    return -1;
                }
                //为0说明解码完成,非0正在解码
                if(got_picture){
                    //AVFrame转为像素格式YUV420,宽高
                    //2 6输入、输出数据
                    //3 7输入、输出画面一行的数据的大小 AVFrame 转换是一行一行转换的
                    //4 输入数据第一列要转码的位置 从0开始
                    //5 输入画面的高度
                    sws_scale(img_convert_ctx, (const uint8_t* const*)pFrame->data, pFrame->linesize, 0, pCodecCtx->height,
                              pFrameYUV->data, pFrameYUV->linesize);
                    //输出到YUV文件
                    //AVFrame像素帧写入文件
                    //data解码后的图像像素数据(音频采样数据)
                    //Y 亮度 UV 色度(压缩了) 人对亮度更加敏感
                    //U V 个数是Y的1/4
                    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
                    //Output info
                    char pictype_str[10]={0};
                    switch(pFrame->pict_type){
                        case AV_PICTURE_TYPE_I:sprintf(pictype_str,"I");break;
                        case AV_PICTURE_TYPE_P:sprintf(pictype_str,"P");break;
                        case AV_PICTURE_TYPE_B:sprintf(pictype_str,"B");break;
                        default:sprintf(pictype_str,"Other");break;
                    }
                    LOGI("Frame Index: %5d. Type:%s",frame_cnt,pictype_str);
                    //释放资源
                    frame_cnt++;
                }
            }
            av_free_packet(packet);
        }
    


    这只是说如何解码视频,还有音频没有解码,但是过程相似,甚至更简单

    参考文章:
    https://www.cnblogs.com/CoderTian/p/6791638.html




  • 相关阅读:
    Linux shell read命令
    mysql 详解 01
    centos 6/7 mysql 5.7 修改root密码 mysql root密码找回
    iptables基本原理讲解与规则的增删改查
    nginx反向代理https访问502, nginx反向代理, 支持SNI的https回源,SNI源点,nginx反向代理报错
    nginx 配置 强制访问https
    有名管道FIFO进程间数据传输实例
    命名管道FIFO及其读写规则
    224、Basic Calculator
    (匿名)管道的读写规则
  • 原文地址:https://www.cnblogs.com/jianpanwuzhe/p/8421239.html
Copyright © 2011-2022 走看看