diff options
author | Jaakko Keränen <jaakko.keranen@iki.fi> | 2020-10-15 17:16:49 +0300 |
---|---|---|
committer | Jaakko Keränen <jaakko.keranen@iki.fi> | 2020-10-15 17:16:49 +0300 |
commit | c3795ba3779ec79a94ad298fc5e72b6edfc4b4e7 (patch) | |
tree | 6e57868dafad0bf25d20c5cc6c72f4d8a145dd46 | |
parent | b073e4c6aad0a4c35ea89c4592d5b48a39bb61f5 (diff) |
Player: Streaming MP3 playback
Feed more MPEG input bitstream as it comes in.
-rw-r--r-- | src/audio/player.c | 76 |
1 files changed, 46 insertions, 30 deletions
diff --git a/src/audio/player.c b/src/audio/player.c index ef238378..13c8c58b 100644 --- a/src/audio/player.c +++ b/src/audio/player.c | |||
@@ -239,36 +239,54 @@ enum iDecoderStatus decodeMpeg_Decoder_(iDecoder *d) { | |||
239 | #if defined (LAGRANGE_ENABLE_MPG123) | 239 | #if defined (LAGRANGE_ENABLE_MPG123) |
240 | const iBlock *input = &d->input->data; | 240 | const iBlock *input = &d->input->data; |
241 | if (!d->mpeg) { | 241 | if (!d->mpeg) { |
242 | d->inputPos = 0; | ||
242 | d->mpeg = mpg123_new(NULL, NULL); | 243 | d->mpeg = mpg123_new(NULL, NULL); |
243 | mpg123_format_none(d->mpeg); | 244 | mpg123_format_none(d->mpeg); |
244 | mpg123_format(d->mpeg, d->outputFreq, d->output.numChannels, MPG123_ENC_SIGNED_16); | 245 | mpg123_format(d->mpeg, d->outputFreq, d->output.numChannels, MPG123_ENC_SIGNED_16); |
245 | mpg123_open_feed(d->mpeg); | 246 | mpg123_open_feed(d->mpeg); |
247 | } | ||
248 | /* Feed more input. */ { | ||
246 | lock_Mutex(&d->input->mtx); | 249 | lock_Mutex(&d->input->mtx); |
247 | mpg123_feed(d->mpeg, constData_Block(input), size_Block(input)); | 250 | if (d->input->isComplete) { |
251 | d->totalInputSize = size_Block(input); | ||
252 | } | ||
253 | if (d->inputPos < size_Block(input)) { | ||
254 | mpg123_feed(d->mpeg, constData_Block(input) + d->inputPos, size_Block(input) - d->inputPos); | ||
255 | if (d->inputPos == 0) { | ||
256 | long r; int ch, enc; | ||
257 | mpg123_getformat(d->mpeg, &r, &ch, &enc); | ||
258 | iAssert(r == d->outputFreq); | ||
259 | iAssert(ch == d->output.numChannels); | ||
260 | iAssert(enc == MPG123_ENC_SIGNED_16); | ||
261 | } | ||
262 | d->inputPos = size_Block(input); | ||
263 | } | ||
248 | unlock_Mutex(&d->input->mtx); | 264 | unlock_Mutex(&d->input->mtx); |
249 | long r; int ch, enc; | ||
250 | mpg123_getformat(d->mpeg, &r, &ch, &enc); | ||
251 | iAssert(r == d->outputFreq); | ||
252 | iAssert(ch == d->output.numChannels); | ||
253 | iAssert(enc == MPG123_ENC_SIGNED_16); | ||
254 | } | 265 | } |
255 | while (size_Array(&d->pendingOutput) < d->output.count) { | 266 | while (size_Array(&d->pendingOutput) < d->output.count) { |
256 | int16_t buffer[512]; | 267 | int16_t buffer[512]; |
257 | size_t bytesWritten = 0; | 268 | size_t bytesRead = 0; |
258 | const int rc = mpg123_read(d->mpeg, buffer, sizeof(buffer), &bytesWritten); | 269 | const int rc = mpg123_read(d->mpeg, buffer, sizeof(buffer), &bytesRead); |
259 | const float gain = d->gain; | 270 | const float gain = d->gain; |
260 | for (size_t i = 0; i < bytesWritten / 2; i++) { | 271 | for (size_t i = 0; i < bytesRead / 2; i++) { |
261 | buffer[i] *= gain; | 272 | buffer[i] *= gain; |
262 | } | 273 | } |
263 | pushBackN_Array(&d->pendingOutput, buffer, bytesWritten / 2 / d->output.numChannels); | 274 | pushBackN_Array(&d->pendingOutput, buffer, bytesRead / 2 / d->output.numChannels); |
264 | if (rc == MPG123_NEED_MORE) { | 275 | if (rc == MPG123_NEED_MORE) { |
265 | status = needMoreInput_DecoderStatus; | 276 | status = needMoreInput_DecoderStatus; |
266 | break; | 277 | break; |
267 | } | 278 | } |
268 | else if (rc == MPG123_DONE) { | 279 | else if (rc == MPG123_DONE || bytesRead == 0) { |
269 | break; | 280 | break; |
270 | } | 281 | } |
271 | } | 282 | } |
283 | /* Check if we know the total length already. This info should be available eventually. */ | ||
284 | if (d->totalSamples == 0) { | ||
285 | const off_t off = mpg123_length(d->mpeg); | ||
286 | if (off != MPG123_ERR) { | ||
287 | d->totalSamples = off; | ||
288 | } | ||
289 | } | ||
272 | writePending_Decoder_(d); | 290 | writePending_Decoder_(d); |
273 | #endif | 291 | #endif |
274 | return status; | 292 | return status; |
@@ -286,20 +304,18 @@ static iThreadResult run_Decoder_(iThread *thread) { | |||
286 | if (!d->type) break; | 304 | if (!d->type) break; |
287 | /* Have data to work on and a place to save output? */ | 305 | /* Have data to work on and a place to save output? */ |
288 | enum iDecoderStatus status = ok_DecoderStatus; | 306 | enum iDecoderStatus status = ok_DecoderStatus; |
289 | if (!isEmpty_Range(&inputRange)) { | 307 | switch (d->type) { |
290 | switch (d->type) { | 308 | case wav_DecoderType: |
291 | case wav_DecoderType: | 309 | status = decodeWav_Decoder_(d, inputRange); |
292 | status = decodeWav_Decoder_(d, inputRange); | 310 | break; |
293 | break; | 311 | case vorbis_DecoderType: |
294 | case vorbis_DecoderType: | 312 | status = decodeVorbis_Decoder_(d); |
295 | status = decodeVorbis_Decoder_(d); | 313 | break; |
296 | break; | 314 | case mpeg_DecoderType: |
297 | case mpeg_DecoderType: | 315 | status = decodeMpeg_Decoder_(d); |
298 | status = decodeMpeg_Decoder_(d); | 316 | break; |
299 | break; | 317 | default: |
300 | default: | 318 | break; |
301 | break; | ||
302 | } | ||
303 | } | 319 | } |
304 | if (status == needMoreInput_DecoderStatus) { | 320 | if (status == needMoreInput_DecoderStatus) { |
305 | lock_Mutex(&d->input->mtx); | 321 | lock_Mutex(&d->input->mtx); |
@@ -326,13 +342,13 @@ void init_Decoder(iDecoder *d, iInputBuf *input, const iContentSpec *spec) { | |||
326 | d->inputFormat = spec->inputFormat; | 342 | d->inputFormat = spec->inputFormat; |
327 | d->totalInputSize = spec->totalInputSize; | 343 | d->totalInputSize = spec->totalInputSize; |
328 | d->outputFreq = spec->output.freq; | 344 | d->outputFreq = spec->output.freq; |
345 | d->currentSample = 0; | ||
346 | d->totalSamples = spec->totalSamples; | ||
329 | init_Array(&d->pendingOutput, spec->output.channels * SDL_AUDIO_BITSIZE(spec->output.format) / 8); | 347 | init_Array(&d->pendingOutput, spec->output.channels * SDL_AUDIO_BITSIZE(spec->output.format) / 8); |
330 | init_SampleBuf(&d->output, | 348 | init_SampleBuf(&d->output, |
331 | spec->output.format, | 349 | spec->output.format, |
332 | spec->output.channels, | 350 | spec->output.channels, |
333 | spec->output.samples * 2); | 351 | spec->output.samples * 2); |
334 | d->currentSample = 0; | ||
335 | d->totalSamples = spec->totalSamples; | ||
336 | d->vorbis = NULL; | 352 | d->vorbis = NULL; |
337 | #if defined (LAGRANGE_ENABLE_MPG123) | 353 | #if defined (LAGRANGE_ENABLE_MPG123) |
338 | d->mpeg = NULL; | 354 | d->mpeg = NULL; |
@@ -514,9 +530,9 @@ static iContentSpec contentSpec_Player_(const iPlayer *d) { | |||
514 | mpg123_handle *mh = mpg123_new(NULL, NULL); | 530 | mpg123_handle *mh = mpg123_new(NULL, NULL); |
515 | mpg123_open_feed(mh); | 531 | mpg123_open_feed(mh); |
516 | mpg123_feed(mh, constData_Block(&d->data->data), size_Block(&d->data->data)); | 532 | mpg123_feed(mh, constData_Block(&d->data->data), size_Block(&d->data->data)); |
517 | long rate = 0; | 533 | long rate = 0; |
518 | int channels = 0; | 534 | int channels = 0; |
519 | int encoding = 0; | 535 | int encoding = 0; |
520 | if (mpg123_getformat(mh, &rate, &channels, &encoding) == MPG123_OK) { | 536 | if (mpg123_getformat(mh, &rate, &channels, &encoding) == MPG123_OK) { |
521 | content.output.freq = rate; | 537 | content.output.freq = rate; |
522 | content.output.channels = channels; | 538 | content.output.channels = channels; |