mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-09-29 08:11:13 +00:00
LibMedia: Pass Matroska codec private data to the FFmpeg decoder
This is necessary to give the H.264 decoder the data it needs to initialize, including frame size and profile.
This commit is contained in:
parent
81001b37ce
commit
457a69786b
Notes:
sideshowbarker
2024-07-17 00:25:35 +09:00
Author: https://github.com/Zaggy1024 Commit: https://github.com/LadybirdBrowser/ladybird/commit/457a69786b Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/230 Reviewed-by: https://github.com/ADKaster
|
@ -8,6 +8,7 @@
|
|||
|
||||
#include <AK/ByteBuffer.h>
|
||||
#include <AK/ByteString.h>
|
||||
#include <AK/FixedArray.h>
|
||||
#include <AK/FlyString.h>
|
||||
#include <AK/HashMap.h>
|
||||
#include <AK/OwnPtr.h>
|
||||
|
@ -119,6 +120,12 @@ public:
|
|||
void set_language(FlyString const& language) { m_language = language; }
|
||||
FlyString codec_id() const { return m_codec_id; }
|
||||
void set_codec_id(FlyString const& codec_id) { m_codec_id = codec_id; }
|
||||
ReadonlyBytes codec_private_data() const { return m_codec_private_data.span(); }
|
||||
ErrorOr<void> set_codec_private_data(ReadonlyBytes codec_private_data)
|
||||
{
|
||||
m_codec_private_data = TRY(FixedArray<u8>::create(codec_private_data));
|
||||
return {};
|
||||
}
|
||||
double timestamp_scale() const { return m_timestamp_scale; }
|
||||
void set_timestamp_scale(double timestamp_scale) { m_timestamp_scale = timestamp_scale; }
|
||||
u64 codec_delay() const { return m_codec_delay; }
|
||||
|
@ -146,6 +153,7 @@ private:
|
|||
TrackType m_track_type { Invalid };
|
||||
FlyString m_language = "eng"_fly_string;
|
||||
FlyString m_codec_id;
|
||||
FixedArray<u8> m_codec_private_data;
|
||||
double m_timestamp_scale { 1 };
|
||||
u64 m_codec_delay { 0 };
|
||||
u64 m_timestamp_offset { 0 };
|
||||
|
|
|
@ -104,6 +104,11 @@ DecoderErrorOr<CodecID> MatroskaDemuxer::get_codec_id_for_track(Track track)
|
|||
return get_codec_id_for_string(codec_id);
|
||||
}
|
||||
|
||||
DecoderErrorOr<ReadonlyBytes> MatroskaDemuxer::get_codec_initialization_data_for_track(Track track)
|
||||
{
|
||||
return TRY(m_reader.track_for_track_number(track.identifier()))->codec_private_data();
|
||||
}
|
||||
|
||||
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.
|
||||
|
|
|
@ -35,6 +35,8 @@ public:
|
|||
|
||||
DecoderErrorOr<CodecID> get_codec_id_for_track(Track track) override;
|
||||
|
||||
DecoderErrorOr<ReadonlyBytes> get_codec_initialization_data_for_track(Track track) override;
|
||||
|
||||
DecoderErrorOr<Sample> get_next_sample_for_track(Track track) override;
|
||||
|
||||
private:
|
||||
|
|
|
@ -50,6 +50,7 @@ constexpr u32 TRACK_UID_ID = 0x73C5;
|
|||
constexpr u32 TRACK_TYPE_ID = 0x83;
|
||||
constexpr u32 TRACK_LANGUAGE_ID = 0x22B59C;
|
||||
constexpr u32 TRACK_CODEC_ID = 0x86;
|
||||
constexpr u32 TRACK_CODEC_PRIVATE = 0x63A2;
|
||||
constexpr u32 TRACK_TIMESTAMP_SCALE_ID = 0x23314F;
|
||||
constexpr u32 TRACK_OFFSET_ID = 0x537F;
|
||||
constexpr u32 TRACK_VIDEO_ID = 0xE0;
|
||||
|
@ -480,6 +481,12 @@ static DecoderErrorOr<NonnullRefPtr<TrackEntry>> parse_track_entry(Streamer& str
|
|||
track_entry->set_codec_id(DECODER_TRY_ALLOC(String::from_byte_string(TRY_READ(streamer.read_string()))));
|
||||
dbgln_if(MATROSKA_TRACE_DEBUG, "Read Track's CodecID attribute: {}", track_entry->codec_id());
|
||||
break;
|
||||
case TRACK_CODEC_PRIVATE: {
|
||||
auto codec_private_data = TRY_READ(streamer.read_raw_octets(TRY_READ(streamer.read_variable_size_integer())));
|
||||
DECODER_TRY_ALLOC(track_entry->set_codec_private_data(codec_private_data));
|
||||
dbgln_if(MATROSKA_TRACE_DEBUG, "Read Track's CodecID attribute: {}", track_entry->codec_id());
|
||||
break;
|
||||
}
|
||||
case TRACK_TIMESTAMP_SCALE_ID:
|
||||
track_entry->set_timestamp_scale(TRY_READ(streamer.read_float()));
|
||||
dbgln_if(MATROSKA_TRACE_DEBUG, "Read Track's TrackTimestampScale attribute: {}", track_entry->timestamp_scale());
|
||||
|
|
|
@ -26,6 +26,8 @@ public:
|
|||
|
||||
virtual DecoderErrorOr<CodecID> get_codec_id_for_track(Track track) = 0;
|
||||
|
||||
virtual DecoderErrorOr<ReadonlyBytes> get_codec_initialization_data_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.
|
||||
|
|
|
@ -34,7 +34,7 @@ static AVPixelFormat negotiate_output_format(AVCodecContext*, AVPixelFormat cons
|
|||
return AV_PIX_FMT_NONE;
|
||||
}
|
||||
|
||||
DecoderErrorOr<NonnullOwnPtr<FFmpegVideoDecoder>> FFmpegVideoDecoder::try_create(CodecID codec_id)
|
||||
DecoderErrorOr<NonnullOwnPtr<FFmpegVideoDecoder>> FFmpegVideoDecoder::try_create(CodecID codec_id, ReadonlyBytes codec_initialization_data)
|
||||
{
|
||||
AVCodecContext* codec_context = nullptr;
|
||||
AVPacket* packet = nullptr;
|
||||
|
@ -60,6 +60,18 @@ DecoderErrorOr<NonnullOwnPtr<FFmpegVideoDecoder>> FFmpegVideoDecoder::try_create
|
|||
|
||||
codec_context->thread_count = static_cast<int>(min(Core::System::hardware_concurrency(), 4));
|
||||
|
||||
if (!codec_initialization_data.is_empty()) {
|
||||
if (codec_initialization_data.size() > NumericLimits<int>::max())
|
||||
return DecoderError::corrupted("Codec initialization data is too large"sv);
|
||||
|
||||
codec_context->extradata = static_cast<u8*>(av_malloc(codec_initialization_data.size() + AV_INPUT_BUFFER_PADDING_SIZE));
|
||||
if (!codec_context->extradata)
|
||||
return DecoderError::with_description(DecoderErrorCategory::Memory, "Failed to allocate codec initialization data buffer for FFmpeg codec"sv);
|
||||
|
||||
memcpy(codec_context->extradata, codec_initialization_data.data(), codec_initialization_data.size());
|
||||
codec_context->extradata_size = static_cast<int>(codec_initialization_data.size());
|
||||
}
|
||||
|
||||
if (avcodec_open2(codec_context, codec, nullptr) < 0)
|
||||
return DecoderError::format(DecoderErrorCategory::Unknown, "Unknown error occurred when opening FFmpeg codec {}", codec_id);
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@ namespace Media::FFmpeg {
|
|||
|
||||
class FFmpegVideoDecoder final : public VideoDecoder {
|
||||
public:
|
||||
static DecoderErrorOr<NonnullOwnPtr<FFmpegVideoDecoder>> try_create(CodecID);
|
||||
static DecoderErrorOr<NonnullOwnPtr<FFmpegVideoDecoder>> try_create(CodecID, ReadonlyBytes codec_initialization_data);
|
||||
FFmpegVideoDecoder(AVCodecContext* codec_context, AVPacket* packet, AVFrame* frame);
|
||||
~FFmpegVideoDecoder();
|
||||
|
||||
|
|
|
@ -701,7 +701,8 @@ DecoderErrorOr<NonnullOwnPtr<PlaybackManager>> PlaybackManager::create(NonnullOw
|
|||
dbgln_if(PLAYBACK_MANAGER_DEBUG, "Selecting video track number {}", track.identifier());
|
||||
|
||||
auto codec_id = TRY(demuxer->get_codec_id_for_track(track));
|
||||
NonnullOwnPtr<VideoDecoder> decoder = TRY(FFmpeg::FFmpegVideoDecoder::try_create(codec_id));
|
||||
auto codec_initialization_data = TRY(demuxer->get_codec_initialization_data_for_track(track));
|
||||
NonnullOwnPtr<VideoDecoder> decoder = TRY(FFmpeg::FFmpegVideoDecoder::try_create(codec_id, codec_initialization_data));
|
||||
auto frame_queue = DECODER_TRY_ALLOC(VideoFrameQueue::create());
|
||||
auto playback_manager = DECODER_TRY_ALLOC(try_make<PlaybackManager>(demuxer, track, move(decoder), move(frame_queue)));
|
||||
|
||||
|
|
Loading…
Reference in a new issue