basic working flac and mp3 seeking

flac impl is fairly slow as it doesn't use the seek tables; for some
reason miniflac seems to get *really* upset if you seek the stream.
custom
jacqueline 1 year ago
parent f54347794f
commit 77145e56f4
  1. 1
      src/audio/audio_decoder.cpp
  2. 46
      src/codecs/mad.cpp
  3. 140
      src/codecs/miniflac.cpp

@ -167,7 +167,6 @@ auto Decoder::BeginDecoding(std::shared_ptr<TaggedStream> stream) -> bool {
auto Decoder::ContinueDecoding() -> bool { auto Decoder::ContinueDecoding() -> bool {
auto res = codec_->DecodeTo(codec_buffer_); auto res = codec_->DecodeTo(codec_buffer_);
if (res.has_error()) { if (res.has_error()) {
ESP_LOGI(kTag, "RAN INTO DECODING ERROR");
return true; return true;
} }

@ -44,6 +44,7 @@ MadMp3Decoder::MadMp3Decoder()
mad_frame_init(frame_.get()); mad_frame_init(frame_.get());
mad_synth_init(synth_.get()); mad_synth_init(synth_.get());
} }
MadMp3Decoder::~MadMp3Decoder() { MadMp3Decoder::~MadMp3Decoder() {
mad_stream_finish(stream_.get()); mad_stream_finish(stream_.get());
mad_frame_finish(frame_.get()); mad_frame_finish(frame_.get());
@ -58,7 +59,7 @@ auto MadMp3Decoder::GetBytesUsed() -> std::size_t {
} }
} }
auto MadMp3Decoder::OpenStream(std::shared_ptr<IStream> input,uint32_t offset) 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;
@ -113,6 +114,45 @@ auto MadMp3Decoder::OpenStream(std::shared_ptr<IStream> input,uint32_t offset)
auto cbr_length = input->Size().value() / (header.bitrate / 8); auto cbr_length = input->Size().value() / (header.bitrate / 8);
output.total_samples = cbr_length * output.sample_rate_hz * channels; output.total_samples = cbr_length * output.sample_rate_hz * channels;
} }
mad_timer_t timer;
mad_timer_reset(&timer);
bool need_refill = false;
bool seek_err = false;
while (mad_timer_count(timer, MAD_UNITS_SECONDS) < offset) {
if (seek_err) {
return cpp::fail(ICodec::Error::kMalformedData);
}
if (need_refill && buffer_.Refill(input_.get())) {
return cpp::fail(ICodec::Error::kMalformedData);
}
need_refill = false;
buffer_.ConsumeBytes([&](cpp::span<std::byte> buf) -> size_t {
mad_stream_buffer(stream_.get(),
reinterpret_cast<const unsigned char*>(buf.data()),
buf.size());
while (mad_header_decode(&header, stream_.get()) < 0) {
if (MAD_RECOVERABLE(stream_->error)) {
continue;
}
if (stream_->error == MAD_ERROR_BUFLEN) {
need_refill = true;
return GetBytesUsed();
}
// The error is unrecoverable. Give up.
seek_err = true;
return 0;
}
mad_timer_add(&timer, header.duration);
return GetBytesUsed();
});
}
return output; return output;
} }
@ -222,8 +262,8 @@ auto MadMp3Decoder::SkipID3Tags(IStream& stream) -> void {
} }
/* /*
* Implementation taken from SDL_mixer and modified. Original is zlib-licensed, * Implementation taken from SDL_mixer and modified. Original is
* copyright (C) 1997-2022 Sam Lantinga <slouken@libsdl.org> * zlib-licensed, copyright (C) 1997-2022 Sam Lantinga <slouken@libsdl.org>
*/ */
auto MadMp3Decoder::GetVbrLength(const mad_header& header) auto MadMp3Decoder::GetVbrLength(const mad_header& header)
-> std::optional<uint32_t> { -> std::optional<uint32_t> {

@ -5,6 +5,7 @@
*/ */
#include "miniflac.hpp" #include "miniflac.hpp"
#include <stdint.h>
#include <cstdint> #include <cstdint>
#include <cstdlib> #include <cstdlib>
@ -49,16 +50,16 @@ MiniFlacDecoder::~MiniFlacDecoder() {
} }
} }
auto MiniFlacDecoder::OpenStream(std::shared_ptr<IStream> input,uint32_t offset) auto MiniFlacDecoder::OpenStream(std::shared_ptr<IStream> input,
uint32_t offset)
-> cpp::result<OutputFormat, Error> { -> cpp::result<OutputFormat, Error> {
input_ = input; input_ = input;
MINIFLAC_RESULT res; MINIFLAC_RESULT res;
auto read_until_result = [&](auto fn) { auto read_until_result = [&](auto fn) {
while (true) { while (true) {
bool eof = buffer_.Refill(input_.get());
buffer_.ConsumeBytes(fn); buffer_.ConsumeBytes(fn);
if (res == MINIFLAC_CONTINUE && !eof) { if (res == MINIFLAC_CONTINUE && !buffer_.Refill(input_.get())) {
continue; continue;
} }
break; break;
@ -111,137 +112,43 @@ auto MiniFlacDecoder::OpenStream(std::shared_ptr<IStream> input,uint32_t offset)
return cpp::fail(Error::kMalformedData); return cpp::fail(Error::kMalformedData);
} }
// Seeking
offset = 50;
if (offset) { if (offset) {
// TODO: This assumes a constant sample_rate // TODO: This assumes a constant sample_rate
uint64_t target_sample = sample_rate * offset; uint64_t target_sample = sample_rate * offset;
uint32_t num_seekpoints; ESP_LOGI(kTag, "seeking to %lu seconds (sample=%llu)", offset,
read_until_result([&](cpp::span<std::byte> buf) -> size_t { target_sample);
uint32_t bytes_used = 0;
res = miniflac_seektable_seekpoints( while (true) {
flac_.get(), reinterpret_cast<const uint8_t*>(buf.data()),
buf.size_bytes(), &bytes_used, &num_seekpoints);
return bytes_used;
});
if (res != MINIFLAC_OK) {
// TODO: Not having a seektable is not malformed
// but currently seeking will not work without it.
return cpp::fail(Error::kMalformedData);
}
// Loop over the seek table
ESP_LOGI(kTag, "Found seektable with %lu points", num_seekpoints);
uint64_t sample_number;
uint64_t sample_offset_bytes;
uint16_t num_samples_in_target;
for (uint32_t i = 0; i < num_seekpoints; i++) {
// Get sample number
read_until_result([&](cpp::span<std::byte> buf) -> size_t {
uint32_t bytes_used = 0;
res = miniflac_seektable_sample_number(
flac_.get(), reinterpret_cast<const uint8_t*>(buf.data()),
buf.size_bytes(), &bytes_used, &sample_number);
return bytes_used;
});
if (res != MINIFLAC_OK) {
return cpp::fail(Error::kMalformedData);
}
read_until_result([&](cpp::span<std::byte> buf) -> size_t {
uint32_t bytes_used = 0;
res = miniflac_seektable_sample_offset(
flac_.get(), reinterpret_cast<const uint8_t*>(buf.data()),
buf.size_bytes(), &bytes_used, &sample_offset_bytes);
return bytes_used;
});
if (res != MINIFLAC_OK) {
return cpp::fail(Error::kMalformedData);
}
read_until_result([&](cpp::span<std::byte> buf) -> size_t { read_until_result([&](cpp::span<std::byte> buf) -> size_t {
uint32_t bytes_used = 0; uint32_t bytes_used = 0;
res = miniflac_seektable_samples( res = miniflac_sync(flac_.get(),
flac_.get(), reinterpret_cast<const uint8_t*>(buf.data()), reinterpret_cast<const uint8_t*>(buf.data()),
buf.size_bytes(), &bytes_used, &num_samples_in_target); buf.size_bytes(), &bytes_used);
return bytes_used; return bytes_used;
}); });
if (res != MINIFLAC_OK) { if (res != MINIFLAC_OK) {
return cpp::fail(Error::kMalformedData); return cpp::fail(Error::kMalformedData);
} }
ESP_LOGI(kTag, "Seektable entry %lu", i); if (!miniflac_is_frame(flac_.get())) {
continue;
}
// Check if we want to seek to this seektable position? uint64_t samples_in_frame = miniflac_frame_block_size(flac_.get());
if (sample_number + num_samples_in_target >= target_sample) { if (samples_in_frame <= target_sample) {
ESP_LOGI(kTag, "Break on Seektable entry %lu", i); target_sample -= samples_in_frame;
} else {
break; break;
} }
} }
// Seek forward to target_sample
if (sample_number > 0) {
ESP_LOGI(kTag, "total samples: %llu", total_samples);
ESP_LOGI(kTag, "TARGET SAMPLE: %llu", target_sample);
ESP_LOGI(kTag, "SAMPLE NUMBER: %llu", sample_number);
}
uint64_t byte_offset = sample_offset_bytes;
ESP_LOGI(kTag, "Byte offset is forward %llu bytes", byte_offset);
ESP_LOGI(kTag, "Decoder state pre: %d", flac_->state);
while(flac_.get()->state == MINIFLAC_METADATA) {
read_until_result([&](cpp::span<std::byte> buf) -> size_t {
uint32_t bytes_used = 0;
res = miniflac_sync(
flac_.get(), reinterpret_cast<const uint8_t*>(buf.data()),
buf.size_bytes(), &bytes_used);
return bytes_used;
});
if (res != MINIFLAC_OK) {
ESP_LOGI(kTag, "Decoder error 1 %d", res);
}
}
ESP_LOGI(kTag, "Decoder state post: %d", flac_->state);
ESP_LOGI(kTag, "Going to skip forward %llu bytes", byte_offset);
if (input_.get()->CanSeek()) {
ESP_LOGI(kTag, "Skipping forward %llu bytes", byte_offset);
buffer_.Empty();
input_.get()->SeekTo(byte_offset, IStream::SeekFrom::kCurrentPosition);
ESP_LOGI(kTag, "Skipped %llu bytes", byte_offset);
}
ESP_LOGI(kTag, "Pre-refill");
buffer_.Refill(input_.get());
ESP_LOGI(kTag, "Post-refill");
read_until_result([&](cpp::span<std::byte> buf) -> size_t {
uint32_t bytes_used = 0;
res = miniflac_sync(
flac_.get(), reinterpret_cast<const uint8_t*>(buf.data()),
buf.size_bytes(), &bytes_used);
return bytes_used;
});
if (res != MINIFLAC_OK) {
ESP_LOGI(kTag, "Decoder error 1 %d", res);
}
ESP_LOGI(kTag, "JOB'S DONE");
} }
ESP_LOGI(kTag, "Decoder state: %d", flac_->state);
ESP_LOGI(kTag, "Frame header state: %d", flac_->frame.header.state);
// TODO: Sample number is not guaranteed, could be block index.
ESP_LOGI(kTag, "Ended up... at sample %llu", flac_->frame.header.sample_number);
ESP_LOGI(kTag, "and block index: %lu", flac_->frame.header.frame_number);
ESP_LOGI(kTag, "total samples: %llu", total_samples);
OutputFormat format{ OutputFormat format{
.num_channels = static_cast<uint8_t>(channels), .num_channels = static_cast<uint8_t>(channels),
.sample_rate_hz = static_cast<uint32_t>(sample_rate), .sample_rate_hz = static_cast<uint32_t>(sample_rate),
.total_samples = total_samples * channels, .total_samples = total_samples * channels,
}; };
return format; return format;
} }
@ -253,7 +160,6 @@ auto MiniFlacDecoder::DecodeTo(cpp::span<sample::Sample> output)
MINIFLAC_RESULT res = MINIFLAC_CONTINUE; MINIFLAC_RESULT res = MINIFLAC_CONTINUE;
while (res == MINIFLAC_CONTINUE && !is_eof) { while (res == MINIFLAC_CONTINUE && !is_eof) {
is_eof = buffer_.Refill(input_.get()); is_eof = buffer_.Refill(input_.get());
ESP_LOGI(kTag, "EOF? %s", is_eof ? "true" : "false");
buffer_.ConsumeBytes([&](cpp::span<std::byte> buf) -> size_t { buffer_.ConsumeBytes([&](cpp::span<std::byte> buf) -> size_t {
// FIXME: We should do a miniflac_sync first, in order to check that // FIXME: We should do a miniflac_sync first, in order to check that
// our sample buffers have enough space for the next frame. // our sample buffers have enough space for the next frame.
@ -273,8 +179,7 @@ auto MiniFlacDecoder::DecodeTo(cpp::span<sample::Sample> output)
.is_stream_finished = true, .is_stream_finished = true,
}; };
} else { } else {
ESP_LOGI(kTag, "Failed: decoder result: %d", res); return cpp::fail(Error::kMalformedData);
// return cpp::fail(Error::kMalformedData);
} }
} }
@ -297,7 +202,6 @@ auto MiniFlacDecoder::DecodeTo(cpp::span<sample::Sample> output)
} }
current_sample_.reset(); current_sample_.reset();
ESP_LOGI(kTag, "Samples written %lu", (uint32_t)samples_written);
return OutputInfo{.samples_written = samples_written, return OutputInfo{.samples_written = samples_written,
.is_stream_finished = samples_written == 0 && is_eof}; .is_stream_finished = samples_written == 0 && is_eof};
} }

Loading…
Cancel
Save