一个星期的努力终于搞定了视频的播放,利用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. "); 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. "); 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. "); exit(1); } pCodecCtx=pFormatCtx->streams[videoindex]->codec; pCodec=avcodec_find_decoder(pCodecCtx->codec_id); if(pCodec==NULL) { printf("Codec not found. "); 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. "); 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: "); 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. "); 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 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