2022-10-29 17:02:43 -05:00
/*
* Copyright ( c ) 2022 , Gregory Bertilson < zaggy1024 @ gmail . com >
*
* SPDX - License - Identifier : BSD - 2 - Clause
*/
2025-09-11 18:37:06 -05:00
# include <AK/Assertions.h>
2023-06-01 16:17:13 +02:00
# include <AK/Debug.h>
2025-09-17 14:42:43 -05:00
# include <LibMedia/CodedFrame.h>
2025-09-11 18:37:06 -05:00
# include <LibMedia/DecoderError.h>
2022-10-29 17:02:43 -05:00
2024-06-10 15:04:03 -05:00
# include "MatroskaDemuxer.h"
namespace Media : : Matroska {
2022-10-29 17:02:43 -05:00
DecoderErrorOr < NonnullOwnPtr < MatroskaDemuxer > > MatroskaDemuxer : : from_file ( StringView filename )
{
2022-11-11 17:14:27 -06:00
return make < MatroskaDemuxer > ( TRY ( Reader : : from_file ( filename ) ) ) ;
2022-10-29 17:02:43 -05:00
}
2023-09-26 00:54:34 +02:00
DecoderErrorOr < NonnullOwnPtr < MatroskaDemuxer > > MatroskaDemuxer : : from_mapped_file ( NonnullOwnPtr < Core : : MappedFile > mapped_file )
2023-05-11 12:25:52 +01:00
{
return make < MatroskaDemuxer > ( TRY ( Reader : : from_mapped_file ( move ( mapped_file ) ) ) ) ;
}
2022-11-04 18:00:09 -05:00
DecoderErrorOr < NonnullOwnPtr < MatroskaDemuxer > > MatroskaDemuxer : : from_data ( ReadonlyBytes data )
2022-10-29 17:02:43 -05:00
{
2022-11-11 17:14:27 -06:00
return make < MatroskaDemuxer > ( TRY ( Reader : : from_data ( data ) ) ) ;
2022-10-29 17:02:43 -05:00
}
2025-09-11 18:25:05 -05:00
static TrackEntry : : TrackType matroska_track_type_from_track_type ( TrackType type )
2022-10-29 17:02:43 -05:00
{
switch ( type ) {
case TrackType : : Video :
2025-09-11 18:25:05 -05:00
return TrackEntry : : TrackType : : Video ;
2022-10-29 17:02:43 -05:00
case TrackType : : Audio :
2025-09-11 18:25:05 -05:00
return TrackEntry : : TrackType : : Audio ;
2022-10-29 17:02:43 -05:00
case TrackType : : Subtitles :
2025-09-11 18:25:05 -05:00
return TrackEntry : : TrackType : : Subtitle ;
case TrackType : : Unknown :
return TrackEntry : : TrackType : : Invalid ;
}
VERIFY_NOT_REACHED ( ) ;
}
static TrackType track_type_from_matroska_track_type ( TrackEntry : : TrackType type )
{
switch ( type ) {
case TrackEntry : : TrackType : : Video :
return TrackType : : Video ;
case TrackEntry : : TrackType : : Audio :
return TrackType : : Audio ;
case TrackEntry : : TrackType : : Subtitle :
return TrackType : : Subtitles ;
case TrackEntry : : TrackType : : Invalid :
return TrackType : : Unknown ;
case TrackEntry : : TrackType : : Complex :
case TrackEntry : : TrackType : : Logo :
case TrackEntry : : TrackType : : Buttons :
case TrackEntry : : TrackType : : Control :
case TrackEntry : : TrackType : : Metadata :
2022-10-29 17:02:43 -05:00
break ;
}
2025-09-11 18:25:05 -05:00
VERIFY_NOT_REACHED ( ) ;
}
static Track track_from_track_entry ( TrackEntry const & track_entry )
{
Track track ( track_type_from_matroska_track_type ( track_entry . track_type ( ) ) , track_entry . track_number ( ) ) ;
if ( track . type ( ) = = TrackType : : Video ) {
auto video_track = track_entry . video_track ( ) ;
if ( video_track . has_value ( ) ) {
track . set_video_data ( {
. pixel_width = video_track - > pixel_width ,
. pixel_height = video_track - > pixel_height ,
} ) ;
}
}
return track ;
}
2022-10-29 17:02:43 -05:00
2025-09-11 18:25:05 -05:00
DecoderErrorOr < Vector < Track > > MatroskaDemuxer : : get_tracks_for_type ( TrackType type )
{
auto matroska_track_type = matroska_track_type_from_track_type ( type ) ;
2022-10-29 17:02:43 -05:00
Vector < Track > tracks ;
2022-11-11 17:14:27 -06:00
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 ) ;
2025-09-11 18:25:05 -05:00
DECODER_TRY_ALLOC ( tracks . try_append ( track_from_track_entry ( track_entry ) ) ) ;
2022-11-11 17:14:27 -06:00
return IterationDecision : : Continue ;
} ) ) ;
2022-10-29 17:02:43 -05:00
return tracks ;
}
2025-09-11 18:25:05 -05:00
DecoderErrorOr < Optional < Track > > MatroskaDemuxer : : get_preferred_track_for_type ( TrackType type )
{
auto matroska_track_type = matroska_track_type_from_track_type ( type ) ;
Optional < Track > result ;
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 ) ;
result = track_from_track_entry ( track_entry ) ;
return IterationDecision : : Break ;
} ) ) ;
return result ;
}
2022-11-11 17:14:27 -06:00
DecoderErrorOr < MatroskaDemuxer : : TrackStatus * > MatroskaDemuxer : : get_track_status ( Track track )
2022-10-29 17:02:43 -05:00
{
2022-11-11 17:14:27 -06:00
if ( ! m_track_statuses . contains ( track ) ) {
auto iterator = TRY ( m_reader . create_sample_iterator ( track . identifier ( ) ) ) ;
2025-09-11 18:38:38 -05:00
DECODER_TRY_ALLOC ( m_track_statuses . try_set ( track , TrackStatus ( move ( iterator ) ) ) ) ;
2022-11-11 17:14:27 -06:00
}
2022-10-29 17:02:43 -05:00
return & m_track_statuses . get ( track ) . release_value ( ) ;
}
2024-01-02 15:45:53 +01:00
CodecID MatroskaDemuxer : : get_codec_id_for_string ( FlyString const & codec_id )
2023-11-22 14:48:06 +01:00
{
dbgln_if ( MATROSKA_DEBUG , " Codec ID: {} " , codec_id ) ;
if ( codec_id = = " V_VP8 " )
return CodecID : : VP8 ;
if ( codec_id = = " V_VP9 " )
return CodecID : : VP9 ;
if ( codec_id = = " V_MPEG1 " )
return CodecID : : MPEG1 ;
if ( codec_id = = " V_MPEG2 " )
return CodecID : : H262 ;
if ( codec_id = = " V_MPEG4/ISO/AVC " )
return CodecID : : H264 ;
if ( codec_id = = " V_MPEGH/ISO/HEVC " )
return CodecID : : H265 ;
if ( codec_id = = " V_AV1 " )
return CodecID : : AV1 ;
if ( codec_id = = " V_THEORA " )
return CodecID : : Theora ;
if ( codec_id = = " A_VORBIS " )
return CodecID : : Vorbis ;
if ( codec_id = = " A_OPUS " )
return CodecID : : Opus ;
return CodecID : : Unknown ;
}
2024-06-19 17:13:01 -05:00
DecoderErrorOr < CodecID > MatroskaDemuxer : : get_codec_id_for_track ( Track track )
{
auto codec_id = TRY ( m_reader . track_for_track_number ( track . identifier ( ) ) ) - > codec_id ( ) ;
return get_codec_id_for_string ( codec_id ) ;
}
2024-06-19 18:22:21 -05:00
DecoderErrorOr < ReadonlyBytes > MatroskaDemuxer : : get_codec_initialization_data_for_track ( Track track )
{
return TRY ( m_reader . track_for_track_number ( track . identifier ( ) ) ) - > codec_private_data ( ) ;
}
2024-07-16 23:44:30 -06:00
DecoderErrorOr < Optional < AK : : Duration > > MatroskaDemuxer : : seek_to_most_recent_keyframe ( Track track , AK : : Duration timestamp , Optional < AK : : Duration > earliest_available_sample )
2022-10-29 17:02:43 -05:00
{
2022-11-14 00:54:21 -06:00
// Removing the track status will cause us to start from the beginning.
2022-11-12 04:04:13 -06:00
if ( timestamp . is_zero ( ) ) {
m_track_statuses . remove ( track ) ;
2022-11-13 20:33:23 -06:00
return timestamp ;
2022-11-12 04:04:13 -06:00
}
auto & track_status = * TRY ( get_track_status ( track ) ) ;
2023-02-06 01:25:02 -06:00
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 ) ;
2022-11-13 20:33:23 -06:00
return track_status . iterator . last_timestamp ( ) ;
2022-10-29 17:02:43 -05:00
}
2025-09-17 14:42:43 -05:00
DecoderErrorOr < CodedFrame > MatroskaDemuxer : : get_next_sample_for_track ( Track track )
2022-10-29 17:02:43 -05:00
{
2022-11-11 17:14:27 -06:00
// 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 ) ) ;
2022-10-29 17:02:43 -05:00
2022-11-11 17:14:27 -06:00
if ( ! status . block . has_value ( ) | | status . frame_index > = status . block - > frame_count ( ) ) {
status . block = TRY ( status . iterator . next_block ( ) ) ;
status . frame_index = 0 ;
2022-10-29 17:02:43 -05:00
}
2024-06-19 17:09:15 -05:00
auto cicp = TRY ( m_reader . track_for_track_number ( track . identifier ( ) ) ) - > video_track ( ) - > color_format . to_cicp ( ) ;
2025-03-10 14:54:02 +00:00
auto sample_data = DECODER_TRY_ALLOC ( ByteBuffer : : copy ( status . block - > frame ( status . frame_index + + ) ) ) ;
2025-09-17 14:42:43 -05:00
return CodedFrame ( status . block - > timestamp ( ) , move ( sample_data ) , CodedVideoFrameData ( cicp ) ) ;
2022-10-29 17:02:43 -05:00
}
2025-09-11 18:37:06 -05:00
DecoderErrorOr < AK : : Duration > MatroskaDemuxer : : total_duration ( )
2022-10-29 17:02:43 -05:00
{
2022-11-13 19:13:50 -06:00
auto duration = TRY ( m_reader . segment_information ( ) ) . duration ( ) ;
2024-07-16 23:44:30 -06:00
return duration . value_or ( AK : : Duration : : zero ( ) ) ;
2022-10-29 17:02:43 -05:00
}
2025-09-11 18:37:06 -05:00
DecoderErrorOr < AK : : Duration > MatroskaDemuxer : : duration_of_track ( Track const & )
{
return total_duration ( ) ;
}
2022-10-29 17:02:43 -05:00
}