diff --git a/recording-daemon/Makefile b/recording-daemon/Makefile index fd5bfe7d3..ee9b3a7cb 100644 --- a/recording-daemon/Makefile +++ b/recording-daemon/Makefile @@ -21,7 +21,7 @@ LDFLAGS+= `pkg-config --libs libavutil` include ../lib/lib.Makefile SRCS= epoll.c garbage.c inotify.c main.c metafile.c stream.c recaux.c rtplib.c packet.c \ - decoder.c loglib.c auxlib.c + decoder.c loglib.c auxlib.c output.c OBJS= $(SRCS:.c=.o) diff --git a/recording-daemon/decoder.c b/recording-daemon/decoder.c index da7a1ce2f..725754ab7 100644 --- a/recording-daemon/decoder.c +++ b/recording-daemon/decoder.c @@ -8,6 +8,7 @@ #include "types.h" #include "log.h" #include "str.h" +#include "output.h" struct decoder_s { @@ -19,24 +20,6 @@ struct decoder_s { }; -struct output_s { - char *filename; - - // format params - int clockrate; - int channels; - - AVCodecContext *avcctx; - AVFormatContext *fmtctx; - AVStream *avst; - AVPacket avpkt; - AVAudioFifo *fifo; - int64_t fifo_pts; // pts of first data in fifo - int64_t mux_dts; // last dts passed to muxer - AVFrame *frame; -}; - - struct decoder_def_s { const char *rtpname; int clockrate_mult; @@ -83,15 +66,6 @@ typedef struct decoder_def_s decoder_def_t; -static int output_codec_id; -static const char *output_file_format; - - - -static void output_shutdown(output_t *output); -static int output_config(output_t *output, unsigned int clockrate, unsigned int channels); - - static const decoder_def_t *decoder_find(const str *name) { for (int i = 0; i < G_N_ELEMENTS(decoders); i++) { @@ -162,73 +136,6 @@ err: } -static int output_flush(output_t *output) { - while (av_audio_fifo_size(output->fifo) >= output->frame->nb_samples) { - - if (av_audio_fifo_read(output->fifo, (void **) output->frame->data, - output->frame->nb_samples) <= 0) - abort(); - - dbg("%p output fifo pts %lu", output, (unsigned long) output->fifo_pts); - output->frame->pts = output->fifo_pts; - -#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; -#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; -#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); - - // 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); - - output->fifo_pts += output->frame->nb_samples; - output->mux_dts = output->avpkt.dts + 1; // min next expected dts - } - - return 0; -} - - -static int output_add(output_t *output, AVFrame *frame) { - if (!output) - return -1; - - dbg("%p output fifo size %u fifo_pts %lu", output, (unsigned int) av_audio_fifo_size(output->fifo), - (unsigned long) output->fifo_pts); - // fix up output pts - if (av_audio_fifo_size(output->fifo) == 0) - output->fifo_pts = frame->pts; - - if (av_audio_fifo_write(output->fifo, (void **) frame->extended_data, frame->nb_samples) < 0) - return -1; - - return output_flush(output); -} - - int decoder_input(decoder_t *dec, const str *data, unsigned long ts, output_t *output) { if (G_UNLIKELY(!dec)) return -1; @@ -269,7 +176,6 @@ int decoder_input(decoder_t *dec, const str *data, unsigned long ts, output_t *o return 0; #endif - dec->frame->pts = dec->frame->pkt_pts; dbg("%p dec frame pts %lu pkt_pts %lu", dec, (unsigned long) dec->frame->pts, (unsigned long) dec->frame->pkt_dts); @@ -281,99 +187,6 @@ int decoder_input(decoder_t *dec, const str *data, unsigned long ts, output_t *o } -output_t *output_new(const char *filename) { - output_t *ret = g_slice_alloc0(sizeof(*ret)); - if (asprintf(&ret->filename, "%s.%s", filename, output_file_format) <= 0) - abort(); - ret->clockrate = -1; - ret->channels = -1; - ret->frame = av_frame_alloc(); - return ret; -} - - -static int output_config(output_t *output, unsigned int clockrate, unsigned int channels) { - // anything to do? - if (G_UNLIKELY(output->clockrate != clockrate)) - goto format_mismatch; - if (G_UNLIKELY(output->channels != channels)) - goto format_mismatch; - - // all good - return 0; - -format_mismatch: - // XXX support reset/config change - - // copy params - output->clockrate = clockrate; - output->channels = channels; - - // XXX error reporting - output->fmtctx = avformat_alloc_context(); - if (!output->fmtctx) - goto err; - output->fmtctx->oformat = av_guess_format(output_file_format, NULL, NULL); - if (!output->fmtctx->oformat) - goto err; - - AVCodec *codec = avcodec_find_encoder(output_codec_id); - // XXX error handling - output->avst = avformat_new_stream(output->fmtctx, codec); - if (!output->avst) - goto err; -#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57, 0, 0) - output->avcctx = avcodec_alloc_context3(codec); - if (!output->avcctx) - goto err; -#else - output->avcctx = output->avst->codec; -#endif - - output->avcctx->channels = output->channels; - output->avcctx->channel_layout = av_get_default_channel_layout(output->channels); - output->avcctx->sample_rate = output->clockrate; - output->avcctx->sample_fmt = AV_SAMPLE_FMT_S16; - output->avcctx->time_base = (AVRational){output->clockrate,1}; - output->avst->time_base = output->avcctx->time_base; - -#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57, 0, 0) - avcodec_parameters_from_context(output->avst->codecpar, output->avcctx); -#endif - - int i = avcodec_open2(output->avcctx, codec, NULL); - if (i) - goto err; - i = avio_open(&output->fmtctx->pb, output->filename, AVIO_FLAG_WRITE); - if (i < 0) - goto err; - i = avformat_write_header(output->fmtctx, NULL); - if (i) - goto err; - - av_init_packet(&output->avpkt); - - // output frame and fifo - output->frame->nb_samples = output->avcctx->frame_size ? : 256; - output->frame->format = output->avcctx->sample_fmt; - output->frame->sample_rate = output->avcctx->sample_rate; - output->frame->channel_layout = output->avcctx->channel_layout; - if (!output->frame->channel_layout) - output->frame->channel_layout = av_get_default_channel_layout(output->avcctx->channels); - if (av_frame_get_buffer(output->frame, 0) < 0) - abort(); - - output->fifo = av_audio_fifo_alloc(output->avcctx->sample_fmt, output->avcctx->channels, - output->frame->nb_samples); - - return 0; - -err: - output_shutdown(output); - return -1; -} - - void decoder_close(decoder_t *dec) { if (!dec) return; @@ -381,43 +194,3 @@ void decoder_close(decoder_t *dec) { av_frame_free(&dec->frame); g_slice_free1(sizeof(*dec), dec); } - - -static void output_shutdown(output_t *output) { - if (!output) - return; - av_write_trailer(output->fmtctx); - avcodec_close(output->avcctx); - avio_closep(&output->fmtctx->pb); - avformat_free_context(output->fmtctx); - av_audio_fifo_free(output->fifo); - av_frame_free(&output->frame); - - output->avcctx = NULL; - output->fmtctx = NULL; - output->avst = NULL; - output->fifo = NULL; -} - - -void output_close(output_t *output) { - if (!output) - return; - output_shutdown(output); - free(output->filename); - g_slice_free1(sizeof(*output), output); -} - - -void output_init(const char *format) { - if (!strcmp(format, "wav")) { - output_codec_id = AV_CODEC_ID_PCM_S16LE; - output_file_format = "wav"; - } - else if (!strcmp(format, "mp3")) { - output_codec_id = AV_CODEC_ID_MP3; - output_file_format = "mp3"; - } - else - die("Unknown output format '%s'", format); -} diff --git a/recording-daemon/decoder.h b/recording-daemon/decoder.h index 126b6a42b..e2770cec5 100644 --- a/recording-daemon/decoder.h +++ b/recording-daemon/decoder.h @@ -5,14 +5,9 @@ #include "str.h" -void output_init(const char *format); - decoder_t *decoder_new(const char *payload_str); int decoder_input(decoder_t *, const str *, unsigned long ts, output_t *); void decoder_close(decoder_t *); -output_t *output_new(const char *filename); -void output_close(output_t *); - #endif diff --git a/recording-daemon/main.c b/recording-daemon/main.c index 0b0adae02..89d65aa0c 100644 --- a/recording-daemon/main.c +++ b/recording-daemon/main.c @@ -18,6 +18,7 @@ #include "loglib.h" #include "auxlib.h" #include "decoder.h" +#include "output.h" diff --git a/recording-daemon/output.c b/recording-daemon/output.c new file mode 100644 index 000000000..e037cd73f --- /dev/null +++ b/recording-daemon/output.c @@ -0,0 +1,234 @@ +#include "output.h" +#include +#include +#include +#include +#include "log.h" + + +struct output_s { + char *filename; + + // format params + int clockrate; + int channels; + + AVCodecContext *avcctx; + AVFormatContext *fmtctx; + AVStream *avst; + AVPacket avpkt; + AVAudioFifo *fifo; + int64_t fifo_pts; // pts of first data in fifo + int64_t mux_dts; // last dts passed to muxer + AVFrame *frame; +}; + + + +static int output_codec_id; +static const char *output_file_format; + + + +static void output_shutdown(output_t *output); + + + +static int output_flush(output_t *output) { + while (av_audio_fifo_size(output->fifo) >= output->frame->nb_samples) { + + if (av_audio_fifo_read(output->fifo, (void **) output->frame->data, + output->frame->nb_samples) <= 0) + abort(); + + dbg("%p output fifo pts %lu", output, (unsigned long) output->fifo_pts); + output->frame->pts = output->fifo_pts; + +#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; +#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; +#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); + + // 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); + + output->fifo_pts += output->frame->nb_samples; + output->mux_dts = output->avpkt.dts + 1; // min next expected dts + } + + return 0; +} + + +int output_add(output_t *output, AVFrame *frame) { + if (!output) + return -1; + + dbg("%p output fifo size %u fifo_pts %lu", output, (unsigned int) av_audio_fifo_size(output->fifo), + (unsigned long) output->fifo_pts); + // fix up output pts + if (av_audio_fifo_size(output->fifo) == 0) + output->fifo_pts = frame->pts; + + if (av_audio_fifo_write(output->fifo, (void **) frame->extended_data, frame->nb_samples) < 0) + return -1; + + return output_flush(output); +} + + +output_t *output_new(const char *filename) { + output_t *ret = g_slice_alloc0(sizeof(*ret)); + if (asprintf(&ret->filename, "%s.%s", filename, output_file_format) <= 0) + abort(); + ret->clockrate = -1; + ret->channels = -1; + ret->frame = av_frame_alloc(); + return ret; +} + + +int output_config(output_t *output, unsigned int clockrate, unsigned int channels) { + // anything to do? + if (G_UNLIKELY(output->clockrate != clockrate)) + goto format_mismatch; + if (G_UNLIKELY(output->channels != channels)) + goto format_mismatch; + + // all good + return 0; + +format_mismatch: + // XXX support reset/config change + + // copy params + output->clockrate = clockrate; + output->channels = channels; + + // XXX error reporting + output->fmtctx = avformat_alloc_context(); + if (!output->fmtctx) + goto err; + output->fmtctx->oformat = av_guess_format(output_file_format, NULL, NULL); + if (!output->fmtctx->oformat) + goto err; + + AVCodec *codec = avcodec_find_encoder(output_codec_id); + // XXX error handling + output->avst = avformat_new_stream(output->fmtctx, codec); + if (!output->avst) + goto err; +#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57, 0, 0) + output->avcctx = avcodec_alloc_context3(codec); + if (!output->avcctx) + goto err; +#else + output->avcctx = output->avst->codec; +#endif + + output->avcctx->channels = output->channels; + output->avcctx->channel_layout = av_get_default_channel_layout(output->channels); + output->avcctx->sample_rate = output->clockrate; + output->avcctx->sample_fmt = AV_SAMPLE_FMT_S16; + output->avcctx->time_base = (AVRational){output->clockrate,1}; + output->avst->time_base = output->avcctx->time_base; + +#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57, 0, 0) + avcodec_parameters_from_context(output->avst->codecpar, output->avcctx); +#endif + + int i = avcodec_open2(output->avcctx, codec, NULL); + if (i) + goto err; + i = avio_open(&output->fmtctx->pb, output->filename, AVIO_FLAG_WRITE); + if (i < 0) + goto err; + i = avformat_write_header(output->fmtctx, NULL); + if (i) + goto err; + + av_init_packet(&output->avpkt); + + // output frame and fifo + output->frame->nb_samples = output->avcctx->frame_size ? : 256; + output->frame->format = output->avcctx->sample_fmt; + output->frame->sample_rate = output->avcctx->sample_rate; + output->frame->channel_layout = output->avcctx->channel_layout; + if (!output->frame->channel_layout) + output->frame->channel_layout = av_get_default_channel_layout(output->avcctx->channels); + if (av_frame_get_buffer(output->frame, 0) < 0) + abort(); + + output->fifo = av_audio_fifo_alloc(output->avcctx->sample_fmt, output->avcctx->channels, + output->frame->nb_samples); + + return 0; + +err: + output_shutdown(output); + return -1; +} + + +static void output_shutdown(output_t *output) { + if (!output) + return; + av_write_trailer(output->fmtctx); + avcodec_close(output->avcctx); + avio_closep(&output->fmtctx->pb); + avformat_free_context(output->fmtctx); + av_audio_fifo_free(output->fifo); + av_frame_free(&output->frame); + + output->avcctx = NULL; + output->fmtctx = NULL; + output->avst = NULL; + output->fifo = NULL; +} + + +void output_close(output_t *output) { + if (!output) + return; + output_shutdown(output); + free(output->filename); + g_slice_free1(sizeof(*output), output); +} + + +void output_init(const char *format) { + if (!strcmp(format, "wav")) { + output_codec_id = AV_CODEC_ID_PCM_S16LE; + output_file_format = "wav"; + } + else if (!strcmp(format, "mp3")) { + output_codec_id = AV_CODEC_ID_MP3; + output_file_format = "mp3"; + } + else + die("Unknown output format '%s'", format); +} diff --git a/recording-daemon/output.h b/recording-daemon/output.h new file mode 100644 index 000000000..694a387f4 --- /dev/null +++ b/recording-daemon/output.h @@ -0,0 +1,17 @@ +#ifndef _OUTPUT_H_ +#define _OUTPUT_H_ + +#include "types.h" +#include + + +void output_init(const char *format); + +output_t *output_new(const char *filename); +void output_close(output_t *); + +int output_config(output_t *output, unsigned int clockrate, unsigned int channels); +int output_add(output_t *output, AVFrame *frame); + + +#endif diff --git a/recording-daemon/packet.c b/recording-daemon/packet.c index 8f98595f7..1d1b4eba3 100644 --- a/recording-daemon/packet.c +++ b/recording-daemon/packet.c @@ -11,6 +11,7 @@ #include "decoder.h" #include "rtcplib.h" #include "main.h" +#include "output.h" static int ptr_cmp(const void *a, const void *b, void *dummy) {