Android开发Android技术知识Android开发

part03_牛刀小试_视频解码原理-使用ffmpeg将MP4转

2018-05-11  本文已影响201人  IT魔幻师

一、视频基础知识

二、解码流程

三、小试牛刀,将mp4文件转换为yuv格式的视频文件

    //ffmpeg都是使用C语言编写,如果是用C++引入头文件时记得加上extern "C"
    extern "C" {
    //编码
    #include "libavcodec/avcodec.h"
    //封装格式处理
    #include "libavformat/avformat.h"
    //像素处理
    #include "libswscale/swscale.h"
    }


    /**
     *将一个MP4格式的视频转码成yuv420p格式文件
     *inputStr:MP4文件路径
     *outStr:转码后的输出路径
     */
    void mp4toYUV420p(char *inputStr,char *outStr){
     // 1.注册各大组件,执行ffmgpe都必须调用此函数
        av_register_all();
        //2.得到一个ffmpeg的上下文(上下文里面封装了视频的比特率,分辨率等等信息...非常重要)
        AVFormatContext *pContext = avformat_alloc_context();
        //3.打开一个视频
        if(avformat_open_input(&pContext, inputStr, NULL, NULL)<0) {
            LOGE("打开失败");
            return;
        }
        //4.获取视频信息(将视频信息封装到上下文中)
        if (avformat_find_stream_info(pContext, NULL) < 0) {
            LOGE("获取信息失败");
            return;
        }
        //5.用来记住视频流的索引
        int vedio_stream_idx=-1;
        //从上下文中寻找找到视频流
        for (int i = 0; i < pContext->nb_streams; ++i) {
            LOGE("循环  %d", i);
            //codec:每一个流 对应的解码上下文
            //codec_type:流的类型
            if (pContext->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
                //如果找到的流类型 == AVMEDIA_TYPE_VIDEO 即视频流,就将其索引保存下来
                vedio_stream_idx = i;
            }
        }
    
        //获取到解码器上下文
        AVCodecContext* pCodecCtx=pContext->streams[vedio_stream_idx]->codec;
        //获取解码器
        AVCodec *pCodex=avcodec_find_decoder(pCodecCtx->codec_id);
    
        //6.打开解码器。 (ffempg版本升级名字叫做avcodec_open2)
        if (avcodec_open2(pCodecCtx, pCodex, NULL) < 0) {
            LOGE("解码失败");
            return;
        }
        //----------------------解码前准备--------------------------------------
        //准备开始解码时需要一个AVPacket存储数据(通过av_malloc分配内存)
        AVPacket *packet = (AVPacket *) av_malloc(sizeof(AVPacket));
        av_init_packet(packet);//初始化结构体
    
        //解封装需要AVFrame
        AVFrame *frame = av_frame_alloc();
        //声明一个yuvframe的缓冲区
        AVFrame *yuvFrame = av_frame_alloc();
        //给yuvframe  的缓冲区 初始化
        uint8_t  *out_buffer= (uint8_t *) av_malloc(avpicture_get_size(AV_PIX_FMT_YUV420P, pCodecCtx->width, pCodecCtx->height));
        //给缓冲区进行替换
        int re=avpicture_fill((AVPicture *) yuvFrame, out_buffer, AV_PIX_FMT_YUV420P, pCodecCtx->width, pCodecCtx->height);
        LOGE("宽 %d  高 %d",pCodecCtx->width,pCodecCtx->height);
    
    
        //格式转码需要的转换上下文(根据封装格式的宽高和编码格式,以及需要得到的格式的宽高)
        //pCodecCtx->pix_fmt :mp4的上下文
        //AV_PIX_FMT_YUV420P : 目标格式
        //SWS_BILINEAR :标准格式 无损耗
        //NULL,NULL,NULL : 过滤器等
        SwsContext *swsContext=sws_getContext(pCodecCtx->width,pCodecCtx->height,pCodecCtx->pix_fmt,
                                              pCodecCtx->width,pCodecCtx->height,AV_PIX_FMT_YUV420P
                ,SWS_BILINEAR,NULL,NULL,NULL
        );
        int frameCount = 0;
    
        //转换为YUV后输出到一个文件中
        FILE *fp_yuv = fopen(outStr, "wb");
    
    
        //------------------------一桢一帧开始解码--------------------
        int got_frame;
        while (av_read_frame(pContext, packet) >= 0) {//开始读每一帧的数据
            //7.解封装(将packet解压给frame,即:拿到了视频数据frame)
            avcodec_decode_video2(pCodecCtx, frame, &got_frame, packet);//解封装函数
            LOGE("解码%d  ",frameCount++);
    
            if (got_frame > 0) {
                //8.转码(转码上下文,原数据,一行数据,开始位置,yuv的缓冲数组,yuv一行的数据)
                sws_scale(swsContext, (const uint8_t *const *) frame->data, frame->linesize, 0, frame->height, yuvFrame->data,
                          yuvFrame->linesize
                );
                //计算图像的总大小
                int y_size = pCodecCtx->width * pCodecCtx->height;
                //写入yuv格式信息
                fwrite(yuvFrame->data[0], 1, y_size, fp_yuv);//写入亮度信息Y
                fwrite(yuvFrame->data[1], 1, y_size/4, fp_yuv);//写入u
                fwrite(yuvFrame->data[2], 1, y_size/4, fp_yuv);//写入
            }
            av_free_packet(packet);v//释放
        }
        fclose(fp_yuv);//关闭文件流
        av_frame_free(&frame);
        av_frame_free(&yuvFrame);
        avcodec_close(pCodecCtx);
        avformat_free_context(pContext);
    
        free(inputStr);
        free(outStr);
    }
上一篇 下一篇

猜你喜欢

热点阅读