diff --git a/Userland/Libraries/LibVideo/CodecID.h b/Userland/Libraries/LibVideo/CodecID.h new file mode 100644 index 0000000000..6cbbe4d35f --- /dev/null +++ b/Userland/Libraries/LibVideo/CodecID.h @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2023, Stephan Vedder + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include + +namespace Video { + +enum class CodecID : u32 { + Unknown, + // On2 / Google + VP8, + VP9, + // MPEG + H261, + MPEG1, + H262, + H263, + H264, + H265, + // AOMedia + AV1, + // Xiph + Theora, + Vorbis, + Opus, +}; + +} + +namespace AK { +template<> +struct Formatter : Formatter { + ErrorOr format(FormatBuilder& builder, Video::CodecID value) + { + StringView codec; + switch (value) { + case Video::CodecID::Unknown: + codec = "Unknown"sv; + break; + case Video::CodecID::VP8: + codec = "VP8"sv; + break; + case Video::CodecID::VP9: + codec = "VP9"sv; + break; + case Video::CodecID::H261: + codec = "H.261"sv; + break; + case Video::CodecID::H262: + codec = "H.262"sv; + break; + case Video::CodecID::H263: + codec = "H.263"sv; + break; + case Video::CodecID::H264: + codec = "H.264"sv; + break; + case Video::CodecID::H265: + codec = "H.265"sv; + break; + case Video::CodecID::MPEG1: + codec = "MPEG1"sv; + break; + case Video::CodecID::AV1: + codec = "AV1"sv; + break; + case Video::CodecID::Theora: + codec = "Theora"sv; + break; + case Video::CodecID::Vorbis: + codec = "Vorbis"sv; + break; + case Video::CodecID::Opus: + codec = "Opus"sv; + break; + } + return builder.put_string(codec); + } +}; +} diff --git a/Userland/Libraries/LibVideo/Containers/Demuxer.h b/Userland/Libraries/LibVideo/Containers/Demuxer.h index 08acde7707..b09252fbaa 100644 --- a/Userland/Libraries/LibVideo/Containers/Demuxer.h +++ b/Userland/Libraries/LibVideo/Containers/Demuxer.h @@ -8,6 +8,7 @@ #include #include +#include #include #include #include @@ -28,6 +29,8 @@ public: return sample.release_nonnull(); } + virtual DecoderErrorOr get_codec_id_for_track(Track track) = 0; + // Returns the timestamp of the keyframe that was seeked to. // The value is `Optional` to allow the demuxer to decide not to seek so that it can keep its position // in the case that the timestamp is closer to the current time than the nearest keyframe. diff --git a/Userland/Libraries/LibVideo/Containers/Matroska/MatroskaDemuxer.cpp b/Userland/Libraries/LibVideo/Containers/Matroska/MatroskaDemuxer.cpp index a45a9cb74c..f8befa7eed 100644 --- a/Userland/Libraries/LibVideo/Containers/Matroska/MatroskaDemuxer.cpp +++ b/Userland/Libraries/LibVideo/Containers/Matroska/MatroskaDemuxer.cpp @@ -71,6 +71,32 @@ DecoderErrorOr MatroskaDemuxer::get_track_status( return &m_track_statuses.get(track).release_value(); } +CodecID MatroskaDemuxer::get_codec_id_for_string(DeprecatedFlyString const& codec_id) +{ + 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; +} + DecoderErrorOr> MatroskaDemuxer::seek_to_most_recent_keyframe(Track track, Duration timestamp, Optional earliest_available_sample) { // Removing the track status will cause us to start from the beginning. @@ -119,4 +145,10 @@ DecoderErrorOr MatroskaDemuxer::duration() return duration.value_or(Duration::zero()); } +DecoderErrorOr 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); +} + } diff --git a/Userland/Libraries/LibVideo/Containers/Matroska/MatroskaDemuxer.h b/Userland/Libraries/LibVideo/Containers/Matroska/MatroskaDemuxer.h index 59bd151a27..2a2c0eb935 100644 --- a/Userland/Libraries/LibVideo/Containers/Matroska/MatroskaDemuxer.h +++ b/Userland/Libraries/LibVideo/Containers/Matroska/MatroskaDemuxer.h @@ -33,6 +33,8 @@ public: DecoderErrorOr duration() override; + DecoderErrorOr get_codec_id_for_track(Track track) override; + protected: DecoderErrorOr> get_next_sample_for_track(Track track) override; @@ -44,6 +46,7 @@ private: }; DecoderErrorOr get_track_status(Track track); + CodecID get_codec_id_for_string(DeprecatedFlyString const& codec_id); Reader m_reader; diff --git a/Userland/Libraries/LibVideo/PlaybackManager.cpp b/Userland/Libraries/LibVideo/PlaybackManager.cpp index 8ef34ef3f8..cae1875998 100644 --- a/Userland/Libraries/LibVideo/PlaybackManager.cpp +++ b/Userland/Libraries/LibVideo/PlaybackManager.cpp @@ -52,9 +52,19 @@ DecoderErrorOr> PlaybackManager::create(NonnullOw dbgln_if(PLAYBACK_MANAGER_DEBUG, "Selecting video track number {}", track.identifier()); - auto decoder = DECODER_TRY_ALLOC(try_make()); + auto codec_id = TRY(demuxer->get_codec_id_for_track(track)); + OwnPtr decoder; + switch (codec_id) { + case CodecID::VP9: + decoder = DECODER_TRY_ALLOC(try_make()); + break; + + default: + return DecoderError::format(DecoderErrorCategory::Invalid, "Unsupported codec: {}", codec_id); + } + auto decoder_non_null = decoder.release_nonnull(); auto frame_queue = DECODER_TRY_ALLOC(VideoFrameQueue::create()); - auto playback_manager = DECODER_TRY_ALLOC(try_make(demuxer, track, move(decoder), move(frame_queue))); + auto playback_manager = DECODER_TRY_ALLOC(try_make(demuxer, track, move(decoder_non_null), move(frame_queue))); playback_manager->m_state_update_timer = DECODER_TRY_ALLOC(Core::Timer::create_single_shot(0, [&self = *playback_manager] { self.timer_callback(); }));