Bug Summary

File:audio/audio_source.cpp
Location:line 230, column 2
Description:This statement is never executed

Annotated Source Code

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
21extern "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
37const size_t AudioSource::BUFFER_SIZE = (size_t)FF_MIN_BUFFER_SIZE16384;
38
39AudioSource::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
53AudioSource::~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
63std::string AudioSource::Path() const
64{
65 return this->path;
66}
67
68std::uint8_t AudioSource::ChannelCount() const
69{
70 return this->stream->codec->channels;
71}
72
73size_t AudioSource::BufferSampleCapacity() const
74{
75 return this->buffer.size() / this->BytesPerSample();
76}
77
78double AudioSource::SampleRate() const
79{
80 return (double)this->stream->codec->sample_rate;
81}
82
83std::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
95std::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
105size_t AudioSource::BytesPerSample() const
106{
107 size_t bps = av_get_bytes_per_sample(this->resampler->AVOutputFormat());
108 return ChannelCount() * bps;
109}
110
111std::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
129std::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
149AudioSource::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
168void 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
183AudioSource::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
203bool 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
210Resampler::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
218static 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 */
228SampleFormat 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
240void 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
255void AudioSource::InitialiseStream()
256{
257 FindStreamInfo();
258 FindStreamAndInitialiseCodec();
259}
260
261void AudioSource::FindStreamInfo()
262{
263 if (avformat_find_stream_info(this->context, nullptr) < 0) {
264 throw FileError(MSG_DECODE_NOAUDIO);
265 }
266}
267
268void 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
281void 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
293void AudioSource::InitialiseFrame()
294{
295 this->frame = av_frame_alloc();
296 if (this->frame == nullptr) {
297 throw std::bad_alloc();
298 }
299}
300
301void 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
308void 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
325bool 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
332bool 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
355std::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}