mirror of
				https://github.com/RGBCube/serenity
				synced 2025-10-31 10:42:45 +00:00 
			
		
		
		
	 6a351376aa
			
		
	
	
		6a351376aa
		
	
	
	
	
		
			
			If a local include does not point to a file in the repository, it should be a system include instead.
		
			
				
	
	
		
			122 lines
		
	
	
	
		
			4.5 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			122 lines
		
	
	
	
		
			4.5 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /*
 | |
|  * Copyright (c) 2022, Gregory Bertilson <zaggy1024@gmail.com>
 | |
|  *
 | |
|  * SPDX-License-Identifier: BSD-2-Clause
 | |
|  */
 | |
| 
 | |
| #include "MatroskaDemuxer.h"
 | |
| #include <AK/Debug.h>
 | |
| 
 | |
| namespace Video::Matroska {
 | |
| 
 | |
| DecoderErrorOr<NonnullOwnPtr<MatroskaDemuxer>> MatroskaDemuxer::from_file(StringView filename)
 | |
| {
 | |
|     return make<MatroskaDemuxer>(TRY(Reader::from_file(filename)));
 | |
| }
 | |
| 
 | |
| DecoderErrorOr<NonnullOwnPtr<MatroskaDemuxer>> MatroskaDemuxer::from_mapped_file(NonnullRefPtr<Core::MappedFile> mapped_file)
 | |
| {
 | |
|     return make<MatroskaDemuxer>(TRY(Reader::from_mapped_file(move(mapped_file))));
 | |
| }
 | |
| 
 | |
| DecoderErrorOr<NonnullOwnPtr<MatroskaDemuxer>> MatroskaDemuxer::from_data(ReadonlyBytes data)
 | |
| {
 | |
|     return make<MatroskaDemuxer>(TRY(Reader::from_data(data)));
 | |
| }
 | |
| 
 | |
| DecoderErrorOr<Vector<Track>> MatroskaDemuxer::get_tracks_for_type(TrackType type)
 | |
| {
 | |
|     TrackEntry::TrackType matroska_track_type;
 | |
| 
 | |
|     switch (type) {
 | |
|     case TrackType::Video:
 | |
|         matroska_track_type = TrackEntry::TrackType::Video;
 | |
|         break;
 | |
|     case TrackType::Audio:
 | |
|         matroska_track_type = TrackEntry::TrackType::Audio;
 | |
|         break;
 | |
|     case TrackType::Subtitles:
 | |
|         matroska_track_type = TrackEntry::TrackType::Subtitle;
 | |
|         break;
 | |
|     }
 | |
| 
 | |
|     Vector<Track> tracks;
 | |
|     TRY(m_reader.for_each_track_of_type(matroska_track_type, [&](TrackEntry const& track_entry) -> DecoderErrorOr<IterationDecision> {
 | |
|         VERIFY(track_entry.track_type() == matroska_track_type);
 | |
|         Track track(type, track_entry.track_number());
 | |
| 
 | |
|         switch (type) {
 | |
|         case TrackType::Video:
 | |
|             if (auto video_track = track_entry.video_track(); video_track.has_value())
 | |
|                 track.set_video_data({ TRY(duration()), video_track->pixel_width, video_track->pixel_height });
 | |
|             break;
 | |
|         default:
 | |
|             break;
 | |
|         }
 | |
| 
 | |
|         DECODER_TRY_ALLOC(tracks.try_append(track));
 | |
|         return IterationDecision::Continue;
 | |
|     }));
 | |
| 
 | |
|     return tracks;
 | |
| }
 | |
| 
 | |
| DecoderErrorOr<MatroskaDemuxer::TrackStatus*> MatroskaDemuxer::get_track_status(Track track)
 | |
| {
 | |
|     if (!m_track_statuses.contains(track)) {
 | |
|         auto iterator = TRY(m_reader.create_sample_iterator(track.identifier()));
 | |
|         DECODER_TRY_ALLOC(m_track_statuses.try_set(track, { iterator }));
 | |
|     }
 | |
| 
 | |
|     return &m_track_statuses.get(track).release_value();
 | |
| }
 | |
| 
 | |
| DecoderErrorOr<Optional<Duration>> MatroskaDemuxer::seek_to_most_recent_keyframe(Track track, Duration timestamp, Optional<Duration> earliest_available_sample)
 | |
| {
 | |
|     // Removing the track status will cause us to start from the beginning.
 | |
|     if (timestamp.is_zero()) {
 | |
|         m_track_statuses.remove(track);
 | |
|         return timestamp;
 | |
|     }
 | |
| 
 | |
|     auto& track_status = *TRY(get_track_status(track));
 | |
|     auto seeked_iterator = TRY(m_reader.seek_to_random_access_point(track_status.iterator, timestamp));
 | |
|     VERIFY(seeked_iterator.last_timestamp().has_value());
 | |
| 
 | |
|     auto last_sample = earliest_available_sample;
 | |
|     if (!last_sample.has_value()) {
 | |
|         last_sample = track_status.iterator.last_timestamp();
 | |
|     }
 | |
|     if (last_sample.has_value()) {
 | |
|         bool skip_seek = seeked_iterator.last_timestamp().value() <= last_sample.value() && last_sample.value() <= timestamp;
 | |
|         dbgln_if(MATROSKA_DEBUG, "The last available sample at {}ms is {}closer to target timestamp {}ms than the keyframe at {}ms, {}", last_sample->to_milliseconds(), skip_seek ? ""sv : "not "sv, timestamp.to_milliseconds(), seeked_iterator.last_timestamp()->to_milliseconds(), skip_seek ? "skipping seek"sv : "seeking"sv);
 | |
|         if (skip_seek) {
 | |
|             return OptionalNone();
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     track_status.iterator = move(seeked_iterator);
 | |
|     return track_status.iterator.last_timestamp();
 | |
| }
 | |
| 
 | |
| DecoderErrorOr<NonnullOwnPtr<Sample>> MatroskaDemuxer::get_next_sample_for_track(Track track)
 | |
| {
 | |
|     // FIXME: This makes a copy of the sample, which shouldn't be necessary.
 | |
|     //        Matroska should make a RefPtr<ByteBuffer>, probably.
 | |
|     auto& status = *TRY(get_track_status(track));
 | |
| 
 | |
|     if (!status.block.has_value() || status.frame_index >= status.block->frame_count()) {
 | |
|         status.block = TRY(status.iterator.next_block());
 | |
|         status.frame_index = 0;
 | |
|     }
 | |
|     auto cicp = TRY(m_reader.track_for_track_number(track.identifier())).video_track()->color_format.to_cicp();
 | |
|     return make<VideoSample>(status.block->frame(status.frame_index++), cicp, status.block->timestamp());
 | |
| }
 | |
| 
 | |
| DecoderErrorOr<Duration> MatroskaDemuxer::duration()
 | |
| {
 | |
|     auto duration = TRY(m_reader.segment_information()).duration();
 | |
|     return duration.value_or(Duration::zero());
 | |
| }
 | |
| 
 | |
| }
 |