LibMedia: Calculate Matroska block timestamps for their actual tracks
Instead of using a single track entry for all blocks in the file, use a lookup to get the info needed to calculate the timestamp for the specific track a block belongs to. No change in behavior for SampleIterator, since that only returns blocks from the track that was passed. This will be useful for MSE, since it demuxes all tracks at once.
This commit is contained in:
parent
1834dfdb63
commit
6b45a11716
8 changed files with 107 additions and 60 deletions
|
|
@ -164,6 +164,29 @@ private:
|
|||
Optional<AudioTrack> m_audio_track;
|
||||
};
|
||||
|
||||
struct TrackBlockContext {
|
||||
String codec_id;
|
||||
double timestamp_scale { 1 };
|
||||
u64 codec_delay { 0 };
|
||||
u64 seek_pre_roll { 0 };
|
||||
u64 timestamp_offset { 0 };
|
||||
u64 default_duration { 0 };
|
||||
|
||||
static TrackBlockContext from_track_entry(TrackEntry const& entry)
|
||||
{
|
||||
return {
|
||||
.codec_id = entry.codec_id(),
|
||||
.timestamp_scale = entry.timestamp_scale(),
|
||||
.codec_delay = entry.codec_delay(),
|
||||
.seek_pre_roll = entry.seek_pre_roll(),
|
||||
.timestamp_offset = entry.timestamp_offset(),
|
||||
.default_duration = entry.default_duration(),
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
using TrackBlockContexts = HashMap<u64, TrackBlockContext>;
|
||||
|
||||
class Block {
|
||||
public:
|
||||
enum Lacing : u8 {
|
||||
|
|
@ -175,7 +198,7 @@ public:
|
|||
|
||||
u64 track_number() const { return m_track_number; }
|
||||
void set_track_number(u64 track_number) { m_track_number = track_number; }
|
||||
AK::Duration timestamp() const { return m_timestamp; }
|
||||
Optional<AK::Duration> timestamp() const { return m_timestamp; }
|
||||
void set_timestamp(AK::Duration timestamp) { m_timestamp = timestamp; }
|
||||
Optional<AK::Duration> duration() const { return m_duration; }
|
||||
void set_duration(AK::Duration duration) { m_duration = duration; }
|
||||
|
|
@ -197,7 +220,7 @@ public:
|
|||
|
||||
private:
|
||||
u64 m_track_number { 0 };
|
||||
AK::Duration m_timestamp { AK::Duration::zero() };
|
||||
Optional<AK::Duration> m_timestamp;
|
||||
Optional<AK::Duration> m_duration;
|
||||
bool m_only_keyframes { false };
|
||||
bool m_invisible { false };
|
||||
|
|
|
|||
|
|
@ -179,7 +179,7 @@ DecoderErrorOr<CodedFrame> MatroskaDemuxer::get_next_sample_for_track(Track cons
|
|||
|
||||
VERIFY(status.block.has_value());
|
||||
|
||||
auto timestamp = status.block->timestamp();
|
||||
auto timestamp = status.block->timestamp().value();
|
||||
auto duration = status.block->duration().value_or(AK::Duration::zero());
|
||||
auto flags = status.block->only_keyframes() ? FrameFlags::Keyframe : FrameFlags::None;
|
||||
auto aux_data = [&] -> CodedFrame::AuxiliaryData {
|
||||
|
|
|
|||
|
|
@ -682,7 +682,7 @@ DecoderErrorOr<Cluster> Reader::parse_cluster_element(Streamer& streamer, u64 ti
|
|||
return cluster;
|
||||
}
|
||||
|
||||
static AK::Duration block_timestamp_to_duration(AK::Duration cluster_timestamp, u64 segment_timestamp_scale, TrackEntry const& track, i16 timestamp_offset)
|
||||
static AK::Duration block_timestamp_to_duration(AK::Duration cluster_timestamp, u64 segment_timestamp_scale, TrackBlockContext const& context, i16 timestamp_offset)
|
||||
{
|
||||
// https://www.matroska.org/technical/notes.html
|
||||
// Block Timestamps:
|
||||
|
|
@ -695,11 +695,11 @@ static AK::Duration block_timestamp_to_duration(AK::Duration cluster_timestamp,
|
|||
// of that track. To get the timestamp in nanoseconds of the first frame in a Block or
|
||||
// SimpleBlock, the formula becomes:
|
||||
// `( ( Cluster\Timestamp + ( block timestamp * TrackTimestampScale ) ) * TimestampScale ) - CodecDelay`
|
||||
auto timestamp_offset_in_cluster_offset = AK::clamp_to<i64>(static_cast<double>(timestamp_offset * AK::clamp_to<i64>(segment_timestamp_scale)) * track.timestamp_scale());
|
||||
timestamp_offset_in_cluster_offset = saturating_sub(timestamp_offset_in_cluster_offset, AK::clamp_to<i64>(track.codec_delay()));
|
||||
auto timestamp_offset_in_cluster_offset = AK::clamp_to<i64>(static_cast<double>(timestamp_offset * AK::clamp_to<i64>(segment_timestamp_scale)) * context.timestamp_scale);
|
||||
timestamp_offset_in_cluster_offset = saturating_sub(timestamp_offset_in_cluster_offset, AK::clamp_to<i64>(context.codec_delay));
|
||||
// This is only mentioned in the elements specification under TrackOffset.
|
||||
// https://www.matroska.org/technical/elements.html
|
||||
timestamp_offset_in_cluster_offset = saturating_add(timestamp_offset_in_cluster_offset, AK::clamp_to<i64>(track.timestamp_offset()));
|
||||
timestamp_offset_in_cluster_offset = saturating_add(timestamp_offset_in_cluster_offset, AK::clamp_to<i64>(context.timestamp_offset));
|
||||
return cluster_timestamp + AK::Duration::from_nanoseconds(timestamp_offset_in_cluster_offset);
|
||||
}
|
||||
|
||||
|
|
@ -778,16 +778,15 @@ DecoderErrorOr<Vector<ByteBuffer>> SampleIterator::get_frames(Block block)
|
|||
return frames;
|
||||
}
|
||||
|
||||
static void set_block_duration_to_default(Block& block, TrackEntry const& track)
|
||||
static void set_block_duration_to_default(Block& block, TrackBlockContext const& context)
|
||||
{
|
||||
if (track.default_duration() != 0)
|
||||
block.set_duration(AK::Duration::from_nanoseconds(AK::clamp_to<i64>(track.default_duration())));
|
||||
if (context.default_duration != 0)
|
||||
block.set_duration(AK::Duration::from_nanoseconds(AK::clamp_to<i64>(context.default_duration)));
|
||||
}
|
||||
|
||||
DecoderErrorOr<Block> Reader::parse_simple_block(Streamer& streamer, AK::Duration cluster_timestamp, u64 segment_timestamp_scale, TrackEntry const& track)
|
||||
DecoderErrorOr<Block> Reader::parse_simple_block(Streamer& streamer, AK::Duration cluster_timestamp, u64 segment_timestamp_scale, TrackBlockContexts const& contexts)
|
||||
{
|
||||
Block block;
|
||||
set_block_duration_to_default(block, track);
|
||||
|
||||
auto content_size = TRY(streamer.read_variable_size_integer());
|
||||
auto content_end = streamer.position() + content_size;
|
||||
|
|
@ -795,7 +794,6 @@ DecoderErrorOr<Block> Reader::parse_simple_block(Streamer& streamer, AK::Duratio
|
|||
block.set_track_number(TRY(streamer.read_variable_size_integer()));
|
||||
|
||||
auto timestamp_offset = TRY(streamer.read_i16());
|
||||
block.set_timestamp(block_timestamp_to_duration(cluster_timestamp, segment_timestamp_scale, track, timestamp_offset));
|
||||
|
||||
auto flags = TRY(streamer.read_octet());
|
||||
block.set_only_keyframes((flags & (1u << 7u)) != 0);
|
||||
|
|
@ -806,14 +804,24 @@ DecoderErrorOr<Block> Reader::parse_simple_block(Streamer& streamer, AK::Duratio
|
|||
auto data_position = streamer.position();
|
||||
auto data_size = content_end - data_position;
|
||||
block.set_data(data_position, data_size);
|
||||
|
||||
auto maybe_context = contexts.get(block.track_number());
|
||||
if (maybe_context.has_value()) {
|
||||
auto const& context = maybe_context.value();
|
||||
block.set_timestamp(block_timestamp_to_duration(cluster_timestamp, segment_timestamp_scale, context, timestamp_offset));
|
||||
|
||||
set_block_duration_to_default(block, context);
|
||||
}
|
||||
|
||||
TRY(streamer.seek_to_position(content_end));
|
||||
return block;
|
||||
}
|
||||
|
||||
DecoderErrorOr<Block> Reader::parse_block_group(Streamer& streamer, AK::Duration cluster_timestamp, u64 segment_timestamp_scale, TrackEntry const& track)
|
||||
DecoderErrorOr<Block> Reader::parse_block_group(Streamer& streamer, AK::Duration cluster_timestamp, u64 segment_timestamp_scale, TrackBlockContexts const& contexts)
|
||||
{
|
||||
Block block;
|
||||
set_block_duration_to_default(block, track);
|
||||
i16 timestamp_offset = 0;
|
||||
Optional<u64> raw_block_duration;
|
||||
|
||||
auto parsed_a_block = false;
|
||||
TRY(Reader::parse_master_element(streamer, "BlockGroup"sv, [&](u64 element_id) -> DecoderErrorOr<ElementIterationDecision> {
|
||||
|
|
@ -826,9 +834,7 @@ DecoderErrorOr<Block> Reader::parse_block_group(Streamer& streamer, AK::Duration
|
|||
auto content_end = streamer.position() + content_size;
|
||||
|
||||
block.set_track_number(TRY(streamer.read_variable_size_integer()));
|
||||
|
||||
auto timestamp_offset = TRY(streamer.read_i16());
|
||||
block.set_timestamp(block_timestamp_to_duration(cluster_timestamp, segment_timestamp_scale, track, timestamp_offset));
|
||||
timestamp_offset = TRY(streamer.read_i16());
|
||||
|
||||
auto flags = TRY(streamer.read_octet());
|
||||
block.set_invisible((flags & (1u << 3)) != 0);
|
||||
|
|
@ -838,14 +844,11 @@ DecoderErrorOr<Block> Reader::parse_block_group(Streamer& streamer, AK::Duration
|
|||
auto data_size = content_end - data_position;
|
||||
block.set_data(data_position, data_size);
|
||||
TRY(streamer.seek_to_position(content_end));
|
||||
parsed_a_block = true;
|
||||
break;
|
||||
}
|
||||
case BLOCK_DURATION_ID: {
|
||||
auto duration = TRY(streamer.read_u64());
|
||||
auto duration_nanoseconds = saturating_mul(AK::clamp_to<i64>(duration), AK::clamp_to<i64>(segment_timestamp_scale));
|
||||
if (track.timestamp_scale() != 1)
|
||||
duration_nanoseconds = AK::clamp_to<i64>(static_cast<double>(duration_nanoseconds) * track.timestamp_scale());
|
||||
block.set_duration(AK::Duration::from_nanoseconds(duration_nanoseconds));
|
||||
raw_block_duration = TRY(streamer.read_u64());
|
||||
break;
|
||||
}
|
||||
default:
|
||||
|
|
@ -856,13 +859,31 @@ DecoderErrorOr<Block> Reader::parse_block_group(Streamer& streamer, AK::Duration
|
|||
return ElementIterationDecision::Continue;
|
||||
}));
|
||||
|
||||
auto maybe_context = contexts.get(block.track_number());
|
||||
if (maybe_context.has_value()) {
|
||||
auto const& context = maybe_context.value();
|
||||
block.set_timestamp(block_timestamp_to_duration(cluster_timestamp, segment_timestamp_scale, context, timestamp_offset));
|
||||
|
||||
if (raw_block_duration.has_value()) {
|
||||
auto duration_nanoseconds = saturating_mul(AK::clamp_to<i64>(*raw_block_duration), AK::clamp_to<i64>(segment_timestamp_scale));
|
||||
if (context.timestamp_scale != 1)
|
||||
duration_nanoseconds = AK::clamp_to<i64>(static_cast<double>(duration_nanoseconds) * context.timestamp_scale);
|
||||
block.set_duration(AK::Duration::from_nanoseconds(duration_nanoseconds));
|
||||
} else {
|
||||
set_block_duration_to_default(block, context);
|
||||
}
|
||||
}
|
||||
|
||||
return block;
|
||||
}
|
||||
|
||||
DecoderErrorOr<SampleIterator> Reader::create_sample_iterator(NonnullRefPtr<MediaStreamCursor> const& stream_consumer, u64 track_number)
|
||||
{
|
||||
dbgln_if(MATROSKA_DEBUG, "Creating sample iterator starting at {} relative to segment at {}", m_first_cluster_position, m_segment_contents_position);
|
||||
return SampleIterator(stream_consumer, TRY(track_for_track_number(track_number)), m_segment_information.timestamp_scale(), m_segment_contents_position, m_first_cluster_position);
|
||||
auto track_entry = TRY(track_for_track_number(track_number));
|
||||
TrackBlockContexts track_contexts;
|
||||
track_contexts.set(track_number, TrackBlockContext::from_track_entry(track_entry));
|
||||
return SampleIterator(stream_consumer, track_number, move(track_contexts), m_segment_information.timestamp_scale(), m_segment_contents_position, m_first_cluster_position);
|
||||
}
|
||||
|
||||
static DecoderErrorOr<CueTrackPosition> parse_cue_track_position(Streamer& streamer)
|
||||
|
|
@ -1045,7 +1066,7 @@ static DecoderErrorOr<void> search_clusters_for_keyframe_before_timestamp(Sample
|
|||
}
|
||||
|
||||
auto block = block_result.release_value();
|
||||
if (block.timestamp() > timestamp)
|
||||
if (block.timestamp().value() > timestamp)
|
||||
break;
|
||||
|
||||
if (block.only_keyframes()) {
|
||||
|
|
@ -1075,9 +1096,10 @@ bool Reader::has_cues_for_track(u64 track_number)
|
|||
|
||||
DecoderErrorOr<SampleIterator> Reader::seek_to_random_access_point(SampleIterator iterator, AK::Duration timestamp)
|
||||
{
|
||||
timestamp -= AK::Duration::from_nanoseconds(AK::clamp_to<i64>(iterator.m_track->seek_pre_roll()));
|
||||
auto seek_pre_roll = iterator.m_track_block_contexts.get(iterator.m_track_number)->seek_pre_roll;
|
||||
timestamp -= AK::Duration::from_nanoseconds(AK::clamp_to<i64>(seek_pre_roll));
|
||||
|
||||
auto cue_points = cue_points_for_track(iterator.m_track->track_number());
|
||||
auto cue_points = cue_points_for_track(iterator.m_track_number);
|
||||
auto seek_target = CuePointTarget::Block;
|
||||
|
||||
// If no cues are present for the track, use the first track's cues.
|
||||
|
|
@ -1095,8 +1117,8 @@ DecoderErrorOr<SampleIterator> Reader::seek_to_random_access_point(SampleIterato
|
|||
if (!iterator.last_timestamp().has_value() || timestamp < iterator.last_timestamp().value()) {
|
||||
// If the timestamp is before the iterator's current position, then we need to start from the beginning of the Segment.
|
||||
if (timestamp > AK::Duration::zero())
|
||||
warnln("Seeking track {} to {}s required restarting the sample iterator from the start, streaming may be broken for this file.", timestamp, iterator.m_track->track_number());
|
||||
iterator = TRY(create_sample_iterator(iterator.m_stream_cursor, iterator.m_track->track_number()));
|
||||
warnln("Seeking track {} to {}s required restarting the sample iterator from the start, streaming may be broken for this file.", timestamp, iterator.m_track_number);
|
||||
iterator = TRY(create_sample_iterator(iterator.m_stream_cursor, iterator.m_track_number));
|
||||
TRY(search_clusters_for_keyframe_before_timestamp(iterator, timestamp));
|
||||
return iterator;
|
||||
}
|
||||
|
|
@ -1135,13 +1157,13 @@ DecoderErrorOr<Block> SampleIterator::next_block()
|
|||
m_current_cluster = TRY(Reader::parse_cluster_element(streamer, m_segment_timestamp_scale));
|
||||
} else if (element_id == SIMPLE_BLOCK_ID) {
|
||||
dbgln_if(MATROSKA_TRACE_DEBUG, " Iterator is parsing a new simple block.");
|
||||
auto candidate_block = TRY(Reader::parse_simple_block(streamer, m_current_cluster->timestamp(), m_segment_timestamp_scale, m_track));
|
||||
if (candidate_block.track_number() == m_track->track_number())
|
||||
auto candidate_block = TRY(Reader::parse_simple_block(streamer, m_current_cluster->timestamp(), m_segment_timestamp_scale, m_track_block_contexts));
|
||||
if (candidate_block.track_number() == m_track_number)
|
||||
block = move(candidate_block);
|
||||
} else if (element_id == BLOCK_GROUP_ID) {
|
||||
dbgln_if(MATROSKA_TRACE_DEBUG, " Iterator is parsing a new block group.");
|
||||
auto candidate_block = TRY(Reader::parse_block_group(streamer, m_current_cluster->timestamp(), m_segment_timestamp_scale, m_track));
|
||||
if (candidate_block.track_number() == m_track->track_number())
|
||||
auto candidate_block = TRY(Reader::parse_block_group(streamer, m_current_cluster->timestamp(), m_segment_timestamp_scale, m_track_block_contexts));
|
||||
if (candidate_block.track_number() == m_track_number)
|
||||
block = move(candidate_block);
|
||||
} else if (element_id == SEGMENT_ELEMENT_ID) {
|
||||
dbgln("Malformed file, found a segment element within the root segment element. Jumping into it.");
|
||||
|
|
@ -1161,9 +1183,10 @@ DecoderErrorOr<Block> SampleIterator::next_block()
|
|||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
|
||||
SampleIterator::SampleIterator(NonnullRefPtr<MediaStreamCursor> const& stream_cursor, TrackEntry& track, u64 timestamp_scale, size_t segment_contents_position, size_t position)
|
||||
SampleIterator::SampleIterator(NonnullRefPtr<MediaStreamCursor> const& stream_cursor, u64 track_number, TrackBlockContexts&& track_contexts, u64 timestamp_scale, size_t segment_contents_position, size_t position)
|
||||
: m_stream_cursor(stream_cursor)
|
||||
, m_track(track)
|
||||
, m_track_number(track_number)
|
||||
, m_track_block_contexts(move(track_contexts))
|
||||
, m_segment_timestamp_scale(timestamp_scale)
|
||||
, m_segment_contents_position(segment_contents_position)
|
||||
, m_position(position)
|
||||
|
|
|
|||
|
|
@ -51,8 +51,8 @@ public:
|
|||
static DecoderErrorOr<SegmentInformation> parse_segment_information_element(Streamer&);
|
||||
static DecoderErrorOr<NonnullRefPtr<TrackEntry>> parse_track_entry(Streamer&);
|
||||
static DecoderErrorOr<Cluster> parse_cluster_element(Streamer&, u64 timestamp_scale);
|
||||
static DecoderErrorOr<Block> parse_simple_block(Streamer&, AK::Duration cluster_timestamp, u64 segment_timestamp_scale, TrackEntry const&);
|
||||
static DecoderErrorOr<Block> parse_block_group(Streamer&, AK::Duration cluster_timestamp, u64 segment_timestamp_scale, TrackEntry const&);
|
||||
static DecoderErrorOr<Block> parse_simple_block(Streamer&, AK::Duration cluster_timestamp, u64 segment_timestamp_scale, TrackBlockContexts const&);
|
||||
static DecoderErrorOr<Block> parse_block_group(Streamer&, AK::Duration cluster_timestamp, u64 segment_timestamp_scale, TrackBlockContexts const&);
|
||||
|
||||
Optional<AK::Duration> duration() const { return m_segment_information.duration(); }
|
||||
|
||||
|
|
@ -112,18 +112,18 @@ public:
|
|||
DecoderErrorOr<Vector<ByteBuffer>> get_frames(Block);
|
||||
Cluster const& current_cluster() const { return *m_current_cluster; }
|
||||
Optional<AK::Duration> const& last_timestamp() const { return m_last_timestamp; }
|
||||
TrackEntry const& track() const { return *m_track; }
|
||||
MediaStreamCursor& cursor() { return m_stream_cursor; }
|
||||
|
||||
private:
|
||||
friend class Reader;
|
||||
|
||||
SampleIterator(NonnullRefPtr<MediaStreamCursor> const& stream_cursor, TrackEntry& track, u64 timestamp_scale, size_t segment_contents_position, size_t position);
|
||||
SampleIterator(NonnullRefPtr<MediaStreamCursor> const& stream_cursor, u64 track_number, TrackBlockContexts&&, u64 timestamp_scale, size_t segment_contents_position, size_t position);
|
||||
|
||||
DecoderErrorOr<void> seek_to_cue_point(TrackCuePoint const& cue_point, CuePointTarget);
|
||||
|
||||
NonnullRefPtr<MediaStreamCursor> m_stream_cursor;
|
||||
NonnullRefPtr<TrackEntry> m_track;
|
||||
u64 m_track_number;
|
||||
TrackBlockContexts m_track_block_contexts;
|
||||
u64 m_segment_timestamp_scale { 0 };
|
||||
size_t m_segment_contents_position { 0 };
|
||||
|
||||
|
|
|
|||
|
|
@ -8,9 +8,9 @@
|
|||
|
||||
#include "TestMediaCommon.h"
|
||||
|
||||
static NonnullOwnPtr<Media::VideoDecoder> make_decoder(Media::Matroska::SampleIterator const& iterator)
|
||||
static NonnullOwnPtr<Media::VideoDecoder> make_decoder(Media::Matroska::TrackEntry const& track)
|
||||
{
|
||||
return MUST(Media::FFmpeg::FFmpegVideoDecoder::try_create(Media::CodecID::H264, iterator.track().codec_private_data()));
|
||||
return MUST(Media::FFmpeg::FFmpegVideoDecoder::try_create(Media::CodecID::H264, track.codec_private_data()));
|
||||
}
|
||||
|
||||
TEST_CASE(avc_in_matroska)
|
||||
|
|
|
|||
|
|
@ -26,30 +26,31 @@ static inline void decode_video(StringView path, size_t expected_frame_count, T
|
|||
auto file = MUST(Core::File::open(path, Core::File::OpenMode::Read));
|
||||
auto stream = Media::IncrementallyPopulatedStream::create_from_buffer(MUST(file->read_until_eof()));
|
||||
auto matroska_reader = MUST(Media::Matroska::Reader::from_stream(stream->create_cursor()));
|
||||
u64 video_track = 0;
|
||||
RefPtr<Media::Matroska::TrackEntry const> video_track_entry;
|
||||
MUST(matroska_reader.for_each_track_of_type(Media::Matroska::TrackEntry::TrackType::Video, [&](Media::Matroska::TrackEntry const& track_entry) -> Media::DecoderErrorOr<IterationDecision> {
|
||||
video_track = track_entry.track_number();
|
||||
video_track_entry = track_entry;
|
||||
return IterationDecision::Break;
|
||||
}));
|
||||
VERIFY(video_track != 0);
|
||||
EXPECT(video_track_entry);
|
||||
|
||||
auto iterator = MUST(matroska_reader.create_sample_iterator(stream->create_cursor(), video_track));
|
||||
auto iterator = MUST(matroska_reader.create_sample_iterator(stream->create_cursor(), video_track_entry->track_number()));
|
||||
size_t frame_count = 0;
|
||||
NonnullOwnPtr<Media::VideoDecoder> decoder = create_decoder(iterator);
|
||||
NonnullOwnPtr<Media::VideoDecoder> decoder = create_decoder(*video_track_entry);
|
||||
|
||||
auto last_timestamp = AK::Duration::min();
|
||||
|
||||
while (frame_count <= expected_frame_count) {
|
||||
auto block_result = iterator.next_block();
|
||||
if (block_result.is_error() && block_result.error().category() == Media::DecoderErrorCategory::EndOfStream) {
|
||||
VERIFY(frame_count == expected_frame_count);
|
||||
EXPECT_EQ(frame_count, expected_frame_count);
|
||||
return;
|
||||
}
|
||||
|
||||
auto block = block_result.release_value();
|
||||
EXPECT(block.timestamp().has_value());
|
||||
auto frames = MUST(iterator.get_frames(block));
|
||||
for (auto const& frame : frames) {
|
||||
MUST(decoder->receive_coded_data(block.timestamp(), block.duration().value_or(AK::Duration::zero()), frame));
|
||||
MUST(decoder->receive_coded_data(block.timestamp().value(), block.duration().value_or(AK::Duration::zero()), frame));
|
||||
while (true) {
|
||||
auto frame_result = decoder->get_decoded_frame({});
|
||||
if (frame_result.is_error()) {
|
||||
|
|
|
|||
|
|
@ -283,7 +283,7 @@ TEST_CASE(block_group)
|
|||
EXPECT_EQ(first_block.duration()->to_milliseconds(), 33);
|
||||
|
||||
auto second_block = MUST(iterator.next_block());
|
||||
EXPECT_EQ(second_block.timestamp().to_milliseconds(), 33);
|
||||
EXPECT_EQ(second_block.timestamp().value().to_milliseconds(), 33);
|
||||
EXPECT(second_block.only_keyframes());
|
||||
}
|
||||
|
||||
|
|
@ -303,7 +303,7 @@ TEST_CASE(fixed_size_lacing)
|
|||
|
||||
// Block 1: 4 frames × 4 bytes
|
||||
auto block1 = MUST(iterator.next_block());
|
||||
EXPECT_EQ(block1.timestamp().to_milliseconds(), 0);
|
||||
EXPECT_EQ(block1.timestamp().value().to_milliseconds(), 0);
|
||||
EXPECT(block1.only_keyframes());
|
||||
EXPECT_EQ(block1.lacing(), Media::Matroska::Block::Lacing::FixedSize);
|
||||
auto frames1 = MUST(iterator.get_frames(block1));
|
||||
|
|
@ -313,7 +313,7 @@ TEST_CASE(fixed_size_lacing)
|
|||
|
||||
// Block 2: 2 frames × 8 bytes
|
||||
auto block2 = MUST(iterator.next_block());
|
||||
EXPECT_EQ(block2.timestamp().to_milliseconds(), 33);
|
||||
EXPECT_EQ(block2.timestamp().value().to_milliseconds(), 33);
|
||||
EXPECT_EQ(block2.lacing(), Media::Matroska::Block::Lacing::FixedSize);
|
||||
auto frames2 = MUST(iterator.get_frames(block2));
|
||||
EXPECT_EQ(frames2.size(), 2u);
|
||||
|
|
@ -322,7 +322,7 @@ TEST_CASE(fixed_size_lacing)
|
|||
|
||||
// Block 3: 3 frames × 1 byte
|
||||
auto block3 = MUST(iterator.next_block());
|
||||
EXPECT_EQ(block3.timestamp().to_milliseconds(), 66);
|
||||
EXPECT_EQ(block3.timestamp().value().to_milliseconds(), 66);
|
||||
EXPECT_EQ(block3.lacing(), Media::Matroska::Block::Lacing::FixedSize);
|
||||
auto frames3 = MUST(iterator.get_frames(block3));
|
||||
EXPECT_EQ(frames3.size(), 3u);
|
||||
|
|
@ -434,27 +434,27 @@ TEST_CASE(seeking)
|
|||
auto iterator = MUST(matroska_reader.create_sample_iterator(stream->create_cursor(), video_track));
|
||||
|
||||
auto first_block = MUST(iterator.next_block());
|
||||
EXPECT_EQ(first_block.timestamp().to_milliseconds(), 0);
|
||||
EXPECT_EQ(first_block.timestamp().value().to_milliseconds(), 0);
|
||||
EXPECT(first_block.only_keyframes());
|
||||
|
||||
iterator = MUST(matroska_reader.seek_to_random_access_point(iterator, AK::Duration::from_milliseconds(150)));
|
||||
auto block_after_forward_seek = MUST(iterator.next_block());
|
||||
EXPECT_EQ(block_after_forward_seek.timestamp().to_milliseconds(), 100);
|
||||
EXPECT_EQ(block_after_forward_seek.timestamp().value().to_milliseconds(), 100);
|
||||
EXPECT(block_after_forward_seek.only_keyframes());
|
||||
|
||||
iterator = MUST(matroska_reader.seek_to_random_access_point(iterator, AK::Duration::from_milliseconds(220)));
|
||||
auto block_at_200 = MUST(iterator.next_block());
|
||||
EXPECT_EQ(block_at_200.timestamp().to_milliseconds(), 200);
|
||||
EXPECT_EQ(block_at_200.timestamp().value().to_milliseconds(), 200);
|
||||
EXPECT(block_at_200.only_keyframes());
|
||||
|
||||
iterator = MUST(matroska_reader.seek_to_random_access_point(iterator, AK::Duration::from_milliseconds(50)));
|
||||
auto block_at_0 = MUST(iterator.next_block());
|
||||
EXPECT_EQ(block_at_0.timestamp().to_milliseconds(), 0);
|
||||
EXPECT_EQ(block_at_0.timestamp().value().to_milliseconds(), 0);
|
||||
EXPECT(block_at_0.only_keyframes());
|
||||
|
||||
iterator = MUST(matroska_reader.seek_to_random_access_point(iterator, AK::Duration::from_milliseconds(100)));
|
||||
auto block_exact_100 = MUST(iterator.next_block());
|
||||
EXPECT_EQ(block_exact_100.timestamp().to_milliseconds(), 100);
|
||||
EXPECT_EQ(block_exact_100.timestamp().value().to_milliseconds(), 100);
|
||||
EXPECT(block_exact_100.only_keyframes());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,9 +8,9 @@
|
|||
|
||||
#include "TestMediaCommon.h"
|
||||
|
||||
static NonnullOwnPtr<Media::VideoDecoder> make_decoder(Media::Matroska::SampleIterator const& iterator)
|
||||
static NonnullOwnPtr<Media::VideoDecoder> make_decoder(Media::Matroska::TrackEntry const& track)
|
||||
{
|
||||
return MUST(Media::FFmpeg::FFmpegVideoDecoder::try_create(Media::CodecID::VP9, iterator.track().codec_private_data()));
|
||||
return MUST(Media::FFmpeg::FFmpegVideoDecoder::try_create(Media::CodecID::VP9, track.codec_private_data()));
|
||||
}
|
||||
|
||||
TEST_CASE(webm_in_vp9)
|
||||
|
|
|
|||
Loading…
Reference in a new issue