FFmpeg 解碼本地視訊並實現播放功能
阿新 • • 發佈:2019-01-04
本文寫於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,i<pFormartContext->nb_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();