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