Decode num_frames PCM frames starting at offset.
Caller must hold at least a shared lock on m_context_mutex.
465{
466 if (!audio->is_valid()) {
467 set_error(
"Invalid audio context for decoding");
468 return {};
469 }
470
472 int ch = static_cast<int>(audio->channels);
473
474 std::vector<Kakshya::DataVariant> output;
475 if (use_planar) {
476 output.resize(ch);
477 for (auto& v : output) {
478 v = std::vector<double>();
479 std::get<std::vector<double>>(v).reserve(num_frames);
480 }
481 } else {
482 output.resize(1);
483 output[0] = std::vector<double>();
484 std::get<std::vector<double>>(output[0]).reserve(num_frames * static_cast<size_t>(ch));
485 }
486
487 uint64_t decoded = 0;
488 bool eof_reached = false;
489
490 AVPacket* pkt = av_packet_alloc();
491 AVFrame* frame = av_frame_alloc();
492 if (!pkt || !frame) {
493 av_packet_free(&pkt);
494 av_frame_free(&frame);
495 set_error(
"Failed to allocate packet/frame");
496 return {};
497 }
498
500 int max_resampled = static_cast<int>(av_rescale_rnd(
501 static_cast<int64_t>(num_frames), out_rate, audio->sample_rate, AV_ROUND_UP));
502
503 AVSampleFormat tgt_fmt = use_planar ? AV_SAMPLE_FMT_DBLP : AV_SAMPLE_FMT_DBL;
504 uint8_t** resample_buf = nullptr;
505 int linesize = 0;
506
507 if (av_samples_alloc_array_and_samples(
508 &resample_buf, &linesize, ch, max_resampled, tgt_fmt, 0)
509 < 0) {
510 av_packet_free(&pkt);
511 av_frame_free(&frame);
512 set_error(
"Failed to allocate resample buffer");
513 return {};
514 }
515
516 while (decoded < num_frames) {
517 if (!eof_reached) {
518 int ret = av_read_frame(demux->format_context, pkt);
519 if (ret == AVERROR_EOF) {
520 eof_reached = true;
521 avcodec_send_packet(audio->codec_context, nullptr);
522 } else if (ret < 0) {
523 eof_reached = true;
524 } else if (pkt->stream_index == audio->stream_index) {
525 avcodec_send_packet(audio->codec_context, pkt);
526 av_packet_unref(pkt);
527 } else {
528 av_packet_unref(pkt);
529 }
530 }
531
532 int receive_ret = 0;
533 while (decoded < num_frames) {
534 receive_ret = avcodec_receive_frame(audio->codec_context, frame);
535
536 if (receive_ret == AVERROR(EAGAIN))
537 break;
538 if (receive_ret == AVERROR_EOF) {
539
540 break;
541 }
542 if (receive_ret < 0)
543 break;
544
545 int out_samples = swr_convert(
546 audio->swr_context,
547 resample_buf, max_resampled,
548 const_cast<const uint8_t**>(frame->data),
549 frame->nb_samples);
550
551 if (out_samples > 0) {
552 uint64_t to_copy = std::min(static_cast<uint64_t>(out_samples),
553 num_frames - decoded);
554 if (use_planar) {
555 for (int c = 0; c < ch; ++c) {
556 auto* src = reinterpret_cast<double*>(resample_buf[c]);
557 auto& dst = std::get<std::vector<double>>(output[c]);
558 dst.insert(dst.end(), src, src + to_copy);
559 }
560 } else {
561 auto* src = reinterpret_cast<double*>(resample_buf[0]);
562 auto& dst = std::get<std::vector<double>>(output[0]);
563 dst.insert(dst.end(), src, src + to_copy * static_cast<uint64_t>(ch));
564 }
565 decoded += to_copy;
566 }
567 av_frame_unref(frame);
568 }
569
570 if (eof_reached && receive_ret == AVERROR_EOF)
571 break;
572 }
573
574 while (true) {
575 int n = swr_convert(audio->swr_context, resample_buf, max_resampled, nullptr, 0);
576 if (n <= 0)
577 break;
578
579 uint64_t to_copy = std::min(static_cast<uint64_t>(n),
580 (num_frames > decoded) ? (num_frames - decoded) : 0);
581
582 if (to_copy > 0) {
583 if (use_planar) {
584 for (int c = 0; c < ch; ++c) {
585 auto* src = reinterpret_cast<double*>(resample_buf[c]);
586 auto& dst = std::get<std::vector<double>>(output[c]);
587 dst.insert(dst.end(), src, src + to_copy);
588 }
589 } else {
590 auto* src = reinterpret_cast<double*>(resample_buf[0]);
591 auto& dst = std::get<std::vector<double>>(output[0]);
592 dst.insert(dst.end(), src, src + to_copy * static_cast<uint64_t>(ch));
593 }
594 decoded += to_copy;
595 } else {
596 break;
597 }
598 }
599
600 av_freep(&resample_buf[0]);
601 av_freep(&resample_buf);
602 av_packet_free(&pkt);
603 av_frame_free(&frame);
604
606 return output;
607}
uint32_t m_target_sample_rate
Target sample rate for resampling (0 = use source rate).
void set_error(const std::string &error) const
Set the last error message.
std::atomic< uint64_t > m_current_frame_position
Current frame position for reading.
AudioReadOptions m_audio_options
Audio-specific read options.
@ DEINTERLEAVE
Output planar (per-channel) doubles instead of interleaved.