mirror of
https://github.com/RGBCube/serenity
synced 2025-05-14 05:54:58 +00:00
LibVideo: Add the concept of codec identifiers
This is required for detecting which decoder should be used. Only a small subset of codecs identifiers is added for now
This commit is contained in:
parent
ff48b7333c
commit
1f55cc942d
5 changed files with 135 additions and 2 deletions
85
Userland/Libraries/LibVideo/CodecID.h
Normal file
85
Userland/Libraries/LibVideo/CodecID.h
Normal file
|
@ -0,0 +1,85 @@
|
|||
/*
|
||||
* Copyright (c) 2023, Stephan Vedder <stephan.vedder@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/Format.h>
|
||||
|
||||
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<Video::CodecID> : Formatter<StringView> {
|
||||
ErrorOr<void> 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);
|
||||
}
|
||||
};
|
||||
}
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
#include <AK/NonnullOwnPtr.h>
|
||||
#include <LibCore/EventReceiver.h>
|
||||
#include <LibVideo/CodecID.h>
|
||||
#include <LibVideo/DecoderError.h>
|
||||
#include <LibVideo/Sample.h>
|
||||
#include <LibVideo/Track.h>
|
||||
|
@ -28,6 +29,8 @@ public:
|
|||
return sample.release_nonnull<VideoSample>();
|
||||
}
|
||||
|
||||
virtual DecoderErrorOr<CodecID> 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.
|
||||
|
|
|
@ -71,6 +71,32 @@ DecoderErrorOr<MatroskaDemuxer::TrackStatus*> 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<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.
|
||||
|
@ -119,4 +145,10 @@ DecoderErrorOr<Duration> MatroskaDemuxer::duration()
|
|||
return duration.value_or(Duration::zero());
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -33,6 +33,8 @@ public:
|
|||
|
||||
DecoderErrorOr<Duration> duration() override;
|
||||
|
||||
DecoderErrorOr<CodecID> get_codec_id_for_track(Track track) override;
|
||||
|
||||
protected:
|
||||
DecoderErrorOr<NonnullOwnPtr<Sample>> get_next_sample_for_track(Track track) override;
|
||||
|
||||
|
@ -44,6 +46,7 @@ private:
|
|||
};
|
||||
|
||||
DecoderErrorOr<TrackStatus*> get_track_status(Track track);
|
||||
CodecID get_codec_id_for_string(DeprecatedFlyString const& codec_id);
|
||||
|
||||
Reader m_reader;
|
||||
|
||||
|
|
|
@ -52,9 +52,19 @@ DecoderErrorOr<NonnullOwnPtr<PlaybackManager>> PlaybackManager::create(NonnullOw
|
|||
|
||||
dbgln_if(PLAYBACK_MANAGER_DEBUG, "Selecting video track number {}", track.identifier());
|
||||
|
||||
auto decoder = DECODER_TRY_ALLOC(try_make<VP9::Decoder>());
|
||||
auto codec_id = TRY(demuxer->get_codec_id_for_track(track));
|
||||
OwnPtr<VideoDecoder> decoder;
|
||||
switch (codec_id) {
|
||||
case CodecID::VP9:
|
||||
decoder = DECODER_TRY_ALLOC(try_make<VP9::Decoder>());
|
||||
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<PlaybackManager>(demuxer, track, move(decoder), move(frame_queue)));
|
||||
auto playback_manager = DECODER_TRY_ALLOC(try_make<PlaybackManager>(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(); }));
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue