1. 程式人生 > >FFmpeg 解碼本地視訊並實現播放功能

FFmpeg 解碼本地視訊並實現播放功能

本文寫於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();