• 在CB2010中调用ffmpeg(5)


    1. 视频编码

    今天的目的在于将视频文件中的帧取出来,然后保存。

    可以在上一次的基础上进行。最终代码如下。

    void __fastcall TFFMPEGTestForm::Button1Click(TObject *Sender) { // 取得文件信息

        const char *fileName = "d:\test.mp4";

        AVFormatContext * pFormatCtx = NULL;

        // 读取文件的头部并且把信息保存到AVFormatContext结构体中

        int err = avformat_open_input(&pFormatCtx, fileName, NULL, 0);

        if (err != 0)

            return;

        err = av_find_stream_info(pFormatCtx); // Retrieve stream information

        if (err < 0)

            return;

        // FormatCtx->streams是一组大小为pFormatCtx->nb_streams的指针

        for (uint32_t i = 0; i < pFormatCtx->nb_streams; i++) {

            // stream 结构数据

            AVStream *pStream = pFormatCtx->streams[i];

            // 帧率信息

            AVRational frameRate = pStream->r_frame_rate;

            // 时间单位比率

            AVRational timeBase = pStream->time_base;

            // stream duration

            int64_t duration = pStream->duration;

     

            // 获取Codec数据结构

            AVCodecContext *pCodecCtx = pStream->codec;

            AVMediaType codecType = pCodecCtx->codec_type;

     

            AVCodecID codecId = pCodecCtx->codec_id;

            // enum AVCodecID codec_id; /* see AV_CODEC_ID_xxx */

     

            if (codecType == AVMEDIA_TYPE_VIDEO) {

                // 获取Video基本信息

                int width = pCodecCtx->width;

                int height = pCodecCtx->height;

                PixelFormat pixelFormat = pCodecCtx->pix_fmt;

            }

            else if (codecType == AVMEDIA_TYPE_AUDIO) {

                // 获取Audio基本信息

                int channels = pCodecCtx->channels;

                int sample_rate = pCodecCtx->sample_rate;

                AVSampleFormat sampleFmt = pCodecCtx->sample_fmt;

            }

            // Find the decoder for the video stream

            AVCodec *pCodec = avcodec_find_decoder(pCodecCtx->codec_id);

            if (pCodec == NULL)

                av_log(NULL, AV_LOG_ERROR, "Unsupported codec! ");

            else if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0)

                av_log(NULL, AV_LOG_ERROR, "Could not open codec! ");

            else {

                AVFrame * pFrame = avcodec_alloc_frame();

                AVFrame * pFrameRGB = avcodec_alloc_frame();

                if (pFrameRGB == NULL)

                    av_log(NULL, AV_LOG_ERROR, "alloc frame failure! ");

                else {

                    int numBytes = avpicture_get_size(PIX_FMT_RGB24, pCodecCtx->width, pCodecCtx->height);

                    uint8_t * buffer = (uint8_t*)av_malloc(numBytes*sizeof(uint8_t));

                    avpicture_fill((AVPicture*)pFrameRGB, buffer, PIX_FMT_RGB24, pCodecCtx->width, pCodecCtx->height);

                    int frameFinished;

                    AVPacket packet;

                    i = 0;

                    while (1) {

                        if (av_read_frame(pFormatCtx, &packet) < 0) // 创建AVPacket,填充其data数据缓冲区

                            break;

                        // while (av_read_frame(pFormatCtx, &packet) >= 0) {

                        // Is this a packet from the video stream?

                        if (packet.stream_index == video_stream_index) {

                            // Decode video frame

                            frameFinished = 0;

                            avcodec_decode_video2(pCodecCtx, pFrame, &frameFinished, &packet);

                            // avcodec_decode_video(pCodecCtx, pFrame, &frameFinished, packet.data, packet.size);

                            // Did we get a video frame?

                            if (frameFinished) {

                                // Convert the image from its native format to RGB

                                img_convert((AVPicture*)pFrameRGB, PIX_FMT_RGB24, (AVPicture*)pFrame, pCodecCtx->pix_fmt,

                                    pCodecCtx->width, pCodecCtx->height);

                                if (++i % 100 == 1)

                                    SaveFrame(pFrameRGB, pCodecCtx->width, pCodecCtx->height, i);

                            }

                        }

                    }

                    // Free the packet that was allocated by av_read_frame

                    av_free_packet(&packet);

                    av_free(buffer);

                }

                av_free(pFrameRGB);

                av_free(pFrame);

            }

            avcodec_close(pCodecCtx);

        }

        // 释放

        if (pFormatCtx != NULL) {

            av_close_input_file(pFormatCtx);

            pFormatCtx = NULL;

        }

    }

    上面代码编译时,会提示img_convert找不到。

    原因在于早期的ffmpeg库中,采用img_convert图像格式转换,而较新的ffmpeg库中去掉了该函数,加入了swscale库,用该库中的sws_scale函数可以替代img_convert,两个函数原型如下:

    int img_convert(AVPicture *dst, int dst_pix_fmt,

                    const AVPicture *src, int src_pix_fmt,

                    int src_width, int src_height)

    int sws_scale(struct SwsContext *ctx, uint8_t* src[], int srcStride[],

                        int srcSliceY, int srcSliceH, uint8_t* dst[], int dstStride[])

    为了方便,可以自己写一个img_convert函数,然后函数内部用sws_scale来实现,只是对于一些错误的处理及返回值处理不太严格,但基本能用,代码如下:

    int img_convert(AVPicture *dst, int dst_pix_fmt,

                    const AVPicture *src, int src_pix_fmt,

                    int src_width, int src_height)

    {

        int w;

        int h;

        SwsContext *pSwsCtx;

        w = src_width;

        h = src_height;

        pSwsCtx = sws_getContext(w, h, src_pix_fmt, 

                                w, h, dst_pix_fmt,

                                SWS_BICUBIC, NULL, NULL, NULL);

        sws_scale(pSwsCtx, src->data, src->linesize,

                0, h, dst->data, dst->linesize);

       

         

     //这里释放掉pSwsCtx的内存

    sws_freeContext(pSwsCtx);
     

        return 0;

    }

     

    其实,稍微多用一下代码就知道,这个ffmpeg感觉与GDI一样,需要完成一些准备工作,然后按相应的流程进行处理,最后释放。

    应该可以设计成为一个类库,这样只管用。

    这项工作可以留待以后,把这个东东搞熟了再说。

    运行结果,生成了一堆PPM文件

    这些PPM文件可以用XnView程序查看。

    这种结果,确实很爽的。现在的成果,可以取得视频文件的任意一帧,并保存为文件。

  • 相关阅读:
    “2O9”学习小组第十三周学习总结
    2020—2021—1学期20202408董怡文《网络空间安全导论》第十三周学习总结
    2020—2021—1学期20202408董怡文《网络空间安全导论》第十二周学习总结
    “2O9”学习小组第十一周学习总结
    2020—2021—1学期20202408董怡文《网络空间安全导论》第十一周学习总结
    “2O9”学习小组第十周学习总结(网空导论第3章)
    2020—2021—1学期20202408董怡文《网络空间安全导论》第十周学习总结
    2020—2021—1学期20202408董怡文《网络空间安全导论》第九周学习总结
    2020—2021—1学期 20202408《网络空间安全导论》第八周学习总结
    “2O9”学习小组第八周学习总结(网空导论1,2,6,9章,7章(1))
  • 原文地址:https://www.cnblogs.com/drgraph/p/3612862.html
Copyright © 2020-2023  润新知