| 102 | } |
| 103 | |
| 104 | Abyss::Streams::AudioStream::AudioStream(FileSystem::InputStream stream) : _stream(std::move(stream)), _ringBuffer(1024 * 1024) { |
| 105 | const auto streamSize = _stream.size(); |
| 106 | const int decodeBufferSize = streamSize < DecodeBufferSize ? static_cast<int>(streamSize) : DecodeBufferSize; |
| 107 | |
| 108 | _avBuffer = static_cast<unsigned char *>(av_malloc(DecodeBufferSize)); // AVIO is going to free this automagically... because why not? |
| 109 | memset(_avBuffer, 0, DecodeBufferSize); |
| 110 | |
| 111 | _avioContext = avio_alloc_context(_avBuffer, decodeBufferSize, 0, this, &AudioStream::streamReadCallback, nullptr, &AudioStream::streamSeekCallback); |
| 112 | |
| 113 | _avFormatContext = avformat_alloc_context(); |
| 114 | _avFormatContext->pb = _avioContext; |
| 115 | _avFormatContext->flags |= AVFMT_FLAG_CUSTOM_IO; |
| 116 | |
| 117 | int avError; |
| 118 | |
| 119 | if ((avError = avformat_open_input(&_avFormatContext, "", nullptr, nullptr)) < 0) |
| 120 | throw std::runtime_error(absl::StrCat("Failed to open input stream: ", AvErrorCodeToString(avError))); |
| 121 | |
| 122 | if ((avError = avformat_find_stream_info(_avFormatContext, nullptr)) < 0) |
| 123 | throw std::runtime_error(absl::StrCat("Failed to find stream info: ", AvErrorCodeToString(avError))); |
| 124 | |
| 125 | _audioStreamIdx = -1; |
| 126 | for (uint32_t i = 0; i < _avFormatContext->nb_streams; i++) { |
| 127 | if (_avFormatContext->streams[i]->codecpar->codec_type != AVMEDIA_TYPE_AUDIO) |
| 128 | continue; |
| 129 | |
| 130 | _audioStreamIdx = static_cast<int>(i); |
| 131 | break; |
| 132 | } |
| 133 | |
| 134 | if (_audioStreamIdx < 0) |
| 135 | throw std::runtime_error("No audio stream found"); |
| 136 | |
| 137 | const auto audioCodecPar = _avFormatContext->streams[_audioStreamIdx]->codecpar; |
| 138 | auto audioDecoder = avcodec_find_decoder(audioCodecPar->codec_id); |
| 139 | |
| 140 | if (audioDecoder == nullptr) |
| 141 | throw std::runtime_error("Missing audio codec."); |
| 142 | |
| 143 | _audioCodecContext = avcodec_alloc_context3(audioDecoder); |
| 144 | if ((avError = avcodec_parameters_to_context(_audioCodecContext, audioCodecPar)) < 0) |
| 145 | throw std::runtime_error(absl::StrCat("Failed to copy codec parameters to decoder context: ", AvErrorCodeToString(avError))); |
| 146 | |
| 147 | if ((avError = avcodec_open2(_audioCodecContext, audioDecoder, nullptr)) < 0) |
| 148 | throw std::runtime_error(absl::StrCat("Failed to open audio codec: ", AvErrorCodeToString(avError))); |
| 149 | |
| 150 | _resampleContext = swr_alloc(); |
| 151 | |
| 152 | av_opt_set_int(_resampleContext, "in_channel_layout", av_get_default_channel_layout(_audioCodecContext->channels), 0); |
| 153 | |
| 154 | av_opt_set_int(_resampleContext, "in_sample_rate", _audioCodecContext->sample_rate, 0); |
| 155 | av_opt_set_sample_fmt(_resampleContext, "in_sample_fmt", _audioCodecContext->sample_fmt, 0); |
| 156 | |
| 157 | av_opt_set_int(_resampleContext, "out_channel_layout", AV_CH_LAYOUT_STEREO, 0); |
| 158 | av_opt_set_int(_resampleContext, "out_sample_rate", 44100, 0); |
| 159 | av_opt_set_sample_fmt(_resampleContext, "out_sample_fmt", AV_SAMPLE_FMT_S16, 0); |
| 160 | |
| 161 | if ((avError = swr_init(_resampleContext)) < 0) |