验证码: 看不清楚,换一张 查询 注册会员,免验证
  • {{ basic.site_slogan }}
  • 打开微信扫一扫,
    您还可以在这里找到我们哟

    关注我们

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

阅读:1039 来源:乙速云 作者:代码code

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);
      }

      二、完整代码

      /// 
      /// 帧转图片
      /// 如果外部提供的缓存长度不足则不会写入。
      /// 
      /// [in]视频帧
      /// [in]图片编码器ID,如jpg:AV_CODEC_ID_MJPEG,png:AV_CODEC_ID_PNG
      /// [out]图片缓存,由外部提供
      /// [in]图片缓存长度
      /// 返回图片实际长度
      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");
      }
      /// 
      /// 将视频帧保存为jpg图片
      /// 
      /// 视频帧
      /// 保存的路径
      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);
      }
      /// 
      /// 通过裸数据生成avframe
      /// 
      /// 帧数据
      /// 帧宽
      /// 帧高
      /// 像素格式
      /// avframe,使用完成后需要调用av_frame_free释放
      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;
      }
    分享到:
    *特别声明:以上内容来自于网络收集,著作权属原作者所有,如有侵权,请联系我们: hlamps#outlook.com (#换成@)。
    相关文章
    {{ v.title }}
    {{ v.description||(cleanHtml(v.content)).substr(0,100)+'···' }}
    你可能感兴趣
    推荐阅读 更多>