本文写于17年,FFMpeg 版本是3.0,demo在我的github可见:FFmpeg_decoder
av_registerall();
avformart_network_init();
AVDictionary *opts = NULL;
av_dict_set(&opts,"rtsp_transport","udp",0);
av_dict_set(&opts,"max_delay","0.3",0);//av_dict_set函数是实现将字典进行赋值操作的方式
if (avformat_open_input(&pFormartContext, [url UTF8String], inputFormart, &opts)!=0) {
NSLog(@"打开文件路径失败");
return nil;
}
if(avformart_find_stream(pFormartContext,&opt)<0){
NSLog(@"解码失败,拿不到formart信息");
//其实在avformart 操作的时候已经进行了一部分的解码操作
return;
}
videoStream=-1;
for(int i=0,inb_streams;i++){
if(pFormartContext->streams[i]->codecpar->codec_type==AVMEDIA_TYPE_VIDEO){
videoStream=i;
//在这里找到需要的视频流AVStream
}
}
if(videoStream=-1){
NSLog(@"上下文中不包含视频流");
return;
}
pcodecPar=pFormartConetext->streams[videoStream]->codecpar;//AVCodecParameters 是新的ffmpeg 的参数
//实例化codecContext
codeC=avcodec_find_decoder(pcodecPar->codec_id);
if(codeC==null){
NSLog(@"没有找到解码器");
return;
}
pCodecContext=avcodec_alloc_context3(codeC);
//对codecContext进行上下文的填充
if(avcodec_parameters_to_context(pCodecContext, pCodecPar)<0)return;
//对avframe 进行实例化
pframe=av_frame_alloc();
pRGBFrame=av_frame_alloc();
//开始正式的解码操作
if (avcodec_open2(pCodecContext, codeC, &opts)<0) {
NSLog(@"打开codeCcontext 失败");
return nil;
}
-(BOOL)displayNextFrame{
int frameFinshed=0;
if (pFormartContext==NULL) {
NSLog(@"源数据为空");
return false;
}
while (!frameFinshed&&av_read_frame(pFormartContext, &pPackct)>=0) {
if (pPackct.stream_index==videoStream) {
//开始解码视频 如果可写的话会给frameFinshed 赋值为1
avcodec_decode_video2(pCodecContext, pFrame, &frameFinshed, &pPackct);
}
}
if (frameFinshed == 0 && isReleaseResources == NO) {
[self releaseResources];
}
return frameFinshed!=0;
}
图像构建
-(UIImage *)imageFromAVframe{
struct swsContext *img_covert_ctx;
/**
* @param 源数据image的宽
* @param 源数据image的高
* @param 源数据的像素构成格式
* @param 目标图像的宽
* @param 目标图像的高
* @param 目标转化的图像格式
* @param 使用的转化算法
*/
img_conver_ctx=sws_getContext(pCodecContext->width,pCodecContext->height,pCodecContext->pix_fmt,_outputWidth,_outputHeight,AV_PIX_FMT_RGB24,SWS_FAST_BILINEAR,NULL,NULL,NULL);
if(img_conver_ctx==null){
NSLog(@"构建图像失败");
return nil;
}
//使用av_image_get_buffer_size来得到解码图像的缓存区
uint8_t *out_buffer=(unsigned char *)av_malloc(av_image_get_buffer_size(AV_PIX_FMT_RGB24, pCodecContext->width, pCodecContext->height, 1));
//填充数据
/**
* @param 想要用来存储的data
* @param 目标的linesize
* @param buffer需要占用的空间,可以为NULL
* @param pix_fmt 目标的像素格式
* @param 目标的宽度
* @param 目标的高度
* @param 1
*/
av_image_fill_arrays(pYUVFrame->data, pYUVFrame->linesize, out_buffer, AV_PIX_FMT_RGB24, pCodecContext->width, pCodecContext->height,1);
//实现对图像的转化
sws_scale(img_convert_ctx, (const uint8_t**)pFrame->data, pFrame->linesize, 0, pCodecContext->height, pYUVFrame->data, pYUVFrame->linesize);
//释放掉占用的内存空间
sws_freeContext(img_convert_ctx);
av_free(out_buffer);
CGBitmapInfo bitmapInfo = kCGBitmapByteOrderDefault;
//将avframe转化成cgImage->UIImage 在不同的格式的AVFrame 而言有不同的linesize 具体的没有研究过
CFDataRef data = CFDataCreate(kCFAllocatorDefault,
pYUVFrame->data[0],
pYUVFrame->linesize[0] * _outputHeight);
CGDataProviderRef provider = CGDataProviderCreateWithCFData(data);
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
NSLog(@"myWidth%d----%d",_outputWidth,_outputHeight);
CGImageRef cgImage = CGImageCreate(_outputWidth,
_outputHeight,
8,
24,
pYUVFrame->linesize[0],
colorSpace,
bitmapInfo,
provider,
NULL,
NO,
kCGRenderingIntentDefault);
UIImage *image = [UIImage imageWithCGImage:cgImage];
CGImageRelease(cgImage);
CGColorSpaceRelease(colorSpace);
CGDataProviderRelease(provider);
CFRelease(data);
return image;
}
总结一下
使用ffmpeg3.0+ 的时候发现有一些方法被废弃下面是我总结遇到的api
AVCodedecContext *pCodecContex=pContext->streams[videoIndex]->codec; //废弃了
pcodecPar=pFormartConetext->streams[videoStream]->codecpar;
//找到AVCodecParameters->找到codeC->使用codeC来初始化avcodec_context
avcodec_decode_video2 转换成了avcodec_send_packet 和avcodec_receive_frame 具体实际使用 暂时并未成功
AVpicture 被废弃,需要使用av_image_fill_arrays();