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

来源:互联网 发布:建筑学需要用的软件 编辑:程序博客网 时间:2024/06/05 01:11

最近学习了如和使用 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);}






0 0