[M120 Migration][MM] Framerate calculation 55/307755/3
authorwuxiaoliang <xliang.wu@samsung.com>
Tue, 12 Mar 2024 05:35:46 +0000 (13:35 +0800)
committerYanqing Lu <yanqing.lu@samsung.com>
Wed, 13 Mar 2024 06:03:27 +0000 (06:03 +0000)
Migrated from:

https://review.tizen.org/gerrit/#/c/platform/framework/web/chromium-efl/+/290568/
https://review.tizen.org/gerrit/#/c/platform/framework/web/chromium-efl/+/296928/

Change-Id: Ib627bb501bcc64895ab93189c35507598106ac5b
Signed-off-by: wuxiaoliang <xliang.wu@samsung.com>
38 files changed:
media/base/stream_parser.h
media/base/video_decoder_config.h
media/filters/chunk_demuxer.cc
media/filters/chunk_demuxer.h
media/filters/source_buffer_state.cc
media/filters/source_buffer_state.h
media/filters/source_buffer_stream.cc
media/filters/source_buffer_stream.h
media/filters/stream_parser_factory.cc
media/filters/stream_parser_factory.h
media/formats/mp4/avc.cc
media/formats/mp4/avc.h
media/formats/mp4/bitstream_converter.h
media/formats/mp4/hevc.cc
media/formats/mp4/hevc.h
media/formats/mp4/mp4_stream_parser.cc
media/formats/mp4/mp4_stream_parser.h
media/formats/mp4/track_run_iterator.cc
media/formats/mp4/track_run_iterator.h
media/formats/webm/webm_cluster_parser.cc
media/formats/webm/webm_cluster_parser.h
media/formats/webm/webm_stream_parser.cc
media/formats/webm/webm_stream_parser.h
media/formats/webm/webm_tracks_parser.h
media/mojo/mojom/media_types.mojom
media/mojo/mojom/video_decoder_config_mojom_traits.cc
media/mojo/mojom/video_decoder_config_mojom_traits.h
media/video/BUILD.gn
media/video/h264_parser.cc
media/video/h264_parser.h
media/video/h265_parser.cc
media/video/h265_parser.h
media/video/h26x_tools.cc [new file with mode: 0644]
media/video/h26x_tools.h [new file with mode: 0644]
tizen_src/chromium_impl/media/base/tizen/stream_framerate.cc [new file with mode: 0644]
tizen_src/chromium_impl/media/base/tizen/stream_framerate.h [new file with mode: 0644]
tizen_src/chromium_impl/media/filters/media_player_esplusplayer_tv.cc
tizen_src/chromium_impl/media/media_efl.gni

index 2e8fbfe..51b87e8 100644 (file)
 #include "media/base/eme_constants.h"
 #include "media/base/media_export.h"
 
+#if BUILDFLAG(IS_TIZEN_TV)
+#include "tizen_src/chromium_impl/media/base/tizen/stream_framerate.h"
+#endif
+
 namespace media {
 
 class MediaLog;
@@ -81,6 +85,12 @@ class MEDIA_EXPORT StreamParser {
     int detected_video_track_count = 0;
   };
 
+#if BUILDFLAG(IS_TIZEN_TV)
+  typedef base::RepeatingCallback<void(
+      const StreamFramerate::Framerate& params)>
+      FramerateSetCB;
+#endif
+
   // Indicates completion of parser initialization.
   //   params - Stream parameters.
   typedef base::OnceCallback<void(const InitParameters& params)> InitCB;
@@ -180,6 +190,10 @@ class MEDIA_EXPORT StreamParser {
   [[nodiscard]] virtual ParseStatus Parse(int max_pending_bytes_to_inspect) = 0;
   [[nodiscard]] virtual bool ProcessChunks(
       std::unique_ptr<BufferQueue> buffer_queue);
+
+#if BUILDFLAG(IS_TIZEN_TV)
+  virtual void SetFramerateCallback(const FramerateSetCB& framerate_set_cb) {}
+#endif
 };
 
 // Appends to |merged_buffers| the provided buffers in decode-timestamp order.
index 32d882d..436cfc0 100644 (file)
@@ -167,6 +167,10 @@ class MEDIA_EXPORT VideoDecoderConfig {
 #if BUILDFLAG(IS_TIZEN_TV)
   void set_hdr_info(const std::string& hdr_info) { hdr_info_ = hdr_info; }
   const std::string& hdr_info() const { return hdr_info_; }
+  void set_framerate_num(const int num) { framerate_num_ = num; }
+  void set_framerate_den(const int den) { framerate_den_ = den; }
+  int framerate_num() const { return framerate_num_; }
+  int framerate_den() const { return framerate_den_; }
 #endif
 
  private:
@@ -197,6 +201,8 @@ class MEDIA_EXPORT VideoDecoderConfig {
   bool is_rtc_ = false;
 
 #if BUILDFLAG(IS_TIZEN_TV)
+  int framerate_num_ = 0;
+  int framerate_den_ = 0;
   std::string hdr_info_;
 #endif
 
index 908d252..df72051 100644 (file)
@@ -37,11 +37,13 @@ namespace {
 std::unique_ptr<media::StreamParser> CreateParserForTypeAndCodecs(
     const std::string& content_type,
     const std::string& codecs,
-    media::MediaLog* media_log) {
+    media::MediaLog* media_log,
+    bool* has_audio,
+    bool* has_video) {
   std::vector<std::string> parsed_codec_ids;
   media::SplitCodecs(codecs, &parsed_codec_ids);
   return media::StreamParserFactory::Create(content_type, parsed_codec_ids,
-                                            media_log);
+                                            media_log, has_audio, has_video);
 }
 
 // Helper to calculate the expected codecs parsed from initialization segments
@@ -74,6 +76,17 @@ void ChunkDemuxerStream::StartReturningData() {
   ChangeState_Locked(RETURNING_DATA_FOR_READS);
 }
 
+#if BUILDFLAG(IS_TIZEN_TV)
+void ChunkDemuxerStream::SetFramerate(
+    const StreamFramerate::Framerate& framerate) {
+  if (!stream_) {
+    LOG(ERROR) << "stream is null,this:" << (void*)this;
+    return;
+  }
+  stream_->updateFramerate(framerate);
+}
+#endif
+
 void ChunkDemuxerStream::AbortReads() {
   DVLOG(1) << "ChunkDemuxerStream::AbortReads()";
   base::AutoLock auto_lock(lock_);
@@ -510,6 +523,11 @@ void ChunkDemuxer::Initialize(DemuxerHost* host,
   }
 
   std::move(open_cb).Run();
+
+#if BUILDFLAG(IS_TIZEN_TV)
+  framerate_set_cb_ = base::BindPostTaskToCurrentDefault(base::BindRepeating(
+      &ChunkDemuxer::OnFramerateSet, base::Unretained(this)));
+#endif
 }
 
 void ChunkDemuxer::Stop() {
@@ -676,7 +694,7 @@ ChunkDemuxer::Status ChunkDemuxer::AddId(
       media::StreamParserFactory::Create(std::move(audio_config)));
   DCHECK(stream_parser);
 
-  return AddIdInternal(id, std::move(stream_parser), expected_codec);
+  return AddIdInternal(id, std::move(stream_parser), expected_codec, false);
 }
 
 ChunkDemuxer::Status ChunkDemuxer::AddId(
@@ -704,7 +722,7 @@ ChunkDemuxer::Status ChunkDemuxer::AddId(
       media::StreamParserFactory::Create(std::move(video_config)));
   DCHECK(stream_parser);
 
-  return AddIdInternal(id, std::move(stream_parser), expected_codec);
+  return AddIdInternal(id, std::move(stream_parser), expected_codec, true);
 }
 
 ChunkDemuxer::Status ChunkDemuxer::AddId(const std::string& id,
@@ -723,8 +741,12 @@ ChunkDemuxer::Status ChunkDemuxer::AddId(const std::string& id,
   // needed. See https://crbug.com/786975.
   CHECK(init_cb_);
 
+  bool has_audio = false;
+  bool has_video = false;
+
   std::unique_ptr<media::StreamParser> stream_parser(
-      CreateParserForTypeAndCodecs(content_type, codecs, media_log_));
+      CreateParserForTypeAndCodecs(content_type, codecs, media_log_, &has_audio,
+                                   &has_video));
   if (!stream_parser) {
     DVLOG(1) << __func__ << " failed: unsupported content_type=" << content_type
              << " codecs=" << codecs;
@@ -732,13 +754,14 @@ ChunkDemuxer::Status ChunkDemuxer::AddId(const std::string& id,
   }
 
   return AddIdInternal(id, std::move(stream_parser),
-                       ExpectedCodecs(content_type, codecs));
+                       ExpectedCodecs(content_type, codecs), has_video);
 }
 
 ChunkDemuxer::Status ChunkDemuxer::AddIdInternal(
     const std::string& id,
     std::unique_ptr<media::StreamParser> stream_parser,
-    std::string expected_codecs) {
+    std::string expected_codecs,
+    bool has_video) {
   DVLOG(2) << __func__ << " id=" << id
            << " expected_codecs=" << expected_codecs;
   lock_.AssertAcquired();
@@ -756,6 +779,15 @@ ChunkDemuxer::Status ChunkDemuxer::AddIdInternal(
                               base::Unretained(this), id),
           media_log_);
 
+#if BUILDFLAG(IS_TIZEN_TV)
+  StreamParser::FramerateSetCB framerate_set_cb;
+  if (has_video)
+    framerate_set_cb = framerate_set_cb_;
+  else
+    // Audio don't need this call back.
+    framerate_set_cb.Reset();
+#endif
+
   // TODO(wolenetz): Change these to DCHECKs or switch to returning
   // kReachedIdLimit once less verification in release build is needed. See
   // https://crbug.com/786975.
@@ -767,7 +799,11 @@ ChunkDemuxer::Status ChunkDemuxer::AddIdInternal(
 
   source_state->Init(base::BindOnce(&ChunkDemuxer::OnSourceInitDone,
                                     base::Unretained(this), id),
-                     expected_codecs, encrypted_media_init_data_cb_);
+                     expected_codecs,
+#if BUILDFLAG(IS_TIZEN_TV)
+                     framerate_set_cb,
+#endif
+                     encrypted_media_init_data_cb_);
 
   // TODO(wolenetz): Change to DCHECKs once less verification in release build
   // is needed. See https://crbug.com/786975.
@@ -1193,9 +1229,12 @@ bool ChunkDemuxer::CanChangeType(const std::string& id,
 
   // CanChangeType() doesn't care if there has or hasn't been received a first
   // initialization segment for the source buffer corresponding to |id|.
+  bool has_audio = false;
+  bool has_video = false;
 
   std::unique_ptr<media::StreamParser> stream_parser(
-      CreateParserForTypeAndCodecs(content_type, codecs, media_log_));
+      CreateParserForTypeAndCodecs(content_type, codecs, media_log_, &has_audio,
+                                   &has_video));
   return !!stream_parser;
 }
 
@@ -1210,11 +1249,27 @@ void ChunkDemuxer::ChangeType(const std::string& id,
   DCHECK(state_ == INITIALIZING || state_ == INITIALIZED) << state_;
   DCHECK(IsValidId_Locked(id));
 
+  bool has_audio = false;
+  bool has_video = false;
+
   std::unique_ptr<media::StreamParser> stream_parser(
-      CreateParserForTypeAndCodecs(content_type, codecs, media_log_));
+      CreateParserForTypeAndCodecs(content_type, codecs, media_log_, &has_audio,
+                                   &has_video));
   // Caller should query CanChangeType() first to protect from failing this.
   DCHECK(stream_parser);
+
+#if BUILDFLAG(IS_TIZEN_TV)
+  StreamParser::FramerateSetCB framerate_set_cb;
+  if (has_video)
+    framerate_set_cb = framerate_set_cb_;
+  else
+    // Audio don't need this call back.
+    framerate_set_cb.Reset();
+#endif
   source_state_map_[id]->ChangeType(std::move(stream_parser),
+#if BUILDFLAG(IS_TIZEN_TV)
+                                    framerate_set_cb,
+#endif
                                     ExpectedCodecs(content_type, codecs));
 }
 
@@ -1456,6 +1511,15 @@ bool ChunkDemuxer::IsSeekWaitingForData_Locked() const {
   return false;
 }
 
+#if BUILDFLAG(IS_TIZEN_TV)
+void ChunkDemuxer::OnFramerateSet(const StreamFramerate::Framerate& framerate) {
+  if (framerate.num && framerate.den) {
+    for (const auto& s : video_streams_)
+      s->SetFramerate(framerate);
+  }
+}
+#endif
+
 void ChunkDemuxer::OnSourceInitDone(
     const std::string& source_id,
     const StreamParser::InitParameters& params) {
index 013993f..b38efa0 100644 (file)
@@ -54,6 +54,10 @@ class MEDIA_EXPORT ChunkDemuxerStream : public DemuxerStream {
   void CompletePendingReadIfPossible();
   void Shutdown();
 
+#if BUILDFLAG(IS_TIZEN_TV)
+  void SetFramerate(const StreamFramerate::Framerate& framerate);
+#endif
+
   // SourceBufferStream manipulation methods.
   void Seek(base::TimeDelta time);
   bool IsSeekWaitingForData() const;
@@ -467,7 +471,8 @@ class MEDIA_EXPORT ChunkDemuxer : public Demuxer {
   ChunkDemuxer::Status AddIdInternal(
       const std::string& id,
       std::unique_ptr<media::StreamParser> stream_parser,
-      std::string expected_codecs);
+      std::string expected_codecs,
+      bool has_video);
 
   // Helper for vide and audio track changing.
   void FindAndEnableProperTracks(const std::vector<MediaTrack::Id>& track_ids,
@@ -492,6 +497,10 @@ class MEDIA_EXPORT ChunkDemuxer : public Demuxer {
   void OnSourceInitDone(const std::string& source_id,
                         const StreamParser::InitParameters& params);
 
+#if BUILDFLAG(IS_TIZEN_TV)
+  void OnFramerateSet(const StreamFramerate::Framerate& framerate);
+#endif
+
   // Creates a DemuxerStream of the specified |type| for the SourceBufferState
   // with the given |source_id|.
   // Returns a pointer to a new ChunkDemuxerStream instance, which is owned by
@@ -571,6 +580,10 @@ class MEDIA_EXPORT ChunkDemuxer : public Demuxer {
   // into the INITIALIZED only after all ids/SourceBuffers got init segment.
   std::set<std::string> pending_source_init_ids_;
 
+#if BUILDFLAG(IS_TIZEN_TV)
+  StreamParser::FramerateSetCB framerate_set_cb_;
+#endif
+
   base::TimeDelta duration_ = kNoTimestamp;
 
   // The duration passed to the last SetDuration(). If SetDuration() is never
index 039ada0..60d1051 100644 (file)
@@ -140,19 +140,31 @@ SourceBufferState::~SourceBufferState() {
   Shutdown();
 }
 
-void SourceBufferState::Init(StreamParser::InitCB init_cb,
-                             const std::string& expected_codecs,
-                             const StreamParser::EncryptedMediaInitDataCB&
-                                 encrypted_media_init_data_cb) {
+void SourceBufferState::Init(
+    StreamParser::InitCB init_cb,
+    const std::string& expected_codecs,
+#if BUILDFLAG(IS_TIZEN_TV)
+    const StreamParser::FramerateSetCB& framerate_set_cb,
+#endif
+    const StreamParser::EncryptedMediaInitDataCB&
+        encrypted_media_init_data_cb) {
   DCHECK_EQ(state_, UNINITIALIZED);
   init_cb_ = std::move(init_cb);
   encrypted_media_init_data_cb_ = encrypted_media_init_data_cb;
   state_ = PENDING_PARSER_CONFIG;
   InitializeParser(expected_codecs);
+
+#if BUILDFLAG(IS_TIZEN_TV)
+  if (framerate_set_cb)
+    stream_parser_->SetFramerateCallback(framerate_set_cb);
+#endif
 }
 
 void SourceBufferState::ChangeType(
     std::unique_ptr<StreamParser> new_stream_parser,
+#if BUILDFLAG(IS_TIZEN_TV)
+    const StreamParser::FramerateSetCB& framerate_set_cb,
+#endif
     const std::string& new_expected_codecs) {
   DCHECK_GE(state_, PENDING_PARSER_CONFIG);
   DCHECK_NE(state_, PENDING_PARSER_INIT);
@@ -165,6 +177,11 @@ void SourceBufferState::ChangeType(
 
   stream_parser_ = std::move(new_stream_parser);
   InitializeParser(new_expected_codecs);
+
+#if BUILDFLAG(IS_TIZEN_TV)
+  if (framerate_set_cb)
+    stream_parser_->SetFramerateCallback(framerate_set_cb);
+#endif
 }
 
 void SourceBufferState::SetSequenceMode(bool sequence_mode) {
index 53e98cf..30df4bf 100644 (file)
@@ -44,6 +44,9 @@ class MEDIA_EXPORT SourceBufferState {
 
   void Init(StreamParser::InitCB init_cb,
             const std::string& expected_codecs,
+#if BUILDFLAG(IS_TIZEN_TV)
+            const StreamParser::FramerateSetCB& framerate_set_cb,
+#endif
             const StreamParser::EncryptedMediaInitDataCB&
                 encrypted_media_init_data_cb);
 
@@ -51,6 +54,9 @@ class MEDIA_EXPORT SourceBufferState {
   // first ensure that ResetParserState() was done to flush any pending frames
   // from the old stream parser.
   void ChangeType(std::unique_ptr<StreamParser> new_stream_parser,
+#if BUILDFLAG(IS_TIZEN_TV)
+                  const StreamParser::FramerateSetCB& framerate_set_cb,
+#endif
                   const std::string& new_expected_codecs);
 
   // Appends media data to the StreamParser, but no parsing is done of it yet,
index c909fa1..031f38d 100644 (file)
@@ -1783,6 +1783,17 @@ base::TimeDelta SourceBufferStream::GetMaxInterbufferDistance() const {
   return max_interbuffer_distance_;
 }
 
+#if BUILDFLAG(IS_TIZEN_TV)
+void SourceBufferStream::updateFramerate(
+    const StreamFramerate::Framerate& framerate) {
+  CHECK(current_config_index_ >= 0 &&
+        static_cast<size_t>(current_config_index_) < video_configs_.size());
+
+  video_configs_[current_config_index_].set_framerate_num(framerate.num);
+  video_configs_[current_config_index_].set_framerate_den(framerate.den);
+}
+#endif
+
 bool SourceBufferStream::UpdateAudioConfig(const AudioDecoderConfig& config,
                                            bool allow_codec_change) {
   DCHECK(!audio_configs_.empty());
index 403bbda..4cc450a 100644 (file)
@@ -157,6 +157,10 @@ class MEDIA_EXPORT SourceBufferStream {
   const AudioDecoderConfig& GetCurrentAudioDecoderConfig();
   const VideoDecoderConfig& GetCurrentVideoDecoderConfig();
 
+#if BUILDFLAG(IS_TIZEN_TV)
+  void updateFramerate(const StreamFramerate::Framerate& framerate);
+#endif
+
   // Notifies this object that the audio config has changed and buffers in
   // future Append() calls should be associated with this new config.
   // If the codec is allowed to change, the caller should set
index aca8dff..5ed64e5 100644 (file)
@@ -602,7 +602,9 @@ SupportsType StreamParserFactory::IsTypeSupported(
 std::unique_ptr<StreamParser> StreamParserFactory::Create(
     base::StringPiece type,
     base::span<const std::string> codecs,
-    MediaLog* media_log) {
+    MediaLog* media_log,
+    bool* has_audio,
+    bool* has_video) {
   std::unique_ptr<StreamParser> stream_parser;
   ParserFactoryFunction factory_function;
   std::vector<CodecInfo::HistogramTag> audio_codecs;
@@ -637,6 +639,8 @@ std::unique_ptr<StreamParser> StreamParserFactory::Create(
       }
     }
 
+    *has_audio = !audio_codecs.empty();
+    *has_video = !video_codecs.empty();
     stream_parser.reset(factory_function(codecs, media_log));
   }
 
index 14a550b..57d87ab 100644 (file)
@@ -57,7 +57,9 @@ class MEDIA_EXPORT StreamParserFactory {
   static std::unique_ptr<StreamParser> Create(
       base::StringPiece type,
       base::span<const std::string> codecs,
-      MediaLog* media_log);
+      MediaLog* media_log,
+      bool* has_audio,
+      bool* has_video);
   static std::unique_ptr<StreamParser> Create(
       std::unique_ptr<AudioDecoderConfig> audio_config);
   static std::unique_ptr<StreamParser> Create(
index 40609a8..2420fd5 100644 (file)
@@ -357,6 +357,12 @@ bool AVCBitstreamConverter::ConvertAndAnalyzeFrame(
   *analysis_result = Analyze(frame_buf, subsamples);
 
   if (analysis_result->is_keyframe.value_or(is_keyframe)) {
+#if BUILDFLAG(IS_TIZEN_TV)
+    if (framerate_.has_value()) {
+      H264Parser{}.UpdateTimingInfoIfNeeded(
+          &avc_config_->sps_list, framerate_->den, 2 * framerate_->num);
+    }
+#endif
     // If this is a keyframe, we (re-)inject SPS and PPS headers at the start of
     // a frame. If subsample info is present, we also update the clear byte
     // count for that first subsample.
@@ -366,6 +372,14 @@ bool AVCBitstreamConverter::ConvertAndAnalyzeFrame(
   return true;
 }
 
+#if BUILDFLAG(IS_TIZEN_TV)
+void AVCBitstreamConverter::SetFramerate(
+    const StreamFramerate::Framerate& framerate) {
+  framerate_ = framerate;
+  LOG(INFO) << "frame rate: " << framerate_->toDouble();
+}
+#endif
+
 BitstreamConverter::AnalysisResult AVCBitstreamConverter::Analyze(
     std::vector<uint8_t>* frame_buf,
     std::vector<SubsampleEntry>* subsamples) const {
index 97a3502..17e5c36 100644 (file)
 #include "media/base/media_export.h"
 #include "media/formats/mp4/bitstream_converter.h"
 
+#if BUILDFLAG(IS_TIZEN_TV)
+#include "media/video/h264_parser.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+#endif
+
 namespace media {
 
 struct SubsampleEntry;
@@ -78,6 +83,9 @@ class AVCBitstreamConverter : public BitstreamConverter {
                               bool is_keyframe,
                               std::vector<SubsampleEntry>* subsamples,
                               AnalysisResult* analysis_result) const override;
+#if BUILDFLAG(IS_TIZEN_TV)
+  void SetFramerate(const StreamFramerate::Framerate& framerate) override;
+#endif
 
  private:
   ~AVCBitstreamConverter() override;
@@ -85,6 +93,10 @@ class AVCBitstreamConverter : public BitstreamConverter {
       std::vector<uint8_t>* frame_buf,
       std::vector<SubsampleEntry>* subsamples) const override;
   std::unique_ptr<AVCDecoderConfigurationRecord> avc_config_;
+
+#if BUILDFLAG(IS_TIZEN_TV)
+  absl::optional<StreamFramerate::Framerate> framerate_;
+#endif
 };
 
 }  // namespace mp4
index 072c485..2a315d2 100644 (file)
 #include "media/base/media_export.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
+#if BUILDFLAG(IS_TIZEN_TV)
+#include "media/base/tizen/stream_framerate.h"
+#endif
+
 namespace media {
 
 struct SubsampleEntry;
@@ -55,6 +59,11 @@ class MEDIA_EXPORT BitstreamConverter
       std::vector<SubsampleEntry>* subsamples,
       AnalysisResult* analysis_result) const = 0;
 
+#if BUILDFLAG(IS_TIZEN_TV)
+  // For overriding timing info in SPS/VPS.
+  virtual void SetFramerate(const StreamFramerate::Framerate& framerate) = 0;
+#endif
+
  protected:
   friend class base::RefCountedThreadSafe<BitstreamConverter>;
   virtual ~BitstreamConverter();
index e186e37..d9605c2 100644 (file)
 #include "media/video/h265_nalu_parser.h"
 #endif  // BUILDFLAG(ENABLE_HEVC_PARSER_AND_HW_DECODER)
 
+#if BUILDFLAG(IS_TIZEN_TV)
+#include "media/video/h265_parser.h"
+#endif
+
 namespace media {
 namespace mp4 {
 
@@ -643,12 +647,34 @@ bool HEVCBitstreamConverter::ConvertAndAnalyzeFrame(
     // If this is a keyframe, we (re-)inject HEVC params headers at the start of
     // a frame. If subsample info is present, we also update the clear byte
     // count for that first subsample.
+#if BUILDFLAG(IS_TIZEN_TV)
+    if (framerate_.has_value()) {
+      auto spses_array_iterator =
+          find_if(hevc_config_->arrays.begin(), hevc_config_->arrays.end(),
+                  [](const HEVCDecoderConfigurationRecord::HVCCNALArray& ref) {
+                    return (ref.first_byte & 0x3f) == H265NALU::SPS_NUT;
+                  });
+
+      if (spses_array_iterator != hevc_config_->arrays.end()) {
+        H265Parser{}.UpdateTimingInfoIfNeeded(&spses_array_iterator->units,
+                                              framerate_->den, framerate_->num);
+      }
+    }
+#endif
     RCHECK(HEVC::InsertParamSetsAnnexB(*hevc_config_, frame_buf, subsamples));
   }
 
   return true;
 }
 
+#if BUILDFLAG(IS_TIZEN_TV)
+void HEVCBitstreamConverter::SetFramerate(
+    const StreamFramerate::Framerate& framerate) {
+  framerate_ = framerate;
+  LOG(INFO) << "frame rate: " << framerate_->toDouble();
+}
+#endif
+
 BitstreamConverter::AnalysisResult HEVCBitstreamConverter::Analyze(
     std::vector<uint8_t>* frame_buf,
     std::vector<SubsampleEntry>* subsamples) const {
index f286f89..b973f8b 100644 (file)
 #include "media/formats/mp4/bitstream_converter.h"
 #include "media/formats/mp4/box_definitions.h"
 
+#if BUILDFLAG(IS_TIZEN_TV)
+#include "third_party/abseil-cpp/absl/types/optional.h"
+#endif
+
 namespace media {
 
 struct SubsampleEntry;
@@ -119,12 +123,20 @@ class HEVCBitstreamConverter : public BitstreamConverter {
                               std::vector<SubsampleEntry>* subsamples,
                               AnalysisResult* analysis_result) const override;
 
+#if BUILDFLAG(IS_TIZEN_TV)
+  void SetFramerate(const StreamFramerate::Framerate& framerate) override;
+#endif
+
  private:
   ~HEVCBitstreamConverter() override;
   AnalysisResult Analyze(
       std::vector<uint8_t>* frame_buf,
       std::vector<SubsampleEntry>* subsamples) const override;
   std::unique_ptr<HEVCDecoderConfigurationRecord> hevc_config_;
+
+#if BUILDFLAG(IS_TIZEN_TV)
+  absl::optional<StreamFramerate::Framerate> framerate_;
+#endif
 };
 
 }  // namespace mp4
index 436f0ce..9214b68 100644 (file)
@@ -40,6 +40,7 @@ namespace {
 const int kMaxEmptySampleLogs = 20;
 const int kMaxInvalidConversionLogs = 20;
 const int kMaxVideoKeyframeMismatchLogs = 10;
+StreamParser::InitParameters params_(kInfiniteDuration);
 
 // Caller should be prepared to handle return of EncryptionScheme::kUnencrypted
 // in case of unsupported scheme.
@@ -118,6 +119,14 @@ void MP4StreamParser::Init(
   media_log_ = media_log;
 }
 
+#if BUILDFLAG(IS_TIZEN_TV)
+void MP4StreamParser::SetFramerateCallback(
+    const FramerateSetCB& framerate_set_cb) {
+  DCHECK(!framerate_set_cb.is_null());
+  framerate_set_cb_ = framerate_set_cb;
+}
+#endif
+
 void MP4StreamParser::Reset() {
   queue_.Reset();
   max_parse_offset_ = 0;
@@ -702,6 +711,12 @@ bool MP4StreamParser::ParseMoov(BoxReader* reader) {
 
   RCHECK(config_cb_.Run(std::move(media_tracks)));
 
+#if BUILDFLAG(IS_TIZEN_TV)
+  // Recheck framerate when video config change happen.
+  if (has_video_)
+    average_framerate_.reset();
+#endif
+
   StreamParser::InitParameters params(kInfiniteDuration);
   if (moov_->extends.header.fragment_duration > 0) {
     params.duration = TimeDeltaFromRational(
@@ -751,7 +766,12 @@ bool MP4StreamParser::ParseMoov(BoxReader* reader) {
   if (init_cb_) {
     params.detected_audio_track_count = detected_audio_track_count;
     params.detected_video_track_count = detected_video_track_count;
+
+#if BUILDFLAG(IS_TIZEN_TV)
+    params_ = params;
+#else
     std::move(init_cb_).Run(params);
+#endif
   }
 
   return true;
@@ -766,6 +786,49 @@ bool MP4StreamParser::ParseMoof(BoxReader* reader) {
   RCHECK(runs_->Init(moof));
   RCHECK(ComputeHighestEndOffset(moof));
 
+#if BUILDFLAG(IS_TIZEN_TV)
+  if (runs_ && !runs_->is_audio() && !average_framerate_.has_value()) {
+    // Min Trun sample count for calculate fps.
+    constexpr const size_t kMinTrunSampleCount = 10;
+
+    const TrackRunSampleInfo& trun_sample_info = runs_->GetTrunSampleInfo();
+    const auto& traf_sample_count = trun_sample_info.trun_sample_size;
+    const auto& total_duration = trun_sample_info.trun_duration;
+
+    LOG(INFO) << "Estimating framerate"
+              << ", total traf sample count: " << traf_sample_count
+              << ", total duration: " << total_duration
+              << ", time scale: " << trun_sample_info.time_scale;
+
+    if (traf_sample_count == 0 || total_duration == 0) {
+      LOG(ERROR) << "Cannot estimate framerate";
+    } else {
+      if (traf_sample_count < kMinTrunSampleCount) {
+        LOG(WARNING) << "Low sample count for framerate estimation";
+      }
+      // Base num to calculate mp4 framerate
+      constexpr const int64_t kMp4FramerateBase = 1001;
+      StreamFramerate sft(kMp4FramerateBase * trun_sample_info.time_scale *
+                              traf_sample_count / total_duration,
+                          kMp4FramerateBase);
+      int num{0};
+      int den{0};
+      sft.Calculate(&num, &den);
+      average_framerate_ = StreamFramerate::Framerate{num, den};
+
+      if (!framerate_set_cb_.is_null()) {
+        framerate_set_cb_.Run(average_framerate_.value());
+      }
+      if (runs_ && runs_->video_description().frame_bitstream_converter)
+        runs_->video_description().frame_bitstream_converter->SetFramerate(
+            average_framerate_.value());
+    }
+  }
+  if (init_cb_) {
+    std::move(init_cb_).Run(params_);
+  }
+#endif
+
   if (!moof.pssh.empty())
     OnEncryptedMediaInitData(moof.pssh);
 
index abae96d..100e470 100644 (file)
@@ -59,6 +59,10 @@ class MEDIA_EXPORT MP4StreamParser : public StreamParser {
   VideoTransformation CalculateRotation(const TrackHeader& track,
                                         const MovieHeader& movie);
 
+#if BUILDFLAG(IS_TIZEN_TV)
+  void SetFramerateCallback(const FramerateSetCB& framerate_set_cb) override;
+#endif
+
  private:
   enum State {
     kWaitingForInit,
@@ -125,6 +129,10 @@ class MEDIA_EXPORT MP4StreamParser : public StreamParser {
   EndMediaSegmentCB end_of_segment_cb_;
   raw_ptr<MediaLog> media_log_;
 
+#if BUILDFLAG(IS_TIZEN_TV)
+  FramerateSetCB framerate_set_cb_;
+#endif
+
   // Bytes of the mp4 stream.
   // `max_parse_offset_` tracks the point in `queue_` beyond which no data may
   // yet be parsed even if it is less than the queue's tail offset. This allows
@@ -168,6 +176,10 @@ class MEDIA_EXPORT MP4StreamParser : public StreamParser {
   const bool has_sbr_;
   const bool has_flac_;
 
+#if BUILDFLAG(IS_TIZEN_TV)
+  absl::optional<StreamFramerate::Framerate> average_framerate_;
+#endif
+
   // Tracks the number of MEDIA_LOGS for skipping empty trun samples.
   int num_empty_samples_skipped_;
 
index c20a501..299510c 100644 (file)
@@ -284,6 +284,10 @@ class CompareMinTrackRunDataOffset {
 bool TrackRunIterator::Init(const MovieFragment& moof) {
   runs_.clear();
 
+#if BUILDFLAG(IS_TIZEN_TV)
+  trun_sample_info_ = {0, 0, 0};
+#endif
+
   for (size_t i = 0; i < moof.tracks.size(); i++) {
     const TrackFragment& traf = moof.tracks[i];
 
@@ -453,6 +457,28 @@ bool TrackRunIterator::Init(const MovieFragment& moof) {
 
         run_start_dts += tri.samples[k].duration;
 
+#if BUILDFLAG(IS_TIZEN_TV)
+        if (tri.samples[k].duration && !tri.is_audio) {
+          trun_sample_info_.time_scale = tri.timescale;
+          const size_t kMinTrunSampleCount = 10;
+          // The duration gap should be in range : [-%20~ 20%].
+          const size_t kBaseDividerCount = 5;
+          if (trun_sample_info_.trun_sample_size > kMinTrunSampleCount) {
+            const int64_t average_duration = trun_sample_info_.trun_duration /
+                                             trun_sample_info_.trun_sample_size;
+            const int64_t duration_gap =
+                std::abs(tri.samples[k].duration - average_duration);
+            if (duration_gap <= average_duration / kBaseDividerCount) {
+              trun_sample_info_.trun_duration += tri.samples[k].duration;
+              trun_sample_info_.trun_sample_size++;
+            }
+          } else {
+            trun_sample_info_.trun_duration += tri.samples[k].duration;
+            trun_sample_info_.trun_sample_size++;
+          }
+        }
+#endif
+
         if (!is_sample_to_group_valid) {
           // Set group description index to 0 to read encryption information
           // from TrackEncryption Box.
@@ -548,6 +574,12 @@ bool TrackRunIterator::AdvanceRun() {
   return ResetRun();
 }
 
+#if BUILDFLAG(IS_TIZEN_TV)
+const TrackRunSampleInfo& TrackRunIterator::GetTrunSampleInfo() {
+  return trun_sample_info_;
+}
+#endif
+
 bool TrackRunIterator::ResetRun() {
   // TODO(sandersd): Should we clear all the values if the run is not valid?
   if (!IsRunValid())
index c7fd267..eb37840 100644 (file)
@@ -33,6 +33,14 @@ DecodeTimestamp MEDIA_EXPORT DecodeTimestampFromRational(int64_t numer,
 struct SampleInfo;
 struct TrackRunInfo;
 
+#if BUILDFLAG(IS_TIZEN_TV)
+struct TrackRunSampleInfo {
+  int64_t time_scale;
+  int64_t trun_duration;
+  size_t trun_sample_size;
+};
+#endif
+
 class MEDIA_EXPORT TrackRunIterator {
  public:
   // Create a new TrackRunIterator. A reference to |moov| will be retained for
@@ -74,6 +82,11 @@ class MEDIA_EXPORT TrackRunIterator {
   // in bytes past the the head of the MOOF box).
   int64_t GetMaxClearOffset();
 
+#if BUILDFLAG(IS_TIZEN_TV)
+  // Get trun samples' info : duration, sample count and time_scale.
+  const TrackRunSampleInfo& GetTrunSampleInfo();
+#endif
+
   // Property of the current run. Only valid if IsRunValid().
   uint32_t track_id() const;
   int64_t aux_info_offset() const;
@@ -120,6 +133,11 @@ class MEDIA_EXPORT TrackRunIterator {
   int64_t sample_dts_;
   int64_t sample_cts_;
   int64_t sample_offset_;
+
+#if BUILDFLAG(IS_TIZEN_TV)
+  // Accumulates info on samples from all trun boxes in a traf box.
+  TrackRunSampleInfo trun_sample_info_;
+#endif
 };
 
 }  // namespace mp4
index f44fe5f..3f6bc7f 100644 (file)
@@ -70,6 +70,10 @@ WebMClusterParser::~WebMClusterParser() = default;
 void WebMClusterParser::Reset() {
   last_block_timecode_.reset();
   cluster_timecode_ = -1;
+#if BUILDFLAG(IS_TIZEN_TV)
+  block_count_ = 0;
+  block_time_code_ = 0;
+#endif
   cluster_start_time_ = kNoTimestamp;
   cluster_ended_ = false;
   parser_.Reset();
@@ -234,6 +238,10 @@ WebMParserClient* WebMClusterParser::OnListStart(int id) {
   if (id == kWebMIdCluster) {
     cluster_timecode_ = -1;
     cluster_start_time_ = kNoTimestamp;
+#if BUILDFLAG(IS_TIZEN_TV)
+    block_count_ = 0;
+    block_time_code_ = 0;
+#endif
   } else if (id == kWebMIdBlockGroup) {
     block_data_.reset();
     block_data_size_ = -1;
@@ -445,6 +453,10 @@ bool WebMClusterParser::OnBlock(bool is_simple_block,
     track = &video_;
     encryption_key_id = video_encryption_key_id_;
     buffer_type = DemuxerStream::VIDEO;
+#if BUILDFLAG(IS_TIZEN_TV)
+    block_count_++;
+    block_time_code_ = timecode;
+#endif
   } else if (ignored_tracks_.find(track_num) != ignored_tracks_.end()) {
     return true;
   } else {
index 350b669..57aa295 100644 (file)
@@ -192,6 +192,11 @@ class MEDIA_EXPORT WebMClusterParser : public WebMParserClient {
   // Returns true if the last Parse() call stopped at the end of a cluster.
   bool cluster_ended() const { return cluster_ended_; }
 
+#if BUILDFLAG(IS_TIZEN_TV)
+  int BlockCount() const { return block_count_; }
+  int BlockTimeCode() const { return block_time_code_; }
+#endif
+
  private:
   // WebMParserClient methods.
   WebMParserClient* OnListStart(int id) override;
@@ -292,6 +297,11 @@ class MEDIA_EXPORT WebMClusterParser : public WebMParserClient {
   DecodeTimestamp ready_buffer_upper_bound_;
 
   raw_ptr<MediaLog> media_log_;
+
+#if BUILDFLAG(IS_TIZEN_TV)
+  int block_count_{0};
+  int block_time_code_{0};
+#endif
 };
 
 }  // namespace media
index db4ed43..3b59407 100644 (file)
@@ -27,6 +27,10 @@ namespace media {
 
 WebMStreamParser::WebMStreamParser()
     : state_(kWaitingForInit),
+#if BUILDFLAG(IS_TIZEN_TV)
+      is_framerate_set_(false),
+      has_default_duration_(true),
+#endif
       unknown_segment_size_(false) {
 }
 
@@ -59,6 +63,14 @@ void WebMStreamParser::Init(
   media_log_ = media_log;
 }
 
+#if BUILDFLAG(IS_TIZEN_TV)
+void WebMStreamParser::SetFramerateCallback(
+    const FramerateSetCB& framerate_set_cb) {
+  DCHECK(!framerate_set_cb.is_null());
+  framerate_set_cb_ = framerate_set_cb;
+}
+#endif
+
 void WebMStreamParser::Flush() {
   DCHECK_NE(state_, kWaitingForInit);
 
@@ -285,6 +297,33 @@ int WebMStreamParser::ParseInfoAndTracks(const uint8_t* data, int size) {
   if (video_config.is_encrypted())
     OnEncryptedMediaInitData(tracks_parser.video_encryption_key_id());
 
+#if BUILDFLAG(IS_TIZEN_TV)
+  if (!framerate_set_cb_.is_null()) {
+    is_framerate_set_ = false;
+    StreamFramerate::Framerate framerate;
+
+    if (tracks_parser.GetVideoDefaultDuration(timecode_scale_in_us) !=
+        kNoTimestamp) {
+      const int64_t default_video_duration =
+          tracks_parser.video_default_duration();
+
+      // Base num to calculate webm framerate
+      constexpr const int64_t kWebmFramerateBase = 1000000000;
+
+      StreamFramerate sft(kWebmFramerateBase, default_video_duration);
+      sft.Calculate(&framerate.num, &framerate.den);
+      is_framerate_set_ = true;
+      has_default_duration_ = true;
+      framerate_set_cb_.Run(framerate);
+    } else {
+      stream_scale_time_ = info_parser.timecode_scale_ns();
+      // Try other way to calculate framerate if default_video_duration not
+      // exists.
+      has_default_duration_ = false;
+    }
+  }
+#endif
+
   std::unique_ptr<MediaTracks> media_tracks = tracks_parser.media_tracks();
   CHECK(media_tracks.get());
   if (!config_cb_.Run(std::move(media_tracks))) {
@@ -329,6 +368,26 @@ int WebMStreamParser::ParseCluster(const uint8_t* data, int size) {
     return -1;
   }
 
+#if BUILDFLAG(IS_TIZEN_TV)
+  if (!framerate_set_cb_.is_null() && !is_framerate_set_ &&
+      !has_default_duration_) {
+    const int cluster_block_count = cluster_parser_->BlockCount();
+    const int cluster_block_time_code = cluster_parser_->BlockTimeCode();
+    const int kMinBlockCount = 20;
+    if (cluster_block_count >= kMinBlockCount && cluster_block_time_code) {
+      StreamFramerate::Framerate framerate;
+      // avg_frame_duration in us
+      const int64_t avg_frame_duration =
+          cluster_block_time_code * 1000 / (cluster_block_count - 1);
+
+      StreamFramerate sft(stream_scale_time_, avg_frame_duration);
+      sft.Calculate(&framerate.num, &framerate.den);
+      is_framerate_set_ = true;
+      framerate_set_cb_.Run(framerate);
+    }
+  }
+#endif
+
   if (cluster_ended) {
     ChangeState(kParsingHeaders);
     end_of_segment_cb_.Run();
index ffcac85..cfe5e54 100644 (file)
@@ -43,6 +43,10 @@ class MEDIA_EXPORT WebMStreamParser : public StreamParser {
                                          size_t size) override;
   [[nodiscard]] ParseStatus Parse(int max_pending_bytes_to_inspect) override;
 
+#if BUILDFLAG(IS_TIZEN_TV)
+  void SetFramerateCallback(const FramerateSetCB& framerate_set_cb) override;
+#endif
+
  private:
   enum State {
     kWaitingForInit,
@@ -85,6 +89,13 @@ class MEDIA_EXPORT WebMStreamParser : public StreamParser {
   EndMediaSegmentCB end_of_segment_cb_;
   raw_ptr<MediaLog> media_log_;
 
+#if BUILDFLAG(IS_TIZEN_TV)
+  FramerateSetCB framerate_set_cb_;
+  bool is_framerate_set_;
+  int64_t stream_scale_time_;
+  bool has_default_duration_;
+#endif
+
   bool unknown_segment_size_;
 
   std::unique_ptr<WebMClusterParser> cluster_parser_;
index 99b000e..78d621f 100644 (file)
@@ -88,6 +88,12 @@ class MEDIA_EXPORT WebMTracksParser : public WebMParserClient {
     return std::move(media_tracks_);
   }
 
+#if BUILDFLAG(IS_TIZEN_TV)
+  const int64_t video_default_duration() const {
+    return video_default_duration_;
+  }
+#endif
+
  private:
   // To test PrecisionCappedDefaultDuration.
   FRIEND_TEST_ALL_PREFIXES(WebMTracksParserTest, PrecisionCapping);
index 475edc8..d86cdf8 100644 (file)
@@ -198,6 +198,10 @@ struct VideoDecoderConfig {
   gfx.mojom.HDRMetadata? hdr_metadata;
   [EnableIf=is_tizen_tv]
   string hdr_info;
+  [EnableIf=is_tizen_tv]
+  int32 framerate_num;
+  [EnableIf=is_tizen_tv]
+  int32 framerate_den;
 };
 
 // Native struct media::SubsampleEntry;
index a292413..ed74736 100644 (file)
@@ -68,6 +68,9 @@ bool StructTraits<media::mojom::VideoDecoderConfigDataView,
   if (!input.ReadHdrInfo(&hdr))
     return false;
   output->set_hdr_info(hdr.c_str());
+
+  output->set_framerate_num(input.framerate_num());
+  output->set_framerate_den(input.framerate_den());
 #endif
 
   if (!output->IsValidConfig())
index 072da54..5dff94d 100644 (file)
@@ -78,6 +78,14 @@ struct StructTraits<media::mojom::VideoDecoderConfigDataView,
   static std::string hdr_info(const media::VideoDecoderConfig& input) {
     return input.hdr_info();
   }
+
+  static int32_t framerate_num(const media::VideoDecoderConfig& input) {
+    return input.framerate_num();
+  }
+
+  static int32_t framerate_den(const media::VideoDecoderConfig& input) {
+    return input.framerate_den();
+  }
 #endif
 
   static bool Read(media::mojom::VideoDecoderConfigDataView input,
index 4e27ad1..97debb1 100644 (file)
@@ -31,6 +31,8 @@ source_set("video") {
     "h264_parser.h",
     "h264_poc.cc",
     "h264_poc.h",
+    "h26x_tools.cc",
+    "h26x_tools.h",
     "half_float_maker.cc",
     "half_float_maker.h",
     "offloading_video_encoder.cc",
index 12206e4..39cc2c9 100644 (file)
@@ -13,6 +13,7 @@
 #include "base/numerics/safe_math.h"
 #include "media/base/subsample_entry.h"
 #include "media/video/bit_reader_macros.h"
+#include "media/video/h26x_tools.h"
 #include "ui/gfx/geometry/rect.h"
 #include "ui/gfx/geometry/size.h"
 #include "ui/gfx/hdr_metadata.h"
@@ -265,6 +266,116 @@ void H264Parser::Reset() {
   previous_nalu_range_.clear();
 }
 
+void H264Parser::UpdateTimingInfoIfNeeded(
+    std::vector<std::vector<uint8_t>>* spses_array,
+    int num_units_in_tick,
+    int time_scale) {
+  media::H264Parser::Result result;
+  media::H264NALU nalu;
+
+  for (auto& sps : *spses_array) {
+    // Reset bit reader, skip first byte with NAL identifier
+    br_.Initialize(sps.data() + 1, sps.size() - 1);
+    int sps_id = 0;
+    result = ParseSPS(&sps_id);
+    DCHECK(result == media::H264Parser::kOk);
+
+    if (active_SPSes_.find(sps_id) == active_SPSes_.end()) {
+      DVLOG(3) << "Error while parsing SPS unit";
+      continue;
+    }
+
+    if (!active_SPSes_[sps_id]->vui_parameters_present_flag) {
+      DVLOG(1) << "VUI parameters are not present in SPS. "
+               << "Adding framerate information skipped.";
+      continue;
+    }
+
+    if (active_SPSes_[sps_id]->timing_info_present_flag) {
+      LOG(INFO) << "VUI timing info present in SPS " << sps_id;
+      continue;
+    }
+
+    auto new_sps =
+        PrepareSPSWithTimingInfo(sps, sps_id, num_units_in_tick, time_scale);
+
+    LOG(INFO) << "Inserting VUI timing info: " << time_scale << '/'
+              << num_units_in_tick << " into SPS " << sps_id;
+
+    sps.swap(new_sps);
+  }
+}
+
+std::vector<uint8_t> H264Parser::PrepareSPSWithTimingInfo(
+    const std::vector<uint8_t>& old_sps,
+    int sps_id,
+    int num_units_in_tick,
+    int time_scale) {
+  const auto& sps_parameters = active_SPSes_[sps_id];
+
+  int bit_offset_from_end = sps_parameters->timing_info_bit_offset_from_nal_end;
+  int currently_processed_bit_index =
+      (bit_offset_from_end - 1 + kBitsInByte) % kBitsInByte;
+  int byte_number =
+      ((old_sps.size() * kBitsInByte) - bit_offset_from_end) / kBitsInByte;
+
+  std::vector<uint8_t> new_sps(old_sps.begin(),
+                               std::next(old_sps.begin(), byte_number + 1));
+
+  // SPS will be extended by the following parameters
+  // num_units_in_tick                   - 4 bytes
+  // time_scale                          - 4 bytes
+  // fixed_frame_rate_flag               - 1 bit
+  //
+  // Summarizing - 4 + 4 + 1 = 9 bytes need to be added
+  new_sps.reserve(old_sps.size() + 9);
+
+  // Enable timing_info_present_flag
+  InsertBitIntoVector(&new_sps, &currently_processed_bit_index, &byte_number,
+                      1);
+
+  // Rewritten value of timing_info_present_flag, so offset from end needs to be
+  // decreased
+  --bit_offset_from_end;
+
+  // Add num_units_in_tick value
+  InsertValueIntoVector(&new_sps, &currently_processed_bit_index, &byte_number,
+                        num_units_in_tick);
+
+  // Add time_scale value
+  InsertValueIntoVector(&new_sps, &currently_processed_bit_index, &byte_number,
+                        time_scale);
+
+  // Add fixed_frame_rate_flag value
+  InsertBitIntoVector(&new_sps, &currently_processed_bit_index, &byte_number,
+                      0);
+
+  // Add remaining values from old_sps
+  for (int offset = bit_offset_from_end; offset > 0; --offset) {
+    int o_byte_number = ((old_sps.size() * kBitsInByte) - offset) / kBitsInByte;
+    int o_byte_value = old_sps[o_byte_number];
+
+    // Don't propagate emulation_prevention_three_byte added to original SPS
+    if (o_byte_number > 1 && !old_sps[o_byte_number - 2] &&
+        !old_sps[o_byte_number - 1] && old_sps[o_byte_number] == 3) {
+      continue;
+    }
+
+    int o_bit_number = (offset - 1 + kBitsInByte) % kBitsInByte;
+    int o_bit_value = (1u << o_bit_number) & o_byte_value;
+    InsertBitIntoVector(&new_sps, &currently_processed_bit_index, &byte_number,
+                        o_bit_value);
+  }
+
+  // 7.4.1 General NAL unit semantics
+  // "The last byte of the NAL unit shall not be equal to 0x00."
+  while (!new_sps.back()) {
+    new_sps.pop_back();
+  }
+
+  return new_sps;
+}
+
 void H264Parser::SetStream(const uint8_t* stream, off_t stream_size) {
   std::vector<SubsampleEntry> subsamples;
   SetEncryptedStream(stream, stream_size, subsamples);
@@ -874,9 +985,12 @@ H264Parser::Result H264Parser::ParseVUIParameters(H264SPS* sps) {
     READ_UE_OR_RETURN(&data);  // chroma_sample_loc_type_bottom_field
   }
 
+  sps->timing_info_bit_offset_from_nal_end = br_.NumBitsLeft();
+
   // Read and ignore timing info.
-  READ_BOOL_OR_RETURN(&data);  // timing_info_present_flag
-  if (data) {
+  READ_BOOL_OR_RETURN(
+      &sps->timing_info_present_flag);  // timing_info_present_flag
+  if (sps->timing_info_present_flag) {
     READ_BITS_OR_RETURN(16, &data);  // num_units_in_tick
     READ_BITS_OR_RETURN(16, &data);  // num_units_in_tick
     READ_BITS_OR_RETURN(16, &data);  // time_scale
index f927ade..604f06c 100644 (file)
@@ -208,6 +208,9 @@ struct MEDIA_EXPORT H264SPS {
 
   int chroma_array_type;
 
+  // Not part of standard, used only for replacing timing_info data
+  int timing_info_bit_offset_from_nal_end;
+
   // Get corresponding SPS |level_idc| and |constraint_set3_flag| value from
   // requested |profile| and |level| (see Spec A.3.1).
   static void GetLevelConfigFromProfileLevel(VideoCodecProfile profile,
@@ -535,6 +538,10 @@ class MEDIA_EXPORT H264Parser {
   // Parse a SEI, returning it in |*sei|, provided and managed by the caller.
   Result ParseSEI(H264SEI* sei);
 
+  void UpdateTimingInfoIfNeeded(std::vector<std::vector<uint8_t>>* spses_array,
+                                int num_units_in_tick,
+                                int time_scale);
+
   // The return value of this method changes for every successful call to
   // AdvanceToNextNALU().
   // This returns the subsample information for the last NALU that was output
@@ -542,6 +549,11 @@ class MEDIA_EXPORT H264Parser {
   std::vector<SubsampleEntry> GetCurrentSubsamples();
 
  private:
+  std::vector<uint8_t> PrepareSPSWithTimingInfo(
+      const std::vector<uint8_t>& old_sps,
+      int sps_id,
+      int num_units_in_tick,
+      int time_scale);
   // Move the stream pointer to the beginning of the next NALU,
   // i.e. pointing at the next start code.
   // Return true if a NALU has been found.
index 7871795..2883eb5 100644 (file)
@@ -17,6 +17,7 @@
 #include "media/base/decrypt_config.h"
 #include "media/base/video_codecs.h"
 #include "media/video/bit_reader_macros.h"
+#include "media/video/h26x_tools.h"
 #include "ui/gfx/geometry/rect.h"
 #include "ui/gfx/geometry/size.h"
 #include "ui/gfx/hdr_metadata.h"
@@ -138,6 +139,120 @@ H265Parser::H265Parser() = default;
 
 H265Parser::~H265Parser() = default;
 
+void H265Parser::UpdateTimingInfoIfNeeded(
+    std::vector<std::vector<uint8_t>>* spses_array,
+    int num_units_in_tick,
+    int time_scale) {
+  media::H265Parser::Result result;
+
+  for (auto& sps : *spses_array) {
+    // Reset bit reader, skip first 2 bytes with NAL header
+    br_.Initialize(sps.data() + 2, sps.size() - 2);
+    int sps_id = 0;
+    result = ParseSPS(&sps_id);
+    DCHECK(result == media::H265Parser::kOk);
+
+    if (active_sps_.find(sps_id) == active_sps_.end()) {
+      DVLOG(3) << "Error while parsing SPS unit";
+      continue;
+    }
+
+    if (!active_sps_[sps_id]->vui_parameters_present_flag) {
+      DVLOG(1) << "VUI parameters are not present in SPS. "
+               << "Adding framerate information skipped.";
+      continue;
+    }
+
+    if (active_sps_[sps_id]->vui_parameters.timing_info_present_flag) {
+      LOG(INFO) << "VUI timing info present in SPS " << sps_id;
+      continue;
+    }
+
+    auto new_sps =
+        PrepareSPSWithTimingInfo(sps, sps_id, num_units_in_tick, time_scale);
+
+    LOG(INFO) << "Inserting VUI timing info: " << time_scale << '/'
+              << num_units_in_tick << " into SPS " << sps_id;
+
+    sps.swap(new_sps);
+  }
+}
+
+std::vector<uint8_t> H265Parser::PrepareSPSWithTimingInfo(
+    const std::vector<uint8_t>& old_sps,
+    int sps_id,
+    int num_units_in_tick,
+    int time_scale) {
+  const auto& vui_parameters = active_sps_[sps_id]->vui_parameters;
+
+  int bit_offset_from_end = vui_parameters.timing_info_bit_offset_from_nal_end;
+  int currently_processed_bit_index =
+      (bit_offset_from_end - 1 + kBitsInByte) % kBitsInByte;
+  int byte_number =
+      ((old_sps.size() * kBitsInByte) - bit_offset_from_end) / kBitsInByte;
+
+  std::vector<uint8_t> new_sps(old_sps.begin(),
+                               std::next(old_sps.begin(), byte_number + 1));
+
+  // SPS will be extended by the following parameters
+  // num_units_in_tick                   - 4 bytes
+  // time_scale                          - 4 bytes
+  // vui_poc_proportional_to_timing_flag - 1 bit
+  // vui_hrd_parameters_present_flag     - 1 bit
+  //
+  // Summarizing - 4 + 4 + 1 = 9 bytes need to be added
+  new_sps.reserve(old_sps.size() + 9);
+
+  // Enable timing_info_present_flag
+  InsertBitIntoVector(&new_sps, &currently_processed_bit_index, &byte_number,
+                      1);
+
+  // Rewritten value of timing_info_present_flag, so offset from end needs to be
+  // decreased
+  --bit_offset_from_end;
+
+  // Add num_units_in_tick value
+  InsertValueIntoVector(&new_sps, &currently_processed_bit_index, &byte_number,
+                        num_units_in_tick);
+
+  // Add time_scale value
+  InsertValueIntoVector(&new_sps, &currently_processed_bit_index, &byte_number,
+                        time_scale);
+
+  // Add vui_poc_proportional_to_timing_flag value
+  InsertBitIntoVector(&new_sps, &currently_processed_bit_index, &byte_number,
+                      0);
+
+  // Add vui_hrd_parameters_present_flag value
+  InsertBitIntoVector(&new_sps, &currently_processed_bit_index, &byte_number,
+                      0);
+
+  // Add remaining values from old_sps
+  for (int offset = bit_offset_from_end; offset > 0; --offset) {
+    int o_byte_number = ((old_sps.size() * kBitsInByte) - offset) / kBitsInByte;
+    int o_byte_value = old_sps[o_byte_number];
+
+    // Don't propagate emulation_prevention_three_byte added to original SPS
+    if (o_byte_number > 1 && !old_sps[o_byte_number - 2] &&
+        !old_sps[o_byte_number - 1] && old_sps[o_byte_number] == 3) {
+      continue;
+    }
+
+    int o_bit_number = (offset - 1 + kBitsInByte) % kBitsInByte;
+    int o_bit_value = (1u << o_bit_number) & o_byte_value;
+    InsertBitIntoVector(&new_sps, &currently_processed_bit_index, &byte_number,
+                        o_bit_value);
+  }
+
+  // 7.4.2.1 General NAL unit semantics
+  // "The last byte of the NAL unit shall not be equal to 0x00."
+  while (!new_sps.back()) {
+    new_sps.pop_back();
+  }
+
+  return new_sps;
+}
+
 int H265ProfileTierLevel::GetMaxLumaPs() const {
   // From Table A.8 - General tier and level limits.
   // |general_level_idc| is 30x the actual level.
@@ -733,9 +848,8 @@ H265Parser::Result H265Parser::ParseSPS(int* sps_id) {
   }
   READ_BOOL_OR_RETURN(&sps->sps_temporal_mvp_enabled_flag);
   READ_BOOL_OR_RETURN(&sps->strong_intra_smoothing_enabled_flag);
-  bool vui_parameters_present_flag;
-  READ_BOOL_OR_RETURN(&vui_parameters_present_flag);
-  if (vui_parameters_present_flag) {
+  READ_BOOL_OR_RETURN(&sps->vui_parameters_present_flag);
+  if (sps->vui_parameters_present_flag) {
     res = ParseVuiParameters(*sps, &sps->vui_parameters);
     if (res != kOk)
       return res;
@@ -1799,9 +1913,12 @@ H265Parser::Result H265Parser::ParseVuiParameters(const H265SPS& sps,
     READ_UE_OR_RETURN(&vui->def_disp_win_bottom_offset);
   }
 
-  // Read and ignore timing info.
-  READ_BOOL_OR_RETURN(&data);  // timing_info_present_flag
-  if (data) {
+  vui->timing_info_bit_offset_from_nal_end = br_.NumBitsLeft();
+
+  // timing_info_present_flag
+  READ_BOOL_OR_RETURN(&vui->timing_info_present_flag);
+
+  if (vui->timing_info_present_flag) {
     SKIP_BITS_OR_RETURN(32);     // vui_num_units_in_tick
     SKIP_BITS_OR_RETURN(32);     // vui_time_scale
     READ_BOOL_OR_RETURN(&data);  // vui_poc_proportional_to_timing_flag
index 0d7ab99..4f3687f 100644 (file)
@@ -127,6 +127,11 @@ struct MEDIA_EXPORT H265VUIParameters {
   int def_disp_win_right_offset;
   int def_disp_win_top_offset;
   int def_disp_win_bottom_offset;
+
+  bool timing_info_present_flag = false;
+  // Not part of standard, used only for replacing timing_info data
+  int timing_info_bit_offset_from_nal_end = 0;
+
   bool bitstream_restriction_flag;
   int min_spatial_segmentation_idc;
   int max_bytes_per_pic_denom;
@@ -237,6 +242,7 @@ struct MEDIA_EXPORT H265SPS {
   int pic_size_in_ctbs_y;
   int wp_offset_half_range_y;
   int wp_offset_half_range_c;
+  bool vui_parameters_present_flag = false;
   uint32_t sps_max_latency_pictures[kMaxSubLayers];
 
   // Helpers to compute frequently-used values. They do not verify that the
@@ -542,6 +548,10 @@ class MEDIA_EXPORT H265Parser : public H265NaluParser {
 
   static VideoCodecProfile ProfileIDCToVideoCodecProfile(int profile_idc);
 
+  void UpdateTimingInfoIfNeeded(std::vector<std::vector<uint8_t>>* spses_array,
+                                int num_units_in_tick,
+                                int time_scale);
+
  private:
   Result ParseProfileTierLevel(bool profile_present,
                                int max_num_sub_layers_minus1,
@@ -562,6 +572,11 @@ class MEDIA_EXPORT H265Parser : public H265NaluParser {
   Result ParsePredWeightTable(const H265SPS& sps,
                               const H265SliceHeader& shdr,
                               H265PredWeightTable* pred_weight_table);
+  std::vector<uint8_t> PrepareSPSWithTimingInfo(
+      const std::vector<uint8_t>& old_sps,
+      int sps_id,
+      int num_units_in_tick,
+      int time_scale);
 
   // VPSes, PPSes and SPSes stored for future reference.
   base::flat_map<int, std::unique_ptr<H265VPS>> active_vps_;
diff --git a/media/video/h26x_tools.cc b/media/video/h26x_tools.cc
new file mode 100644 (file)
index 0000000..d62b0c6
--- /dev/null
@@ -0,0 +1,33 @@
+// Copyright 2023 Samsung Electronics. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "media/video/h26x_tools.h"
+
+namespace media {
+
+void InsertBitIntoVector(std::vector<uint8_t>* vec,
+                         int* bits_left_in_byte,
+                         int* current_index,
+                         int value) {
+  if (*bits_left_in_byte < 0) {
+    // 7.4.2.1 General NAL unit semantics - emulation_prevention_three_byte
+    if (*current_index > 1 && !(*vec)[*current_index - 2] &&
+        !(*vec)[*current_index - 1] && (*vec)[*current_index] <= 3) {
+      vec->push_back((*vec)[*current_index]);
+      (*vec)[*current_index] = 3;
+      ++(*current_index);
+    }
+
+    *bits_left_in_byte = (*bits_left_in_byte + kBitsInByte) % kBitsInByte;
+    vec->push_back(0);
+    ++(*current_index);
+  }
+
+  unsigned bit_value = !!value;
+  (*vec)[*current_index] ^=
+      (-bit_value ^ (*vec)[*current_index]) & (1u << *bits_left_in_byte);
+  --(*bits_left_in_byte);
+}
+
+}  // namespace media
diff --git a/media/video/h26x_tools.h b/media/video/h26x_tools.h
new file mode 100644 (file)
index 0000000..da10652
--- /dev/null
@@ -0,0 +1,35 @@
+// Copyright 2023 Samsung Electronics. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MEDIA_VIDEO_H26X_TOOLS_H_
+#define MEDIA_VIDEO_H26X_TOOLS_H_
+
+#include <cstdint>
+#include <vector>
+
+namespace media {
+
+constexpr const int kBitsInByte = 8;
+
+void InsertBitIntoVector(std::vector<uint8_t>* vec,
+                         int* bits_left_in_byte,
+                         int* current_index,
+                         int value);
+
+template <typename T>
+void InsertValueIntoVector(std::vector<uint8_t>* vec,
+                           int* bits_left_in_byte,
+                           int* current_index,
+                           T value) {
+  int value_size_in_bits = sizeof(value) * kBitsInByte;
+  for (int value_index = value_size_in_bits - 1; value_index >= 0;
+       --value_index) {
+    InsertBitIntoVector(vec, bits_left_in_byte, current_index,
+                        value & (1u << value_index));
+  }
+}
+
+}  // namespace media
+
+#endif  // MEDIA_VIDEO_H26X_TOOLS_H_
diff --git a/tizen_src/chromium_impl/media/base/tizen/stream_framerate.cc b/tizen_src/chromium_impl/media/base/tizen/stream_framerate.cc
new file mode 100644 (file)
index 0000000..a41bfaf
--- /dev/null
@@ -0,0 +1,87 @@
+// Copyright 2018 Samsung Electronics. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @file    stream_framerate.cc
+ * @brief   This file implement method to calculate stream framerate.
+ */
+
+#include "tizen_src/chromium_impl/media/base/tizen/stream_framerate.h"
+
+const int64_tz kMaxFramerateReduceNum = 30000;
+
+#define FFABS(a) ((a) >= 0 ? (a) : (-(a)))
+#define FFMIN(a, b) ((a) > (b) ? (b) : (a))
+
+static int64_tz inline av_gcd(int64_tz a, int64_tz b) {
+  if (b)
+    return av_gcd(b, a % b);
+  else
+    return a;
+}
+
+namespace media {
+
+StreamFramerate::StreamFramerate(const int64_tz base_num,
+                                 const int64_tz base_den) {
+  framerate_num_base_ = base_num;
+  framerate_den_base_ = base_den;
+}
+
+bool StreamFramerate::Calculate(int* framerate_num, int* framerate_den) {
+  // Calculate reduced framerate_num and framerate_den.
+  return Reduce(framerate_num, framerate_den, framerate_num_base_,
+                framerate_den_base_, kMaxFramerateReduceNum);
+}
+
+bool StreamFramerate::Reduce(int* dst_num,
+                             int* dst_den,
+                             int64_tz num,
+                             int64_tz den,
+                             int64_tz max) {
+  // Do reduction of a fraction to get reduced framerate_num and framerate_den.
+  Framerate a0 = {0, 1}, a1 = {1, 0};
+  int sign = (num < 0) ^ (den < 0);
+  int64_tz gcd = av_gcd(FFABS(num), FFABS(den));
+
+  if (gcd) {
+    num = FFABS(num) / gcd;
+    den = FFABS(den) / gcd;
+  }
+  if (num <= max && den <= max) {
+    a1 = (Framerate){(int)num, (int)den};
+    den = 0;
+  }
+
+  while (den) {
+    uint64_tz x = num / den;
+    int64_tz next_den = num - den * x;
+    int64_tz a2n = x * a1.num + a0.num;
+    int64_tz a2d = x * a1.den + a0.den;
+
+    if (a2n > max || a2d > max) {
+      if (a1.num)
+        x = (max - a0.num) / a1.num;
+      if (a1.den)
+        x = FFMIN((int64_tz)x, (max - a0.den) / a1.den);
+
+      if (den * (2 * (int64_tz)x * a1.den + a0.den) > num * a1.den)
+        a1 =
+            (Framerate){(int)(x * a1.num) + a0.num, (int)(x * a1.den) + a0.den};
+      break;
+    }
+
+    a0 = a1;
+    a1 = (Framerate){(int)a2n, (int)a2d};
+    num = den;
+    den = next_den;
+  }
+
+  *dst_num = sign ? -a1.num : a1.num;
+  *dst_den = a1.den;
+
+  return den == 0;
+}
+
+}  // namespace media
diff --git a/tizen_src/chromium_impl/media/base/tizen/stream_framerate.h b/tizen_src/chromium_impl/media/base/tizen/stream_framerate.h
new file mode 100644 (file)
index 0000000..37bf0e6
--- /dev/null
@@ -0,0 +1,58 @@
+// Copyright 2023 Samsung Electronics. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @file    stream_framerate.h
+ * @brief   This file implement method to calculate stream framerate.
+ */
+
+#ifndef MEDIA_BASE_STREAM_FRAMERATE_H_
+#define MEDIA_BASE_STREAM_FRAMERATE_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include "media/base/media_export.h"
+
+typedef signed long long int int64_tz;
+typedef unsigned long long int uint64_tz;
+
+namespace media {
+
+class MEDIA_EXPORT StreamFramerate {
+ public:
+  // Framerate struct.
+  struct Framerate {
+    int num;
+    int den;
+    double toDouble() const { return den ? static_cast<double>(num) / den : 0; }
+  };
+
+  StreamFramerate(const int64_tz base_num, const int64_tz base_den);
+
+  // Do reduction of a fraction to get reduced framerate_num and framerate_den.
+  bool Reduce(int* dst_num,
+              int* dst_den,
+              int64_tz num,
+              int64_tz den,
+              int64_tz max);
+
+  bool Calculate(int* framerate_num, int* framerate_den);
+
+ private:
+  int64_tz framerate_num_base_;
+  int64_tz framerate_den_base_;
+
+  friend bool operator==(const StreamFramerate::Framerate& lhs,
+                         const StreamFramerate::Framerate& rhs);
+};
+
+inline bool operator==(const StreamFramerate::Framerate& lhs,
+                       const StreamFramerate::Framerate& rhs) {
+  return lhs.num == rhs.num && lhs.den == rhs.den;
+}
+
+}  // namespace media
+
+#endif  // MEDIA_BASE_STREAM_FRAMERATE_H_
index f220826..33d2e73 100644 (file)
@@ -96,6 +96,24 @@ int MediaPlayerESPlusPlayerTV::SetVideoStreamInfo(
     const media::VideoDecoderConfig& video_config,
     esplusplayer_video_stream_info& video_stream_info) {
   MediaPlayerESPlusPlayer::SetVideoStreamInfo(video_config, video_stream_info);
+
+  bool validFramerate =
+      video_config.framerate_num() > 0 && video_config.framerate_den() > 0;
+  if (validFramerate) {
+    double framerate = static_cast<double>(video_config.framerate_num()) /
+                       static_cast<double>(video_config.framerate_den());
+    if (framerate > kMaxFramerate) {
+      video_stream_info.framerate_num = kMaxFramerate;
+      video_stream_info.framerate_den = 1;
+    } else {
+      video_stream_info.framerate_num = video_config.framerate_num();
+      video_stream_info.framerate_den = video_config.framerate_den();
+    }
+  }
+
+  LOG(INFO) << "Framerate: [" << video_stream_info.framerate_num << "] ["
+            << video_stream_info.framerate_den << "]";
+
   if (!SetVideoSubmitDataType(video_config)) {
     LOG(ERROR) << "SetVideoSubmitDataType failed.";
     return -1;
index ead5b06..02bc620 100644 (file)
@@ -88,6 +88,8 @@ if (tizen_multimedia) {
       "//tizen_src/chromium_impl/media/filters/media_player_bridge_capi_tv.h",
       "//tizen_src/chromium_impl/media/filters/media_player_esplusplayer_tv.cc",
       "//tizen_src/chromium_impl/media/filters/media_player_esplusplayer_tv.h",
+      "//tizen_src/chromium_impl/media/base/tizen/stream_framerate.cc",
+      "//tizen_src/chromium_impl/media/base/tizen/stream_framerate.h",
     ]
     external_media_video_decode_config += [
       "//tizen_src/build:drmdecrypt",