广告
返回顶部
首页 > 资讯 > 后端开发 > 其他教程 >C++ ffmpeg如何实现将视频帧转换成jpg或png等图片
  • 936
分享到

C++ ffmpeg如何实现将视频帧转换成jpg或png等图片

2023-07-05 17:07:46 936人浏览 独家记忆
摘要

本篇内容介绍了“c++ FFmpeg如何实现将视频帧转换成jpg或png等图片”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!一、如

本篇内容介绍了“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等图片”的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识可以关注编程网网站,小编将为大家输出更多高质量的实用文章!

    --结束END--

    本文标题: C++ ffmpeg如何实现将视频帧转换成jpg或png等图片

    本文链接: https://www.lsjlt.com/news/353918.html(转载时请注明来源链接)

    有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

    本篇文章演示代码以及资料文档资料下载

    下载Word文档到电脑,方便收藏和打印~

    下载Word文档
    猜你喜欢
    • C++ ffmpeg如何实现将视频帧转换成jpg或png等图片
      本篇内容介绍了“C++ ffmpeg如何实现将视频帧转换成jpg或png等图片”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!一、如...
      99+
      2023-07-05
    软考高级职称资格查询
    编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
    • 官方手机版

    • 微信公众号

    • 商务合作