ladybird/Libraries/LibMedia/Codecs/Opus.cpp
Zaggy1024 c901896972 LibMedia: Implement buffered range scanning for Ogg
Unfortunately, this necessarily involves parsing codec frames to get
their durations. Ogg's granule positions indicate the last sample of
the last complete frame of the page, so the navigator has to determine
the durations of every packet in the page to offset it back to its
start.

Vorbis is an especially involved codec to determine that for, since it
has initial data defining a mapping of indices to big or small block
sizes that has to be held onto, and that mapping is preceded by a bunch
of conditionally parsed bits. Also, a frame's block size calculation
involves the block size of the previous frame for an overlap add. To
avoid bringing that complexity directly into LibMedia, we delegate the
parsing/calculation to FFmpeg's av_vorbis_parse functions.
2026-05-28 10:30:20 -05:00

64 lines
2.1 KiB
C++

/*
* Copyright (c) 2026-present, the Ladybird developers.
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <LibMedia/Codecs/Opus.h>
#include <LibMedia/MediaStream.h>
namespace Media::Codecs {
DecoderErrorOr<u32> Opus::parse_frame_duration_in_samples(MediaStreamCursor& cursor, size_t frame_size)
{
if (frame_size == 0)
return DecoderError::corrupted("Opus frame is too small"sv);
// https://datatracker.ietf.org/doc/html/rfc6716#section-3.1
auto toc_byte = TRY(cursor.read_value<u8>());
auto configuration_number = (toc_byte >> 3) & 0b1'1111;
auto packet_code = toc_byte & 0b11;
// clang-format off
constexpr Array frame_durations = {
10000, 20000, 40000, 60000, // SILK-only NB
10000, 20000, 40000, 60000, // SILK-only MB
10000, 20000, 40000, 60000, // SILK-only WB
10000, 20000, // Hybrid SWB
10000, 20000, // Hybrid FB
2500, 5000, 10000, 20000, // CELT-only NB
2500, 5000, 10000, 20000, // CELT-only WB
2500, 5000, 10000, 20000, // CELT-only SWB
2500, 5000, 10000, 20000, // CELT-only FB
};
// clang-format on
auto frame_duration = frame_durations[configuration_number];
auto packet_duration = TRY([&] -> DecoderErrorOr<int> {
switch (packet_code) {
case 0:
return frame_duration;
case 1:
case 2:
return frame_duration * 2;
case 3: {
if (frame_size == 1)
return DecoderError::corrupted("Opus frame is too small"sv);
auto frame_count_byte = TRY(cursor.read_value<u8>());
auto frame_count = frame_count_byte & 0b11'1111;
return frame_duration * frame_count;
}
default:
VERIFY_NOT_REACHED();
}
}());
return packet_duration * 48 / 1000;
}
DecoderErrorOr<AK::Duration> Opus::parse_frame_duration(MediaStreamCursor& cursor, size_t frame_size)
{
return AK::Duration::from_time_units(TRY(parse_frame_duration_in_samples(cursor, frame_size)), 1, 48000);
}
}