"tizen/dummy_drm.cc",
"tizen/dummy_drm.h",
"tizen/extended_video_decoder_config.h",
+ "tizen/lazy_frame_ranges.cc",
+ "tizen/lazy_frame_ranges.h",
"tizen/media_video_codec.cc",
"tizen/media_video_codec.h",
"tizen/nv12_data.h",
--- /dev/null
+// Copyright 2024 Samsung Electronics Inc. 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/filters/tizen/lazy_frame_ranges.h"
+
+#include "base/logging.h"
+
+namespace media {
+
+void LazyFrameRanges::Insert(const DecoderBuffer& buffer) {
+ // Check for EOS packet, which has |kNoTimestamp| dts.
+ if (buffer.end_of_stream()) {
+ if (ranges_.size() != 0) {
+ ranges_.clear();
+ ranges_.Add(base::TimeDelta::Min(), base::TimeDelta::Max());
+ }
+ } else if (buffer.dts() != kNoTimestamp) {
+ if (last_dts_ != kNoTimestamp) {
+ auto duration = buffer.dts() - last_dts_;
+ ranges_.Add(last_pts_, last_pts_ + duration);
+
+ // Fill small gaps in ranges due to TimeDelta inaccuracy, eg. 1.86853
+ // and 1.86854 Start from the end to omit the problem with changing Ranges
+ // size.
+ for (int i = ranges_.size() - 2; i >= 0; --i) {
+ if (ranges_.end(i) + base::Milliseconds(1) >= ranges_.start(i + 1)) {
+ ranges_.Add(ranges_.end(i), ranges_.start(i + 1));
+ }
+ }
+ }
+
+ if (buffer.is_key_frame()) {
+ if (ranges_.size() == 0) {
+ // Since we don't have information about duration and range won't accept
+ // zero duration range, we need to use possibly small value to immitate
+ // range.
+ ranges_.Add(buffer.timestamp(),
+ buffer.timestamp() + base::Milliseconds(1));
+ } else {
+ ranges_.Add(ranges_.start(0), buffer.timestamp());
+ }
+ }
+ last_pts_ = buffer.timestamp();
+ last_dts_ = buffer.dts();
+ } else {
+ LOG(ERROR) << "Non-eos buffer without DTS";
+ last_pts_ = kNoTimestamp;
+ last_dts_ = kNoTimestamp;
+ }
+}
+
+void LazyFrameRanges::Clear() {
+ last_pts_ = kNoTimestamp;
+ last_dts_ = kNoTimestamp;
+ ranges_.clear();
+}
+
+bool LazyFrameRanges::IsInRange(base::TimeDelta timestamp) const {
+ if (ranges_.size() == 0) {
+ return false;
+ }
+
+ return timestamp <= ranges_.end(0);
+}
+
+} // namespace media
--- /dev/null
+// Copyright 2024 Samsung Electronics Inc. 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_FILTERS_TIZEN_LAZY_FRAME_RANGES_H_
+#define MEDIA_FILTERS_TIZEN_LAZY_FRAME_RANGES_H_
+
+#include "base/time/time.h"
+#include "media/base/decoder_buffer.h"
+#include "media/base/ranges.h"
+#include "media/base/timestamp_constants.h"
+
+namespace media {
+
+class LazyFrameRanges {
+ public:
+ // Updates |ranges_| with timestamp and duration from |buffer|.
+ void Insert(const DecoderBuffer& buffer);
+ void Clear();
+
+ bool IsInRange(base::TimeDelta timestamp) const;
+
+ private:
+ base::TimeDelta last_pts_ = kNoTimestamp;
+ base::TimeDelta last_dts_ = kNoTimestamp;
+ Ranges<base::TimeDelta> ranges_;
+};
+
+} // namespace media
+
+#endif // MEDIA_FILTERS_TIZEN_LAZY_FRAME_RANGES_H_
--- /dev/null
+// Copyright 2024 Samsung Electronics Inc. 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/filters/tizen/lazy_frame_ranges.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace media {
+
+class LazyFrameRangesTest : public testing::Test {
+ public:
+ LazyFrameRangesTest() = default;
+ ~LazyFrameRangesTest() = default;
+
+ LazyFrameRangesTest(const LazyFrameRangesTest&) = delete;
+ LazyFrameRangesTest& operator=(const LazyFrameRangesTest&) = delete;
+
+ void InsertKeyframe(base::TimeDelta pts, base::TimeDelta dts) {
+ auto buffer = base::WrapRefCounted(new DecoderBuffer(0));
+ buffer->set_timestamp(pts);
+ buffer->set_dts(dts);
+ buffer->set_is_key_frame(true);
+ ranges.Insert(*buffer);
+ }
+
+ void InsertDelta(base::TimeDelta pts, base::TimeDelta dts) {
+ auto buffer = base::WrapRefCounted(new DecoderBuffer(0));
+ buffer->set_timestamp(pts);
+ buffer->set_dts(dts);
+ ranges.Insert(*buffer);
+ }
+
+ void InsertEOS() {
+ auto buffer = DecoderBuffer::CreateEOSBuffer();
+ ranges.Insert(*buffer);
+ }
+
+ void TearDown() override { ranges.Clear(); }
+
+ LazyFrameRanges ranges;
+};
+
+TEST_F(LazyFrameRangesTest, SimpleKeyframe) {
+ InsertKeyframe(base::Milliseconds(0), base::Milliseconds(0));
+ EXPECT_TRUE(ranges.IsInRange(base::Milliseconds(0)));
+}
+
+TEST_F(LazyFrameRangesTest, SingleDeltaAfterKeyframe) {
+ InsertKeyframe(base::Milliseconds(0), base::Milliseconds(0));
+ EXPECT_TRUE(ranges.IsInRange(base::Milliseconds(0)));
+ InsertDelta(base::Milliseconds(10), base::Milliseconds(10));
+ EXPECT_TRUE(ranges.IsInRange(base::Milliseconds(10)));
+}
+
+TEST_F(LazyFrameRangesTest, DoubleDeltaAfterKeyframe) {
+ InsertKeyframe(base::Milliseconds(0), base::Milliseconds(0));
+ EXPECT_TRUE(ranges.IsInRange(base::Milliseconds(0)));
+ InsertDelta(base::Milliseconds(10), base::Milliseconds(10));
+ EXPECT_TRUE(ranges.IsInRange(base::Milliseconds(10)));
+ InsertDelta(base::Milliseconds(20), base::Milliseconds(20));
+ EXPECT_TRUE(ranges.IsInRange(base::Milliseconds(20)));
+}
+
+TEST_F(LazyFrameRangesTest, BidirectionalAfterKeyframeMissing) {
+ InsertKeyframe(base::Milliseconds(0), base::Milliseconds(0));
+ EXPECT_TRUE(ranges.IsInRange(base::Milliseconds(0)));
+ InsertDelta(base::Milliseconds(20), base::Milliseconds(10));
+ EXPECT_FALSE(ranges.IsInRange(base::Milliseconds(20)));
+}
+
+TEST_F(LazyFrameRangesTest, BidirectionalAfterKeyframe) {
+ InsertKeyframe(base::Milliseconds(0), base::Milliseconds(0));
+ EXPECT_TRUE(ranges.IsInRange(base::Milliseconds(0)));
+
+ InsertDelta(base::Milliseconds(20), base::Milliseconds(10));
+ EXPECT_FALSE(ranges.IsInRange(base::Milliseconds(20)));
+
+ InsertDelta(base::Milliseconds(10), base::Milliseconds(20));
+ EXPECT_TRUE(ranges.IsInRange(base::Milliseconds(10)));
+ EXPECT_FALSE(ranges.IsInRange(base::Milliseconds(20)));
+}
+
+TEST_F(LazyFrameRangesTest, MultipleBidirectionalAfterKeyframe) {
+ InsertKeyframe(base::Milliseconds(0), base::Milliseconds(0));
+ EXPECT_TRUE(ranges.IsInRange(base::Milliseconds(0)));
+
+ InsertDelta(base::Milliseconds(40), base::Milliseconds(10));
+ EXPECT_FALSE(ranges.IsInRange(base::Milliseconds(40)));
+
+ InsertDelta(base::Milliseconds(30), base::Milliseconds(20));
+ EXPECT_FALSE(ranges.IsInRange(base::Milliseconds(30)));
+ EXPECT_FALSE(ranges.IsInRange(base::Milliseconds(40)));
+
+ InsertDelta(base::Milliseconds(20), base::Milliseconds(30));
+ EXPECT_FALSE(ranges.IsInRange(base::Milliseconds(20)));
+ EXPECT_FALSE(ranges.IsInRange(base::Milliseconds(30)));
+ EXPECT_FALSE(ranges.IsInRange(base::Milliseconds(40)));
+
+ InsertDelta(base::Milliseconds(10), base::Milliseconds(40));
+ EXPECT_TRUE(ranges.IsInRange(base::Milliseconds(10)));
+ EXPECT_FALSE(ranges.IsInRange(base::Milliseconds(20)));
+ EXPECT_FALSE(ranges.IsInRange(base::Milliseconds(30)));
+ EXPECT_FALSE(ranges.IsInRange(base::Milliseconds(40)));
+}
+
+TEST_F(LazyFrameRangesTest, EosFinishesMultipleBidirectional) {
+ InsertKeyframe(base::Milliseconds(0), base::Milliseconds(0));
+ EXPECT_TRUE(ranges.IsInRange(base::Milliseconds(0)));
+
+ InsertDelta(base::Milliseconds(40), base::Milliseconds(10));
+ EXPECT_FALSE(ranges.IsInRange(base::Milliseconds(40)));
+
+ InsertDelta(base::Milliseconds(30), base::Milliseconds(20));
+ EXPECT_FALSE(ranges.IsInRange(base::Milliseconds(30)));
+ EXPECT_FALSE(ranges.IsInRange(base::Milliseconds(40)));
+
+ InsertDelta(base::Milliseconds(20), base::Milliseconds(30));
+ EXPECT_FALSE(ranges.IsInRange(base::Milliseconds(20)));
+ EXPECT_FALSE(ranges.IsInRange(base::Milliseconds(30)));
+ EXPECT_FALSE(ranges.IsInRange(base::Milliseconds(40)));
+
+ InsertDelta(base::Milliseconds(10), base::Milliseconds(40));
+ EXPECT_FALSE(ranges.IsInRange(base::Milliseconds(20)));
+ EXPECT_FALSE(ranges.IsInRange(base::Milliseconds(30)));
+ EXPECT_FALSE(ranges.IsInRange(base::Milliseconds(40)));
+
+ InsertEOS();
+ EXPECT_TRUE(ranges.IsInRange(base::Milliseconds(20)));
+ EXPECT_TRUE(ranges.IsInRange(base::Milliseconds(30)));
+ EXPECT_TRUE(ranges.IsInRange(base::Milliseconds(40)));
+}
+
+TEST_F(LazyFrameRangesTest, KeyframeFinishesMultipleBidirectional) {
+ InsertKeyframe(base::Milliseconds(0), base::Milliseconds(0));
+ EXPECT_TRUE(ranges.IsInRange(base::Milliseconds(0)));
+
+ InsertDelta(base::Milliseconds(40), base::Milliseconds(10));
+ EXPECT_FALSE(ranges.IsInRange(base::Milliseconds(40)));
+
+ InsertDelta(base::Milliseconds(30), base::Milliseconds(20));
+ EXPECT_FALSE(ranges.IsInRange(base::Milliseconds(30)));
+ EXPECT_FALSE(ranges.IsInRange(base::Milliseconds(40)));
+
+ InsertDelta(base::Milliseconds(20), base::Milliseconds(30));
+ EXPECT_FALSE(ranges.IsInRange(base::Milliseconds(20)));
+ EXPECT_FALSE(ranges.IsInRange(base::Milliseconds(30)));
+ EXPECT_FALSE(ranges.IsInRange(base::Milliseconds(40)));
+
+ InsertDelta(base::Milliseconds(10), base::Milliseconds(40));
+ EXPECT_FALSE(ranges.IsInRange(base::Milliseconds(20)));
+ EXPECT_FALSE(ranges.IsInRange(base::Milliseconds(30)));
+ EXPECT_FALSE(ranges.IsInRange(base::Milliseconds(40)));
+
+ InsertKeyframe(base::Milliseconds(60), base::Milliseconds(50));
+ EXPECT_TRUE(ranges.IsInRange(base::Milliseconds(20)));
+ EXPECT_TRUE(ranges.IsInRange(base::Milliseconds(30)));
+ EXPECT_TRUE(ranges.IsInRange(base::Milliseconds(40)));
+ EXPECT_TRUE(ranges.IsInRange(base::Milliseconds(60)));
+}
+
+} // namespace media
const auto old_state = decoder_state_;
decoder_state_ = DecoderState::kResetting;
ClearDecodingQueue(DecoderStatus::Codes::kAborted);
- last_dts_ = base::TimeDelta::Max();
- dts_offset_ = kNoTimestamp;
+ ranges_.Clear();
// TODO(vdwasm) Some optimization might be done here to push
// |TTvdDecodedFrame| back to the pool instead of deleting them.
<< buffer->timestamp().InMicroseconds()
<< ", duration: "
<< buffer->duration().InMicroseconds()
+ << ", dts: " << buffer->dts()
<< ", is eos: " << buffer->end_of_stream()
<< ", is keyframe: " << buffer->is_key_frame();
TRACE_EVENT0("gpu", "VideoDecoder.Decode");
// error of 1us (internal unit of base::TimeDelta).
// Low delay mode does not have any bi-directional frames, so decoding order
// is also rendering order.
- // Also when eos was already pushed to decoder, it means there won't be any
- // more new frames, so pending one should be decodable (all the references
- // must been pushed to decoder).
- const bool can_decode_with_rounding_error =
- ((decoding_request.timestamp <= last_dts_) ||
- (decoding_request.timestamp - last_dts_) <= base::Microseconds(1) ||
- !eos_cb_.is_null());
- if (!low_delay_ && !can_decode_with_rounding_error) {
+ if (!low_delay_ && !ranges_.IsInRange(decoding_request.timestamp)) {
return false;
}
// buffers are rejected in |Decode| method.
TIZEN_MEDIA_LOG_ASSERT(buffer->timestamp() != kNoTimestamp);
- // Check for EOS packet, which has |kNoTimestamp| dts.
- if (buffer->dts() != kNoTimestamp) {
- if (dts_offset_ == kNoTimestamp) {
- dts_offset_ = buffer->timestamp() - buffer->dts();
- TIZEN_MEDIA_LOG_ASSERT(dts_offset_ != kNoTimestamp);
- }
-
- last_dts_ = buffer->dts() + dts_offset_;
+ // Calculating ranges makes no sense for RTC, since it cannot contain
+ // bidirectional frames.
+ if (!config_.is_rtc()) {
+ ranges_.Insert(*buffer);
}
DecodingRequest decoding_request;
allocated_decoder_.reset();
ttvd_decoded_frame_pool_.clear();
- last_dts_ = base::TimeDelta::Max();
- dts_offset_ = kNoTimestamp;
+ ranges_.Clear();
decoding_requests_.clear();
decoding_results_.clear();
last_processing_frame_ =
#include "media/base/callback_registry.h"
#include "media/base/cdm_context.h"
#include "media/base/decoder_status.h"
+#include "media/base/ranges.h"
#include "media/base/timestamp_constants.h"
#include "media/base/video_codecs.h"
#include "media/base/video_decoder.h"
#include "media/filters/tizen/decoder_promotion.h"
#include "media/filters/tizen/dummy_drm.h"
#include "media/filters/tizen/extended_video_decoder_config.h"
+#include "media/filters/tizen/lazy_frame_ranges.h"
#include "media/filters/tizen/ttvd_decoded_frame.h"
#include "media/gpu/command_buffer_helper.h"
#include "ui/gfx/geometry/rect_f.h"
};
std::pair<base::TimeDelta, ProcessingAction> last_processing_frame_;
- // DTS of the last buffer that came through |HandleDecodingRequest|
- base::TimeDelta last_dts_ = base::TimeDelta::Max();
-
- // Offset used for aligning DTS with PTS based on the first PTS value
- // received.
- base::TimeDelta dts_offset_ = kNoTimestamp;
+ // Ranges of video corresponding data that were accepted by decoder.
+ LazyFrameRanges ranges_;
// Rendering callback gathered from overlay system. It should be
// used to draw decoded video picture when we're in overlay mode.
"encoded_file_video_capture_device_unittest.cc",
"file_video_capture_device_factory_unittest.cc",
"//media/filters/tizen/decoder_promotion_test.cc",
+ "//media/filters/tizen/lazy_frame_ranges_test.cc",
"//media/filters/tizen/omx/mock_omx_wrapper.cc",
"//media/filters/tizen/omx/mock_omx_wrapper.h",
"//media/filters/tizen/omx/omx_facade_video_test.cc"