diff --git a/daemon/.gitignore b/daemon/.gitignore index 5efb5c54b..91ee68b88 100644 --- a/daemon/.gitignore +++ b/daemon/.gitignore @@ -8,3 +8,4 @@ auxlib.c loglib.c rtplib.c codeclib.c +resample.c diff --git a/daemon/Makefile b/daemon/Makefile index bf11749fa..199820732 100644 --- a/daemon/Makefile +++ b/daemon/Makefile @@ -19,6 +19,11 @@ CFLAGS+= `pkg-config --cflags libiptc` endif CFLAGS+= -I. -I../kernel-module/ -I../lib/ CFLAGS+= -D_GNU_SOURCE +CFLAGS+= `pkg-config --cflags libavcodec` +CFLAGS+= `pkg-config --cflags libavformat` +CFLAGS+= `pkg-config --cflags libavutil` +CFLAGS+= `pkg-config --cflags libavresample` +CFLAGS+= `pkg-config --cflags libavfilter` CFLAGS+= -DRE_PLUGIN_DIR="\"/usr/lib/rtpengine\"" @@ -49,6 +54,11 @@ LDFLAGS+= `pkg-config --libs json-glib-1.0` ifeq ($(with_iptables_option),yes) LDFLAGS+= `pkg-config --libs libiptc` endif +LDFLAGS+= `pkg-config --libs libavcodec` +LDFLAGS+= `pkg-config --libs libavformat` +LDFLAGS+= `pkg-config --libs libavutil` +LDFLAGS+= `pkg-config --libs libavresample` +LDFLAGS+= `pkg-config --libs libavfilter` include ../lib/lib.Makefile @@ -57,7 +67,7 @@ SRCS= main.c kernel.c poller.c aux.c control_tcp.c streambuf.c call.c control_u crypto.c rtp.c call_interfaces.c dtls.c log.c cli.c graphite.c ice.c socket.c \ media_socket.c homer.c recording.c statistics.c cdr.c ssrc.c iptables.c tcp_listener.c \ codec.c -LIBSRCS= loglib.c auxlib.c rtplib.c codeclib.c +LIBSRCS= loglib.c auxlib.c rtplib.c codeclib.c resample.c OBJS= $(SRCS:.c=.o) $(LIBSRCS:.c=.o) diff --git a/daemon/call.h b/daemon/call.h index 4bddf6c2c..1a5c778a8 100644 --- a/daemon/call.h +++ b/daemon/call.h @@ -162,6 +162,7 @@ enum call_type { #define MEDIA_FLAG_ICE_LITE SHARED_FLAG_ICE_LITE #define MEDIA_FLAG_ICE_CONTROLLING 0x00200000 #define MEDIA_FLAG_LOOP_CHECK 0x00400000 +#define MEDIA_FLAG_TRANSCODE 0x00800000 /* access macros */ #define SP_ISSET(p, f) bf_isset(&(p)->sp_flags, SP_FLAG_ ## f) diff --git a/daemon/codec.c b/daemon/codec.c index 3c5639c2d..c368260d5 100644 --- a/daemon/codec.c +++ b/daemon/codec.c @@ -1,5 +1,6 @@ #include "codec.h" #include +#include #include "call.h" #include "log.h" #include "rtplib.h" @@ -9,6 +10,7 @@ static codec_handler_func handler_func_stub; +static codec_handler_func handler_func_transcode; static struct codec_handler codec_handler_stub = { @@ -18,21 +20,53 @@ static struct codec_handler codec_handler_stub = { +static void __handler_shutdown(struct codec_handler *handler) { + if (handler->decoder) + decoder_close(handler->decoder); + handler->decoder = NULL; +} + static void __make_stub(struct codec_handler *handler) { + __handler_shutdown(handler); handler->func = handler_func_stub; } static void __codec_handler_free(void *pp) { struct codec_handler *h = pp; + __handler_shutdown(h); g_slice_free1(sizeof(*h), h); } +static void __make_transcoder(struct codec_handler *handler, struct rtp_payload_type *source, + struct rtp_payload_type *dest) +{ + assert(source->codec_def != NULL); + assert(dest->codec_def != NULL); + + __handler_shutdown(handler); + + handler->func = handler_func_transcode; + handler->decoder = decoder_new_fmt(source->codec_def, source->clock_rate, 1, 0); + if (!handler->decoder) + goto err; + + ilog(LOG_DEBUG, "Created transcode context for '" STR_FORMAT "' -> '" STR_FORMAT "'", + STR_FMT(&source->encoding), STR_FMT(&dest->encoding)); + + return; + +err: + __make_stub(handler); +} + // call must be locked in W void codec_handlers_update(struct call_media *receiver, struct call_media *sink) { if (!receiver->codec_handlers) receiver->codec_handlers = g_hash_table_new_full(g_int_hash, g_int_equal, NULL, __codec_handler_free); + MEDIA_CLEAR(receiver, TRANSCODE); + // we go through the list of codecs that the receiver supports and compare it // with the list of codecs supported by the sink. if the receiver supports // a codec that the sink doesn't support, we must transcode. @@ -42,7 +76,10 @@ void codec_handlers_update(struct call_media *receiver, struct call_media *sink) struct rtp_payload_type *pref_dest_codec = NULL; for (GList *l = sink->codecs_prefs_send.head; l; l = l->next) { struct rtp_payload_type *pt = l->data; - // XXX if supported ... + if (!pt->codec_def) + pt->codec_def = codec_find(&pt->encoding); + if (!pt->codec_def) // not supported, next + continue; ilog(LOG_DEBUG, "Default sink codec is " STR_FORMAT, STR_FMT(&pt->encoding)); pref_dest_codec = pt; break; @@ -74,14 +111,16 @@ void codec_handlers_update(struct call_media *receiver, struct call_media *sink) if (g_hash_table_lookup(sink->codec_names, &pt->encoding)) { // the sink supports this codec. forward without transcoding. + // XXX check format parameters as well ilog(LOG_DEBUG, "Sink supports codec " STR_FORMAT, STR_FMT(&pt->encoding)); __make_stub(handler); continue; } - // the sink does not support this codec XXX do something + // the sink does not support this codec -> transcode ilog(LOG_DEBUG, "Sink does not support codec " STR_FORMAT, STR_FMT(&pt->encoding)); - __make_stub(handler); + MEDIA_SET(receiver, TRANSCODE); + __make_transcoder(handler, pt, pref_dest_codec); } } @@ -122,6 +161,9 @@ static int handler_func_stub(struct codec_handler *h, struct call_media *media, g_queue_push_tail(out, p); return 0; } +static int handler_func_transcode(struct codec_handler *h, struct call_media *media, const str *s, GQueue *out) { + return 0; +} void codec_packet_free(void *pp) { struct codec_packet *p = pp; diff --git a/daemon/codec.h b/daemon/codec.h index caa3e1e54..d7f18e899 100644 --- a/daemon/codec.h +++ b/daemon/codec.h @@ -4,6 +4,7 @@ #include #include "str.h" +#include "codeclib.h" struct call_media; @@ -16,6 +17,7 @@ typedef int codec_handler_func(struct codec_handler *, struct call_media *, cons struct codec_handler { int rtp_payload_type; codec_handler_func *func; + decoder_t *decoder; }; struct codec_packet { diff --git a/daemon/main.c b/daemon/main.c index 5fd75113c..06e6088b5 100644 --- a/daemon/main.c +++ b/daemon/main.c @@ -38,6 +38,7 @@ #include "iptables.h" #include "statistics.h" #include "graphite.h" +#include "codeclib.h" @@ -555,6 +556,7 @@ static void init_everything() { if (call_interfaces_init()) abort(); statistics_init(); + codeclib_init(); } diff --git a/lib/.ycm_extra_conf.py b/lib/.ycm_extra_conf.py new file mode 100644 index 000000000..d60552bbf --- /dev/null +++ b/lib/.ycm_extra_conf.py @@ -0,0 +1,106 @@ +import os +import ycm_core +from clang_helpers import PrepareClangFlags + +# Set this to the absolute path to the folder (NOT the file!) containing the +# compile_commands.json file to use that instead of 'flags'. See here for +# more details: http://clang.llvm.org/docs/JSONCompilationDatabase.html +# Most projects will NOT need to set this to anything; you can just change the +# 'flags' list of compilation flags. Notice that YCM itself uses that approach. +compilation_database_folder = '' + +# These are the compilation flags that will be used in case there's no +# compilation database set. +flags = [ + '-g', + '-Wall', + '-pthread', + '-fno-strict-aliasing', + '-I/usr/include/glib-2.0', + '-I/usr/lib/x86_64-linux-gnu/glib-2.0/include', + '-pthread', + '-D_GNU_SOURCE', + '-D__DEBUG=1', + '-D__YCM=1', + '-DRTPENGINE_VERSION="dummy"', + '-DRE_PLUGIN_DIR="/usr/lib/rtpengine"', + '-DWITH_IPTABLES_OPTION', + '-O2', + '-fstack-protector', + '--param=ssp-buffer-size=4', + '-Wformat', + '-Werror=format-security', + '-D_FORTIFY_SOURCE=2', + # THIS IS IMPORTANT! Without a "-std=" flag, clang won't + # know which language to use when compiling headers. So it will guess. + # Badly. So C++ headers will be compiled as C headers. + # You don't want that so ALWAYS specify + # a "-std=". + # For a C project, you would set this to something like 'c99' instead of + # 'c++11'. + '-std=c99', + # ...and the same thing goes for the magic -x option which specifies the + # language that the files to be compiled are written in. This is mostly + # relevant for c++ headers. + # For a C project, you would set this to 'c' instead of 'c++'. + '-x', + 'c', +] + +if compilation_database_folder: + database = ycm_core.CompilationDatabase(compilation_database_folder) +else: + database = None + + +def DirectoryOfThisScript(): + return os.path.dirname(os.path.abspath(__file__)) + + +def MakeRelativePathsInFlagsAbsolute(flags, working_directory): + if not working_directory: + return flags + new_flags = [] + make_next_absolute = False + path_flags = ['-isystem', '-I', '-iquote', '--sysroot='] + for flag in flags: + new_flag = flag + + if make_next_absolute: + make_next_absolute = False + if not flag.startswith('/'): + new_flag = os.path.join(working_directory, flag) + + for path_flag in path_flags: + if flag == path_flag: + make_next_absolute = True + break + + if flag.startswith(path_flag): + path = flag[len(path_flag):] + new_flag = path_flag + os.path.join(working_directory, path) + break + + if new_flag: + new_flags.append(new_flag) + return new_flags + + +def FlagsForFile(filename): + if database: + # Bear in mind that compilation_info.compiler_flags_ does NOT return a + # python list, but a "list-like" StringVec object + compilation_info = database.GetCompilationInfoForFile(filename) + final_flags = PrepareClangFlags( + MakeRelativePathsInFlagsAbsolute( + compilation_info.compiler_flags_, + compilation_info.compiler_working_dir_), + filename) + else: + relative_to = DirectoryOfThisScript() + final_flags = MakeRelativePathsInFlagsAbsolute(flags, relative_to) + + return { + 'flags': final_flags, + 'do_cache': True + } diff --git a/lib/codeclib.c b/lib/codeclib.c index 1b6d2c8a7..a812b7d5b 100644 --- a/lib/codeclib.c +++ b/lib/codeclib.c @@ -1,7 +1,22 @@ #include "codeclib.h" #include +#include +#include #include #include "str.h" +#include "log.h" +#include "resample.h" + + + + +#ifndef dbg +#ifdef __DEBUG +#define dbg(x...) ilog(LOG_DEBUG, x) +#else +#define dbg(x...) ((void)0) +#endif +#endif @@ -51,3 +66,203 @@ const codec_def_t *codec_find(const str *name) { } return NULL; } + + + +decoder_t *decoder_new_fmt(const codec_def_t *def, int clockrate, int channels, int resample) { + const char *err = NULL; + + clockrate *= def->clockrate_mult; + + decoder_t *ret = g_slice_alloc0(sizeof(*ret)); + + format_init(&ret->in_format); + ret->in_format.channels = channels; + ret->in_format.clockrate = clockrate; + // output defaults to same as input + ret->out_format = ret->in_format; + if (resample) + ret->out_format.clockrate = resample; + // sample format to be determined later when decoded frames arrive + + AVCodec *codec = NULL; + if (def->avcodec_name) + codec = avcodec_find_decoder_by_name(def->avcodec_name); + if (!codec) + codec = avcodec_find_decoder(def->avcodec_id); + if (!codec) { + ilog(LOG_WARN, "Codec '%s' not supported", def->rtpname); + goto err; + } + + ret->avcctx = avcodec_alloc_context3(codec); + err = "failed to alloc codec context"; + if (!ret->avcctx) + goto err; + ret->avcctx->channels = channels; + ret->avcctx->sample_rate = clockrate; + err = "failed to open codec context"; + int i = avcodec_open2(ret->avcctx, codec, NULL); + if (i) + goto err; + + for (const enum AVSampleFormat *sfmt = codec->sample_fmts; sfmt && *sfmt != -1; sfmt++) + dbg("supported sample format for input codec %s: %s", + codec->name, av_get_sample_fmt_name(*sfmt)); + + av_init_packet(&ret->avpkt); + + ret->pts = (uint64_t) -1LL; + ret->rtp_ts = (unsigned long) -1L; + ret->mixer_idx = (unsigned int) -1; + + return ret; + +err: + decoder_close(ret); + if (err) + ilog(LOG_ERR, "Error creating media decoder: %s", err); + return NULL; +} + + +void decoder_close(decoder_t *dec) { + if (!dec) + return; + /// XXX drain inputs and outputs +#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(56, 1, 0) + avcodec_free_context(&dec->avcctx); +#else + avcodec_close(dec->avcctx); + av_free(dec->avcctx); +#endif + resample_shutdown(&dec->mix_resample); + resample_shutdown(&dec->output_resample); + g_slice_free1(sizeof(*dec), dec); +} + +int decoder_input_data(decoder_t *dec, const str *data, unsigned long ts, + int (*callback)(decoder_t *, AVFrame *, void *u1, void *u2), void *u1, void *u2) +{ + const char *err; + + if (G_UNLIKELY(!dec)) + return -1; + + dbg("%p dec pts %llu rtp_ts %llu incoming ts %lu", dec, (unsigned long long) dec->pts, + (unsigned long long) dec->rtp_ts, (unsigned long) ts); + + if (G_UNLIKELY(dec->rtp_ts == (unsigned long) -1L)) { + // initialize pts + dec->pts = 0; + } + else { + // shift pts according to rtp ts shift + dec->pts += (ts - dec->rtp_ts); + // XXX handle lost packets here if timestamps don't line up? + } + dec->rtp_ts = ts; + + dec->avpkt.data = (unsigned char *) data->s; + dec->avpkt.size = data->len; + dec->avpkt.pts = dec->pts; + + AVFrame *frame = NULL; + + // loop until all input is consumed and all available output has been processed + int keep_going; + do { + keep_going = 0; + int got_frame = 0; + err = "failed to alloc av frame"; + frame = av_frame_alloc(); + if (!frame) + goto err; + +#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57, 36, 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; + } + } + + int ret = avcodec_receive_frame(dec->avcctx, 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; + } +#else + // only do this if we have any input left + if (dec->avpkt.size == 0) + break; + + int ret = avcodec_decode_audio4(dec->avcctx, 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 + + if (got_frame) { + dbg("raw frame from decoder pts %llu samples %u", (unsigned long long) frame->pts, frame->nb_samples); + +#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(57, 36, 0) + frame->pts = frame->pkt_pts; +#endif + if (G_UNLIKELY(frame->pts == AV_NOPTS_VALUE)) + frame->pts = dec->avpkt.pts; + dec->avpkt.pts += frame->nb_samples; + + if (callback(dec, frame, u1, u2)) + return -1; + frame = NULL; + } + } while (keep_going); + + av_frame_free(&frame); + return 0; + +err: + ilog(LOG_ERR, "Error decoding media packet: %s", err); + av_frame_free(&frame); + return -1; +} + + +void codeclib_init() { + av_register_all(); + avcodec_register_all(); + avfilter_register_all(); + avformat_network_init(); +} diff --git a/lib/codeclib.h b/lib/codeclib.h index d4a7611f0..632e9be62 100644 --- a/lib/codeclib.h +++ b/lib/codeclib.h @@ -2,20 +2,85 @@ #define __CODECLIB_H__ +#include +#include #include "str.h" +struct codec_def_s; +struct decoder_s; +struct format_s; +struct resample_s; + +typedef struct codec_def_s codec_def_t; +typedef struct decoder_s decoder_t; +typedef struct format_s format_t; +typedef struct resample_s resample_t; + + + struct codec_def_s { const char *rtpname; int clockrate_mult; int avcodec_id; const char *avcodec_name; }; -typedef struct codec_def_s codec_def_t; + +struct format_s { + int clockrate; + int channels; + int format; // enum AVSampleFormat +}; + +struct resample_s { + AVAudioResampleContext *avresample; +}; + +struct decoder_s { + format_t in_format, + out_format; + + resample_t mix_resample, + output_resample; + + AVCodecContext *avcctx; + AVPacket avpkt; + unsigned long rtp_ts; + uint64_t pts; + + unsigned int mixer_idx; +}; + + + +void codeclib_init(void); const codec_def_t *codec_find(const str *name); +decoder_t *decoder_new_fmt(const codec_def_t *def, int clockrate, int channels, int resample); +void decoder_close(decoder_t *dec); +int decoder_input_data(decoder_t *dec, const str *data, unsigned long ts, + int (*callback)(decoder_t *, AVFrame *, void *u1, void *u2), void *u1, void *u2); + + + +INLINE int format_eq(const format_t *a, const format_t *b) { + if (G_UNLIKELY(a->clockrate != b->clockrate)) + return 0; + if (G_UNLIKELY(a->channels != b->channels)) + return 0; + if (G_UNLIKELY(a->format != b->format)) + return 0; + return 1; +} +INLINE void format_init(format_t *f) { + f->clockrate = -1; + f->channels = -1; + f->format = -1; +} + + #endif diff --git a/recording-daemon/resample.c b/lib/resample.c similarity index 99% rename from recording-daemon/resample.c rename to lib/resample.c index df9228cb4..d38795d12 100644 --- a/recording-daemon/resample.c +++ b/lib/resample.c @@ -10,7 +10,7 @@ #include #include #include "log.h" -#include "types.h" +#include "codeclib.h" diff --git a/recording-daemon/resample.h b/lib/resample.h similarity index 90% rename from recording-daemon/resample.h rename to lib/resample.h index ba8f155d0..c0e428fa2 100644 --- a/recording-daemon/resample.h +++ b/lib/resample.h @@ -2,7 +2,7 @@ #define _RESAMPLE_H_ -#include "types.h" +#include "codeclib.h" #include diff --git a/recording-daemon/.gitignore b/recording-daemon/.gitignore index 959c66119..dccf6fc2b 100644 --- a/recording-daemon/.gitignore +++ b/recording-daemon/.gitignore @@ -8,3 +8,4 @@ auxlib.c loglib.c rtplib.c codeclib.c +resample.c diff --git a/recording-daemon/Makefile b/recording-daemon/Makefile index 27e8e9fa3..15f25061d 100644 --- a/recording-daemon/Makefile +++ b/recording-daemon/Makefile @@ -25,8 +25,8 @@ LDFLAGS+= `mysql_config --libs` include ../lib/lib.Makefile SRCS= epoll.c garbage.c inotify.c main.c metafile.c stream.c recaux.c packet.c \ - decoder.c output.c mix.c resample.c db.c log.c forward.c -LIBSRCS= loglib.c auxlib.c rtplib.c codeclib.c + decoder.c output.c mix.c db.c log.c forward.c +LIBSRCS= loglib.c auxlib.c rtplib.c codeclib.c resample.c OBJS= $(SRCS:.c=.o) $(LIBSRCS:.c=.o) diff --git a/recording-daemon/decoder.c b/recording-daemon/decoder.c index 9a409999c..83f76a64c 100644 --- a/recording-daemon/decoder.c +++ b/recording-daemon/decoder.c @@ -18,31 +18,11 @@ #include "codeclib.h" -struct decoder_s { - format_t in_format, - out_format; - - resample_t mix_resample, - output_resample; - - AVCodecContext *avcctx; - AVPacket avpkt; - unsigned long rtp_ts; - uint64_t pts; - - unsigned int mixer_idx; -}; - - - - int resample_audio; decoder_t *decoder_new(const char *payload_str) { - const char *err = NULL; - str name; char *slash = strchr(payload_str, '/'); if (!slash) { @@ -68,58 +48,14 @@ decoder_t *decoder_new(const char *payload_str) { } clockrate *= def->clockrate_mult; - decoder_t *ret = g_slice_alloc0(sizeof(*ret)); - - format_init(&ret->in_format); - ret->in_format.channels = channels; - ret->in_format.clockrate = clockrate; - // output defaults to same as input - ret->out_format = ret->in_format; - if (resample_audio) - ret->out_format.clockrate = resample_audio; - // sample format to be determined later when decoded frames arrive - - AVCodec *codec = NULL; - if (def->avcodec_name) - codec = avcodec_find_decoder_by_name(def->avcodec_name); - if (!codec) - codec = avcodec_find_decoder(def->avcodec_id); - if (!codec) { - ilog(LOG_WARN, "Codec '%s' not supported", def->rtpname); - goto err; - } - - ret->avcctx = avcodec_alloc_context3(codec); - err = "failed to alloc codec context"; - if (!ret->avcctx) - goto err; - ret->avcctx->channels = channels; - ret->avcctx->sample_rate = clockrate; - err = "failed to open codec context"; - int i = avcodec_open2(ret->avcctx, codec, NULL); - if (i) - goto err; - - for (const enum AVSampleFormat *sfmt = codec->sample_fmts; sfmt && *sfmt != -1; sfmt++) - dbg("supported sample format for input codec %s: %s", codec->name, av_get_sample_fmt_name(*sfmt)); - - av_init_packet(&ret->avpkt); - - ret->pts = (uint64_t) -1LL; - ret->rtp_ts = (unsigned long) -1L; - ret->mixer_idx = (unsigned int) -1; - - return ret; - -err: - decoder_close(ret); - if (err) - ilog(LOG_ERR, "Error creating media decoder: %s", err); - return NULL; + return decoder_new_fmt(def, clockrate, channels, resample_audio); } -static int decoder_got_frame(decoder_t *dec, output_t *output, metafile_t *metafile, AVFrame *frame) { +static int decoder_got_frame(decoder_t *dec, AVFrame *frame, void *op, void *mp) { + metafile_t *metafile = mp; + output_t *output = op; + dbg("got frame pts %llu samples %u contents %02x%02x%02x%02x...", (unsigned long long) frame->pts, frame->nb_samples, (unsigned int) frame->extended_data[0][0], (unsigned int) frame->extended_data[0][1], @@ -173,133 +109,5 @@ err: int decoder_input(decoder_t *dec, const str *data, unsigned long ts, output_t *output, metafile_t *metafile) { - const char *err; - - if (G_UNLIKELY(!dec)) - return -1; - - dbg("%p dec pts %llu rtp_ts %llu incoming ts %lu", dec, (unsigned long long) dec->pts, - (unsigned long long) dec->rtp_ts, (unsigned long) ts); - - if (G_UNLIKELY(dec->rtp_ts == (unsigned long) -1L)) { - // initialize pts - dec->pts = 0; - } - else { - // shift pts according to rtp ts shift - dec->pts += (ts - dec->rtp_ts); - // XXX handle lost packets here if timestamps don't line up? - } - dec->rtp_ts = ts; - - dec->avpkt.data = (unsigned char *) data->s; - dec->avpkt.size = data->len; - dec->avpkt.pts = dec->pts; - - AVFrame *frame = NULL; - - // loop until all input is consumed and all available output has been processed - int keep_going; - do { - keep_going = 0; - int got_frame = 0; - err = "failed to alloc av frame"; - frame = av_frame_alloc(); - if (!frame) - goto err; - -#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57, 36, 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; - } - } - - int ret = avcodec_receive_frame(dec->avcctx, 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; - } -#else - // only do this if we have any input left - if (dec->avpkt.size == 0) - break; - - int ret = avcodec_decode_audio4(dec->avcctx, 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 - - if (got_frame) { - dbg("raw frame from decoder pts %llu samples %u", (unsigned long long) frame->pts, frame->nb_samples); - -#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(57, 36, 0) - frame->pts = frame->pkt_pts; -#endif - if (G_UNLIKELY(frame->pts == AV_NOPTS_VALUE)) - frame->pts = dec->avpkt.pts; - dec->avpkt.pts += frame->nb_samples; - - if (decoder_got_frame(dec, output, metafile, frame)) - return -1; - frame = NULL; - } - } while (keep_going); - - av_frame_free(&frame); - return 0; - -err: - ilog(LOG_ERR, "Error decoding media packet: %s", err); - av_frame_free(&frame); - return -1; -} - - -void decoder_close(decoder_t *dec) { - if (!dec) - return; - /// XXX drain inputs and outputs -#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(56, 1, 0) - avcodec_free_context(&dec->avcctx); -#else - avcodec_close(dec->avcctx); - av_free(dec->avcctx); -#endif - resample_shutdown(&dec->mix_resample); - resample_shutdown(&dec->output_resample); - g_slice_free1(sizeof(*dec), dec); + return decoder_input_data(dec, data, ts, decoder_got_frame, output, metafile); } diff --git a/recording-daemon/decoder.h b/recording-daemon/decoder.h index 8eb659dcf..07a82a4c8 100644 --- a/recording-daemon/decoder.h +++ b/recording-daemon/decoder.h @@ -10,7 +10,6 @@ extern int resample_audio; decoder_t *decoder_new(const char *payload_str); int decoder_input(decoder_t *, const str *, unsigned long ts, output_t *, metafile_t *); -void decoder_close(decoder_t *); #endif diff --git a/recording-daemon/main.c b/recording-daemon/main.c index 55bfd610d..290e7f333 100644 --- a/recording-daemon/main.c +++ b/recording-daemon/main.c @@ -23,6 +23,7 @@ #include "decoder.h" #include "output.h" #include "forward.h" +#include "codeclib.h" @@ -96,10 +97,7 @@ static void avlog_ilog(void *ptr, int loglevel, const char *fmt, va_list ap) { static void setup(void) { log_init("rtpengine-recording"); if (output_enabled) { - av_register_all(); - avcodec_register_all(); - avfilter_register_all(); - avformat_network_init(); + codeclib_init(); av_log_set_callback(avlog_ilog); output_init(output_format); if (!g_file_test(output_dir, G_FILE_TEST_IS_DIR)) { diff --git a/recording-daemon/types.h b/recording-daemon/types.h index 364d31a7b..bf4deb469 100644 --- a/recording-daemon/types.h +++ b/recording-daemon/types.h @@ -6,12 +6,12 @@ #include #include #include -#include #include #include #include #include #include "str.h" +#include "codeclib.h" struct iphdr; @@ -24,16 +24,10 @@ struct handler_s; typedef struct handler_s handler_t; struct metafile_s; typedef struct metafile_s metafile_t; -struct decoder_s; -typedef struct decoder_s decoder_t; struct output_s; typedef struct output_s output_t; struct mix_s; typedef struct mix_s mix_t; -struct resample_s; -typedef struct resample_s resample_t; -struct format_s; -typedef struct format_s format_t; typedef void handler_func(handler_t *); @@ -110,18 +104,6 @@ struct metafile_s { }; -struct resample_s { - AVAudioResampleContext *avresample; -}; - - -struct format_s { - int clockrate; - int channels; - int format; // enum AVSampleFormat -}; - - struct output_s { char full_filename[PATH_MAX], // path + filename file_path[PATH_MAX], @@ -143,20 +125,5 @@ struct output_s { }; -INLINE int format_eq(const format_t *a, const format_t *b) { - if (G_UNLIKELY(a->clockrate != b->clockrate)) - return 0; - if (G_UNLIKELY(a->channels != b->channels)) - return 0; - if (G_UNLIKELY(a->format != b->format)) - return 0; - return 1; -} -INLINE void format_init(format_t *f) { - f->clockrate = -1; - f->channels = -1; - f->format = -1; -} - #endif