#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;
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;
[[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.
#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:
bool is_rtc_ = false;
#if BUILDFLAG(IS_TIZEN_TV)
+ int framerate_num_ = 0;
+ int framerate_den_ = 0;
std::string hdr_info_;
#endif
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
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_);
}
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() {
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(
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,
// 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;
}
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();
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.
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.
// 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;
}
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));
}
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) {
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;
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,
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
// 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
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);
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) {
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);
// 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,
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());
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
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;
}
}
+ *has_audio = !audio_codecs.empty();
+ *has_video = !video_codecs.empty();
stream_parser.reset(factory_function(codecs, media_log));
}
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(
*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.
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 {
#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;
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;
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
#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;
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();
#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 {
// 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 {
#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;
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
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.
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;
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(
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;
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);
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,
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
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_;
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];
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.
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())
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
// 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;
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
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();
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;
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 {
// 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;
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
WebMStreamParser::WebMStreamParser()
: state_(kWaitingForInit),
+#if BUILDFLAG(IS_TIZEN_TV)
+ is_framerate_set_(false),
+ has_default_duration_(true),
+#endif
unknown_segment_size_(false) {
}
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);
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))) {
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();
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,
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_;
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);
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;
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())
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,
"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",
#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"
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, ¤tly_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, ¤tly_processed_bit_index, &byte_number,
+ num_units_in_tick);
+
+ // Add time_scale value
+ InsertValueIntoVector(&new_sps, ¤tly_processed_bit_index, &byte_number,
+ time_scale);
+
+ // Add fixed_frame_rate_flag value
+ InsertBitIntoVector(&new_sps, ¤tly_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, ¤tly_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);
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
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,
// 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
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.
#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"
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, ¤tly_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, ¤tly_processed_bit_index, &byte_number,
+ num_units_in_tick);
+
+ // Add time_scale value
+ InsertValueIntoVector(&new_sps, ¤tly_processed_bit_index, &byte_number,
+ time_scale);
+
+ // Add vui_poc_proportional_to_timing_flag value
+ InsertBitIntoVector(&new_sps, ¤tly_processed_bit_index, &byte_number,
+ 0);
+
+ // Add vui_hrd_parameters_present_flag value
+ InsertBitIntoVector(&new_sps, ¤tly_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, ¤tly_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.
}
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;
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
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;
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
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,
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_;
--- /dev/null
+// 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
--- /dev/null
+// 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_
--- /dev/null
+// 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
--- /dev/null
+// 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_
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;
"//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",