中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

C++?ffmpeg如何實現將視頻幀轉換成jpg或png等圖片

發布時間:2023-03-28 16:15:04 來源:億速云 閱讀:246 作者:iii 欄目:開發技術

本篇內容介紹了“C++ ffmpeg如何實現將視頻幀轉換成jpg或png等圖片”的有關知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領大家學習一下如何處理這些情況吧!希望大家仔細閱讀,能夠學有所成!

    一、如何實現

    1、查找編碼器

    首先需要查找圖片編碼器,比如jpg為AV_CODEC_ID_MJPEG,png為AV_CODEC_ID_PNG

    示例代碼:

    enum AVCodecID codec = avcodec_find_encoder(AV_CODEC_ID_MJPEG);

    2、構造編碼器上下文

    有了編碼器就可以構造編碼器上下文了。

    AVCodecContext*ctx = avcodec_alloc_context3(codec);
    ctx->bit_rate = 3000000;
    ctx->width = frame->width;//視頻幀的寬
    ctx->height = frame->height;//視頻幀的高
    ctx->time_base.num = 1;
    ctx->time_base.den = 25;
    ctx->gop_size = 10;
    ctx->max_b_frames = 0;
    ctx->thread_count = 1;
    ctx->pix_fmt = *codec->pix_fmts;//使用編碼器適配的像素格式
    //打開編碼器
    avcodec_open2(ctx, codec, NULL);

    3、像素格式轉換

    如果輸入視頻幀的像素和編碼器的像素格式不相同則需要轉換像素格式,我們采用SwsContext 轉換即可

    AVFrame*rgbFrame = av_frame_alloc();//轉換后的幀
    swsContext = sws_getContext(frame->width, frame->height, (enum AVPixelFormat)frame->format, frame->width, frame->height, ctx->pix_fmt, 1, NULL, NULL, NULL);
    int bufferSize = av_image_get_buffer_size(ctx->pix_fmt, frame->width, frame->height, 1) * 2;
    buffer = (unsigned char*)av_malloc(bufferSize);
    //構造幀的緩存
    av_image_fill_arrays(rgbFrame->data, rgbFrame->linesize, buffer, ctx->pix_fmt, frame->width, frame->height, 1);
    sws_scale(swsContext, frame->data, frame->linesize, 0, frame->height, rgbFrame->data, rgbFrame->linesize);
    //構造必要的參數
    rgbFrame->format = ctx->pix_fmt;
    rgbFrame->width = ctx->width;
    rgbFrame->height = ctx->height;

    4、編碼

    得到轉后的幀就可以編碼

    ret = avcodec_send_frame(ctx, rgbFrame);

    5、獲取圖片數據

    獲取解碼后的包即可得到圖片數據。

    uint8_t* outbuf;//輸出圖片的緩存
    size_t outbufSize;//緩存大小
    AVPacket pkt;
    av_init_packet(&pkt);
    //獲取解碼的包
    avcodec_receive_packet(ctx, &pkt);
    //將圖片數據拷貝到緩存
    if (pkt.size > 0 && pkt.size <= outbufSize)
    memcpy(outbuf, pkt.data, pkt.size);

    6、銷毀資源

    將上述步驟使用的對象銷毀。

    if (swsContext)
    {
        sws_freeContext(swsContext);
    }
    if (rgbFrame)
    {
        av_frame_unref(rgbFrame);
        av_frame_free(&rgbFrame);
    }
    if (buffer)
    {
        av_free(buffer);
    }
    av_packet_unref(&pkt);
    if (ctx)
    {
        avcodec_close(ctx);
        avcodec_free_context(&ctx);
    }

    二、完整代碼

    /// <summary>
    /// 幀轉圖片
    /// 如果外部提供的緩存長度不足則不會寫入。
    /// </summary>
    /// <param name="frame">[in]視頻幀</param>
    /// <param name="codecID">[in]圖片編碼器ID,如jpg:AV_CODEC_ID_MJPEG,png:AV_CODEC_ID_PNG</param>
    /// <param name="outbuf">[out]圖片緩存,由外部提供</param>
    /// <param name="outbufSize">[in]圖片緩存長度</param>
    /// <returns>返回圖片實際長度</returns>
    static int frameToImage(AVFrame* frame, enum AVCodecID codecID, uint8_t* outbuf, size_t outbufSize)
    {
        int ret = 0;
        AVPacket pkt;
        AVCodec* codec;
        AVCodecContext* ctx = NULL;
        AVFrame* rgbFrame = NULL;
        uint8_t* buffer = NULL;
        struct SwsContext* swsContext = NULL;
        av_init_packet(&pkt);
        codec = avcodec_find_encoder(codecID);
        if (!codec)
        {
            printf("avcodec_send_frame error %d", codecID);
            goto end;
        }
        if (!codec->pix_fmts)
        {
            printf("unsupport pix format with codec %s", codec->name);
            goto end;
        }
        ctx = avcodec_alloc_context3(codec);
        ctx->bit_rate = 3000000;
        ctx->width = frame->width;
        ctx->height = frame->height;
        ctx->time_base.num = 1;
        ctx->time_base.den = 25;
        ctx->gop_size = 10;
        ctx->max_b_frames = 0;
        ctx->thread_count = 1;
        ctx->pix_fmt = *codec->pix_fmts;
        ret = avcodec_open2(ctx, codec, NULL);
        if (ret < 0)
        {
            printf("avcodec_open2 error %d", ret);
            goto end;
        }
        if (frame->format != ctx->pix_fmt)
        {
            rgbFrame = av_frame_alloc();
            if (rgbFrame == NULL)
            {
                printf("av_frame_alloc  fail:%d");
                goto end;
            }
            swsContext = sws_getContext(frame->width, frame->height, (enum AVPixelFormat)frame->format, frame->width, frame->height, ctx->pix_fmt, 1, NULL, NULL, NULL);
            if (!swsContext)
            {
                printf("sws_getContext  fail:%d");
                goto end;
            }
            int bufferSize = av_image_get_buffer_size(ctx->pix_fmt, frame->width, frame->height, 1) * 2;
            buffer = (unsigned char*)av_malloc(bufferSize);
            if (buffer == NULL)
            {
                printf("buffer alloc fail:%d", bufferSize);
                goto end;
            }
            av_image_fill_arrays(rgbFrame->data, rgbFrame->linesize, buffer, ctx->pix_fmt, frame->width, frame->height, 1);
            if ((ret = sws_scale(swsContext, frame->data, frame->linesize, 0, frame->height, rgbFrame->data, rgbFrame->linesize)) < 0)
            {
                printf("sws_scale error %d", ret);
            }
            rgbFrame->format = ctx->pix_fmt;
            rgbFrame->width = ctx->width;
            rgbFrame->height = ctx->height;
            ret = avcodec_send_frame(ctx, rgbFrame);
        }
        else
        {
            ret = avcodec_send_frame(ctx, frame);
        }
        if (ret < 0)
        {
            printf("avcodec_send_frame error %d", ret);
            goto end;
        }
        ret = avcodec_receive_packet(ctx, &pkt);
        if (ret < 0)
        {
            printf("avcodec_receive_packet error %d", ret);
            goto end;
        }
        if (pkt.size > 0 && pkt.size <= outbufSize)
            memcpy(outbuf, pkt.data, pkt.size);
        ret = pkt.size;
    end:
        if (swsContext)
        {
            sws_freeContext(swsContext);
        }
        if (rgbFrame)
        {
            av_frame_unref(rgbFrame);
            av_frame_free(&rgbFrame);
        }
        if (buffer)
        {
            av_free(buffer);
        }
        av_packet_unref(&pkt);
        if (ctx)
        {
            avcodec_close(ctx);
            avcodec_free_context(&ctx);
        }
        return ret;
    }

    三、使用示例

    1、截取視頻幀并保存文件

    void main() {
        AVFrame* frame;//視頻解碼得到的幀
        saveFrameToJpg(frame,"snapshot.jpg");
    }
    /// <summary>
    /// 將視頻幀保存為jpg圖片
    /// </summary>
    /// <param name="frame">視頻幀</param>
    /// <param name="path">保存的路徑</param>
    void saveFrameToJpg(AVFrame*frame,const char*path) {
        //確保緩沖區長度大于圖片,使用brga像素格式計算。如果是bmp或tiff依然可能超出長度,需要加一個頭部長度,或直接乘以2。
        int bufSize = av_image_get_buffer_size(AV_PIX_FMT_BGRA, frame->width, frame->height, 64);
        //申請緩沖區
        uint8_t* buf = (uint8_t*)av_malloc(bufSize);
        //將視頻幀轉換成圖片
        int picSize = frameToImage(frame, AV_CODEC_ID_MJPEG, buf, bufSize);
        //寫入文件
        auto f = fopen(path, "wb+");
        if (f)
        {
            fwrite(buf, sizeof(uint8_t), bufSize, f);
            fclose(f);
        }
        //釋放緩沖區
        av_free(buf);
    }

    2、自定義數據構造AVFrame

    void main() {
        uint8_t*frameData;//解碼得到的視頻數據
        AVFrame* frame=allocFrame(frameData,640,360,AV_PIX_FMT_YUV420P);
        saveFrameToJpg(frame,"snapshot.jpg");//此方法定義在示例1中
        av_frame_free(&frame);
    }
    /// <summary>
    /// 通過裸數據生成avframe
    /// </summary>
    /// <param name="frameData">幀數據</param>
    /// <param name="width">幀寬</param>
    /// <param name="height">幀高</param>
    /// <param name="format">像素格式</param>
    /// <returns>avframe,使用完成后需要調用av_frame_free釋放</returns>
    AVFrame* allocFrame(uint8_t*frameData,int width,int height,AVPixelFormat format) {
        AVFrame* frame = av_frame_alloc();
        frame->width = width;
        frame->height = height;
        frame->format = format;
        av_image_fill_arrays(frame->data, frame->linesize, frameData, format, frame->width, frame->height, 64);
        return frame;
    }

    “C++ ffmpeg如何實現將視頻幀轉換成jpg或png等圖片”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識可以關注億速云網站,小編將為大家輸出更多高質量的實用文章!

    向AI問一下細節

    免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

    AI

    洛南县| 绍兴县| 文山县| 慈溪市| 太谷县| 泽普县| 沁阳市| 屏东县| 禹城市| 扶绥县| 高雄市| 新郑市| 仲巴县| 类乌齐县| 鸡东县| 班戈县| 宝清县| 章丘市| 临沂市| 浦城县| 辽中县| 海南省| 二手房| 弥渡县| 钟祥市| 伊金霍洛旗| 福州市| 辽宁省| 碌曲县| 疏勒县| 昌乐县| 咸宁市| 西和县| 胶州市| 仁怀市| 木里| 资源县| 前郭尔| 吉首市| 拜城县| 岱山县|