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();

猜你喜欢

转载自blog.csdn.net/Abe_liu/article/details/85706097