Android 音视频深入 九FFmpeg解码视频生成yuv文件(附源码下载)

落花踏尽游何处,笑入胡姬酒肆中。这篇文章主要讲述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.\n" );
return -1;
}
//3.获取视频文件信息
if(avformat_find_stream_info(pFormatCtx,NULL)< 0){
LOGE(" Couldn‘t find stream information.\n" );
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.\n" );
return -1;
}
//只有知道视频的编码方式,才能够根据编码方式去找到解码器
//获取视频流中的编解码上下文
pCodecCtx=pFormatCtx-> streams[videoindex]-> codec;
//4.根据编解码上下文中的编码id查找对应的解码
pCodec=avcodec_find_decoder(pCodecCtx-> codec_id);
if(pCodec==NULL){
LOGE(" Couldn‘t find Codec.\n" );
return -1;
}
if(avcodec_open2(pCodecCtx, pCodec,NULL)< 0){
LOGE(" Couldn‘t open codec.\n" );
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\n" , input_str);
sprintf(info, " %s[Output]%s\n" ,info,output_str);
sprintf(info, " %s[Format]%s\n" ,info, pFormatCtx-> iformat-> name);
sprintf(info, " %s[Codec]%s\n" ,info, pCodecCtx-> codec-> name);
sprintf(info, " %s[Resolution]%dx%d\n" ,info, pCodecCtx-> width,pCodecCtx-> height);
fp_yuv=fopen(output_str,"wb+"); if(fp_yuv==NULL){ printf("Cannot open output file.\n"); 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.\n" );
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);
}
这只是说如何解码视频,还有音频没有解码,但是过程相似,甚至更简单
【Android 音视频深入 九FFmpeg解码视频生成yuv文件(附源码下载)】参考文章:
https://www.cnblogs.com/CoderTian/p/6791638.html

    推荐阅读