Libav在视频中寻求n秒并在那里保存框架

发布于 2025-02-13 14:42:11 字数 7828 浏览 0 评论 0原文

我一直在尝试在mpegts视频文件中寻找。在探索了许多代码之后,我进入了以下阶段获取帧并保存它们。 但是,在使用AV_SEEK_FRAME之后,我也将得到以下结果:

  1. 初始7-8帧被保存为灰色框架。
  2. 此后,仅从视频开始就获取帧。即0秒开始,我试图从第12秒开始寻找。

您能解释一下我应该如何计算时间戳并正确执行操作吗?


int main(int argc, const char *argv[])
{

  int stream_id;
  int64_t timestamp;
  char ts_buf[60];


  if (argc < 2) {
    printf("You need to specify a media file.\n");
    return -1;
  }
  
  logging("initializing all the containers, codecs and protocols.");

 
  AVFormatContext *pFormatContext = avformat_alloc_context();
  if (!pFormatContext) {
    logging("ERROR could not allocate memory for Format Context");
    return -1;
  }

  logging("opening the input file (%s) and loading format (container) header", argv[1]);
  
  if (avformat_open_input(&pFormatContext, argv[1], NULL, NULL) != 0) {
    logging("ERROR could not open the file");
    return -1;
  }

  logging("format %s, duration %lld us, bit_rate %lld", pFormatContext->iformat->name, pFormatContext->duration, pFormatContext->bit_rate);

  logging("finding stream info from format");
  
  if (avformat_find_stream_info(pFormatContext,  NULL) < 0) {
    logging("ERROR could not get the stream info");
    return -1;
  }

  AVCodec *pCodec = NULL;
  AVCodecParameters *pCodecParameters =  NULL;
  int video_stream_index = -1;

  // loop though all the streams and print its main information
  for (int i = 0; i < pFormatContext->nb_streams; i++)
  {
    AVCodecParameters *pLocalCodecParameters =  NULL;
    pLocalCodecParameters = pFormatContext->streams[i]->codecpar;
    logging("AVStream->time_base before open coded %d/%d", pFormatContext->streams[i]->time_base.num, pFormatContext->streams[i]->time_base.den);
    logging("AVStream->r_frame_rate before open coded %d/%d", pFormatContext->streams[i]->r_frame_rate.num, pFormatContext->streams[i]->r_frame_rate.den);
    logging("AVStream->start_time %" PRId64, pFormatContext->streams[i]->start_time);
    logging("AVStream->duration %" PRId64, pFormatContext->streams[i]->duration);

    logging("finding the proper decoder (CODEC)");

    AVCodec *pLocalCodec = NULL;

    pLocalCodec = avcodec_find_decoder(pLocalCodecParameters->codec_id);

    if (pLocalCodec==NULL) {
      logging("ERROR unsupported codec!");
      // In this example if the codec is not found we just skip it
      continue;
    }

    // when the stream is a video we store its index, codec parameters and codec
    if (pLocalCodecParameters->codec_type == AVMEDIA_TYPE_VIDEO) {
      if (video_stream_index == -1) {
        video_stream_index = i;
        pCodec = pLocalCodec;
        pCodecParameters = pLocalCodecParameters;
      }

      logging("Video Codec: resolution %d x %d", pLocalCodecParameters->width, pLocalCodecParameters->height);
    } else if (pLocalCodecParameters->codec_type == AVMEDIA_TYPE_AUDIO) {
      logging("Audio Codec: %d channels, sample rate %d", pLocalCodecParameters->channels, pLocalCodecParameters->sample_rate);
    }

    // print its name, id and bitrate
    logging("\tCodec %s ID %d bit_rate %lld", pLocalCodec->name, pLocalCodec->id, pLocalCodecParameters->bit_rate);
  }

  if (video_stream_index == -1) {
    logging("File %s does not contain a video stream!", argv[1]);
    return -1;
  }

  AVCodecContext *pCodecContext = avcodec_alloc_context3(pCodec);
  if (!pCodecContext)
  {
    logging("failed to allocated memory for AVCodecContext");
    return -1;
  }

  if (avcodec_parameters_to_context(pCodecContext, pCodecParameters) < 0)
  {
    logging("failed to copy codec params to codec context");
    return -1;
  }

  if (avcodec_open2(pCodecContext, pCodec, NULL) < 0)
  {
    logging("failed to open codec through avcodec_open2");
    return -1;
  }

  AVFrame *pFrame = av_frame_alloc();
  if (!pFrame)
  {
    logging("failed to allocate memory for AVFrame");
    return -1;
  }

  AVPacket *pPacket = av_packet_alloc();
  if (!pPacket)
  {
    logging("failed to allocate memory for AVPacket");
    return -1;
  }

  /*seek for 20 seconds*/
  int64_t incr, pos = 5;

  int64_t seek_target = (pos * AV_TIME_BASE), stream_index = 0, how_many_packets_to_process = 50, response = 0;

  printf("seek_target before : %lu\n", seek_target);
  seek_target = av_rescale_q(seek_target, AV_TIME_BASE_Q, pFormatContext->streams[stream_index]->time_base);

  printf("seek_target after: %lu\n", seek_target);

  do
  {
    response = decode_packet(pFormatContext, pPacket, pCodecContext, pFrame, seek_target);
    if (response < 0)
      break;
    avcodec_flush_buffers(pCodecContext);

    /*av_frame_unref(pFrame);
    av_packet_unref(pPacket);*/
    // stop it, otherwise we'll be saving hundreds of frames
    if (--how_many_packets_to_process <= 0) 
      break;

  }while(1);

logging("releasing all the resources");

  avformat_close_input(&pFormatContext);
  av_packet_free(&pPacket);
  av_frame_free(&pFrame);
  avcodec_free_context(&pCodecContext);
  return 0;
}


int decode_packet(AVFormatContext *pFormatContext, AVPacket *pPacket, AVCodecContext *pCodecContext, AVFrame *pFrame, int64_t seek_target)
{
  if(av_seek_frame(pFormatContext, 0, /*(startTime + frameTimestamp) / 10*/seek_target, AVSEEK_FLAG_BACKWARD) < 0)
  {
    printf("error while seeking\n"/*, pFormatContext->filename*/);
    return -1;
  }
  avcodec_flush_buffers(pCodecContext);

  while(1)
  {
    if(av_read_frame(pFormatContext, pPacket) < 0)
    {
      logging("av_read_frame failure");
      break;
    }

    /* I'm not able to get to correct timestamp to discard prior frames upto desired seconds. This if hasn't worked out well as of now. */
    if((av_q2d(pFormatContext->streams[0]->time_base) * pPacket->pts) < (seek_target * 1000))
    {
      printf("skipping the frame\npFormatContext->streams[0]->time_base: %d %d\tpckt.pts: %lu\tseek: %lu", pFormatContext->streams[0]->time_base.num, pFormatContext->streams[0]->time_base.den, pPacket->pts, seek_target);
      av_packet_unref(pPacket);
      continue;
    }

    // Send Packet for decoding
    int response = avcodec_send_packet(pCodecContext, pPacket);

    if (response < 0) {
      logging("Error while sending a packet to the decoder: %s", av_err2str(response));
      return response;
    }

    while (response >= 0)
    {
      response = avcodec_receive_frame(pCodecContext, pFrame);
      if (response == AVERROR(EAGAIN) || response == AVERROR_EOF) {
        break;
      } else if (response < 0) {
        logging("Error while receiving a frame from the decoder: %s", av_err2str(response));
        return response;
      }

      if (response >= 0) {
        logging(
            "Frame %d (type=%c, size=%d bytes, format=%d) pts %d key_frame %d [DTS %d]: %d",
            pCodecContext->frame_number,
            av_get_picture_type_char(pFrame->pict_type),
            pFrame->pkt_size,
            pFrame->format,
            pFrame->pts,
            pFrame->key_frame,
            pFrame->coded_picture_number,
            pPacket->dts
        );

  char frame_filename[1024];
        snprintf(frame_filename, sizeof(frame_filename), "%s-%d.pgm", "im/frame", pCodecContext->frame_number);

        if (pFrame->format != AV_PIX_FMT_YUV420P)
        {
          logging("Warning: the generated file may not be a grayscale image, but could e.g. be just the R component if the video format is RGB");
        }
        // save a grayscale frame into a .pgm file
        save_gray_frame(pFrame->data[0], pFrame->linesize[0], pFrame->width, pFrame->height, frame_filename);

        av_frame_unref(m_pAVFrame);
        
      }
    }
  }
  return 0;
}


I've been trying to seek in a mpegts video file. After exploring many codes, I've come up to the following stage of fetching frames and saving them.
However after using av_seek_frame also I'm getting following results:

  1. Initial 7-8 frames are saved as grey frames.
  2. Thereafter frames are fetched from beginning of the video only. i.e. 0 seconds onwards, while I'm trying to seek from say 12th seconds onwards.

Can you please explain how should I be calculating the timestamp and do the things correctly?


int main(int argc, const char *argv[])
{

  int stream_id;
  int64_t timestamp;
  char ts_buf[60];


  if (argc < 2) {
    printf("You need to specify a media file.\n");
    return -1;
  }
  
  logging("initializing all the containers, codecs and protocols.");

 
  AVFormatContext *pFormatContext = avformat_alloc_context();
  if (!pFormatContext) {
    logging("ERROR could not allocate memory for Format Context");
    return -1;
  }

  logging("opening the input file (%s) and loading format (container) header", argv[1]);
  
  if (avformat_open_input(&pFormatContext, argv[1], NULL, NULL) != 0) {
    logging("ERROR could not open the file");
    return -1;
  }

  logging("format %s, duration %lld us, bit_rate %lld", pFormatContext->iformat->name, pFormatContext->duration, pFormatContext->bit_rate);

  logging("finding stream info from format");
  
  if (avformat_find_stream_info(pFormatContext,  NULL) < 0) {
    logging("ERROR could not get the stream info");
    return -1;
  }

  AVCodec *pCodec = NULL;
  AVCodecParameters *pCodecParameters =  NULL;
  int video_stream_index = -1;

  // loop though all the streams and print its main information
  for (int i = 0; i < pFormatContext->nb_streams; i++)
  {
    AVCodecParameters *pLocalCodecParameters =  NULL;
    pLocalCodecParameters = pFormatContext->streams[i]->codecpar;
    logging("AVStream->time_base before open coded %d/%d", pFormatContext->streams[i]->time_base.num, pFormatContext->streams[i]->time_base.den);
    logging("AVStream->r_frame_rate before open coded %d/%d", pFormatContext->streams[i]->r_frame_rate.num, pFormatContext->streams[i]->r_frame_rate.den);
    logging("AVStream->start_time %" PRId64, pFormatContext->streams[i]->start_time);
    logging("AVStream->duration %" PRId64, pFormatContext->streams[i]->duration);

    logging("finding the proper decoder (CODEC)");

    AVCodec *pLocalCodec = NULL;

    pLocalCodec = avcodec_find_decoder(pLocalCodecParameters->codec_id);

    if (pLocalCodec==NULL) {
      logging("ERROR unsupported codec!");
      // In this example if the codec is not found we just skip it
      continue;
    }

    // when the stream is a video we store its index, codec parameters and codec
    if (pLocalCodecParameters->codec_type == AVMEDIA_TYPE_VIDEO) {
      if (video_stream_index == -1) {
        video_stream_index = i;
        pCodec = pLocalCodec;
        pCodecParameters = pLocalCodecParameters;
      }

      logging("Video Codec: resolution %d x %d", pLocalCodecParameters->width, pLocalCodecParameters->height);
    } else if (pLocalCodecParameters->codec_type == AVMEDIA_TYPE_AUDIO) {
      logging("Audio Codec: %d channels, sample rate %d", pLocalCodecParameters->channels, pLocalCodecParameters->sample_rate);
    }

    // print its name, id and bitrate
    logging("\tCodec %s ID %d bit_rate %lld", pLocalCodec->name, pLocalCodec->id, pLocalCodecParameters->bit_rate);
  }

  if (video_stream_index == -1) {
    logging("File %s does not contain a video stream!", argv[1]);
    return -1;
  }

  AVCodecContext *pCodecContext = avcodec_alloc_context3(pCodec);
  if (!pCodecContext)
  {
    logging("failed to allocated memory for AVCodecContext");
    return -1;
  }

  if (avcodec_parameters_to_context(pCodecContext, pCodecParameters) < 0)
  {
    logging("failed to copy codec params to codec context");
    return -1;
  }

  if (avcodec_open2(pCodecContext, pCodec, NULL) < 0)
  {
    logging("failed to open codec through avcodec_open2");
    return -1;
  }

  AVFrame *pFrame = av_frame_alloc();
  if (!pFrame)
  {
    logging("failed to allocate memory for AVFrame");
    return -1;
  }

  AVPacket *pPacket = av_packet_alloc();
  if (!pPacket)
  {
    logging("failed to allocate memory for AVPacket");
    return -1;
  }

  /*seek for 20 seconds*/
  int64_t incr, pos = 5;

  int64_t seek_target = (pos * AV_TIME_BASE), stream_index = 0, how_many_packets_to_process = 50, response = 0;

  printf("seek_target before : %lu\n", seek_target);
  seek_target = av_rescale_q(seek_target, AV_TIME_BASE_Q, pFormatContext->streams[stream_index]->time_base);

  printf("seek_target after: %lu\n", seek_target);

  do
  {
    response = decode_packet(pFormatContext, pPacket, pCodecContext, pFrame, seek_target);
    if (response < 0)
      break;
    avcodec_flush_buffers(pCodecContext);

    /*av_frame_unref(pFrame);
    av_packet_unref(pPacket);*/
    // stop it, otherwise we'll be saving hundreds of frames
    if (--how_many_packets_to_process <= 0) 
      break;

  }while(1);

logging("releasing all the resources");

  avformat_close_input(&pFormatContext);
  av_packet_free(&pPacket);
  av_frame_free(&pFrame);
  avcodec_free_context(&pCodecContext);
  return 0;
}


int decode_packet(AVFormatContext *pFormatContext, AVPacket *pPacket, AVCodecContext *pCodecContext, AVFrame *pFrame, int64_t seek_target)
{
  if(av_seek_frame(pFormatContext, 0, /*(startTime + frameTimestamp) / 10*/seek_target, AVSEEK_FLAG_BACKWARD) < 0)
  {
    printf("error while seeking\n"/*, pFormatContext->filename*/);
    return -1;
  }
  avcodec_flush_buffers(pCodecContext);

  while(1)
  {
    if(av_read_frame(pFormatContext, pPacket) < 0)
    {
      logging("av_read_frame failure");
      break;
    }

    /* I'm not able to get to correct timestamp to discard prior frames upto desired seconds. This if hasn't worked out well as of now. */
    if((av_q2d(pFormatContext->streams[0]->time_base) * pPacket->pts) < (seek_target * 1000))
    {
      printf("skipping the frame\npFormatContext->streams[0]->time_base: %d %d\tpckt.pts: %lu\tseek: %lu", pFormatContext->streams[0]->time_base.num, pFormatContext->streams[0]->time_base.den, pPacket->pts, seek_target);
      av_packet_unref(pPacket);
      continue;
    }

    // Send Packet for decoding
    int response = avcodec_send_packet(pCodecContext, pPacket);

    if (response < 0) {
      logging("Error while sending a packet to the decoder: %s", av_err2str(response));
      return response;
    }

    while (response >= 0)
    {
      response = avcodec_receive_frame(pCodecContext, pFrame);
      if (response == AVERROR(EAGAIN) || response == AVERROR_EOF) {
        break;
      } else if (response < 0) {
        logging("Error while receiving a frame from the decoder: %s", av_err2str(response));
        return response;
      }

      if (response >= 0) {
        logging(
            "Frame %d (type=%c, size=%d bytes, format=%d) pts %d key_frame %d [DTS %d]: %d",
            pCodecContext->frame_number,
            av_get_picture_type_char(pFrame->pict_type),
            pFrame->pkt_size,
            pFrame->format,
            pFrame->pts,
            pFrame->key_frame,
            pFrame->coded_picture_number,
            pPacket->dts
        );

  char frame_filename[1024];
        snprintf(frame_filename, sizeof(frame_filename), "%s-%d.pgm", "im/frame", pCodecContext->frame_number);

        if (pFrame->format != AV_PIX_FMT_YUV420P)
        {
          logging("Warning: the generated file may not be a grayscale image, but could e.g. be just the R component if the video format is RGB");
        }
        // save a grayscale frame into a .pgm file
        save_gray_frame(pFrame->data[0], pFrame->linesize[0], pFrame->width, pFrame->height, frame_filename);

        av_frame_unref(m_pAVFrame);
        
      }
    }
  }
  return 0;
}


如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文