avformat_open_input
来打开这个文件并给AVformartcontext
赋值 ,在其中会去查找当前缓存文件的格式 avformat_open_input
来打开这个文件并给AVformartcontext
赋值 ,在其中会去查找当前缓存文件的格式avformat_find_stream_info
使用该方法给每个视频/音频流的AVStream
结构体进行赋值并得到,这个参数在里面实现了一定的解码过程AVstream
有值了以后我们需要拿到当前当前的avcodecContext
和其对应的AVcodeC
(使用avcodec_find_decoder
)avcodec_open2
,初始化一个视音频编解码器的AVCodecContext
,,调用AVCodeC
的初始化到具体的解码器 AVCodeC init()
所以是在avcodec_open2
在开始真正的初始化avcodecContext
AVcodecContext
之后我们就可以开始为解码之后的AVframe
分配空间(使用(unsigned char *)av_mallocz(av_image_get_buffer_size(AV_PIX_FMT_YUV420P, pCodecContex->width, pCodecContex->height, 1)))
av_image_fill_arrays()
:为AVframe
的像素点分配空间sws_getContext
:使用源图像的高和宽得到目标图像的高和宽,flag 为设定图像拉伸的算法av_read_frame
在解码之前来获取一帧的视频帧压缩数据 或者是多帧的音频帧压缩数据 及我们得到的只是AVpacketavcodec_decode_video2
使用该函数来解码得到的AVpacket
,输出一个下一帧的AVframe
函数sws_scale
来对下一帧的AVframe
进行拉伸变化 ,输出想要得到的AVframe
AVformartcontext
,AVstream
,avcodecContext.
下面是代码的具体处理操作:
1.首先需要注册所有的编解码器
//1.注册所有的编解码器等等
av_register_all();
//在需要网络的情况下初始化
avformat_network_init();
//2.初始化AVformartcontext,AVformartcontext 是
pContext=avformat_alloc_context();
//3.得到视频流的URL 地址
//4.尝试打开文件流,在AVformartcontext 中查找当前的AVInputContext的格式
if (avformat_open_input(&pContext, input_str_full, NULL, NULL)!=0) {
NSLog(@"打开文件流失败");
return;
}
avformat_open_input:内部实现就是去打开当前文件并将其赋值给AVformartcontext ,
然后查找到当前AVformartcontext的AVinputcontext的格式,如:flv 等
//5.在AVStream 解码一段视音流信息 ,
if (avformat_find_stream_info(pContext, NULL)<0){
NSLog(@"解码得到AVstream流信息失败");
return;
}
avformat_find_stream_info:该函数主要是给每个视频/音频流的AVStream 结构体进行赋值
其实在他内部本身实现了解码的整个流程:查找解码器->打开解码器->读取完整的一帧压缩编码数据->解码压缩编码数据得到信息
//6.拿到当前avformatcontext ->AVstream 的AVCodecContext 视频流
int videoIndex=-1;
for (int i=0; inb_streams; i++) {
if (pContext->streams[i]->codec->codec_type==AVMEDIA_TYPE_VIDEO) {
videoIndex=i;
}
}
if (videoIndex==-1) {
NSLog(@"当前的数据是错误的");return;
}
//拿到视频流的上下文
AVCodecContext *pCodecContex=pContext->streams[videoIndex]->codec;
//7.通过avcodec_find——decoder 去找寻当前id 下的AVcodeC 解码器
AVCodec *pCodec=avcodec_find_decoder(pCodecContex->codec_id);
if (pCodec==NULL) {
NSLog(@"找不到AVCodeC");
return;
}
//8.初始化一个视音频编解码器的AVCodecContext,,调用AVCodeC的初始化到具体的解码器 AVCodeC init() 所以是在
avcodec_open2在开始真正的初始化avcodecContext
if (avcodec_open2(pCodecContex, pCodec, NULL)!=0 ) {
NSLog(@"打开codeC错误");
return;
}
avcodec_open2:参数为初始化一个codecContext 目标解码器 AVdictionary
//创建一个AVframe 就是用来预测下一帧的视频帧,使用avcodec_decode_video2 预测下一帧
AVFrame *pFrame;
pFrame=av_frame_alloc();
AVFrame *pFrameYUV;
pFrameYUV=av_frame_alloc();
uint8_t *outBuffer;
//9 pFrameYUV分配空间,该函数并没有为AVFrame的像素数据分配空间,需要使用av_image_fill_arrays分配
outBuffer=(unsigned char *)av_mallocz(av_image_get_buffer_size(AV_PIX_FMT_YUV420P, pCodecContex->width,
pCodecContex->height, 1));
//给目标的AVframe 来分配空间
av_image_fill_arrays(pFrameYUV->data, pFrameYUV->linesize, outBuffer, AV_PIX_FMT_YUV420P, pCodecContex->width,
pCodecContex->height, 1);
//10.给AVFrame 分配完了地址空间并给 像素点添加进去之后 需要是用libswscale 包&来进行图像的格式转换和图像的拉伸
AVPacket *packet=(AVPacket *)av_malloc(sizeof(AVPacket));
struct SwsContext *img_conver_ctx;
/**
* Allocate and return an SwsContext. You need it to perform
* scaling/conversion operations using sws_scale().
*
* @param srcW the width of the source image
* @param srcH the height of the source image
* @param srcFormat the source image format
* @param dstW the width of the destination image
* @param dstH the height of the destination image
* @param dstFormat the destination image format
* @param flags specify which algorithm and options to use for rescaling
* @return a pointer to an allocated context, or NULL in case of error
* @note this function is to be removed after a saner alternative is
* written
*/
img_conver_ctx= sws_getContext(pCodecContex->width, pCodecContex->height, pCodecContex->pix_fmt, pCodecContex->width, pCodecContex->height, AV_PIX_FMT_YUV420P, SWS_BICUBIC, NULL, NULL, NULL);
sws_getContext:从sws_getContext()的定义中可以看出,它首先调用了一个函数sws_alloc_context()用于给SwsContext分配内存。然后将传入的源图像,目标图像的宽高,像素格式,以及标志位分别赋值给该SwsContext相应的字段。最后调用一个函数sws_init_context()完成初始化工作
int frame_cnt;
//用来计算处理事件用了多久的时间
clock_t time_start ,timeFinsh;
time_start=clock();
int y_size;
int got_picture, ret;
FILE *fp_yuv;
fp_yuv=fopen(output_str_full, "wb+");
if (fp_yuv==NULL) {
NSLog(@"不能打开该地址下的文件");
return;
}
//11.解码之前都要先用 av_read_frame()获得一帧视频的压缩数据,或者是若干帧的音频数据,
while (av_read_frame(pContext, packet)>=0) {
if (packet->stream_index==videoIndex) {
//avcodec_decode_video2()的作用是解码一帧视频数据。输入一个压缩编码的结构体AVPacket,输出一个解码后的结构体AVFrame
ret=avcodec_decode_video2(pCodecContex, pFrame, &got_picture, packet);
if (ret<0) {
NSLog(@"解码下一帧失败");
return;
}
if (got_picture) {
//对图像进行拉伸等处理,这里是不进行拉伸变换
sws_scale(img_conver_ctx, (const uint8_t* const* )pFrame->data, pFrame->linesize, 0, pCodecContex->height, pFrameYUV->data, pFrameYUV->linesize);
y_size=pCodecContex->width*pCodecContex->height;
fwrite(pFrameYUV->data[0], 1, y_size, fp_yuv);
fwrite(pFrameYUV->data[1], 1, y_size/4, fp_yuv);
fwrite(pFrameYUV->data[2], 1, y_size/4, fp_yuv);
char picture_type_str[10]={0};
switch (pFrame->pict_type) {
case AV_PICTURE_TYPE_I:sprintf(picture_type_str,"I");break;
case AV_PICTURE_TYPE_P:sprintf(picture_type_str,"P");break;
case AV_PICTURE_TYPE_B:sprintf(picture_type_str,"B");break;
default:sprintf(picture_type_str,"Other");break;
}
NSLog(@"frame index:%.5d type:%s",frame_cnt,picture_type_str);
frame_cnt++;
}
}
av_free_packet(packet);
}
while (1) {
ret=avcodec_decode_video2(pCodecContex, pFrame, &got_picture, packet);
if (ret<0) {
return;
}
if (!got_picture) {
break;
}
sws_scale(img_conver_ctx, (const uint8_t* const*)pFrame->data, pFrame->linesize, 0, pCodecContex->height, pFrameYUV->data, pFrameYUV->linesize);
int y_size=pCodecContex->width*pCodecContex->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;
}
printf("Frame Index: %5d. Type:%s\n",frame_cnt,pictype_str);
frame_cnt++;
}
timeFinsh=clock();
double time_duration=timeFinsh-time_start;
NSLog(@"这个过程持续的时间是%f",time_duration);
sws_freeContext(img_conver_ctx);
fclose(fp_yuv);
av_frame_free(&pFrameYUV);
av_frame_free(&pFrame);
avcodec_close(pCodecContex);
avformat_close_input(&pContext);