#include "decoder.h" #include #include #include #include #include #include #include extern "C" { #include } Decoder::Decoder(int inputFd) { fd = dup(inputFd); if (fd < 0) throw std::runtime_error("Cannot duplicate file descriptor"); } Decoder::~Decoder() { av_packet_free(&packet); av_frame_free(&frame); swr_free(&swr); avcodec_free_context(&codec); avformat_close_input(&format); if (io) { av_freep(&io->buffer); avio_context_free(&io); } if (fd >= 0) close(fd); } void Decoder::check(int result, const char* operation) { if (result >= 0) return; if (result == AVERROR_PATCHWELCOME && codec && codec->codec_id == AV_CODEC_ID_TAK) throw std::runtime_error("This TAK codec type is not supported by the available decoders."); char detail[AV_ERROR_MAX_STRING_SIZE]; av_strerror(result, detail, sizeof(detail)); throw std::runtime_error(std::string(operation) + ": " + detail); } int Decoder::readFd(void* opaque, uint8_t* data, int size) { auto* d = static_cast(opaque); ssize_t count; do { count = ::read(d->fd, data, size); } while (count < 0 && errno == EINTR); return count > 0 ? static_cast(count) : count == 0 ? AVERROR_EOF : AVERROR(errno); } int64_t Decoder::seekFd(void* opaque, int64_t offset, int whence) { auto* d = static_cast(opaque); if (whence & AVSEEK_SIZE) { struct stat info {}; return fstat(d->fd, &info) == 0 && S_ISREG(info.st_mode) ? info.st_size : AVERROR(ENOSYS); } const off_t result = lseek(d->fd, offset, whence & ~AVSEEK_FORCE); return result < 0 ? AVERROR(errno) : result; } void Decoder::open() { format = avformat_alloc_context(); if (!format) throw std::bad_alloc(); auto* buffer = static_cast(av_malloc(65536)); if (!buffer) throw std::bad_alloc(); io = avio_alloc_context(buffer, 65536, 0, this, readFd, nullptr, seekFd); if (!io) { av_free(buffer); throw std::bad_alloc(); } format->pb = io; format->flags |= AVFMT_FLAG_CUSTOM_IO; check(avformat_open_input(&format, nullptr, nullptr, nullptr), "Open file"); // TAK's demuxer exposes STREAMINFO directly. Avoid probing codec type 0 // through FFmpeg's unsupported decoder before selecting our native path. const auto* first = format->nb_streams == 1 ? format->streams[0]->codecpar : nullptr; const bool legacyTak = first && first->codec_id == AV_CODEC_ID_TAK && first->extradata_size >= 10 && (first->extradata[0] & 63) == 0; if (!legacyTak) check(avformat_find_stream_info(format, nullptr), "Read stream information"); const AVCodec* decoder = nullptr; streamIndex = av_find_best_stream(format, AVMEDIA_TYPE_AUDIO, -1, -1, &decoder, 0); check(streamIndex, "Find supported audio stream"); stream = format->streams[streamIndex]; codec = avcodec_alloc_context3(decoder); if (!codec) throw std::bad_alloc(); check(avcodec_parameters_to_context(codec, stream->codecpar), "Read codec parameters"); codec->pkt_timebase=stream->time_base; codec->thread_count = 2; if (legacyTak) { tak1 = std::make_unique(stream->codecpar->extradata, stream->codecpar->extradata_size); const auto& info = tak1->info(); codec->sample_rate = info.rate; codec->sample_fmt = AV_SAMPLE_FMT_S32P; codec->bits_per_raw_sample = info.bits; av_channel_layout_uninit(&codec->ch_layout); av_channel_layout_default(&codec->ch_layout, info.channels); } else check(avcodec_open2(codec, decoder, nullptr), "Open audio decoder"); if (codec->sample_rate <= 0) throw std::runtime_error("Invalid audio sample rate"); frame = av_frame_alloc(); packet = av_packet_alloc(); if (!frame || !packet) throw std::bad_alloc(); outputRate(codec->sample_rate); } void Decoder::outputRate(int value) { if (value <= 0) throw std::runtime_error("Invalid output rate"); rate = value; swr_free(&swr); AVChannelLayout stereo = AV_CHANNEL_LAYOUT_STEREO; check(swr_alloc_set_opts2(&swr, &stereo, AV_SAMPLE_FMT_FLT, rate, &codec->ch_layout, codec->sample_fmt, codec->sample_rate, 0, nullptr), "Configure PCM conversion"); check(swr_init(swr), "Initialize PCM conversion"); } int64_t Decoder::durationMs() const { if (stream->duration != AV_NOPTS_VALUE) return av_rescale_q(stream->duration, stream->time_base, AVRational{1,1000}); return format->duration == AV_NOPTS_VALUE ? 0 : format->duration / 1000; } std::string Decoder::description() const { const auto* desc = avcodec_descriptor_get(codec->codec_id); int bits = tak1 ? tak1->info().bits : stream->codecpar->bits_per_raw_sample; return std::string(tak1 ? "TAK 1.0 (native)" : desc ? desc->name : "audio") + " · " + std::to_string(codec->sample_rate) + " Hz · " + std::to_string(codec->ch_layout.nb_channels) + " ch" + (bits > 0 ? " · " + std::to_string(bits) + " bit" : ""); } int Decoder::receiveFrame() { if (!tak1) return avcodec_receive_frame(codec, frame); while (true) { int result = av_read_frame(format, packet); if (result < 0) return result; if (packet->stream_index != streamIndex) { av_packet_unref(packet); continue; } auto audio = tak1->decode(packet->data, packet->size); frame->format = AV_SAMPLE_FMT_S32P; frame->sample_rate = codec->sample_rate; frame->nb_samples = audio.samples; frame->pts = frame->best_effort_timestamp = packet->pts; check(av_channel_layout_copy(&frame->ch_layout, &codec->ch_layout), "Copy TAK channel layout"); check(av_frame_get_buffer(frame, 0), "Allocate TAK PCM frame"); const int shift = 32-tak1->info().bits; for (int c=0; cch_layout.nb_channels; ++c) { auto* destination = reinterpret_cast(frame->data[c]); for (int i=0; i(std::max(capacity, 1)) * 2); uint8_t* out[] = {reinterpret_cast(pending.data())}; int n = swr_convert(swr, out, capacity, nullptr, 0); check(n, "Drain audio converter"); pending.resize(n * 2); return n > 0; } if (result >= 0) { if (!resamplerStarted) { int64_t sourceStart = av_rescale_q(decodedNext, AVRational{1,rate}, AVRational{1,codec->sample_rate}); if (frame->best_effort_timestamp != AV_NOPTS_VALUE) { int64_t origin = stream->start_time == AV_NOPTS_VALUE ? 0 : stream->start_time; sourceStart = av_rescale_q(frame->best_effort_timestamp-origin,stream->time_base,AVRational{1,codec->sample_rate}); } if (timelineOrigin == AV_NOPTS_VALUE) timelineOrigin=sourceStart; sourceStart-=timelineOrigin; if ((codec->codec_id==AV_CODEC_ID_WMAV1 || codec->codec_id==AV_CODEC_ID_WMAV2) && frame->nb_samples>0) { // ASF timestamps are milliseconds, while WMA v1/v2 emits // fixed-sized sample frames. Recover the exact frame boundary. int64_t size=frame->nb_samples; sourceStart=((sourceStart+size/2)/size)*size; } // Restore the global rational resampling phase after a seek. // Without this, e.g. 44.1 -> 48 kHz seeks shift by a fractional sample. int period = codec->sample_rate / std::gcd(codec->sample_rate,rate); int prefix = static_cast((sourceStart % period + period) % period); if (prefix) check(swr_inject_silence(swr,prefix),"Align audio converter"); decodedNext = av_rescale_q(sourceStart-prefix,AVRational{1,codec->sample_rate},AVRational{1,rate}); resamplerStarted=true; } int capacity = swr_get_out_samples(swr, frame->nb_samples); pending.resize(static_cast(std::max(capacity, 1)) * 2); uint8_t* out[] = {reinterpret_cast(pending.data())}; int n = swr_convert(swr, out, capacity, const_cast(frame->extended_data), frame->nb_samples); check(n, "Convert decoded audio"); int64_t start = decodedNext; decodedNext = start + n; pending.resize(n * 2); pendingOffset = static_cast(std::clamp(skipUntil - start, 0, n)) * 2; av_frame_unref(frame); if (pendingOffset < pending.size()) return true; continue; } if (result != AVERROR(EAGAIN)) check(result, "Decode audio"); if (inputEnded) { if (drainSent) throw std::runtime_error("Decoder stalled while draining"); check(avcodec_send_packet(codec, nullptr), "Drain decoder"); drainSent = true; continue; } while (true) { result = av_read_frame(format, packet); if (result == AVERROR_EOF) { inputEnded = true; break; } check(result, "Read audio packet"); if (packet->stream_index == streamIndex) { result = avcodec_send_packet(codec, packet); av_packet_unref(packet); check(result, "Submit audio packet"); break; } av_packet_unref(packet); } } } int Decoder::read(float* output, int frames) { if (frames <= 0) return 0; int copied = 0; while (copied < frames) { if (pendingOffset == pending.size() && !fill()) break; size_t count = std::min(pending.size() - pendingOffset, static_cast((frames - copied) * 2)); std::memcpy(output + copied * 2, pending.data() + pendingOffset, count * sizeof(float)); pendingOffset += count; copied += static_cast(count / 2); } return copied; } void Decoder::seek(int64_t outputFrame) { // Establish the decoded timeline origin. Some containers (notably ASF/WMA) // start decoded audio later than their stream start timestamp. if (timelineOrigin == AV_NOPTS_VALUE && !fill()) throw std::runtime_error("No decoded audio"); int64_t origin = stream->start_time == AV_NOPTS_VALUE ? 0 : stream->start_time; // Give predictive codecs and resampling filters enough history before A. int64_t preroll=std::max(0,outputFrame-int64_t(rate)*2); int64_t target = preroll==0 ? origin : av_rescale_q(preroll, AVRational{1,rate}, stream->time_base) + av_rescale_q(timelineOrigin,AVRational{1,codec->sample_rate},stream->time_base) + origin; check(avformat_seek_file(format, streamIndex, INT64_MIN, target, target, 0), "Seek audio"); if (tak1) tak1->reset(); else avcodec_flush_buffers(codec); av_packet_unref(packet); av_frame_unref(frame); swr_close(swr); check(swr_init(swr), "Reset audio converter"); inputEnded = drainSent = resamplerEnded = resamplerStarted = false; pending.clear(); pendingOffset = 0; skipUntil = decodedNext = outputFrame; }