|
|
@ -63,7 +63,7 @@ auto MadMp3Decoder::OpenStream(std::shared_ptr<IStream> input, uint32_t offset) |
|
|
|
-> cpp::result<OutputFormat, ICodec::Error> { |
|
|
|
-> cpp::result<OutputFormat, ICodec::Error> { |
|
|
|
input_ = input; |
|
|
|
input_ = input; |
|
|
|
|
|
|
|
|
|
|
|
SkipID3Tags(*input); |
|
|
|
auto id3size = SkipID3Tags(*input); |
|
|
|
|
|
|
|
|
|
|
|
// To get the output format for MP3 streams, we simply need to decode the
|
|
|
|
// To get the output format for MP3 streams, we simply need to decode the
|
|
|
|
// first frame header.
|
|
|
|
// first frame header.
|
|
|
@ -116,6 +116,14 @@ auto MadMp3Decoder::OpenStream(std::shared_ptr<IStream> input, uint32_t offset) |
|
|
|
output.total_samples = cbr_length * output.sample_rate_hz * channels; |
|
|
|
output.total_samples = cbr_length * output.sample_rate_hz * channels; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// header.bitrate is only for CBR, but we've calculated total samples for VBR
|
|
|
|
|
|
|
|
// and CBR, so we can use that to calculate sample size and therefore bitrate.
|
|
|
|
|
|
|
|
if (id3size && input->Size()) { |
|
|
|
|
|
|
|
auto data_size = input->Size().value() - id3size.value(); |
|
|
|
|
|
|
|
double sample_size = data_size * 8.0 / output.total_samples.value(); |
|
|
|
|
|
|
|
output.bitrate_kbps = static_cast<uint32_t>(output.sample_rate_hz * channels * sample_size / 1024); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if (offset > 1 && cbr_length > 0) { |
|
|
|
if (offset > 1 && cbr_length > 0) { |
|
|
|
// Constant bitrate seeking
|
|
|
|
// Constant bitrate seeking
|
|
|
|
uint64_t skip_bytes = header.bitrate * (offset - 1) / 8; |
|
|
|
uint64_t skip_bytes = header.bitrate * (offset - 1) / 8; |
|
|
@ -263,15 +271,15 @@ auto MadMp3Decoder::DecodeTo(std::span<sample::Sample> output) |
|
|
|
.is_stream_finished = is_eos_}; |
|
|
|
.is_stream_finished = is_eos_}; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
auto MadMp3Decoder::SkipID3Tags(IStream& stream) -> void { |
|
|
|
auto MadMp3Decoder::SkipID3Tags(IStream& stream) -> std::optional<uint32_t> { |
|
|
|
// First check that the file actually does start with ID3 tags.
|
|
|
|
// First check that the file actually does start with ID3 tags.
|
|
|
|
std::array<std::byte, 3> magic_buf{}; |
|
|
|
std::array<std::byte, 3> magic_buf{}; |
|
|
|
if (stream.Read(magic_buf) != 3) { |
|
|
|
if (stream.Read(magic_buf) != 3) { |
|
|
|
return; |
|
|
|
return {}; |
|
|
|
} |
|
|
|
} |
|
|
|
if (std::memcmp(magic_buf.data(), "ID3", 3) != 0) { |
|
|
|
if (std::memcmp(magic_buf.data(), "ID3", 3) != 0) { |
|
|
|
stream.SeekTo(0, IStream::SeekFrom::kStartOfStream); |
|
|
|
stream.SeekTo(0, IStream::SeekFrom::kStartOfStream); |
|
|
|
return; |
|
|
|
return {}; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// The size of the tags (*not* including the 10-byte header) is located 6
|
|
|
|
// The size of the tags (*not* including the 10-byte header) is located 6
|
|
|
@ -279,14 +287,17 @@ auto MadMp3Decoder::SkipID3Tags(IStream& stream) -> void { |
|
|
|
std::array<std::byte, 4> size_buf{}; |
|
|
|
std::array<std::byte, 4> size_buf{}; |
|
|
|
stream.SeekTo(6, IStream::SeekFrom::kStartOfStream); |
|
|
|
stream.SeekTo(6, IStream::SeekFrom::kStartOfStream); |
|
|
|
if (stream.Read(size_buf) != 4) { |
|
|
|
if (stream.Read(size_buf) != 4) { |
|
|
|
return; |
|
|
|
return {}; |
|
|
|
} |
|
|
|
} |
|
|
|
// Size is encoded with 7-bit ints for some reason.
|
|
|
|
// Size is encoded with 7-bit ints for some reason.
|
|
|
|
uint32_t tags_size = (static_cast<uint32_t>(size_buf[0]) << (7 * 3)) | |
|
|
|
uint32_t tags_size = (static_cast<uint32_t>(size_buf[0]) << (7 * 3)) | |
|
|
|
(static_cast<uint32_t>(size_buf[1]) << (7 * 2)) | |
|
|
|
(static_cast<uint32_t>(size_buf[1]) << (7 * 2)) | |
|
|
|
(static_cast<uint32_t>(size_buf[2]) << 7) | |
|
|
|
(static_cast<uint32_t>(size_buf[2]) << 7) | |
|
|
|
static_cast<uint32_t>(size_buf[3]); |
|
|
|
static_cast<uint32_t>(size_buf[3]); |
|
|
|
stream.SeekTo(10 + tags_size, IStream::SeekFrom::kStartOfStream); |
|
|
|
|
|
|
|
|
|
|
|
auto skip_bytes = 10 + tags_size; |
|
|
|
|
|
|
|
stream.SeekTo(skip_bytes, IStream::SeekFrom::kStartOfStream); |
|
|
|
|
|
|
|
return skip_bytes; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
/*
|
|
|
|