zoukankan      html  css  js  c++  java
  • 实战FFmpeg--iOS平台使用FFmpeg将视频文件转换为YUV文件

      做播放器的开发这里面涉及的东西太多,我只能一步步往前走,慢慢深入。播放器播放视频采用的是渲染yuv文件。首先,要知道yuv文件是怎么转换得来的,其次,要知道怎么把视频文件保存为yuv文件。雷神的文章100行代码实现最简单的基于FFMPEG+SDL的视频播放器(SDL1.x) 让我获益匪浅。以读取工程中的本地文件为例,视频文件使用雷神 100行代码实现最简单的基于FFMPEG+SDL的视频播放器(SDL1.x) 中用到的 nwn.mp4 。

      因为ffmpeg是c语言库,所以100行代码实现最简单的基于FFMPEG+SDL的视频播放器(SDL1.x)中的方法步骤可以直接搬过来用,这篇文章使用的开发环境是VS2010,这个不管,放手去干,把其中的关键代码摘取出来就欧了。但是,要注意新旧ffmpeg库,在新的库中一些老的方法被替换了,所以在搬运雷神文章中的代码的时候,要注意使用新的ffmpeg库中的方法。

      开发环境:Mac OS X 10.10.2, Xcode 6.3,iOS SDK 8.3, FFmpeg 2.0

      FFmpeg 2.0:如何编译FFmpeg 2.0库,可以参看我之前写的文章:实战FFmpeg--编译iOS平台使用的FFmpeg库

      废话不多说,上代码。

      

    #import "ViewController.h"
    
    @interface ViewController ()
    
    @end
    
    @implementation ViewController
    {
        AVFormatContext    *pFormatCtx;
        int                i, videoindex;
        AVCodecContext    *pCodecCtx;
        AVCodec            *pCodec;
        FILE            *fp_yuv;
    }
    - (void)viewDidLoad {
        [super viewDidLoad];
        // Do any additional setup after loading the view, typically from a nib.
        self.view.backgroundColor = [UIColor blueColor];
    
        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(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;
        static struct SwsContext *img_convert_ctx;
        int y_size = pCodecCtx->width * pCodecCtx->height;
        
        AVPacket *packet=(AVPacket *)malloc(sizeof(AVPacket));
        av_new_packet(packet, y_size);
        
        printf("视频文件信息:
    ");
        av_dump_format(pFormatCtx,0,filepath,0);
        
        //获得应用程序沙盒的Documents目录
        NSString* doc_path = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0];
        //创建文件管理器对象
        NSString* filename = [doc_path stringByAppendingPathComponent:@"output.yuv"];
        //创建目录
        NSFileManager *fileManager = [NSFileManager defaultManager];
        if ( ![fileManager fileExistsAtPath:filename]) {
            [fileManager createFileAtPath:filename contents:nil attributes:nil];
        }
        
        const char *fileYUV = [filename UTF8String];
        /*
         * fopen的参数rb+和wb+都是操作可读可写的二进制文件
           区别是
           对于rb+如果打开的文件不存在 会报错(返回NULL)
           对于wb+ 如果文件不存在则会建立,如果文件存在 会覆盖
         */
        fp_yuv=fopen(fileYUV,"rb+"); //
        
        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)
                {
                    img_convert_ctx = sws_getContext(pCodecCtx->width, pCodecCtx->height, pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height, PIX_FMT_YUV420P, SWS_BICUBIC, NULL, NULL, NULL);
                    sws_scale(img_convert_ctx, (const uint8_t* const*)pFrame->data, pFrame->linesize, 0, pCodecCtx->height, pFrameYUV->data, pFrameYUV->linesize);
                    
                    int y_size=pCodecCtx->width*pCodecCtx->height;
                    fwrite(pFrameYUV->data[0],1,y_size,fp_yuv);    //Y
                    fwrite(pFrameYUV->data[1],1,y_size/4,fp_yuv);  //U
                    fwrite(pFrameYUV->data[2],1,y_size/4,fp_yuv);  //V
                    printf("将视频数据写入文件");
                    sleep(1);
                }
            }
            av_free_packet(packet);
        }
        delete[] out_buffer;
        av_free(pFrameYUV);
        avcodec_close(pCodecCtx);
        avformat_close_input(&pFormatCtx);
        fclose(fp_yuv);
    }

      就这么简单,呵呵,知道了视频文件是如何转换为yuv文件的,以及如何将yuv数据按照yuv格式写入到文件并保存。

      得到了yuv文件,就在想yuv文件是如何被OpenGLES渲染,呈现为视频画面的呢,呵呵,这个我将在后续的关于OpenGLES的文章中提到。

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

  • 相关阅读:
    Android面试题
    java面试题大全
    关于索引的sql语句优化之降龙十八掌
    java动态代理的实现
    java动态代理
    进程与线程
    SqlServer聚合函数
    2015年创业中遇到的技术问题:21-30
    hadoop集群ambari搭建(2)之制作hadoop本地源
    Android录屏命令、Android录Gif、Android录视频
  • 原文地址:https://www.cnblogs.com/sunminmin/p/4466448.html
Copyright © 2011-2022 走看看