diff options
Diffstat (limited to 'src/codecs/mad.cpp')
| -rw-r--r-- | src/codecs/mad.cpp | 82 |
1 files changed, 76 insertions, 6 deletions
diff --git a/src/codecs/mad.cpp b/src/codecs/mad.cpp index 23b4ccf6..ca9a0f6e 100644 --- a/src/codecs/mad.cpp +++ b/src/codecs/mad.cpp @@ -6,8 +6,10 @@ #include "mad.hpp" #include <stdint.h> +#include <sys/_stdint.h> #include <cstdint> +#include <cstring> #include <optional> #include "mad.h" @@ -79,12 +81,22 @@ auto MadMp3Decoder::BeginStream(const cpp::span<const std::byte> input) } uint8_t channels = MAD_NCHANNELS(&header); - return {GetBytesUsed(input.size_bytes()), - OutputFormat{ - .num_channels = channels, - .bits_per_sample = 24, // We always scale to 24 bits - .sample_rate_hz = header.samplerate, - }}; + OutputFormat output{ + .num_channels = channels, + .bits_per_sample = 24, // We always scale to 24 bits + .sample_rate_hz = header.samplerate, + .duration_seconds = {}, + .bits_per_second = {}, + }; + + auto vbr_length = GetVbrLength(header); + if (vbr_length) { + output.duration_seconds = vbr_length; + } else { + output.bits_per_second = header.bitrate; + } + + return {GetBytesUsed(input.size_bytes()), output}; } auto MadMp3Decoder::ContinueStream(cpp::span<const std::byte> input, @@ -219,4 +231,62 @@ auto MadMp3Decoder::SeekStream(cpp::span<const std::byte> input, } } +/* + * Implementation taken from SDL_mixer and modified. Original is zlib-licensed, + * copyright (C) 1997-2022 Sam Lantinga <slouken@libsdl.org> + */ +auto MadMp3Decoder::GetVbrLength(const mad_header& header) + -> std::optional<uint32_t> { + if (!stream_.this_frame || !stream_.next_frame || + stream_.next_frame <= stream_.this_frame || + (stream_.next_frame - stream_.this_frame) < 48) { + return {}; + } + + int mpeg_version = (stream_.this_frame[1] >> 3) & 0x03; + + int xing_offset = 0; + switch (mpeg_version) { + case 0x03: /* MPEG1 */ + if (header.mode == MAD_MODE_SINGLE_CHANNEL) { + xing_offset = 4 + 17; + } else { + xing_offset = 4 + 32; + } + break; + default: /* MPEG2 and MPEG2.5 */ + if (header.mode == MAD_MODE_SINGLE_CHANNEL) { + xing_offset = 4 + 17; + } else { + xing_offset = 4 + 9; + } + break; + } + + uint32_t samples_per_frame = 32 * MAD_NSBSAMPLES(&header); + + unsigned char const* frames_count_raw; + uint32_t frames_count = 0; + if (std::memcmp(stream_.this_frame + xing_offset, "Xing", 4) == 0 || + std::memcmp(stream_.this_frame + xing_offset, "Info", 4) == 0) { + /* Xing header to get the count of frames for VBR */ + frames_count_raw = stream_.this_frame + xing_offset + 8; + frames_count = ((uint32_t)frames_count_raw[0] << 24) + + ((uint32_t)frames_count_raw[1] << 16) + + ((uint32_t)frames_count_raw[2] << 8) + + ((uint32_t)frames_count_raw[3]); + } else if (std::memcmp(stream_.this_frame + xing_offset, "VBRI", 4) == 0) { + /* VBRI header to get the count of frames for VBR */ + frames_count_raw = stream_.this_frame + xing_offset + 14; + frames_count = ((uint32_t)frames_count_raw[0] << 24) + + ((uint32_t)frames_count_raw[1] << 16) + + ((uint32_t)frames_count_raw[2] << 8) + + ((uint32_t)frames_count_raw[3]); + } else { + return {}; + } + + return (double)(frames_count * samples_per_frame) / header.samplerate; +} + } // namespace codecs |
