diff --git a/recording-daemon/decoder.c b/recording-daemon/decoder.c index 2a6978172..640918452 100644 --- a/recording-daemon/decoder.c +++ b/recording-daemon/decoder.c @@ -166,6 +166,84 @@ err: } +static AVFrame *decoder_resample_frame(decoder_t *dec) { + const char *err; + + if (dec->in_clockrate == dec->out_clockrate) + return dec->frame; + + if (!dec->avresample) { + dec->avresample = avresample_alloc_context(); + err = "failed to alloc resample context"; + if (!dec->avresample) + goto err; + + av_opt_set_int(dec->avresample, "in_channel_layout", av_get_default_channel_layout(dec->channels), 0); + av_opt_set_int(dec->avresample, "in_sample_fmt", AV_SAMPLE_FMT_S16, 0); + av_opt_set_int(dec->avresample, "in_sample_rate", dec->in_clockrate, 0); + av_opt_set_int(dec->avresample, "out_channel_layout", av_get_default_channel_layout(dec->channels), 0); + av_opt_set_int(dec->avresample, "out_sample_fmt", AV_SAMPLE_FMT_S16, 0); + av_opt_set_int(dec->avresample, "out_sample_rate", dec->out_clockrate, 0); + // av_opt_set_int(dec->avresample, "internal_sample_fmt", AV_SAMPLE_FMT_FLTP, 0); // ? + + err = "failed to init resample context"; + if (avresample_open(dec->avresample) < 0) + goto err; + } + + // get a large enough buffer for resampled audio - this should be enough so we don't + // have to loop + int dst_samples = avresample_available(dec->avresample) + + av_rescale_rnd(avresample_get_delay(dec->avresample) + dec->frame->nb_samples, + dec->out_clockrate, dec->in_clockrate, AV_ROUND_UP); + if (!dec->swr_frame || dec->swr_buffers < dst_samples) { + av_frame_free(&dec->swr_frame); + dec->swr_frame = av_frame_alloc(); + err = "failed to alloc resampling frame"; + if (!dec->swr_frame) + goto err; + av_frame_copy_props(dec->swr_frame, dec->frame); + dec->swr_frame->format = dec->frame->format; + dec->swr_frame->channel_layout = dec->frame->channel_layout; + dec->swr_frame->nb_samples = dst_samples; + dec->swr_frame->sample_rate = dec->out_clockrate; + err = "failed to get resample buffers"; + if (av_frame_get_buffer(dec->swr_frame, 0) < 0) + goto err; + dec->swr_buffers = dst_samples; + } + + dec->swr_frame->nb_samples = dst_samples; + int ret_samples = avresample_convert(dec->avresample, dec->swr_frame->extended_data, + dec->swr_frame->linesize[0], dst_samples, + dec->frame->extended_data, + dec->frame->linesize[0], dec->frame->nb_samples); + err = "failed to resample audio"; + if (ret_samples < 0) + goto err; + + dec->swr_frame->nb_samples = ret_samples; + dec->swr_frame->pts = av_rescale(dec->frame->pts, dec->out_clockrate, dec->in_clockrate); + return dec->swr_frame; + +err: + ilog(LOG_ERR, "Error resampling: %s", err); + return NULL; +} + + +static int decoder_got_frame(decoder_t *dec, output_t *output) { + // do we need to resample? + AVFrame *dec_frame = decoder_resample_frame(dec); + + output_config(output, dec->out_clockrate, dec->channels); + if (output_add(output, dec_frame)) + return -1; + + return 0; +} + + int decoder_input(decoder_t *dec, const str *data, unsigned long ts, output_t *output) { const char *err; @@ -190,91 +268,72 @@ int decoder_input(decoder_t *dec, const str *data, unsigned long ts, output_t *o dec->avpkt.size = data->len; dec->avpkt.pts = dec->pts; -#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57, 0, 0) - int ret = avcodec_send_packet(dec->avcctx, &dec->avpkt); - dbg("send packet ret %i", ret); - err = "failed to send packet to avcodec"; - if (ret) - goto err; + // loop until all input is consumed and all available output has been processed + int keep_going; + do { + keep_going = 0; + int got_frame = 0; - ret = avcodec_receive_frame(dec->avcctx, dec->frame); - dbg("receive frame ret %i", ret); - err = "failed to receive frame from avcodec"; - if (ret) - goto err; -#else - int got_frame = 0; - int ret = avcodec_decode_audio4(dec->avcctx, dec->frame, &got_frame, &dec->avpkt); - dbg("decode frame ret %i, got frame %i", ret, got_frame); - err = "failed to decode audio packet"; - if (ret < 0) - goto err; - if (!got_frame) - return 0; -#endif - - dbg("%p dec frame pts %lu pkt_pts %lu", dec, (unsigned long) dec->frame->pts, - (unsigned long) dec->frame->pkt_dts); - - // do we need to resample? - AVFrame *dec_frame = dec->frame; - if (dec->in_clockrate != dec->out_clockrate) { - if (!dec->avresample) { - dec->avresample = avresample_alloc_context(); - err = "failed to alloc resample context"; - if (!dec->avresample) - goto err; - - av_opt_set_int(dec->avresample, "in_channel_layout", av_get_default_channel_layout(dec->channels), 0); - av_opt_set_int(dec->avresample, "in_sample_fmt", AV_SAMPLE_FMT_S16, 0); - av_opt_set_int(dec->avresample, "in_sample_rate", dec->in_clockrate, 0); - av_opt_set_int(dec->avresample, "out_channel_layout", av_get_default_channel_layout(dec->channels), 0); - av_opt_set_int(dec->avresample, "out_sample_fmt", AV_SAMPLE_FMT_S16, 0); - av_opt_set_int(dec->avresample, "out_sample_rate", dec->out_clockrate, 0); - // av_opt_set_int(dec->avresample, "internal_sample_fmt", AV_SAMPLE_FMT_FLTP, 0); // ? - - err = "failed to init resample context"; - if (avresample_open(dec->avresample) < 0) - goto err; +#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57, 0, 0) + if (dec->avpkt.size) { + int ret = avcodec_send_packet(dec->avcctx, &dec->avpkt); + dbg("send packet ret %i", ret); + err = "failed to send packet to avcodec"; + if (ret == 0) { + // consumed the packet + dec->avpkt.size = 0; + keep_going = 1; + } + else { + if (ret == AVERROR(EAGAIN)) + ; // try again after reading output + else + goto err; + } } - // get a large enough buffer for resampled audio - int dst_samples = av_rescale_rnd(dec->frame->nb_samples, dec->out_clockrate, - dec->in_clockrate, AV_ROUND_UP); - if (!dec->swr_frame || dec->swr_buffers < dst_samples) { - av_frame_free(&dec->swr_frame); - dec->swr_frame = av_frame_alloc(); - err = "failed to alloc resampling frame"; - if (!dec->swr_frame) - goto err; - av_frame_copy_props(dec->swr_frame, dec->frame); - dec->swr_frame->format = dec->frame->format; - dec->swr_frame->channel_layout = dec->frame->channel_layout; - dec->swr_frame->nb_samples = dst_samples; - dec->swr_frame->sample_rate = dec->out_clockrate; - err = "failed to get resample buffers"; - if (av_frame_get_buffer(dec->swr_frame, 0) < 0) + int ret = avcodec_receive_frame(dec->avcctx, dec->frame); + dbg("receive frame ret %i", ret); + err = "failed to receive frame from avcodec"; + if (ret == 0) { + // got a frame + keep_going = 1; + got_frame = 1; + } + else { + if (ret == AVERROR(EAGAIN)) + ; // maybe needs more input now + else goto err; - dec->swr_buffers = dst_samples; } - - dec->swr_frame->nb_samples = dst_samples; - int ret_samples = avresample_convert(dec->avresample, dec->swr_frame->extended_data, - dec->swr_frame->linesize[0], dst_samples, - dec->frame->extended_data, - dec->frame->linesize[0], dec->frame->nb_samples); - err = "failed to resample audio"; - if (ret_samples < 0) +#else + // only do this if we have any input left + if (dec->avpkt.size == 0) + break; + + int ret = avcodec_decode_audio4(dec->avcctx, dec->frame, &got_frame, &dec->avpkt); + dbg("decode frame ret %i, got frame %i", ret, got_frame); + err = "failed to decode audio packet"; + if (ret < 0) goto err; + if (ret > 0) { + // consumed some input + err = "invalid return value"; + if (ret > dec->avpkt.size) + goto err; + dec->avpkt.size -= ret; + dec->avpkt.data += ret; + keep_going = 1; + } + if (got_frame) + keep_going = 1; +#endif - dec_frame = dec->swr_frame; - dec_frame->nb_samples = ret_samples; - dec_frame->pts = av_rescale(dec->frame->pts, dec->out_clockrate, dec->in_clockrate); - } - - output_config(output, dec->out_clockrate, dec->channels); - if (output_add(output, dec_frame)) - return -1; + if (got_frame) { + if (decoder_got_frame(dec, output)) + return -1; + } + } while (keep_going); return 0; diff --git a/recording-daemon/output.c b/recording-daemon/output.c index 75307f262..c4111c494 100644 --- a/recording-daemon/output.c +++ b/recording-daemon/output.c @@ -50,40 +50,76 @@ static int output_flush(output_t *output) { dbg("%p output fifo pts %lu", output, (unsigned long) output->fifo_pts); output->frame->pts = output->fifo_pts; + int keep_going; + int have_frame = 1; + do { + keep_going = 0; + int got_packet = 0; + #if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57, 0, 0) - int ret = avcodec_send_frame(output->avcctx, output->frame); - dbg("%p send frame ret %i", output, ret); - if (ret) - return -1; - - ret = avcodec_receive_packet(output->avcctx, &output->avpkt); - dbg("%p receive packet ret %i", output, ret); - if (ret) - return -1; + if (have_frame) { + int ret = avcodec_send_frame(output->avcctx, output->frame); + dbg("%p send frame ret %i", output, ret); + if (ret == 0) { + // consumed + have_frame = 0; + keep_going = 1; + } + else { + if (ret == AVERROR(EAGAIN)) + ; // check output and maybe try again + else + return -1; + } + } + + int ret = avcodec_receive_packet(output->avcctx, &output->avpkt); + dbg("%p receive packet ret %i", output, ret); + if (ret == 0) { + // got some data + keep_going = 1; + got_packet = 1; + } + else { + if (ret == AVERROR(EAGAIN)) + ; // try again if there's still more input + else + return -1; + } #else - int got_packet = 0; - int ret = avcodec_encode_audio2(output->avcctx, &output->avpkt, output->frame, &got_packet); - dbg("%p encode frame ret %i, got packet %i", output, ret, got_packet); - if (!got_packet) - return 0; + if (!have_frame) + break; + + int ret = avcodec_encode_audio2(output->avcctx, &output->avpkt, output->frame, &got_packet); + dbg("%p encode frame ret %i, got packet %i", output, ret, got_packet); + if (ret == 0) + have_frame = 0; // consumed + else + return -1; // error + if (got_packet) + keep_going = 1; #endif - dbg("%p output avpkt size is %i", output, (int) output->avpkt.size); - dbg("%p output pkt pts/dts is %li/%li", output, (long) output->avpkt.pts, - (long) output->avpkt.dts); - dbg("%p output dts %li", output, (long) output->mux_dts); + if (!got_packet) + continue; + + dbg("%p output avpkt size is %i", output, (int) output->avpkt.size); + dbg("%p output pkt pts/dts is %li/%li", output, (long) output->avpkt.pts, + (long) output->avpkt.dts); + dbg("%p output dts %li", output, (long) output->mux_dts); - // the encoder may return frames with the same dts multiple consecutive times. - // the muxer may not like this, so ensure monotonically increasing dts. - if (output->mux_dts > output->avpkt.dts) - output->avpkt.dts = output->mux_dts; - if (output->avpkt.pts < output->avpkt.dts) - output->avpkt.pts = output->avpkt.dts; + // the encoder may return frames with the same dts multiple consecutive times. + // the muxer may not like this, so ensure monotonically increasing dts. + if (output->mux_dts > output->avpkt.dts) + output->avpkt.dts = output->mux_dts; + if (output->avpkt.pts < output->avpkt.dts) + output->avpkt.pts = output->avpkt.dts; - av_write_frame(output->fmtctx, &output->avpkt); + av_write_frame(output->fmtctx, &output->avpkt); - output->fifo_pts += output->frame->nb_samples; - output->mux_dts = output->avpkt.dts + 1; // min next expected dts + output->fifo_pts += output->frame->nb_samples; + output->mux_dts = output->avpkt.dts + 1; // min next expected dts + } while (keep_going); } return 0;