/home/docs/checkouts/readthedocs.org/user_builds/advanced-micro-devices-rocdecode/checkouts/develop/projects/rocdecode/utils/video_demuxer.h Source File

/home/docs/checkouts/readthedocs.org/user_builds/advanced-micro-devices-rocdecode/checkouts/develop/projects/rocdecode/utils/video_demuxer.h Source File#

17 min read time

Applies to Linux

rocDecode: /home/docs/checkouts/readthedocs.org/user_builds/advanced-micro-devices-rocdecode/checkouts/develop/projects/rocdecode/utils/video_demuxer.h Source File
video_demuxer.h
Go to the documentation of this file.
1 /*
2 Copyright (c) 2023 - 2026 Advanced Micro Devices, Inc. All rights reserved.
3 
4 Permission is hereby granted, free of charge, to any person obtaining a copy
5 of this software and associated documentation files (the "Software"), to deal
6 in the Software without restriction, including without limitation the rights
7 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 copies of the Software, and to permit persons to whom the Software is
9 furnished to do so, subject to the following conditions:
10 
11 The above copyright notice and this permission notice shall be included in
12 all copies or substantial portions of the Software.
13 
14 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 THE SOFTWARE.
21 */
22 
23 #pragma once
24 
25 #include <iostream>
26 extern "C" {
27  #include <libavcodec/avcodec.h>
28  #include <libavformat/avformat.h>
29  #if USE_AVCODEC_GREATER_THAN_58_134 || USE_AVCODEC_GREATER_THAN_60_31
30  #include <libavcodec/bsf.h>
31  #endif
32 }
33 
34 #include <cstdint>
35 #include <cstring>
36 #include <ctime>
37 #include <time.h>
38 #ifndef _WIN32
39 #include <unistd.h>
40 #include <sys/syscall.h>
41 #else
42 #include <process.h>
43 #include <windows.h>
44 #endif
45 #include <thread>
46 #include <sstream>
47 #include <iomanip>
48 #include "rocdecode/rocdecode.h"
49 
50 // Minimal critical logging for video_demuxer.h.
51 // Matches the format produced by the full logger in src/commons.h:
52 // [0, Critical] filename:line: timestamp_us us: [pid:X tid:Y hashid:0xZZZZZ] func(): message
53 #ifndef _WIN32
54 #define DemuxCriticalLog(msg) \
55  do { \
56  struct timespec _ts_; \
57  clock_gettime(CLOCK_MONOTONIC, &_ts_); \
58  uint64_t _us_ = static_cast<uint64_t>(_ts_.tv_sec) * 1000000ULL + _ts_.tv_nsec / 1000ULL; \
59  const char *_f_ = strrchr(__FILE__, '/'); \
60  pid_t _tid_ = static_cast<pid_t>(syscall(SYS_gettid)); \
61  std::ostringstream _htid_oss_; \
62  _htid_oss_ << "0x" << std::hex << std::setw(5) << std::setfill('0') \
63  << (std::hash<std::thread::id>{}(std::this_thread::get_id()) & 0xFFFFF); \
64  std::cerr << "[0, Critical] " << (_f_ ? _f_ + 1 : __FILE__) \
65  << ":" << __LINE__ << ": " << _us_ << " us: [pid:" \
66  << getpid() << " tid:" << _tid_ << " hashid:" << _htid_oss_.str() << "] " \
67  << __func__ << "(): " << (msg) << std::endl; \
68  } while (0)
69 #else
70 #define DemuxCriticalLog(msg) \
71  do { \
72  /* function-local static: the runtime initializes it exactly once, even when \
73  several decode threads reach their first log at the same time */ \
74  static const LARGE_INTEGER _freq_ = [] { LARGE_INTEGER _f_ = {}; QueryPerformanceFrequency(&_f_); return _f_; }(); \
75  LARGE_INTEGER _cnt_; QueryPerformanceCounter(&_cnt_); \
76  /* split the division to keep the counter from overflowing when scaled to us */ \
77  uint64_t _us_ = static_cast<uint64_t>(_cnt_.QuadPart / _freq_.QuadPart) * 1000000ULL \
78  + static_cast<uint64_t>(_cnt_.QuadPart % _freq_.QuadPart) * 1000000ULL / _freq_.QuadPart; \
79  const char *_f_ = strrchr(__FILE__, '\\'); \
80  if (!_f_) _f_ = strrchr(__FILE__, '/'); \
81  DWORD _tid_ = GetCurrentThreadId(); \
82  std::ostringstream _htid_oss_; \
83  _htid_oss_ << "0x" << std::hex << std::setw(5) << std::setfill('0') \
84  << (std::hash<std::thread::id>{}(std::this_thread::get_id()) & 0xFFFFF); \
85  std::cerr << "[0, Critical] " << (_f_ ? _f_ + 1 : __FILE__) \
86  << ":" << __LINE__ << ": " << _us_ << " us: [pid:" \
87  << _getpid() << " tid:" << _tid_ << " hashid:" << _htid_oss_.str() << "] " \
88  << __func__ << "(): " << (msg) << std::endl; \
89  } while (0)
90 #endif
91 
104 typedef enum SeekModeEnum {
105  SEEK_MODE_EXACT_FRAME = 0,
106  SEEK_MODE_PREV_KEY_FRAME = 1,
107  SEEK_MODE_NUM,
109 
114 typedef enum SeekCriteriaEnum {
115  SEEK_CRITERIA_FRAME_NUM = 0,
116  SEEK_CRITERIA_TIME_STAMP = 1,
117  SEEK_CRITERIA_NUM,
119 
120 struct PacketData {
121  int32_t key;
122  int64_t pts;
123  int64_t dts;
124  uint64_t pos;
125  uintptr_t bsl_data;
126  uint64_t bsl;
127  uint64_t duration;
128 };
129 
131 public:
133  : use_seek_(false), seek_frame_(0), seek_mode_(SEEK_MODE_PREV_KEY_FRAME), seek_crit_(SEEK_CRITERIA_FRAME_NUM),
134  out_frame_pts_(0), out_frame_duration_(0), num_frames_decoded_(0U) {}
135 
136  VideoSeekContext(uint64_t frame_id)
137  : use_seek_(true), seek_frame_(frame_id), seek_mode_(SEEK_MODE_PREV_KEY_FRAME),
138  seek_crit_(SEEK_CRITERIA_FRAME_NUM), out_frame_pts_(0), out_frame_duration_(0), num_frames_decoded_(0U) {}
139 
140  VideoSeekContext& operator=(const VideoSeekContext& other) {
141  use_seek_ = other.use_seek_;
142  seek_frame_ = other.seek_frame_;
143  seek_mode_ = other.seek_mode_;
144  seek_crit_ = other.seek_crit_;
145  out_frame_pts_ = other.out_frame_pts_;
146  out_frame_duration_ = other.out_frame_duration_;
147  num_frames_decoded_ = other.num_frames_decoded_;
148  return *this;
149  }
150 
151  /* Will be set to false when not seeking, true otherwise;
152  */
153  bool use_seek_;
154 
155  /* Frame we want to get. Set by user.
156  * Shall be set to frame timestamp in case seek is done by time.
157  */
158  uint64_t seek_frame_;
159 
160  /* Mode in which we seek. */
161  SeekMode seek_mode_;
162 
163  /* Criteria by which we seek. */
164  SeekCriteria seek_crit_;
165 
166  /* PTS of frame found after seek. */
167  int64_t out_frame_pts_;
168 
169  /* Duration of frame found after seek. */
170  int64_t out_frame_duration_;
171 
172  /* Number of frames that were decoded during seek. */
173  uint64_t num_frames_decoded_;
174 
175  /* PTS of frame to seek as set by the user in seek_frame_. */
176  int64_t requested_frame_pts_;
177 };
178 
179 
184 // Video Demuxer Interface class
186  public:
194  public:
195  virtual ~StreamProvider() {}
200  virtual int GetData(uint8_t *buf, int buf_size) = 0;
204  virtual size_t GetBufferSize() = 0;
205  };
209  AVCodecID GetCodecID() { return av_video_codec_id_; };
214  VideoDemuxer(const char *input_file_path) : VideoDemuxer(CreateFmtContextUtil(input_file_path)) {}
219  VideoDemuxer(StreamProvider *stream_provider) : VideoDemuxer(CreateFmtContextUtil(stream_provider)) {av_io_ctx_ = av_fmt_input_ctx_->pb;}
224  if (!av_fmt_input_ctx_) {
225  return;
226  }
227  if (packet_) {
228  av_packet_free(&packet_);
229  }
230  if (packet_filtered_) {
231  av_packet_free(&packet_filtered_);
232  }
233  if (av_bsf_ctx_) {
234  av_bsf_free(&av_bsf_ctx_);
235  }
236  avformat_close_input(&av_fmt_input_ctx_);
237  if (av_io_ctx_) {
238  av_freep(&av_io_ctx_->buffer);
239  av_freep(&av_io_ctx_);
240  }
241  if (data_with_header_) {
242  av_free(data_with_header_);
243  }
244  }
253  bool Demux(uint8_t **video, int *video_size, int64_t *pts = nullptr) {
254  if (!av_fmt_input_ctx_) {
255  return false;
256  }
257  *video_size = 0;
258  if (packet_->data) {
259  av_packet_unref(packet_);
260  }
261  int ret = 0;
262  while ((ret = av_read_frame(av_fmt_input_ctx_, packet_)) >= 0 && packet_->stream_index != av_stream_) {
263  av_packet_unref(packet_);
264  }
265  if (ret < 0) {
266  return false;
267  }
268  if (is_h264_ || is_hevc_) {
269  if (packet_filtered_->data) {
270  av_packet_unref(packet_filtered_);
271  }
272  if (av_bsf_send_packet(av_bsf_ctx_, packet_) != 0) {
273  DemuxCriticalLog("av_bsf_send_packet failed!");
274  return false;
275  }
276  if (av_bsf_receive_packet(av_bsf_ctx_, packet_filtered_) != 0) {
277  DemuxCriticalLog("av_bsf_receive_packet failed!");
278  return false;
279  }
280  *video = packet_filtered_->data;
281  *video_size = packet_filtered_->size;
282  if (packet_filtered_->dts != AV_NOPTS_VALUE) {
283  pkt_dts_ = packet_filtered_->dts;
284  } else {
285  pkt_dts_ = packet_filtered_->pts;
286  }
287  if (pts) {
288  *pts = (int64_t) (packet_filtered_->pts * default_time_scale_ * time_base_);
289  pkt_duration_ = packet_filtered_->duration;
290  }
291  } else {
292  if (is_mpeg4_ && (frame_count_ == 0)) {
293  int ext_data_size = av_fmt_input_ctx_->streams[av_stream_]->codecpar->extradata_size;
294  if (ext_data_size > 0) {
295  if (packet_->size < 3 ||
296  (size_t)ext_data_size > SIZE_MAX - (size_t)(packet_->size - 3)) {
297  DemuxCriticalLog("malformed first MPEG-4 packet!");
298  return false;
299  }
300  size_t payload = (size_t)packet_->size - 3;
301  size_t total = (size_t)ext_data_size + payload;
302  data_with_header_ = (uint8_t *)av_malloc(total);
303  if (!data_with_header_) {
304  DemuxCriticalLog("av_malloc failed!");
305  return false;
306  }
307  memcpy(data_with_header_, av_fmt_input_ctx_->streams[av_stream_]->codecpar->extradata, ext_data_size);
308  memcpy(data_with_header_ + ext_data_size, packet_->data + 3, payload);
309  *video = data_with_header_;
310  *video_size = static_cast<int>(total);
311  }
312  } else {
313  *video = packet_->data;
314  *video_size = packet_->size;
315  }
316  if (packet_->dts != AV_NOPTS_VALUE) {
317  pkt_dts_ = packet_->dts;
318  } else {
319  pkt_dts_ = packet_->pts;
320  }
321  if (pts) {
322  *pts = (int64_t)(packet_->pts * default_time_scale_ * time_base_);
323  pkt_duration_ = packet_->duration;
324  }
325  }
326  frame_count_++;
327  return true;
328  }
338  bool Seek(VideoSeekContext& seek_ctx, uint8_t** pp_video, int* video_size) {
339  /* !!! IMPORTANT !!!
340  * Across this function, packet decode timestamp (DTS) values are used to
341  * compare given timestamp against. This is done because DTS values shall
342  * monotonically increase during the course of decoding unlike PTS values
343  * which may be affected by frame reordering due to B frames.
344  */
345 
346  if (!is_seekable_) {
347  DemuxCriticalLog("Seek isn't supported for this input.");
348  return false;
349  }
350 
351  if (IsVFR() && (SEEK_CRITERIA_FRAME_NUM == seek_ctx.seek_crit_)) {
352  DemuxCriticalLog("Can't seek by frame number in VFR sequences. Seek by timestamp instead.");
353  return false;
354  }
355  int64_t timestamp = 0;
356  // Seek for single frame;
357  auto seek_frame = [&](VideoSeekContext const& seek_ctx, int flags) {
358  bool seek_backward = true;
359  int ret = 0;
360 
361  switch (seek_ctx.seek_crit_) {
362  case SEEK_CRITERIA_FRAME_NUM:
363  timestamp = TsFromFrameNumber(seek_ctx.seek_frame_);
364  ret = av_seek_frame(av_fmt_input_ctx_, av_stream_, timestamp, seek_backward ? AVSEEK_FLAG_BACKWARD | flags : flags);
365  break;
366  case SEEK_CRITERIA_TIME_STAMP:
367  timestamp = TsFromTime(static_cast<double>(seek_ctx.seek_frame_));
368  ret = av_seek_frame(av_fmt_input_ctx_, av_stream_, timestamp, seek_backward ? AVSEEK_FLAG_BACKWARD | flags : flags);
369  break;
370  default:
371  DemuxCriticalLog("Invalid seek criteria");
372  ret = -1;
373  }
374 
375  if (ret < 0) {
376  throw std::runtime_error("ERROR: seeking for frame");
377  }
378  };
379 
380  // Check if frame satisfies seek conditions;
381  auto is_seek_done = [&](PacketData& pkt_data, VideoSeekContext const& seek_ctx) {
382  int64_t target_ts = 0;
383 
384  switch (seek_ctx.seek_crit_) {
385  case SEEK_CRITERIA_FRAME_NUM:
386  target_ts = TsFromFrameNumber(seek_ctx.seek_frame_);
387  break;
388  case SEEK_CRITERIA_TIME_STAMP:
389  target_ts = TsFromTime(static_cast<double>(seek_ctx.seek_frame_));
390  break;
391  default:
392  DemuxCriticalLog("Invalid seek criteria");
393  return -1;
394  }
395 
396  if (pkt_dts_ == target_ts) {
397  return 0;
398  } else if (pkt_dts_ > target_ts) {
399  return 1;
400  } else {
401  return -1;
402  };
403  };
404 
405  /* This will seek for exact frame number;
406  * Note that decoder may not be able to decode such frame; */
407  auto seek_for_exact_frame = [&](PacketData& pkt_data, VideoSeekContext& seek_ctx) {
408  // Repetititive seek until seek condition is satisfied;
409  VideoSeekContext tmp_ctx(seek_ctx.seek_frame_);
410  seek_frame(tmp_ctx, AVSEEK_FLAG_ANY);
411 
412  int seek_done = 0;
413  do {
414  if (!Demux(pp_video, video_size, &pkt_data.pts)) {
415  throw std::runtime_error("ERROR: Demux failed trying to seek for specified frame number/timestamp");
416  }
417  seek_done = is_seek_done(pkt_data, seek_ctx);
418  //TODO: one last condition, check for a target too high than available for timestamp
419  if (seek_done > 0) { // We've gone too far and need to seek backwards;
420  if ((tmp_ctx.seek_frame_--) >= 0) {
421  seek_frame(tmp_ctx, AVSEEK_FLAG_ANY);
422  }
423  } else if (seek_done < 0) { // Need to read more frames until we reach requested number;
424  tmp_ctx.seek_frame_++;
425  seek_frame(tmp_ctx, AVSEEK_FLAG_ANY);
426  }
427  if (tmp_ctx.seek_frame_ == seek_ctx.seek_frame_) // if frame 'N' is too far and frame 'N-1' is too less from target. Avoids infinite loop between N & N-1
428  break;
429  } while (seek_done != 0);
430 
431  seek_ctx.out_frame_pts_ = pkt_data.pts;
432  seek_ctx.out_frame_duration_ = pkt_data.duration = pkt_duration_;
433  seek_ctx.requested_frame_pts_ = (int64_t) (timestamp * default_time_scale_ * time_base_);
434  };
435 
436  // Seek for closest key frame in the past;
437  auto seek_for_prev_key_frame = [&](PacketData& pkt_data, VideoSeekContext& seek_ctx) {
438  seek_frame(seek_ctx, AVSEEK_FLAG_BACKWARD);
439  Demux(pp_video, video_size, &pkt_data.pts);
440  seek_ctx.num_frames_decoded_ = static_cast<uint64_t>(pkt_data.pts / 1000 * frame_rate_);
441  seek_ctx.out_frame_pts_ = pkt_data.pts;
442  seek_ctx.out_frame_duration_ = pkt_data.duration = pkt_duration_;
443  seek_ctx.requested_frame_pts_ = (int64_t) (timestamp * default_time_scale_ * time_base_);
444  };
445 
446  PacketData pktData;
447  pktData.bsl_data = size_t(*pp_video);
448  pktData.bsl = *video_size;
449 
450  switch (seek_ctx.seek_mode_) {
451  case SEEK_MODE_EXACT_FRAME:
452  seek_for_exact_frame(pktData, seek_ctx);
453  break;
454  case SEEK_MODE_PREV_KEY_FRAME:
455  seek_for_prev_key_frame(pktData, seek_ctx);
456  break;
457  default:
458  throw std::runtime_error("ERROR::Unsupported seek mode");
459  break;
460  }
461 
462  return true;
463  }
465  const uint32_t GetWidth() const { return width_;}
467  const uint32_t GetHeight() const { return height_;}
469  const uint32_t GetChromaHeight() const { return chroma_height_;}
471  const uint32_t GetBitDepth() const { return bit_depth_;}
473  const uint32_t GetBytePerPixel() const { return byte_per_pixel_;}
475  const uint32_t GetBitRate() const { return bit_rate_;}
477  const double GetFrameRate() const {return frame_rate_;};
479  bool IsVFR() const { return frame_rate_ != avg_frame_rate_; };
485  int64_t TsFromTime(double ts_sec) {
486  // Convert integer timestamp representation to AV_TIME_BASE and switch to fixed_point
487  auto const ts_tbu = llround(ts_sec * AV_TIME_BASE);
488  // Rescale the timestamp to value represented in stream base units;
489  AVRational time_factor = {1, AV_TIME_BASE};
490  return av_rescale_q(ts_tbu, time_factor, av_fmt_input_ctx_->streams[av_stream_]->time_base);
491  }
492 
498  int64_t TsFromFrameNumber(int64_t frame_num) {
499  auto const ts_sec = static_cast<double>(frame_num) / frame_rate_;
500  return TsFromTime(ts_sec);
501  }
502 
503  private:
504  VideoDemuxer(AVFormatContext *av_fmt_input_ctx) : av_fmt_input_ctx_(av_fmt_input_ctx) {
505  av_log_set_level(AV_LOG_QUIET);
506  if (!av_fmt_input_ctx_) {
507  DemuxCriticalLog("av_fmt_input_ctx_ is not valid!");
508  return;
509  }
510  packet_ = av_packet_alloc();
511  packet_filtered_ = av_packet_alloc();
512  if (!packet_ || !packet_filtered_) {
513  DemuxCriticalLog("av_packet_alloc failed!");
514  return;
515  }
516  if (avformat_find_stream_info(av_fmt_input_ctx_, nullptr) < 0) {
517  DemuxCriticalLog("avformat_find_stream_info failed!");
518  return;
519  }
520  av_stream_ = av_find_best_stream(av_fmt_input_ctx_, AVMEDIA_TYPE_VIDEO, -1, -1, nullptr, 0);
521  if (av_stream_ < 0) {
522  DemuxCriticalLog("av_find_best_stream failed!");
523  av_packet_free(&packet_);
524  av_packet_free(&packet_filtered_);
525  return;
526  }
527  av_video_codec_id_ = av_fmt_input_ctx_->streams[av_stream_]->codecpar->codec_id;
528  width_ = av_fmt_input_ctx_->streams[av_stream_]->codecpar->width;
529  height_ = av_fmt_input_ctx_->streams[av_stream_]->codecpar->height;
530  chroma_format_ = (AVPixelFormat)av_fmt_input_ctx_->streams[av_stream_]->codecpar->format;
531  bit_rate_ = static_cast<uint32_t>(av_fmt_input_ctx_->streams[av_stream_]->codecpar->bit_rate);
532  if (av_fmt_input_ctx_->streams[av_stream_]->r_frame_rate.den != 0)
533  frame_rate_ = static_cast<double>(av_fmt_input_ctx_->streams[av_stream_]->r_frame_rate.num) / static_cast<double>(av_fmt_input_ctx_->streams[av_stream_]->r_frame_rate.den);
534  if (av_fmt_input_ctx_->streams[av_stream_]->avg_frame_rate.den != 0)
535  avg_frame_rate_ = static_cast<double>(av_fmt_input_ctx_->streams[av_stream_]->avg_frame_rate.num) / static_cast<double>(av_fmt_input_ctx_->streams[av_stream_]->avg_frame_rate.den);
536 
537  switch (chroma_format_) {
538  case AV_PIX_FMT_YUV420P10LE:
539  case AV_PIX_FMT_GRAY10LE:
540  bit_depth_ = 10;
541  chroma_height_ = (height_ + 1) >> 1;
542  byte_per_pixel_ = 2;
543  break;
544  case AV_PIX_FMT_YUV420P12LE:
545  bit_depth_ = 12;
546  chroma_height_ = (height_ + 1) >> 1;
547  byte_per_pixel_ = 2;
548  break;
549  case AV_PIX_FMT_YUV444P10LE:
550  bit_depth_ = 10;
551  chroma_height_ = height_ << 1;
552  byte_per_pixel_ = 2;
553  break;
554  case AV_PIX_FMT_YUV444P12LE:
555  bit_depth_ = 12;
556  chroma_height_ = height_ << 1;
557  byte_per_pixel_ = 2;
558  break;
559  case AV_PIX_FMT_YUV444P:
560  bit_depth_ = 8;
561  chroma_height_ = height_ << 1;
562  byte_per_pixel_ = 1;
563  break;
564  case AV_PIX_FMT_YUV420P:
565  case AV_PIX_FMT_YUVJ420P:
566  case AV_PIX_FMT_YUVJ422P:
567  case AV_PIX_FMT_YUVJ444P:
568  case AV_PIX_FMT_GRAY8:
569  bit_depth_ = 8;
570  chroma_height_ = (height_ + 1) >> 1;
571  byte_per_pixel_ = 1;
572  break;
573  default:
574  chroma_format_ = AV_PIX_FMT_YUV420P;
575  bit_depth_ = 8;
576  chroma_height_ = (height_ + 1) >> 1;
577  byte_per_pixel_ = 1;
578  }
579 
580  AVRational time_base = av_fmt_input_ctx_->streams[av_stream_]->time_base;
581  time_base_ = av_q2d(time_base);
582 
583  is_h264_ = av_video_codec_id_ == AV_CODEC_ID_H264 && (!strcmp(av_fmt_input_ctx_->iformat->long_name, "QuickTime / MOV")
584  || !strcmp(av_fmt_input_ctx_->iformat->long_name, "FLV (Flash Video)")
585  || !strcmp(av_fmt_input_ctx_->iformat->long_name, "Matroska / WebM"));
586  is_hevc_ = av_video_codec_id_ == AV_CODEC_ID_HEVC && (!strcmp(av_fmt_input_ctx_->iformat->long_name, "QuickTime / MOV")
587  || !strcmp(av_fmt_input_ctx_->iformat->long_name, "FLV (Flash Video)")
588  || !strcmp(av_fmt_input_ctx_->iformat->long_name, "Matroska / WebM"));
589  is_mpeg4_ = av_video_codec_id_ == AV_CODEC_ID_MPEG4 && (!strcmp(av_fmt_input_ctx_->iformat->long_name, "QuickTime / MOV")
590  || !strcmp(av_fmt_input_ctx_->iformat->long_name, "FLV (Flash Video)")
591  || !strcmp(av_fmt_input_ctx_->iformat->long_name, "Matroska / WebM"));
592 
593  // Check if the input file allow seek functionality.
594 #if USE_AVCODEC_GREATER_THAN_58_134 || USE_AVCODEC_GREATER_THAN_60_31
595  is_seekable_ = true; //for latest version of FFMPeg, read_seek and read_seek2 is not exposed in AVFormatContext
596 #else
597  is_seekable_ = av_fmt_input_ctx_->iformat->read_seek || av_fmt_input_ctx_->iformat->read_seek2;
598 #endif
599 
600  if (is_h264_) {
601  const AVBitStreamFilter *bsf = av_bsf_get_by_name("h264_mp4toannexb");
602  if (!bsf) {
603  DemuxCriticalLog("av_bsf_get_by_name() failed for h264_mp4toannexb");
604  av_packet_free(&packet_);
605  av_packet_free(&packet_filtered_);
606  return;
607  }
608  if (av_bsf_alloc(bsf, &av_bsf_ctx_) != 0) {
609  DemuxCriticalLog("av_bsf_alloc failed!");
610  return;
611  }
612  avcodec_parameters_copy(av_bsf_ctx_->par_in, av_fmt_input_ctx_->streams[av_stream_]->codecpar);
613  if (av_bsf_init(av_bsf_ctx_) < 0) {
614  DemuxCriticalLog("av_bsf_init failed!");
615  return;
616  }
617  }
618  if (is_hevc_) {
619  const AVBitStreamFilter *bsf = av_bsf_get_by_name("hevc_mp4toannexb");
620  if (!bsf) {
621  DemuxCriticalLog("av_bsf_get_by_name() failed for hevc_mp4toannexb");
622  av_packet_free(&packet_);
623  av_packet_free(&packet_filtered_);
624  return;
625  }
626  if (av_bsf_alloc(bsf, &av_bsf_ctx_) != 0 ) {
627  DemuxCriticalLog("av_bsf_alloc failed!");
628  return;
629  }
630  avcodec_parameters_copy(av_bsf_ctx_->par_in, av_fmt_input_ctx_->streams[av_stream_]->codecpar);
631  if (av_bsf_init(av_bsf_ctx_) < 0) {
632  DemuxCriticalLog("av_bsf_init failed!");
633  return;
634  }
635  }
636  }
637  AVFormatContext *CreateFmtContextUtil(StreamProvider *stream_provider) {
638  AVFormatContext *ctx = nullptr;
639  if (!(ctx = avformat_alloc_context())) {
640  DemuxCriticalLog("avformat_alloc_context failed!");
641  return nullptr;
642  }
643  uint8_t *avioc_buffer = nullptr;
644  int avioc_buffer_size = static_cast<int>(stream_provider->GetBufferSize());
645  avioc_buffer = (uint8_t *)av_malloc(avioc_buffer_size);
646  if (!avioc_buffer) {
647  DemuxCriticalLog("av_malloc failed!");
648  return nullptr;
649  }
650  av_io_ctx_ = avio_alloc_context(avioc_buffer, avioc_buffer_size,
651  0, stream_provider, &ReadPacket, nullptr, nullptr);
652  if (!av_io_ctx_) {
653  DemuxCriticalLog("avio_alloc_context failed!");
654  return nullptr;
655  }
656  ctx->pb = av_io_ctx_;
657 
658  if (avformat_open_input(&ctx, nullptr, nullptr, nullptr) != 0) {
659  DemuxCriticalLog("avformat_open_input failed!");
660  return nullptr;
661  }
662  return ctx;
663  }
664  AVFormatContext *CreateFmtContextUtil(const char *input_file_path) {
665  avformat_network_init();
666  AVFormatContext *ctx = nullptr;
667  if (avformat_open_input(&ctx, input_file_path, nullptr, nullptr) != 0 ) {
668  DemuxCriticalLog("avformat_open_input failed!");
669  return nullptr;
670  }
671  return ctx;
672  }
673  static int ReadPacket(void *data, uint8_t *buf, int buf_size) {
674  return ((StreamProvider *)data)->GetData(buf, buf_size);
675  }
676  AVFormatContext *av_fmt_input_ctx_ = nullptr;
677  AVIOContext *av_io_ctx_ = nullptr;
678  AVPacket* packet_ = nullptr;
679  AVPacket* packet_filtered_ = nullptr;
680  AVBSFContext *av_bsf_ctx_ = nullptr;
681  AVCodecID av_video_codec_id_;
682  AVPixelFormat chroma_format_;
683  double frame_rate_ = 0.0;
684  double avg_frame_rate_ = 0.0;
685  uint8_t *data_with_header_ = nullptr;
686  int av_stream_ = 0;
687  bool is_h264_ = false;
688  bool is_hevc_ = false;
689  bool is_mpeg4_ = false;
690  bool is_seekable_ = false;
691  int64_t default_time_scale_ = 1000;
692  double time_base_ = 0.0;
693  uint32_t frame_count_ = 0;
694  uint32_t width_ = 0;
695  uint32_t height_ = 0;
696  uint32_t chroma_height_ = 0;
697  uint32_t bit_depth_ = 0;
698  uint32_t byte_per_pixel_ = 0;
699  uint32_t bit_rate_ = 0;
700  // used for Seek Exact frame
701  int64_t pkt_dts_ = 0;
702  int64_t pkt_duration_ = 0;
703 };
704 
705 static inline rocDecVideoCodec AVCodec2RocDecVideoCodec(AVCodecID av_codec) {
706  switch (av_codec) {
707  case AV_CODEC_ID_MPEG1VIDEO : return rocDecVideoCodec_MPEG1;
708  case AV_CODEC_ID_MPEG2VIDEO : return rocDecVideoCodec_MPEG2;
709  case AV_CODEC_ID_MPEG4 : return rocDecVideoCodec_MPEG4;
710  case AV_CODEC_ID_H264 : return rocDecVideoCodec_AVC;
711  case AV_CODEC_ID_HEVC : return rocDecVideoCodec_HEVC;
712  case AV_CODEC_ID_VP8 : return rocDecVideoCodec_VP8;
713  case AV_CODEC_ID_VP9 : return rocDecVideoCodec_VP9;
714  case AV_CODEC_ID_MJPEG : return rocDecVideoCodec_JPEG;
715  case AV_CODEC_ID_AV1 : return rocDecVideoCodec_AV1;
716  default : return rocDecVideoCodec_NumCodecs;
717  }
718 }
Abstract interface that feeds a custom stream of bytes to the demuxer.
Definition: video_demuxer.h:193
virtual size_t GetBufferSize()=0
Returns the preferred buffer size to use for GetData reads.
virtual int GetData(uint8_t *buf, int buf_size)=0
Reads up to buf_size bytes of stream data into buf.
Demultiplexes a video stream into elementary packets that are passed to the rocDecode parser.
Definition: video_demuxer.h:185
const uint32_t GetHeight() const
Returns the height, in pixels, of the demultiplexed video stream.
Definition: video_demuxer.h:467
const uint32_t GetWidth() const
Returns the width, in pixels, of the demultiplexed video stream.
Definition: video_demuxer.h:465
const uint32_t GetBytePerPixel() const
Returns the number of bytes per pixel for the stream's chroma format.
Definition: video_demuxer.h:473
AVCodecID GetCodecID()
Returns the FFmpeg codec ID of the demultiplexed video stream.
Definition: video_demuxer.h:209
~VideoDemuxer()
Destroys the demuxer and releases the underlying FFmpeg resources.
Definition: video_demuxer.h:223
int64_t TsFromFrameNumber(int64_t frame_num)
Converts a frame number to the stream's internal time base units, using the stream's frame rate.
Definition: video_demuxer.h:498
const uint32_t GetBitDepth() const
Returns the bit depth of the demultiplexed video stream.
Definition: video_demuxer.h:471
const uint32_t GetChromaHeight() const
Returns the chroma plane height, in pixels, for the stream's chroma format.
Definition: video_demuxer.h:469
const double GetFrameRate() const
Returns the real (base) frame rate of the demultiplexed video stream.
Definition: video_demuxer.h:477
VideoDemuxer(const char *input_file_path)
Constructs a demuxer that reads the video stream from a file path.
Definition: video_demuxer.h:214
VideoDemuxer(StreamProvider *stream_provider)
Constructs a demuxer that reads the video stream from a custom StreamProvider.
Definition: video_demuxer.h:219
bool IsVFR() const
Returns true if the stream is variable frame rate (its real and average frame rates differ).
Definition: video_demuxer.h:479
int64_t TsFromTime(double ts_sec)
Converts a timestamp in seconds to the stream's internal time base units.
Definition: video_demuxer.h:485
const uint32_t GetBitRate() const
Returns the bit rate, in bits per second, reported by the input stream.
Definition: video_demuxer.h:475
bool Seek(VideoSeekContext &seek_ctx, uint8_t **pp_video, int *video_size)
Seeks to a frame identified by seek_ctx and demuxes it, instead of demuxing sequentially.
Definition: video_demuxer.h:338
bool Demux(uint8_t **video, int *video_size, int64_t *pts=nullptr)
Extracts the next elementary video packet from the stream, starting at the beginning.
Definition: video_demuxer.h:253
Definition: video_demuxer.h:130
The AMD rocDecode Library.
@ rocDecVideoCodec_AV1
Definition: rocdecode.h:83
@ rocDecVideoCodec_MPEG4
Definition: rocdecode.h:80
@ rocDecVideoCodec_HEVC
Definition: rocdecode.h:82
@ rocDecVideoCodec_MPEG1
Definition: rocdecode.h:78
@ rocDecVideoCodec_JPEG
Definition: rocdecode.h:86
@ rocDecVideoCodec_VP9
Definition: rocdecode.h:85
@ rocDecVideoCodec_AVC
Definition: rocdecode.h:81
@ rocDecVideoCodec_MPEG2
Definition: rocdecode.h:79
@ rocDecVideoCodec_VP8
Definition: rocdecode.h:84
@ rocDecVideoCodec_NumCodecs
Definition: rocdecode.h:87
Definition: video_demuxer.h:120
SeekModeEnum
Enum for Seek mode.
Definition: video_demuxer.h:104
enum SeekModeEnum SeekMode
Enum for Seek mode.
SeekCriteriaEnum
Enum for Seek Criteria.
Definition: video_demuxer.h:114
enum SeekCriteriaEnum SeekCriteria
Enum for Seek Criteria.