使用ffmpeg进行解码的基本流程 和几个重要函数

最近学习了如和使用 ffmpeg进行解码视频和音频 以及转码等 


其实在使用ffmpeg的流程基本都是相同的 

1. 将输入的文件转为常量字符(音频或者视频文件)

2.注册ffmpeg的组件(在这里可以通过使用av_regiest_all()来进行偷懒操作,将所有的组件都进行注册)

3.注册晚组件之后就开始封装全局的上下文AVFormatContext  (使用avformat_aloc_context方法获取)

4.开始检查能否打开视频或音频文件 ,检测是否可以读取音视频文件信息,查找视频流,检测编码器是否可以打开 等等 检测性操作   

--avformat_open_input :该方法用于检测是否可以打开音视频文件

--avformat_find_stream_info:该方法用于查看音视频信息

--通过遍历所有的流 并判断流的类型可以寻找到视频流的位置

--通过编码id 查找 该音视频的编码方式进行对应的解码 

--avcodec_open2 :该方法可以用于检测能否打开解码器


5.检查完所有的硬性条件之后,便开始一帧一帧的解码   并可以在这里对解码的内容进行操作(如转码,绘制到空间上等)

6.关闭,释放所有的指针变量等

7.返回所需的内容




示例:该示例实在android studio 下进行转码操作的一个示例



JNIEXPORT void JNICALL Java_utils_VideoUtils_decode(JNIEnv *env, jclass jcls, jstring input_jstr, jstring output_jstr) {

    //1.将视频文件输入
    //需要转码的视频文件(输入的视频文件)
    //讲视频文件转换为常量字符
    __const char* input_cstr =(env)->GetStringUTFChars(input_jstr,NULL);
    __const char* output_cstr=(env)->GetStringUTFChars(output_jstr,NULL);


    //2.注册所有的组件
    av_register_all();


    //3.封装格式上下文,统领全局的结构体  类似于applicationContext  在这里保存了视频文件封装的相关信息
    AVFormatContext  *formatContext =avformat_alloc_context();


    //4.打开输入视频文件
    if(avformat_open_input(&formatContext,input_cstr,NULL,NULL)!=0){
        LOGE("%s","无法打开输入视频文件");
        return;
    }

    //5.获取视频文件信息
    if(avformat_find_stream_info(formatContext,NULL)<0){
        LOGE("%s","无法获取视频文件信息");
        return;
    }

    //6.获取视频流的索引位置   ------遍历所有类型的流 找到视频流
    int  v_stream_idx=-1;
    int i=0;
    for(;i<formatContext->nb_streams;i++){
        //比对是否是视频流
        //if(formatContext->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO){  在这里  我的  AVMEDIA_TYPE_VIDEO 视频流的type 类型 不被识别   原因  缺少改头文件   avutil.h
        if(formatContext->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO){
            v_stream_idx=i;
            break;
        }
    }


    if(v_stream_idx==-1){
        LOGE("%s","找不到视频流");
        return;
    }
    //7.获取视频流的编码
    //无解的奇怪bug  无法定义AVCodecContext 指针 强行不使用该指针变量进行后续操作
    //AVCodecContext *avcodeContext =formatContext->streams[v_stream_idx]->codec;
    //AVCodec *pCodec = avcodec_find_decoder(avcodeContext->codec_id);


    //存储编解码器信息的结构体
    AVCodec *avCodec=avcodec_find_decoder(formatContext->streams[v_stream_idx]->codec->codec_id);

    if((formatContext->streams[v_stream_idx]->codec)==NULL){
        LOGE("%s","找不到解码器");
        return;
    }

    //8 打开解码器
    if(avcodec_open2((formatContext->streams[v_stream_idx]->codec),avCodec,NULL)<0){
        LOGE("%s","解码器打不开");
        return;
    }



    //输出一下 解码器的信息
    //LOGI("视频的文件格式:%s",formatContext->iformat->name);
    //LOGI("视频时长:%d",(formatContext->duration)/1000);
    //LOGI("视频的宽高:%d",formatContext->streams[v_stream_idx]->codec->width,formatContext->streams[v_stream_idx]->codec->height);
    //LOGI("解码器的名称:%s",avCodec->name);



    //9开辟缓冲区   分配内存
    AVPacket *packet = (AVPacket*)av_malloc(sizeof(AVPacket));
    //内存
    AVFrame * avFrame=av_frame_alloc();
    //YUV
    AVFrame *avFrameYUV =av_frame_alloc();
    //缓冲区分配内存
    uint8_t *out_buffer= (uint8_t *) av_malloc(avpicture_get_size(AV_PIX_FMT_YUV420P, formatContext->streams[v_stream_idx]->codec->width, formatContext->streams[v_stream_idx]->codec->height));
    //初始化缓冲区
    avpicture_fill((AVPicture *) avFrameYUV, out_buffer, AV_PIX_FMT_YUV420P, formatContext->streams[v_stream_idx]->codec->width, formatContext->streams[v_stream_idx]->codec->height);
    //设置用于转码的参数    转之前的 宽  高    格式   转之后的宽高  等
    struct SwsContext *sws_ctx=sws_getContext(formatContext->streams[v_stream_idx]->codec->width,
                                              formatContext->streams[v_stream_idx]->codec->height,
                                              formatContext->streams[v_stream_idx]->codec->pix_fmt,
                                              formatContext->streams[v_stream_idx]->codec->width,
                                              formatContext->streams[v_stream_idx]->codec->width,
                                              AV_PIX_FMT_YUV420P,SWS_BICUBIC,NULL,NULL,NULL);



    int got_picture, ret;
    FILE *wb=fopen(output_cstr, "wb+");
    //用于记录第多少帧  的变量
    int frame_count = 0;

    //10  开始一帧一帧读取压缩数据
    while(av_read_frame(formatContext,packet)>=0){
        //判断 如果是视频压缩数据 (通过判断视频流的索引位置)
        if(packet->stream_index==v_stream_idx){
            //开始解码(一帧)  根据返回值ret 进行判断  小于0  出错  等于0 解码完成  大于0正在解码
            //avPack 解码
            ret=avcodec_decode_video2(formatContext->streams[v_stream_idx]->codec,avFrame,&got_picture,packet);
            if(ret<0){
               LOGE("%s","解码出错");
               return;
            }

            if(got_picture){
             sws_scale(sws_ctx, (const uint8_t *const *) avFrame->data, avFrame->linesize, 0, formatContext->streams[v_stream_idx]->codec->height, avFrameYUV->data, avFrameYUV->linesize);

             //YUV  输出 写入文件
             //几选像素点   宽乘以高
             int y_size=formatContext->streams[v_stream_idx]->codec->width*formatContext->streams[v_stream_idx]->codec->height;
              //写入Y
              fwrite(avFrameYUV->data[0],1,y_size,wb);
               //写入U
              fwrite(avFrameYUV->data[1],1,y_size,wb);
                //写入V
              fwrite(avFrameYUV->data[2],1,y_size,wb);

              frame_count++;
              LOGI("解码第%d帧",frame_count);
            }
        }
        //释放资源
        av_free_packet(packet);
    }

    //关闭文件流  上下文引用等
    fclose(wb);
    avcodec_close(formatContext->streams[v_stream_idx]->codec);
    avformat_free_context(formatContext);

    //返回所需的数据
    env->ReleaseStringChars(input_jstr, (const jchar *) input_cstr);
    env->ReleaseStringChars(output_jstr, (const jchar *) output_cstr);
}






评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值