mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-12-07 21:59:54 +00:00
LibMedia: Allow demuxers to specify a preferred track
...and return all tracks of a matching type from FFmpegDemuxer, rather than only the single best one.
This commit is contained in:
parent
8d64e72655
commit
6653b747ff
Notes:
github-actions[bot]
2025-09-12 09:25:12 +00:00
Author: https://github.com/Zaggy1024
Commit: 6653b747ff
Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/6165
Reviewed-by: https://github.com/gmta ✅
8 changed files with 138 additions and 58 deletions
|
|
@ -25,47 +25,83 @@ DecoderErrorOr<NonnullOwnPtr<MatroskaDemuxer>> MatroskaDemuxer::from_data(Readon
|
|||
return make<MatroskaDemuxer>(TRY(Reader::from_data(data)));
|
||||
}
|
||||
|
||||
DecoderErrorOr<Vector<Track>> MatroskaDemuxer::get_tracks_for_type(TrackType type)
|
||||
static TrackEntry::TrackType matroska_track_type_from_track_type(TrackType type)
|
||||
{
|
||||
TrackEntry::TrackType matroska_track_type;
|
||||
|
||||
switch (type) {
|
||||
case TrackType::Video:
|
||||
matroska_track_type = TrackEntry::TrackType::Video;
|
||||
break;
|
||||
return TrackEntry::TrackType::Video;
|
||||
case TrackType::Audio:
|
||||
matroska_track_type = TrackEntry::TrackType::Audio;
|
||||
break;
|
||||
return TrackEntry::TrackType::Audio;
|
||||
case TrackType::Subtitles:
|
||||
matroska_track_type = TrackEntry::TrackType::Subtitle;
|
||||
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:
|
||||
break;
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
DecoderErrorOr<Vector<Track>> MatroskaDemuxer::get_tracks_for_type(TrackType type)
|
||||
{
|
||||
auto matroska_track_type = matroska_track_type_from_track_type(type);
|
||||
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({
|
||||
.pixel_width = video_track->pixel_width,
|
||||
.pixel_height = video_track->pixel_height
|
||||
});
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
DECODER_TRY_ALLOC(tracks.try_append(track));
|
||||
DECODER_TRY_ALLOC(tracks.try_append(track_from_track_entry(track_entry)));
|
||||
return IterationDecision::Continue;
|
||||
}));
|
||||
|
||||
return tracks;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
DecoderErrorOr<MatroskaDemuxer::TrackStatus*> MatroskaDemuxer::get_track_status(Track track)
|
||||
{
|
||||
if (!m_track_statuses.contains(track)) {
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ public:
|
|||
}
|
||||
|
||||
DecoderErrorOr<Vector<Track>> get_tracks_for_type(TrackType type) override;
|
||||
DecoderErrorOr<Optional<Track>> get_preferred_track_for_type(TrackType type) override;
|
||||
|
||||
DecoderErrorOr<Optional<AK::Duration>> seek_to_most_recent_keyframe(Track track, AK::Duration timestamp, Optional<AK::Duration> earliest_available_sample = OptionalNone()) override;
|
||||
|
||||
|
|
|
|||
|
|
@ -21,6 +21,9 @@ public:
|
|||
virtual ~Demuxer() = default;
|
||||
|
||||
virtual DecoderErrorOr<Vector<Track>> get_tracks_for_type(TrackType type) = 0;
|
||||
// Returns the container's preferred track for a given track type. This must return a value if any track of the
|
||||
// given type is present.
|
||||
virtual DecoderErrorOr<Optional<Track>> get_preferred_track_for_type(TrackType type) = 0;
|
||||
|
||||
virtual DecoderErrorOr<Sample> get_next_sample_for_track(Track track) = 0;
|
||||
|
||||
|
|
|
|||
|
|
@ -85,47 +85,48 @@ DecoderErrorOr<AK::Duration> FFmpegDemuxer::duration_of_track(Track const& track
|
|||
return time_units_to_duration(m_format_context->duration, 1, AV_TIME_BASE);
|
||||
}
|
||||
|
||||
DecoderErrorOr<Vector<Track>> FFmpegDemuxer::get_tracks_for_type(TrackType type)
|
||||
DecoderErrorOr<Track> FFmpegDemuxer::get_track_for_stream_index(u32 stream_index)
|
||||
{
|
||||
AVMediaType media_type;
|
||||
VERIFY(stream_index < m_format_context->nb_streams);
|
||||
|
||||
switch (type) {
|
||||
case TrackType::Video:
|
||||
media_type = AVMediaType::AVMEDIA_TYPE_VIDEO;
|
||||
break;
|
||||
case TrackType::Audio:
|
||||
media_type = AVMediaType::AVMEDIA_TYPE_AUDIO;
|
||||
break;
|
||||
case TrackType::Subtitles:
|
||||
media_type = AVMediaType::AVMEDIA_TYPE_SUBTITLE;
|
||||
break;
|
||||
}
|
||||
|
||||
// Find the best stream to play within the container
|
||||
int best_stream_index = av_find_best_stream(m_format_context, media_type, -1, -1, nullptr, 0);
|
||||
if (best_stream_index == AVERROR_STREAM_NOT_FOUND)
|
||||
return DecoderError::format(DecoderErrorCategory::Unknown, "No stream for given type found in container");
|
||||
if (best_stream_index == AVERROR_DECODER_NOT_FOUND)
|
||||
return DecoderError::format(DecoderErrorCategory::Unknown, "No suitable decoder found for stream");
|
||||
if (best_stream_index < 0)
|
||||
return DecoderError::format(DecoderErrorCategory::Unknown, "Failed to find a stream for the given type");
|
||||
|
||||
auto* stream = m_format_context->streams[best_stream_index];
|
||||
|
||||
Track track(type, best_stream_index);
|
||||
auto& stream = *m_format_context->streams[stream_index];
|
||||
auto type = track_type_from_ffmpeg_media_type(stream.codecpar->codec_type);
|
||||
Track track(type, stream_index);
|
||||
|
||||
if (type == TrackType::Video) {
|
||||
track.set_video_data({
|
||||
.pixel_width = static_cast<u64>(stream->codecpar->width),
|
||||
.pixel_height = static_cast<u64>(stream->codecpar->height),
|
||||
.pixel_width = static_cast<u64>(stream.codecpar->width),
|
||||
.pixel_height = static_cast<u64>(stream.codecpar->height),
|
||||
});
|
||||
}
|
||||
|
||||
Vector<Track> tracks;
|
||||
tracks.append(move(track));
|
||||
return track;
|
||||
}
|
||||
|
||||
DecoderErrorOr<Vector<Track>> FFmpegDemuxer::get_tracks_for_type(TrackType type)
|
||||
{
|
||||
auto media_type = ffmpeg_media_type_from_track_type(type);
|
||||
Vector<Track> tracks = {};
|
||||
for (u32 i = 0; i < m_format_context->nb_streams; i++) {
|
||||
auto& stream = *m_format_context->streams[i];
|
||||
if (stream.codecpar->codec_type != media_type)
|
||||
continue;
|
||||
|
||||
tracks.append(TRY(get_track_for_stream_index(i)));
|
||||
}
|
||||
return tracks;
|
||||
}
|
||||
|
||||
DecoderErrorOr<Optional<Track>> FFmpegDemuxer::get_preferred_track_for_type(TrackType type)
|
||||
{
|
||||
auto media_type = ffmpeg_media_type_from_track_type(type);
|
||||
auto best_stream_index = av_find_best_stream(m_format_context, media_type, -1, -1, nullptr, 0);
|
||||
if (best_stream_index < 0)
|
||||
return OptionalNone();
|
||||
|
||||
return get_track_for_stream_index(best_stream_index);
|
||||
}
|
||||
|
||||
DecoderErrorOr<Optional<AK::Duration>> FFmpegDemuxer::seek_to_most_recent_keyframe(Track track, AK::Duration timestamp, Optional<AK::Duration> earliest_available_sample)
|
||||
{
|
||||
// FIXME: What do we do with this here?
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ public:
|
|||
virtual ~FFmpegDemuxer() override;
|
||||
|
||||
virtual DecoderErrorOr<Vector<Track>> get_tracks_for_type(TrackType type) override;
|
||||
virtual DecoderErrorOr<Optional<Track>> get_preferred_track_for_type(TrackType type) override;
|
||||
|
||||
virtual DecoderErrorOr<Optional<AK::Duration>> seek_to_most_recent_keyframe(Track track, AK::Duration timestamp, Optional<AK::Duration> earliest_available_sample = OptionalNone()) override;
|
||||
|
||||
|
|
@ -39,6 +40,7 @@ public:
|
|||
|
||||
private:
|
||||
DecoderErrorOr<AK::Duration> duration_of_track(Track const& track);
|
||||
DecoderErrorOr<Track> get_track_for_stream_index(u32 stream_index);
|
||||
|
||||
NonnullOwnPtr<SeekableStream> m_stream;
|
||||
AVCodecContext* m_codec_context { nullptr };
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
#pragma once
|
||||
|
||||
#include <LibMedia/CodecID.h>
|
||||
#include <LibMedia/Track.h>
|
||||
|
||||
extern "C" {
|
||||
#include <libavcodec/avcodec.h>
|
||||
|
|
@ -76,4 +77,38 @@ static inline CodecID media_codec_id_from_ffmpeg_codec_id(AVCodecID codec)
|
|||
}
|
||||
}
|
||||
|
||||
static inline AVMediaType ffmpeg_media_type_from_track_type(TrackType track_type)
|
||||
{
|
||||
switch (track_type) {
|
||||
case TrackType::Video:
|
||||
return AVMediaType::AVMEDIA_TYPE_VIDEO;
|
||||
case TrackType::Audio:
|
||||
return AVMediaType::AVMEDIA_TYPE_AUDIO;
|
||||
case TrackType::Subtitles:
|
||||
return AVMediaType::AVMEDIA_TYPE_SUBTITLE;
|
||||
case TrackType::Unknown:
|
||||
return AVMediaType::AVMEDIA_TYPE_UNKNOWN;
|
||||
}
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
|
||||
static inline TrackType track_type_from_ffmpeg_media_type(AVMediaType media_type)
|
||||
{
|
||||
switch (media_type) {
|
||||
case AVMediaType::AVMEDIA_TYPE_VIDEO:
|
||||
return TrackType::Video;
|
||||
case AVMediaType::AVMEDIA_TYPE_AUDIO:
|
||||
return TrackType::Audio;
|
||||
case AVMediaType::AVMEDIA_TYPE_SUBTITLE:
|
||||
return TrackType::Subtitles;
|
||||
case AVMediaType::AVMEDIA_TYPE_DATA:
|
||||
case AVMediaType::AVMEDIA_TYPE_ATTACHMENT:
|
||||
case AVMediaType::AVMEDIA_TYPE_UNKNOWN:
|
||||
return TrackType::Unknown;
|
||||
case AVMediaType::AVMEDIA_TYPE_NB:
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -698,11 +698,12 @@ private:
|
|||
|
||||
DecoderErrorOr<NonnullOwnPtr<PlaybackManager>> PlaybackManager::create(NonnullOwnPtr<Demuxer> demuxer)
|
||||
{
|
||||
auto video_tracks = TRY(demuxer->get_tracks_for_type(TrackType::Video));
|
||||
if (video_tracks.is_empty())
|
||||
auto optional_track = TRY(demuxer->get_preferred_track_for_type(TrackType::Video));
|
||||
if (!optional_track.has_value()) {
|
||||
return DecoderError::with_description(DecoderErrorCategory::Invalid, "No video track is present"sv);
|
||||
auto track = video_tracks[0];
|
||||
}
|
||||
|
||||
auto track = optional_track.release_value();
|
||||
dbgln_if(PLAYBACK_MANAGER_DEBUG, "Selecting video track number {}", track.identifier());
|
||||
|
||||
auto codec_id = TRY(demuxer->get_codec_id_for_track(track));
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ enum class TrackType : u32 {
|
|||
Video,
|
||||
Audio,
|
||||
Subtitles,
|
||||
Unknown,
|
||||
};
|
||||
|
||||
class Track {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue