实战FFmpeg + OpenGLES--iOS平台上视频解码和播放

时间:2022-09-10 17:48:28

  一个星期的努力终于搞定了视频的播放,利用FFmpeg解码视频,将解码的数据通过OpenGLES渲染播放。搞清楚了自己想知道的和完成了自己的学习计划,有点小兴奋。明天就是“五一”,放假三天,更开心啦。

  本文实现视频文件的播放是在自己之前写的文章实战FFmpeg--iOS平台使用FFmpeg将视频文件转换为YUV文件 、 实战OpenGLES--iOS平台使用OpenGLES渲染YUV图片 的基础上改进合成来完成的。不多种解释,直接上代码,清晰明了。

NSString *path = [[NSBundle mainBundle] pathForResource:@"nwn" ofType:@"mp4"];
    const char *filepath= [path UTF8String];
    
    av_register_all();
    avformat_network_init();
    pFormatCtx = avformat_alloc_context();
    if(avformat_open_input(&pFormatCtx,filepath,NULL,NULL)!=0){
        printf("Couldn't open input stream.\n");
        exit(1);
    }
    
    /*
     * av_find_stream_info():ffmpeg版本更新后没有这个函数了,用avformat_find_stream_info这个函数,可以自己看一下版本更新改动
     */
    //    if(av_find_stream_info(pFormatCtx)<0)
    if(avformat_find_stream_info(pFormatCtx, NULL) < 0)
    {
        printf("Couldn't find stream information.\n");
        exit(1);
    }
    
    videoindex=-1;
    for(int i=0; i<pFormatCtx->nb_streams; i++)
        if(pFormatCtx->streams[i]->codec->codec_type==AVMEDIA_TYPE_VIDEO)
        {
            videoindex=i;
            break;
        }
    if(videoindex==-1)
    {
        printf("Didn't find a video stream.\n");
        exit(1);
    }
    pCodecCtx=pFormatCtx->streams[videoindex]->codec;
    pCodec=avcodec_find_decoder(pCodecCtx->codec_id);
    if(pCodec==NULL)
    {
        printf("Codec not found.\n");
        exit(1);
    }
    /*
     * avcodec_open():ffmpeg版本更新后没有这个函数了,用avformat_find_stream_info这个函数,可以自己看一下版本更新改动
     * avcodec_open2(AVCodecContext *avctx, const AVCodec *codec, AVDictionary **options)
     */
    //    if(avcodec_open(pCodecCtx, pCodec)<0)
    if(avcodec_open2(pCodecCtx, pCodec, NULL)<0)
    {
        printf("Could not open codec.\n");
        exit(1);
    }
    AVFrame    *pFrame,*pFrameYUV;
    pFrame=avcodec_alloc_frame();
    pFrameYUV=avcodec_alloc_frame();
    uint8_t *out_buffer;
    out_buffer=new uint8_t[avpicture_get_size(PIX_FMT_YUV420P, pCodecCtx->width, pCodecCtx->height)];
    avpicture_fill((AVPicture *)pFrameYUV, out_buffer, PIX_FMT_YUV420P, pCodecCtx->width, pCodecCtx->height);
    
    int ret, got_picture;
    int y_size = pCodecCtx->width * pCodecCtx->height;
    
    AVPacket *packet=(AVPacket *)malloc(sizeof(AVPacket));
    av_new_packet(packet, y_size);
    
    printf("video infomation:\n");
    av_dump_format(pFormatCtx,0,filepath,0);
    
    while(av_read_frame(pFormatCtx, packet)>=0)
    {
        if(packet->stream_index==videoindex)
        {
            ret = avcodec_decode_video2(pCodecCtx, pFrame, &got_picture, packet);
            if(ret < 0)
            {
                printf("Decode Error.\n");
                exit(1);
            }
            if(got_picture)
            {
                char *buf = (char *)malloc(pFrame->width * pFrame->height * 3 / 2);
        
                AVPicture *pict;
                int w, h;
                char *y, *u, *v;
                pict = (AVPicture *)pFrame;//这里的frame就是解码出来的AVFrame
                w = pFrame->width;
                h = pFrame->height;
                y = buf;
                u = y + w * h;
                v = u + w * h / 4;
                
                for (int i=0; i<h; i++)
                    memcpy(y + w * i, pict->data[0] + pict->linesize[0] * i, w);
                for (int i=0; i<h/2; i++)
                    memcpy(u + w / 2 * i, pict->data[1] + pict->linesize[1] * i, w / 2);
                for (int i=0; i<h/2; i++)
                    memcpy(v + w / 2 * i, pict->data[2] + pict->linesize[2] * i, w / 2);
                
                [myview setVideoSize:pFrame->width height:pFrame->height];
                [myview displayYUV420pData:buf width:pFrame->width height:pFrame->height]; //利用OpenGLES渲染YUV
                free(buf);
            }
        }
        av_free_packet(packet);
    }
    delete[] out_buffer;
    av_free(pFrameYUV);
    avcodec_close(pCodecCtx);
    avformat_close_input(&pFormatCtx);

  本周的学习任务完成啦,写完这篇博客就下班回家,哈哈。五一长假,我要休息下,列出自己下一步的学习计划。下周我想做的是本地音频文件解码播放,以及本地视频文件中音频与视频同步播放。现在所做的都是本地音视频文件的处理,因为本地文件处理起来方便一些,这也让自己学起来也快速一些。关于网络音视频实时流的同步与播放,这个涉及的内容就要复杂一些了,有效学习+时间规划,我肯定能玩透播放器的开发。

  本文完整工程 FFmpegDecode_OpenGLESRenderYUV 的下载地址为:http://pan.baidu.com/s/1dDvpECh