ffmpeg iOS 获取图片

/**
 得到指定时间第一个I帧并且转换成图片
 
 @param url 文件路径
 @param duration 指定时间
 @return 0 : 成功, 其余都是失败
 */
int GetDurationFirstIFrameAndConvertToPic(const char *inFileName, int64_t duration)
{
        // 视频流的格式内容
        AVFormatContext *pFormatCtx = NULL;
        // 读取文件的头部并且把信息保存到我们给的AVFormatContext结构
        if (avformat_open_input(&pFormatCtx, inFileName, NULL, NULL) != 0)
        {
            NSLog(@"打开文件失败");
            return -1;
        }
    
        // 检查在文件中的流的信息
        if (avformat_find_stream_info(pFormatCtx, NULL) < 0)
        {
            NSLog(@"文件中没有流的信息");
            avformat_close_input(&pFormatCtx);
            return -1;
        }
    
        // 打印有关输入或输出格式的详细信息
        av_dump_format(pFormatCtx, 0, inFileName, 0);
    
        int videoStream = -1;
        // 找到第一个视频流
        for (int i = 0; i < pFormatCtx->nb_streams; i++)
        {
            if(pFormatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
            {
                videoStream = i;
                break;
            }
        }
        if (videoStream == -1)
        {
            NSLog(@"没有找到第一个视频流");
            avformat_close_input(&pFormatCtx);
            return -1;
        }
    
        // 流中关于编解码器的信息就是被我们叫做"codec context"(编解码器上下文)的东西。
        // 这里面包含了流中所使用的关于编解码器的所有信息,现在我们有了一个指向他的指针。
        // 但是我们必需要找到真正的编解码器并且打开它
        // 创建编码器上下文
        AVCodecContext *pCodecCtx = avcodec_alloc_context3(NULL);
        if (pCodecCtx == NULL)
        {
            NSLog(@"创建编码器上下文失败");
            avformat_close_input(&pFormatCtx);
            return -1;
        }
    
        if (avcodec_parameters_to_context(pCodecCtx, pFormatCtx->streams[videoStream]->codecpar) < 0)
        {
            NSLog(@"AVCodecContext 赋值失败");
            avcodec_free_context(&pCodecCtx);
            avformat_close_input(&pFormatCtx);
            return -1;
        }
    
        AVCodec *pCodec = avcodec_find_decoder(pCodecCtx->codec_id);
        if (pCodec == NULL)
        {
            NSLog(@"没找到解码器");
            avcodec_free_context(&pCodecCtx);
            avformat_close_input(&pFormatCtx);
            return -1;
        }
    
        // 打开avcodec
        if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0)
        {
            NSLog(@"解码器打开失败");
            avcodec_free_context(&pCodecCtx);
            avformat_close_input(&pFormatCtx);
            return -1;
        }
    
        // YUV帧, 原始帧
        AVFrame *pFrame = av_frame_alloc();
    
        // 因为我们准备输出保存24位RGB色的PPM文件,我们必需把帧的格式从原来的转换为RGB。
        // FFMPEG将为我们做这些转换。
        // 在大多数项目中(包括我们的这个)我们都想把原始的帧转换成一个特定的格式。
        // 让我们先为转换来申请一帧的内存
        AVFrame *pFrameRGB = av_frame_alloc();
    
        // 即使我们申请了一帧的内存,当转换的时候,我们仍然需要一个地方来放置原始的数据。
        // 我们使用avpicture_get_size来获得我们需要的大小,然后手工申请内存空间:
        int numBytes = av_image_get_buffer_size(AV_PIX_FMT_RGB24, pCodecCtx->width, pCodecCtx->height, 1);
        uint8_t *buffer = (uint8_t *)av_malloc(numBytes * sizeof(uint8_t));
    
        // 现在我们使用avpicture_fill来把帧和我们新申请的内存来结合。
        // 关于AVPicture的结成:AVPicture结构体是AVFrame结构体的子集
        // ――AVFrame结构体的开始部分与AVPicture结构体是一样的。
        av_image_fill_arrays(pFrameRGB->data, pFrameRGB->linesize, buffer, AV_PIX_FMT_RGB24, pCodecCtx->width, pCodecCtx->height, 1);
    
        if (pFormatCtx->duration > duration)
        {
            // 播放指定duration的任意帧
            av_seek_frame(pFormatCtx, -1, duration * AV_TIME_BASE, AVSEEK_FLAG_ANY);
            // 清空解码器的缓存
            avcodec_flush_buffers(pCodecCtx);
        }
    
        // 视频像素上下文
        struct SwsContext *sws_ctx = sws_getContext(pCodecCtx->width, pCodecCtx->height, pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height, AV_PIX_FMT_RGB24, SWS_POINT, NULL, NULL, NULL);
    
        // 最后,我们已经准备好来从流中读取数据了。
        // 读取数据
        // 我们将要做的是通过读取包来读取整个视频流,
        // 然后把它解码成帧,最好后转换格式并且保存。
        AVPacket packet;
    
        while (av_read_frame(pFormatCtx, &packet) >= 0)
        {
            // Is this a packet from the video stream?
            if (packet.stream_index == videoStream)
            {
                // Decode video frame
                int result = avcodec_send_packet(pCodecCtx, &packet);
                while (result >= 0)
                {
                    // Did we get a video frame?
                    result = avcodec_receive_frame(pCodecCtx, pFrame);
                    if (result >= 0)
                    {
                        // 将图像从原始格式转换为RGB
                        sws_scale(sws_ctx, (uint8_t const * const *)pFrame->data, pFrame->linesize, 0, pCodecCtx->height, pFrameRGB->data, pFrameRGB->linesize);
    
                        // 帧转成图片,保存在内存
                        image = frameConvertImage(pFrameRGB, pCodecCtx->width, pCodecCtx->height);
    
                        // Save the frame to disk.
    //                    SaveFrameToPPM(pFrameRGB, pCodecCtx->width, pCodecCtx->height);
                        av_packet_unref(&packet);
                        break;
                    }
                }
    
                if (packet.data == NULL)
                    break;
            }
            av_packet_unref(&packet);
        }
    
        sws_freeContext(sws_ctx);
    
        // Free the RGB image.
        av_free(buffer);
        av_frame_free(&pFrameRGB);
    
        // Free the YUV frame.
        av_frame_free(&pFrame);
    
        avcodec_free_context(&pCodecCtx);
    
        // Close the codecs.
        avcodec_close(pCodecCtx);
    
        // Close the video file.
        avformat_close_input(&pFormatCtx);
    
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值