vbr-compatible mp3 duration :)
This commit is contained in:
@@ -144,8 +144,8 @@ void Playback::react(const QueueUpdate& ev) {
|
||||
}
|
||||
|
||||
void Playback::react(const PlaybackUpdate& ev) {
|
||||
// ESP_LOGI(kTag, "elapsed: %lu, total: %lu", ev.seconds_elapsed,
|
||||
// ev.seconds_total);
|
||||
ESP_LOGI(kTag, "elapsed: %lu, total: %lu", ev.seconds_elapsed,
|
||||
ev.seconds_total);
|
||||
}
|
||||
|
||||
void Playback::react(const internal::InputFileOpened& ev) {}
|
||||
|
||||
@@ -42,6 +42,8 @@ class MadMp3Decoder : public ICodec {
|
||||
-> Result<void> override;
|
||||
|
||||
private:
|
||||
auto GetVbrLength(const mad_header& header) -> std::optional<uint32_t>;
|
||||
|
||||
mad_stream stream_;
|
||||
mad_frame frame_;
|
||||
mad_synth synth_;
|
||||
|
||||
+65
-3
@@ -9,6 +9,7 @@
|
||||
#include <sys/_stdint.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <optional>
|
||||
|
||||
#include "mad.h"
|
||||
@@ -88,9 +89,12 @@ auto MadMp3Decoder::BeginStream(const cpp::span<const std::byte> input)
|
||||
.bits_per_second = {},
|
||||
};
|
||||
|
||||
// TODO(jacqueline): Support VBR. Although maybe libtags is the better place
|
||||
// to handle this?
|
||||
output.bits_per_second = header.bitrate;
|
||||
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};
|
||||
}
|
||||
@@ -227,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
|
||||
|
||||
Reference in New Issue
Block a user