File: | audio/audio_source.cpp |
Location: | line 230, column 2 |
Description: | This statement is never executed |
1 | // This file is part of Playslave-C++. |
2 | // Playslave-C++ is licenced under the MIT license: see LICENSE.txt. |
3 | |
4 | /** |
5 | * @file |
6 | * Implementation of the AudioSource class. |
7 | * @see audio/audio_source.hpp |
8 | */ |
9 | |
10 | #include <cassert> |
11 | #include <cstdint> |
12 | #include <cstdlib> |
13 | #include <functional> |
14 | #include <iostream> |
15 | #include <map> |
16 | #include <memory> |
17 | #include <sstream> |
18 | #include <string> |
19 | |
20 | // ffmpeg |
21 | extern "C" { |
22 | #ifdef WIN32 |
23 | #define inline __inline |
24 | #endif |
25 | #include <libavcodec/avcodec.h> |
26 | #include <libavcodec/version.h> // For old version patchups |
27 | #include <libavformat/avformat.h> |
28 | #include <libavutil/opt.h> |
29 | } |
30 | |
31 | #include "../errors.hpp" |
32 | #include "../messages.h" |
33 | #include "../sample_formats.hpp" |
34 | #include "audio_resample.hpp" |
35 | #include "audio_source.hpp" |
36 | |
37 | const size_t AudioSource::BUFFER_SIZE = (size_t)FF_MIN_BUFFER_SIZE16384; |
38 | |
39 | AudioSource::AudioSource(const std::string &path) |
40 | : decode_state(DecodeState::WAITING_FOR_FRAME), |
41 | buffer(BUFFER_SIZE), |
42 | context(nullptr), |
43 | frame(nullptr), |
44 | path(path) |
45 | { |
46 | Open(path); |
47 | InitialiseStream(); |
48 | InitialisePacket(); |
49 | InitialiseFrame(); |
50 | InitialiseResampler(); |
51 | } |
52 | |
53 | AudioSource::~AudioSource() |
54 | { |
55 | if (this->context != nullptr) { |
56 | avformat_free_context(this->context); |
57 | } |
58 | if (this->frame != nullptr) { |
59 | av_frame_free(&this->frame); |
60 | } |
61 | } |
62 | |
63 | std::string AudioSource::Path() const |
64 | { |
65 | return this->path; |
66 | } |
67 | |
68 | std::uint8_t AudioSource::ChannelCount() const |
69 | { |
70 | return this->stream->codec->channels; |
71 | } |
72 | |
73 | size_t AudioSource::BufferSampleCapacity() const |
74 | { |
75 | return this->buffer.size() / this->BytesPerSample(); |
76 | } |
77 | |
78 | double AudioSource::SampleRate() const |
79 | { |
80 | return (double)this->stream->codec->sample_rate; |
81 | } |
82 | |
83 | std::uint64_t AudioSource::SamplePositionFromMicroseconds( |
84 | std::chrono::microseconds usec) const |
85 | { |
86 | // The sample rate is expressed in terms of samples per second, so we |
87 | // need to convert the position to seconds then multiply by the rate. |
88 | // We do things in a slightly peculiar order to minimise rounding. |
89 | |
90 | auto sample_micros = usec * SampleRate(); |
91 | return std::chrono::duration_cast<std::chrono::seconds>(sample_micros) |
92 | .count(); |
93 | } |
94 | |
95 | std::chrono::microseconds AudioSource::MicrosecondPositionFromSamples( |
96 | std::uint64_t samples) const |
97 | { |
98 | // This is basically SamplePositionFromMicroseconds but backwards. |
99 | |
100 | auto position_secs = std::chrono::seconds(samples) / SampleRate(); |
101 | return std::chrono::duration_cast<std::chrono::microseconds>( |
102 | position_secs); |
103 | } |
104 | |
105 | size_t AudioSource::BytesPerSample() const |
106 | { |
107 | size_t bps = av_get_bytes_per_sample(this->resampler->AVOutputFormat()); |
108 | return ChannelCount() * bps; |
109 | } |
110 | |
111 | std::uint64_t AudioSource::Seek(std::chrono::microseconds position) |
112 | { |
113 | // FFmpeg doesn't use microseconds for its seek positions, so we need to |
114 | // convert into its own units. |
115 | std::int64_t ffmpeg_position = AvPositionFromMicroseconds(position); |
116 | if (av_seek_frame(this->context, this->stream_id, ffmpeg_position, |
117 | AVSEEK_FLAG_ANY4) != 0) { |
118 | throw InternalError(MSG_SEEK_FAIL); |
119 | } |
120 | |
121 | // Reset the decoder state, because otherwise the decoder will get very |
122 | // confused. |
123 | this->decode_state = DecodeState::WAITING_FOR_FRAME; |
124 | InitialisePacket(); |
125 | |
126 | return SamplePositionFromMicroseconds(position); |
127 | } |
128 | |
129 | std::int64_t AudioSource::AvPositionFromMicroseconds( |
130 | std::chrono::microseconds position) |
131 | { |
132 | // FFmpeg reports positions in terms of a 'time base', which is |
133 | // expressed as a fraction (num/dem) of a second. |
134 | // So, we need to convert the microseconds into time base units, |
135 | // which is theoretically done by converting into seconds and then |
136 | // dividing by the time base fraction. However, as we're working with |
137 | // integers, we jiggle that calculation around a bit to minimise |
138 | // rounding. |
139 | |
140 | auto position_timebase_microseconds = |
141 | (position * this->stream->time_base.den) / |
142 | this->stream->time_base.num; |
143 | auto position_timebase_seconds = |
144 | std::chrono::duration_cast<std::chrono::seconds>( |
145 | position_timebase_microseconds); |
146 | return position_timebase_seconds.count(); |
147 | } |
148 | |
149 | AudioSource::DecodeResult AudioSource::Decode() |
150 | { |
151 | Resampler::ResultVector decoded; |
152 | |
153 | switch (this->decode_state) { |
154 | case DecodeState::WAITING_FOR_FRAME: |
155 | DoFrame(); |
156 | break; |
157 | case DecodeState::DECODING: |
158 | decoded = DoDecode(); |
159 | break; |
160 | case DecodeState::END_OF_FILE: |
161 | // Intentionally ignore |
162 | break; |
163 | } |
164 | |
165 | return std::make_pair(this->decode_state, decoded); |
166 | } |
167 | |
168 | void AudioSource::DoFrame() |
169 | { |
170 | bool read_frame = ReadFrame(); |
171 | |
172 | if (!read_frame) { |
173 | // We've run out of frames to decode. |
174 | // (TODO: Start flushing the buffer here?) |
175 | this->decode_state = DecodeState::END_OF_FILE; |
176 | } else if (this->packet.stream_index == this->stream_id) { |
177 | // Only switch to decoding if the frame belongs to the audio |
178 | // stream. Else, we ignore it. |
179 | this->decode_state = DecodeState::DECODING; |
180 | } |
181 | } |
182 | |
183 | AudioSource::DecodeVector AudioSource::DoDecode() |
184 | { |
185 | DecodeVector result; |
186 | |
187 | bool finished_decoding = DecodePacket(); |
188 | if (finished_decoding) { |
189 | result = Resample(); |
190 | |
191 | // Get ready to process the next frame. |
192 | InitialisePacket(); |
193 | this->decode_state = DecodeState::WAITING_FOR_FRAME; |
194 | } else { |
195 | // Send through an empty vector, so that the audio output will |
196 | // safely run its course and make way for the next decode round. |
197 | result = std::vector<char>(); |
198 | } |
199 | |
200 | return result; |
201 | } |
202 | |
203 | bool AudioSource::ReadFrame() |
204 | { |
205 | // av_read_frame uses non-zero returns to signal errors. |
206 | int read_result = av_read_frame(this->context, &this->packet); |
207 | return 0 == read_result; |
208 | } |
209 | |
210 | Resampler::ResultVector AudioSource::Resample() |
211 | { |
212 | if (this->frame->nb_samples == 0) { |
213 | return Resampler::ResultVector(); |
214 | } |
215 | return this->resampler->Resample(this->frame); |
216 | } |
217 | |
218 | static const std::map<AVSampleFormat, SampleFormat> sf_from_av = { |
219 | { AV_SAMPLE_FMT_U8, SampleFormat::PACKED_UNSIGNED_INT_8 }, |
220 | { AV_SAMPLE_FMT_S16, SampleFormat::PACKED_SIGNED_INT_16 }, |
221 | { AV_SAMPLE_FMT_S32, SampleFormat::PACKED_SIGNED_INT_32 }, |
222 | { AV_SAMPLE_FMT_FLT, SampleFormat::PACKED_FLOAT_32 } |
223 | }; |
224 | |
225 | /** |
226 | * @return The sample format of the data returned by this decoder. |
227 | */ |
228 | SampleFormat AudioSource::OutputSampleFormat() const |
229 | { |
230 | try |
This statement is never executed | |
231 | { |
232 | return sf_from_av.at(this->resampler->AVOutputFormat()); |
233 | } |
234 | catch (std::out_of_range) |
235 | { |
236 | throw FileError(MSG_DECODE_BADRATE); |
237 | } |
238 | } |
239 | |
240 | void AudioSource::Open(const std::string &path) |
241 | { |
242 | this->context = nullptr; |
243 | |
244 | // FFmpeg reports a file open error using a negative result here. |
245 | if (avformat_open_input(&this->context, path.c_str(), nullptr, |
246 | nullptr) < 0) { |
247 | std::ostringstream os; |
248 | os << "couldn't open " << path; |
249 | throw FileError(os.str()); |
250 | } else if (this->context == nullptr) { |
251 | throw std::bad_alloc(); |
252 | } |
253 | } |
254 | |
255 | void AudioSource::InitialiseStream() |
256 | { |
257 | FindStreamInfo(); |
258 | FindStreamAndInitialiseCodec(); |
259 | } |
260 | |
261 | void AudioSource::FindStreamInfo() |
262 | { |
263 | if (avformat_find_stream_info(this->context, nullptr) < 0) { |
264 | throw FileError(MSG_DECODE_NOAUDIO); |
265 | } |
266 | } |
267 | |
268 | void AudioSource::FindStreamAndInitialiseCodec() |
269 | { |
270 | AVCodec *codec; |
271 | int stream = av_find_best_stream(this->context, AVMEDIA_TYPE_AUDIO, -1, |
272 | -1, &codec, 0); |
273 | |
274 | if (stream < 0) { |
275 | throw FileError(MSG_DECODE_NOSTREAM); |
276 | } |
277 | |
278 | InitialiseCodec(stream, codec); |
279 | } |
280 | |
281 | void AudioSource::InitialiseCodec(int stream, AVCodec *codec) |
282 | { |
283 | AVCodecContext *codec_context = this->context->streams[stream]->codec; |
284 | // A negative result here is FFmpeg's way of reporting an opening error. |
285 | if (avcodec_open2(codec_context, codec, nullptr) < 0) { |
286 | throw FileError(MSG_DECODE_NOCODEC); |
287 | } |
288 | |
289 | this->stream = this->context->streams[stream]; |
290 | this->stream_id = stream; |
291 | } |
292 | |
293 | void AudioSource::InitialiseFrame() |
294 | { |
295 | this->frame = av_frame_alloc(); |
296 | if (this->frame == nullptr) { |
297 | throw std::bad_alloc(); |
298 | } |
299 | } |
300 | |
301 | void AudioSource::InitialisePacket() |
302 | { |
303 | av_init_packet(&this->packet); |
304 | this->packet.data = &*this->buffer.begin(); |
305 | this->packet.size = this->buffer.size(); |
306 | } |
307 | |
308 | void AudioSource::InitialiseResampler() |
309 | { |
310 | Resampler *rs; |
311 | AVCodecContext *codec = this->stream->codec; |
312 | |
313 | // The Audio can only handle packed sample data. |
314 | // If we're using a sample format that is planar, we need to resample it |
315 | // into a packed format. This is done with the PlanarResampler. |
316 | // Otherwise, we pass it through the (dummy) PackedResampler. |
317 | if (UsingPlanarSampleFormat()) { |
318 | rs = new PlanarResampler(codec); |
319 | } else { |
320 | rs = new PackedResampler(codec); |
321 | } |
322 | this->resampler = std::unique_ptr<Resampler>(rs); |
323 | } |
324 | |
325 | bool AudioSource::UsingPlanarSampleFormat() |
326 | { |
327 | // The docs for this function explicitly mention a return value of 1 for |
328 | // planar, 0 for packed. |
329 | return av_sample_fmt_is_planar(this->stream->codec->sample_fmt) == 1; |
330 | } |
331 | |
332 | bool AudioSource::DecodePacket() |
333 | { |
334 | assert(this->packet.data != nullptr)((this->packet.data != nullptr) ? static_cast<void> ( 0) : __assert_fail ("this->packet.data != nullptr", "src/audio/audio_source.cpp" , 334, __PRETTY_FUNCTION__)); |
335 | assert(0 <= this->packet.size)((0 <= this->packet.size) ? static_cast<void> (0) : __assert_fail ("0 <= this->packet.size", "src/audio/audio_source.cpp" , 335, __PRETTY_FUNCTION__)); |
336 | |
337 | auto decode_result = AvCodecDecode(); |
338 | |
339 | // A negative result here is FFmpeg's way of reporting a decode error. |
340 | int bytes_decoded = decode_result.first; |
341 | if (bytes_decoded < 0) { |
342 | throw FileError(MSG_DECODE_FAIL); |
343 | } |
344 | |
345 | // Shift the packet forwards so that, if we haven't finished the packet, |
346 | // we can resume decoding from where we left off this time. |
347 | this->packet.data += bytes_decoded; |
348 | this->packet.size -= bytes_decoded; |
349 | assert(0 <= this->packet.size)((0 <= this->packet.size) ? static_cast<void> (0) : __assert_fail ("0 <= this->packet.size", "src/audio/audio_source.cpp" , 349, __PRETTY_FUNCTION__)); |
350 | |
351 | bool frame_finished = decode_result.second; |
352 | return frame_finished || (0 == this->packet.size); |
353 | } |
354 | |
355 | std::pair<int, bool> AudioSource::AvCodecDecode() |
356 | { |
357 | int frame_finished = 0; |
358 | int bytes_decoded = |
359 | avcodec_decode_audio4(this->stream->codec, this->frame, |
360 | &frame_finished, &this->packet); |
361 | |
362 | return std::make_pair(bytes_decoded, frame_finished != 0); |
363 | } |