static int decode_audio(struct dec_audio *da, struct mp_audio *buffer, int maxlen) { struct ad_mpg123_context *con = da->priv; void *buf = buffer->planes[0]; int ret; if (con->new_format) { ret = set_format(da); if (ret == MPG123_OK) { return 0; // let caller handle format change } else if (ret == MPG123_NEED_MORE) { con->need_data = true; } else { goto mpg123_fail; } } if (con->need_data) { if (feed_new_packet(da) < 0) return -1; } if (!mp_audio_config_equals(&da->decoded, buffer)) return 0; size_t got_now = 0; ret = mpg123_replace_buffer(con->handle, buf, maxlen * con->sample_size); if (ret != MPG123_OK) goto mpg123_fail; ret = mpg123_decode_frame(con->handle, NULL, NULL, &got_now); int got_samples = got_now / con->sample_size; buffer->samples += got_samples; da->pts_offset += got_samples; if (ret == MPG123_NEW_FORMAT) { con->new_format = true; } else if (ret == MPG123_NEED_MORE) { con->need_data = true; } else if (ret != MPG123_OK && ret != MPG123_DONE) { goto mpg123_fail; } update_info(da); return 0; mpg123_fail: MP_ERR(da, "mpg123 decoding error: %s\n", mpg123_strerror(con->handle)); return -1; }
/* This tries to extract a requested amount of decoded data. * Even when you request 0 bytes, it will feed enough input so that * the decoder _could_ have delivered something. * Returns byte count >= 0, -1 on error. * * Thoughts on exact pts keeping: * We have to assume that MPEG frames are cut in pieces by packet boundaries. * Also, it might be possible that the first packet does not contain enough * data to ensure initial stream sync... or re-sync on erroneous streams. * So we need something robust to relate the decoded byte count to the correct * time stamp. This is tricky, though. From the outside, you cannot tell if, * after having fed two packets until the first output arrives, one should * start counting from the first packet's pts or the second packet's. * So, let's just count from the last fed package's pts. If the packets are * exactly cut to MPEG frames, this will cause one frame mismatch in the * beginning (when mpg123 peeks ahead for the following header), but will * be corrected with the third frame already. One might add special code to * not increment the base pts past the first packet's after a resync before * the first decoded bytes arrived. */ static int decode_a_bit(sh_audio_t *sh, unsigned char *buf, int count) { int ret = MPG123_OK; int got = 0; struct ad_mpg123_context *con = sh->context; /* There will be one MPG123_NEW_FORMAT message on first open. * This will be handled in init(). */ do { size_t got_now = 0; /* Feed the decoder. This will only fire from the second round on. */ if (ret == MPG123_NEED_MORE) { int incount; double pts; unsigned char *inbuf; /* Feed more input data. */ incount = ds_get_packet_pts(sh->ds, &inbuf, &pts); if (incount <= 0) break; /* Apparently that's it. EOF. */ /* Next bytes from that presentation time. */ if (pts != MP_NOPTS_VALUE) { sh->pts = pts; sh->pts_bytes = 0; } #ifdef AD_MPG123_FRAMEWISE /* Have to use mpg123_feed() to avoid decoding here. */ ret = mpg123_feed(con->handle, inbuf, incount); #else /* Do not use mpg123_feed(), added in later libmpg123 versions. */ ret = mpg123_decode(con->handle, inbuf, incount, NULL, 0, NULL); #endif if (ret == MPG123_ERR) break; } /* Theoretically, mpg123 could return MPG123_DONE, so be prepared. * Should not happen in our usage, but it is a valid return code. */ else if (ret == MPG123_ERR || ret == MPG123_DONE) break; /* Try to decode a bit. This is the return value that counts * for the loop condition. */ #ifdef AD_MPG123_FRAMEWISE if (!buf) { /* fake call just for feeding to get format */ ret = mpg123_getformat(con->handle, NULL, NULL, NULL); } else { /* This is the decoding. One frame at a time. */ ret = mpg123_replace_buffer(con->handle, buf, count); if (ret == MPG123_OK) ret = mpg123_decode_frame(con->handle, NULL, NULL, &got_now); } #else ret = mpg123_decode(con->handle, NULL, 0, buf + got, count - got, &got_now); #endif got += got_now; sh->pts_bytes += got_now; #ifdef AD_MPG123_FRAMEWISE } while (ret == MPG123_NEED_MORE || (got == 0 && count != 0)); #else } while (ret == MPG123_NEED_MORE || got < count);
/**************************************************************************** * DecodeBlock: the whole thing ****************************************************************************/ static block_t *DecodeBlock( decoder_t *p_dec, block_t **pp_block ) { int i_err; block_t *p_block = pp_block ? *pp_block : NULL; decoder_sys_t *p_sys = p_dec->p_sys; if( !pp_block || !p_block ) return NULL; if( p_block->i_buffer == 0 ) return NULL; if( !date_Get( &p_sys->end_date ) && p_block->i_pts <= VLC_TS_INVALID ) { /* We've just started the stream, wait for the first PTS. */ msg_Dbg( p_dec, "waiting for PTS" ); goto error; } if( p_block->i_flags & (BLOCK_FLAG_DISCONTINUITY | BLOCK_FLAG_CORRUPTED) ) { date_Set( &p_sys->end_date, 0 ); if( p_block->i_flags & BLOCK_FLAG_CORRUPTED ) goto error; } /* Feed mpg123 with raw data */ i_err = mpg123_feed( p_sys->p_handle, p_block->p_buffer, p_block->i_buffer ); if( i_err != MPG123_OK ) { msg_Err( p_dec, "mpg123_feed failed: %s", mpg123_plain_strerror( i_err ) ); goto error; } /* Get details about the stream */ i_err = mpg123_info( p_sys->p_handle, &p_sys->frame_info ); if( i_err == MPG123_NEED_MORE ) { /* Need moar data */ goto error; } else if( i_err != MPG123_OK ) { msg_Err( p_dec, "mpg123_info failed: %s", mpg123_plain_strerror( i_err ) ); goto error; } /* Configure the output */ p_block->i_nb_samples = mpg123_spf( p_sys->p_handle ); p_dec->fmt_out.i_bitrate = p_sys->frame_info.bitrate * 1000; switch( p_sys->frame_info.mode ) { case MPG123_M_STEREO: case MPG123_M_JOINT: p_dec->fmt_out.audio.i_original_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT; break; case MPG123_M_DUAL: p_dec->fmt_out.audio.i_original_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_DUALMONO; break; case MPG123_M_MONO: p_dec->fmt_out.audio.i_original_channels = AOUT_CHAN_CENTER; break; default: msg_Err( p_dec, "Unknown mode"); goto error; } p_dec->fmt_out.audio.i_physical_channels = p_dec->fmt_out.audio.i_original_channels & AOUT_CHAN_PHYSMASK; /* Date management */ if( p_dec->fmt_out.audio.i_rate != p_sys->frame_info.rate ) { p_dec->fmt_out.audio.i_rate = p_sys->frame_info.rate; date_Init( &p_sys->end_date, p_dec->fmt_out.audio.i_rate, 1 ); date_Set( &p_sys->end_date, 0 ); } if( p_block->i_pts > VLC_TS_INVALID && p_block->i_pts != date_Get( &p_sys->end_date ) ) { date_Set( &p_sys->end_date, p_block->i_pts ); } /* Request a new audio buffer */ block_t *p_out = decoder_NewAudioBuffer( p_dec, p_block->i_nb_samples ); if( unlikely( !p_out ) ) goto error; /* Configure the buffer */ p_out->i_nb_samples = p_block->i_nb_samples; p_out->i_dts = p_out->i_pts = date_Get( &p_sys->end_date ); p_out->i_length = date_Increment( &p_sys->end_date, p_block->i_nb_samples ) - p_out->i_pts; /* Make mpg123 write directly into the VLC output buffer */ i_err = mpg123_replace_buffer( p_sys->p_handle, p_out->p_buffer, p_out->i_buffer ); if( i_err != MPG123_OK ) { msg_Err( p_dec, "could not replace buffer: %s", mpg123_plain_strerror( i_err ) ); block_Release( p_out ); goto error; } *pp_block = NULL; /* avoid being fed the same packet again */ /* Do the actual decoding now */ i_err = mpg123_decode_frame( p_sys->p_handle, NULL, NULL, NULL ); if( i_err != MPG123_OK ) { if( i_err != MPG123_NEW_FORMAT ) msg_Err( p_dec, "mpg123_decode_frame error: %s", mpg123_plain_strerror( i_err ) ); block_Release( p_out ); goto error; } block_Release( p_block ); return p_out; error: block_Release( p_block ); return NULL; }
/**************************************************************************** * DecodeBlock: the whole thing ****************************************************************************/ static int DecodeBlock( decoder_t *p_dec, block_t *p_block ) { int i_err; decoder_sys_t *p_sys = p_dec->p_sys; block_t *p_out = NULL; if( !p_sys->b_opened ) { if( p_block ) block_Release( p_block ); return VLCDEC_ECRITICAL; } /* Feed input block */ if( p_block != NULL ) { if( !date_Get( &p_sys->end_date ) && p_block->i_pts <= VLC_TS_INVALID ) { /* We've just started the stream, wait for the first PTS. */ msg_Dbg( p_dec, "waiting for PTS" ); goto end; } if( p_block->i_flags & (BLOCK_FLAG_DISCONTINUITY|BLOCK_FLAG_CORRUPTED) ) { Flush( p_dec ); if( p_block->i_flags & BLOCK_FLAG_CORRUPTED ) goto end; } /* Feed mpg123 with raw data */ i_err = mpg123_feed( p_sys->p_handle, p_block->p_buffer, p_block->i_buffer ); if( i_err != MPG123_OK ) { msg_Err( p_dec, "mpg123_feed failed: %s", mpg123_plain_strerror( i_err ) ); goto end; } } /* Fetch a new output block (if possible) */ if( !p_sys->p_out || p_sys->p_out->i_buffer != mpg123_outblock( p_sys->p_handle ) ) { if( p_sys->p_out ) block_Release( p_sys->p_out ); /* Keep the output buffer for next calls in case it's not used (in case * of MPG123_NEED_MORE status) */ p_sys->p_out = block_Alloc( mpg123_outblock( p_sys->p_handle ) ); if( unlikely( !p_sys->p_out ) ) return VLCDEC_SUCCESS; } /* Do the actual decoding now */ size_t i_bytes = 0; while( true ) { /* Make mpg123 write directly into the VLC output buffer */ i_err = mpg123_replace_buffer( p_sys->p_handle, p_sys->p_out->p_buffer, p_sys->p_out->i_buffer ); if( i_err != MPG123_OK ) { msg_Err( p_dec, "could not replace buffer: %s", mpg123_plain_strerror( i_err ) ); block_Release( p_sys->p_out ); p_sys->p_out = NULL; return VLCDEC_SUCCESS; } i_err = mpg123_decode_frame( p_sys->p_handle, NULL, NULL, &i_bytes ); if( i_err != MPG123_OK ) { if( i_err == MPG123_NEW_FORMAT ) { if( UpdateAudioFormat( p_dec ) != VLC_SUCCESS ) goto end; else continue; } else if( i_err != MPG123_NEED_MORE ) msg_Err( p_dec, "mpg123_decode_frame error: %s", mpg123_plain_strerror( i_err ) ); } else if( p_dec->fmt_out.audio.i_rate == 0 ) { msg_Warn( p_dec, "mpg123_decode_frame returned valid frame without " "updating the format" ); if( UpdateAudioFormat( p_dec ) != VLC_SUCCESS ) goto end; } break; } if( p_block && p_block->i_pts > VLC_TS_INVALID && p_block->i_pts != date_Get( &p_sys->end_date ) ) date_Set( &p_sys->end_date, p_block->i_pts ); if( i_bytes == 0 ) goto end; assert( p_dec->fmt_out.audio.i_rate != 0 ); p_out = p_sys->p_out; p_sys->p_out = NULL; assert( p_out->i_buffer >= i_bytes ); p_out->i_buffer = i_bytes; p_out->i_nb_samples = p_out->i_buffer * p_dec->fmt_out.audio.i_frame_length / p_dec->fmt_out.audio.i_bytes_per_frame; /* Configure the buffer */ p_out->i_dts = p_out->i_pts = date_Get( &p_sys->end_date ); p_out->i_length = date_Increment( &p_sys->end_date, p_out->i_nb_samples ) - p_out->i_pts; end: if( p_block ) block_Release( p_block ); if( p_out != NULL ) decoder_QueueAudio( p_dec, p_out ); return VLCDEC_SUCCESS; }