千锋教育-做有情怀、有良心、有品质的职业教育机构

手机站
千锋教育

千锋学习站 | 随时随地免费学

千锋教育

扫一扫进入千锋手机站

领取全套视频
千锋教育

关注千锋学习站小程序
随时随地免费学习课程

当前位置:首页  >  技术干货  > 从多个方面用法介绍FFmpeg抽帧

从多个方面用法介绍FFmpeg抽帧

来源:千锋教育
发布人:xqq
时间: 2023-11-25 15:03:24 1700895804

一、FFmpeg抽帧及其相关概念

FFmpeg是一个开源的跨平台音视频处理工具,它包含了众多音视频处理库以及命令行工具。其中,抽帧是指按一定的时间间隔从视频文件中提取出某一帧的图像数据,这些图像数据可以用于后续的处理,比如图像识别、物体检测、目标跟踪等。

下面是一些FFmpeg抽帧相关的概念:

帧率:每秒钟处理的帧数。 码率:每秒钟传输的数据量。如果码率过低,则视频画面会出现马赛克、画质模糊等现象。 分辨率:视频的像素宽度和高度。分辨率越高,画质越清晰,但同时文件体积也会变大。 GOP:Group of Pictures,即一组连续的图像帧。在一组GOP中,第一帧为I帧(关键帧),后面的帧分别为P帧(向前预测帧)和B帧(双向预测帧)。 压缩方式:视频的压缩方式可以是有损压缩和无损压缩。由于视频数据庞大,因此一般都采用有损压缩的方式进行处理。

二、使用FFmpeg命令行工具抽帧

在Linux或Windows命令行中,使用FFmpeg命令行工具可以很方便地进行抽帧操作。

要从视频中提取出一帧图像,可以使用以下命令:

 


  ffmpeg -i input.mp4 -ss 00:00:30 -frames:v 1 output.png

 

-i:指定输入文件名。 -ss:指定从哪个时间点开始截取图像,时间格式为HH:MM:SS.xxx。 -frames:v:指定抽帧的数量,这里是1。 output.png:指定输出文件名和格式,这里是PNG格式。

上面的命令将会从input.mp4这个文件中提取第30秒的一帧图像,并保存为output.png

三、使用FFmpeg库进行抽帧

除了命令行工具,FFmpeg还提供了一系列的库函数可以用于开发自己的视频处理应用。

以下是一个使用FFmpeg库进行抽帧的示例代码:

 


  #include 
  #include 
  #include 
  #include 

  #include 
  #include 
  #include 
  #include 

  int main(int argc, char **argv)
  {
      AVFormatContext *fmt_ctx = NULL;
      AVCodecContext *codec_ctx = NULL;
      AVCodec *codec = NULL;
      AVFrame *frame = NULL;
      AVPacket pkt;
      int video_stream_index = -1;
      int ret = 0;

      if (argc < 3) {
          fprintf(stderr, "Usage: %s  \n", argv[0]);
          exit(1);
      }

      av_register_all();
      avcodec_register_all();

      if (avformat_open_input(&fmt_ctx, argv[1], NULL, NULL) != 0) {
          fprintf(stderr, "Failed to open input file\n");
          exit(1);
      }

      if (avformat_find_stream_info(fmt_ctx, NULL) < 0) {
          fprintf(stderr, "Failed to get stream info\n");
          exit(1);
      }

      for (int i = 0; i < fmt_ctx->nb_streams; i++) {
          if (fmt_ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
              video_stream_index = i;
              break;
          }
      }

      if (video_stream_index == -1) {
          fprintf(stderr, "Failed to find video stream\n");
          exit(1);
      }

      codec_ctx = avcodec_alloc_context3(NULL);
      if (!codec_ctx) {
          fprintf(stderr, "Failed to allocate codec context\n");
          exit(1);
      }

      if (avcodec_parameters_to_context(codec_ctx, fmt_ctx->streams[video_stream_index]->codecpar) < 0) {
          fprintf(stderr, "Failed to copy codec parameters to codec context\n");
          exit(1);
      }

      codec = avcodec_find_decoder(codec_ctx->codec_id);
      if (!codec) {
          fprintf(stderr, "Failed to find codec\n");
          exit(1);
      }

      if (avcodec_open2(codec_ctx, codec, NULL) < 0) {
          fprintf(stderr, "Failed to open codec\n");
          exit(1);
      }

      frame = av_frame_alloc();
      if (!frame) {
          fprintf(stderr, "Failed to allocate frame\n");
          exit(1);
      }

      av_init_packet(&pkt);

      while (av_read_frame(fmt_ctx, &pkt) == 0) {
          if (pkt.stream_index == video_stream_index) {
              ret = avcodec_send_packet(codec_ctx, &pkt);
              if (ret < 0) {
                  fprintf(stderr, "Failed to send packet\n");
                  exit(1);
              }

              while (ret >= 0) {
                  ret = avcodec_receive_frame(codec_ctx, frame);
                  if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
                      break;
                  } else if (ret < 0) {
                      fprintf(stderr, "Failed to decode frame\n");
                      exit(1);
                  }

                  char output[500];
                  snprintf(output, sizeof(output), "%s-%d.png", argv[2], frame->nb_samples);

                  FILE *fp = fopen(output, "wb");
                  if (!fp) {
                      fprintf(stderr, "Failed to open output file\n");
                      exit(1);
                  }

                  int width = codec_ctx->width;
                  int height = codec_ctx->height;
                  AVPixelFormat pix_fmt = AV_PIX_FMT_RGB24;
                  uint8_t *src_data[4] = { 0 };
                  int src_linesize[4] = { 0 };

                  int size = av_image_get_buffer_size(pix_fmt, width, height, 1);
                  uint8_t *dst_data = (uint8_t*) malloc(size);

                  av_image_fill_arrays(src_data, src_linesize, (const uint8_t*) frame->data, pix_fmt, width, height, 1);

                  SwsContext *sws = sws_getContext(width, height, codec_ctx->pix_fmt, width, height, pix_fmt, SWS_BILINEAR, NULL, NULL, NULL);
                  sws_scale(sws, src_data, src_linesize, 0, height, &dst_data, &size);

                  fwrite(dst_data, 1, size, fp);
                  fclose(fp);

                  free(dst_data);
                  sws_freeContext(sws);
              }
          }

          av_packet_unref(&pkt);
      }

      av_frame_free(&frame);
      avcodec_close(codec_ctx);
      avformat_close_input(&fmt_ctx);

      return 0;
  }

 

这个程序从指定的输入文件中逐帧读取视频数据,并对每一帧数据进行处理。

要将视频帧保存为图片文件,可以使用以下代码:

 


  char output[500];
  snprintf(output, sizeof(output), "%s-%d.png", argv[2], frame->nb_samples);

  FILE *fp = fopen(output, "wb");
  if (!fp) {
      fprintf(stderr, "Failed to open output file\n");
      exit(1);
  }

  int width = codec_ctx->width;
  int height = codec_ctx->height;
  AVPixelFormat pix_fmt = AV_PIX_FMT_RGB24;
  uint8_t *src_data[4] = { 0 };
  int src_linesize[4] = { 0 };

  int size = av_image_get_buffer_size(pix_fmt, width, height, 1);
  uint8_t *dst_data = (uint8_t*) malloc(size);

  av_image_fill_arrays(src_data, src_linesize, (const uint8_t*) frame->data, pix_fmt, width, height, 1);

  SwsContext *sws = sws_getContext(width, height, codec_ctx->pix_fmt, width, height, pix_fmt, SWS_BILINEAR, NULL, NULL, NULL);
  sws_scale(sws, src_data, src_linesize, 0, height, &dst_data, &size);

  fwrite(dst_data, 1, size, fp);
  fclose(fp);

  free(dst_data);
  sws_freeContext(sws);

 

上面的代码先根据输入视频的宽度、高度以及像素格式计算出输出图像的尺寸和像素格式,然后进行图像缩放,最后将缩放后的图像数据写入文件。

四、常见问题

1. 无法打开输入文件

如果在使用FFmpeg打开输入文件时出现错误,可以检查一下文件路径是否正确,以及文件是否存在。

 


  if (avformat_open_input(&fmt_ctx, argv[1], NULL, NULL) != 0) {
      fprintf(stderr, "Failed to open input file\n");
      exit(1);
  }

 

2. 找不到视频流

如果在视频文件中没有找到视频流,可以通过avformat_find_stream_info()函数查找。

 


  if (avformat_find_stream_info(fmt_ctx, NULL) < 0) {
      fprintf(stderr, "Failed to get stream info\n");
      exit(1);
  }

  for (int i = 0; i < fmt_ctx->nb_streams; i++) {
      if (fmt_ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
          video_stream_index = i;
          break;
      }
  }

  if (video_stream_index == -1) {
      fprintf(stderr, "Failed to find video stream\n");
      exit(1);
  }

 

3. 解码失败

如果在解码视频帧时出现错误,可以通过avcodec_receive_frame()函数返回的错误码进行判断。

 


  while (ret >= 0) {
      ret = avcodec_receive_frame(codec_ctx, frame);
      if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
          break;
      } else if (ret < 0) {
          fprintf(stderr, "Failed to decode frame\n");
          exit(1);
      }

      // ...
  }

 

4. 缩放失败

如果在对视频帧进行缩放时出现错误,可以检查一下输入输出尺寸、像素格式等参数是否正确。

 


  int width = codec_ctx->width;
  int height = codec_ctx->height;
  AVPixelFormat pix_fmt = AV_PIX_FMT_RGB24;
  uint8_t *src_data[4] = { 0 };
  int src_linesize[4] = { 0 };

  int size = av_image_get_buffer_size(pix_fmt, width, height, 1);
  uint8_t *dst_data = (uint8_t*) malloc(size);

  av_image_fill_arrays(src_data, src_linesize, (const uint8_t*) frame->data, pix_fmt, width, height, 1);

  SwsContext *sws = sws_getContext(width, height, codec_ctx->pix_fmt, width, height, pix_fmt, SWS_BILINEAR, NULL, NULL, NULL);
  sws_scale(sws, src_data, src_linesize, 0, height, &dst_data, &size);

  // ...

 

5. 内存泄漏

如果在程序结束时没有正确地释放分配的内存,可能会导致内存泄漏。

 


  av_frame_free(&frame);
  avcodec_close(codec_ctx);
  avformat_close_input(&fmt_ctx);

 

tags: ffmpeg抽帧
声明:本站稿件版权均属千锋教育所有,未经许可不得擅自转载。
10年以上业内强师集结,手把手带你蜕变精英
请您保持通讯畅通,专属学习老师24小时内将与您1V1沟通
免费领取
今日已有369人领取成功
刘同学 138****2860 刚刚成功领取
王同学 131****2015 刚刚成功领取
张同学 133****4652 刚刚成功领取
李同学 135****8607 刚刚成功领取
杨同学 132****5667 刚刚成功领取
岳同学 134****6652 刚刚成功领取
梁同学 157****2950 刚刚成功领取
刘同学 189****1015 刚刚成功领取
张同学 155****4678 刚刚成功领取
邹同学 139****2907 刚刚成功领取
董同学 138****2867 刚刚成功领取
周同学 136****3602 刚刚成功领取
相关推荐HOT