Browse Source

TT#31197 separate out libavcodec specifics

Change-Id: I4f389226320fd8a7930168a4fb885ba214759743
changes/96/18996/8
Richard Fuchs 8 years ago
parent
commit
83784d492c
4 changed files with 415 additions and 283 deletions
  1. +1
    -1
      daemon/codec.c
  2. +375
    -274
      lib/codeclib.c
  3. +36
    -5
      lib/codeclib.h
  4. +3
    -3
      recording-daemon/output.c

+ 1
- 1
daemon/codec.c View File

@ -747,7 +747,7 @@ static struct rtp_payload_type *codec_make_payload_type(const str *codec_str, st
const codec_def_t *dec = codec_find(&codec, 0); const codec_def_t *dec = codec_find(&codec, 0);
if (!dec) if (!dec)
return NULL; return NULL;
if (media->type_id && dec->type != media->type_id)
if (media->type_id && dec->media_type != media->type_id)
return (void *) 0x1; return (void *) 0x1;
// we must support both encoding and decoding // we must support both encoding and decoding
if (!dec->encoder) if (!dec->encoder)


+ 375
- 274
lib/codeclib.c View File

@ -34,6 +34,27 @@ static packetizer_f packetizer_samplestream; // flat stream of samples
static format_init_f opus_init; static format_init_f opus_init;
static set_options_f opus_set_options; static set_options_f opus_set_options;
static void avc_def_init(codec_def_t *, int);
static const char *avc_decoder_init(decoder_t *);
static int avc_decoder_input(decoder_t *dec, const str *data, GQueue *out);
static void avc_decoder_close(decoder_t *);
static const char *avc_encoder_init(encoder_t *enc);
static int avc_encoder_input(encoder_t *enc, AVFrame **frame);
static void avc_encoder_close(encoder_t *enc);
static const codec_type_t codec_type_avcodec = {
.def_init = avc_def_init,
.decoder_init = avc_decoder_init,
.decoder_input = avc_decoder_input,
.decoder_close = avc_decoder_close,
.encoder_init = avc_encoder_init,
.encoder_input = avc_encoder_input,
.encoder_close = avc_encoder_close,
};
static codec_def_t __codec_defs[] = { static codec_def_t __codec_defs[] = {
@ -46,7 +67,8 @@ static codec_def_t __codec_defs[] = {
.default_ptime = 20, .default_ptime = 20,
.packetizer = packetizer_samplestream, .packetizer = packetizer_samplestream,
.bits_per_sample = 8, .bits_per_sample = 8,
.type = MT_AUDIO,
.media_type = MT_AUDIO,
.codec_type = &codec_type_avcodec,
}, },
{ {
.rtpname = "PCMU", .rtpname = "PCMU",
@ -57,7 +79,8 @@ static codec_def_t __codec_defs[] = {
.default_ptime = 20, .default_ptime = 20,
.packetizer = packetizer_samplestream, .packetizer = packetizer_samplestream,
.bits_per_sample = 8, .bits_per_sample = 8,
.type = MT_AUDIO,
.media_type = MT_AUDIO,
.codec_type = &codec_type_avcodec,
}, },
{ {
.rtpname = "G723", .rtpname = "G723",
@ -68,7 +91,8 @@ static codec_def_t __codec_defs[] = {
.default_ptime = 30, .default_ptime = 30,
.default_bitrate = 6300, .default_bitrate = 6300,
.packetizer = packetizer_passthrough, .packetizer = packetizer_passthrough,
.type = MT_AUDIO,
.media_type = MT_AUDIO,
.codec_type = &codec_type_avcodec,
}, },
{ {
.rtpname = "G722", .rtpname = "G722",
@ -79,7 +103,8 @@ static codec_def_t __codec_defs[] = {
.default_ptime = 20, .default_ptime = 20,
.packetizer = packetizer_samplestream, .packetizer = packetizer_samplestream,
.bits_per_sample = 8, .bits_per_sample = 8,
.type = MT_AUDIO,
.media_type = MT_AUDIO,
.codec_type = &codec_type_avcodec,
}, },
{ {
.rtpname = "QCELP", .rtpname = "QCELP",
@ -87,7 +112,8 @@ static codec_def_t __codec_defs[] = {
.clockrate_mult = 1, .clockrate_mult = 1,
.default_ptime = 20, .default_ptime = 20,
.packetizer = packetizer_passthrough, .packetizer = packetizer_passthrough,
.type = MT_AUDIO,
.media_type = MT_AUDIO,
.codec_type = &codec_type_avcodec,
}, },
{ {
.rtpname = "G729", .rtpname = "G729",
@ -97,7 +123,8 @@ static codec_def_t __codec_defs[] = {
.default_channels = 1, .default_channels = 1,
.default_ptime = 20, .default_ptime = 20,
.packetizer = packetizer_passthrough, .packetizer = packetizer_passthrough,
.type = MT_AUDIO,
.media_type = MT_AUDIO,
.codec_type = &codec_type_avcodec,
}, },
{ {
.rtpname = "speex", .rtpname = "speex",
@ -107,7 +134,8 @@ static codec_def_t __codec_defs[] = {
.default_bitrate = 11000, .default_bitrate = 11000,
.default_ptime = 20, .default_ptime = 20,
.packetizer = packetizer_passthrough, .packetizer = packetizer_passthrough,
.type = MT_AUDIO,
.media_type = MT_AUDIO,
.codec_type = &codec_type_avcodec,
}, },
{ {
.rtpname = "GSM", .rtpname = "GSM",
@ -117,7 +145,8 @@ static codec_def_t __codec_defs[] = {
//.default_bitrate = 13200, //.default_bitrate = 13200,
.default_ptime = 20, .default_ptime = 20,
.packetizer = packetizer_passthrough, .packetizer = packetizer_passthrough,
.type = MT_AUDIO,
.media_type = MT_AUDIO,
.codec_type = &codec_type_avcodec,
}, },
{ {
.rtpname = "iLBC", .rtpname = "iLBC",
@ -127,7 +156,8 @@ static codec_def_t __codec_defs[] = {
.default_ptime = 20, .default_ptime = 20,
//.default_bitrate = 15200, //.default_bitrate = 15200,
.packetizer = packetizer_passthrough, .packetizer = packetizer_passthrough,
.type = MT_AUDIO,
.media_type = MT_AUDIO,
.codec_type = &codec_type_avcodec,
}, },
{ {
.rtpname = "opus", .rtpname = "opus",
@ -138,7 +168,8 @@ static codec_def_t __codec_defs[] = {
.default_bitrate = 32000, .default_bitrate = 32000,
.default_ptime = 20, .default_ptime = 20,
.packetizer = packetizer_passthrough, .packetizer = packetizer_passthrough,
.type = MT_AUDIO,
.media_type = MT_AUDIO,
.codec_type = &codec_type_avcodec,
.init = opus_init, .init = opus_init,
.set_options = opus_set_options, .set_options = opus_set_options,
}, },
@ -148,35 +179,40 @@ static codec_def_t __codec_defs[] = {
.avcodec_name = "libvorbis", .avcodec_name = "libvorbis",
.default_ptime = 20, .default_ptime = 20,
.packetizer = packetizer_passthrough, .packetizer = packetizer_passthrough,
.type = MT_AUDIO,
.media_type = MT_AUDIO,
.codec_type = &codec_type_avcodec,
}, },
{ {
.rtpname = "ac3", .rtpname = "ac3",
.avcodec_id = AV_CODEC_ID_AC3, .avcodec_id = AV_CODEC_ID_AC3,
.default_ptime = 20, .default_ptime = 20,
.packetizer = packetizer_passthrough, .packetizer = packetizer_passthrough,
.type = MT_AUDIO,
.media_type = MT_AUDIO,
.codec_type = &codec_type_avcodec,
}, },
{ {
.rtpname = "eac3", .rtpname = "eac3",
.avcodec_id = AV_CODEC_ID_EAC3, .avcodec_id = AV_CODEC_ID_EAC3,
.default_ptime = 20, .default_ptime = 20,
.packetizer = packetizer_passthrough, .packetizer = packetizer_passthrough,
.type = MT_AUDIO,
.media_type = MT_AUDIO,
.codec_type = &codec_type_avcodec,
}, },
{ {
.rtpname = "ATRAC3", .rtpname = "ATRAC3",
.avcodec_id = AV_CODEC_ID_ATRAC3, .avcodec_id = AV_CODEC_ID_ATRAC3,
.default_ptime = 20, .default_ptime = 20,
.packetizer = packetizer_passthrough, .packetizer = packetizer_passthrough,
.type = MT_AUDIO,
.media_type = MT_AUDIO,
.codec_type = &codec_type_avcodec,
}, },
{ {
.rtpname = "ATRAC-X", .rtpname = "ATRAC-X",
.avcodec_id = AV_CODEC_ID_ATRAC3P, .avcodec_id = AV_CODEC_ID_ATRAC3P,
.default_ptime = 20, .default_ptime = 20,
.packetizer = packetizer_passthrough, .packetizer = packetizer_passthrough,
.type = MT_AUDIO,
.media_type = MT_AUDIO,
.codec_type = &codec_type_avcodec,
}, },
#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57, 0, 0) #if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57, 0, 0)
{ {
@ -185,7 +221,8 @@ static codec_def_t __codec_defs[] = {
.avcodec_name = NULL, .avcodec_name = NULL,
.default_ptime = 20, .default_ptime = 20,
.packetizer = packetizer_passthrough, .packetizer = packetizer_passthrough,
.type = MT_AUDIO,
.media_type = MT_AUDIO,
.codec_type = &codec_type_avcodec,
}, },
{ {
.rtpname = "EVRC0", .rtpname = "EVRC0",
@ -194,7 +231,8 @@ static codec_def_t __codec_defs[] = {
.default_clockrate = 8000, .default_clockrate = 8000,
.default_ptime = 20, .default_ptime = 20,
.packetizer = packetizer_passthrough, .packetizer = packetizer_passthrough,
.type = MT_AUDIO,
.media_type = MT_AUDIO,
.codec_type = &codec_type_avcodec,
}, },
{ {
.rtpname = "EVRC1", .rtpname = "EVRC1",
@ -203,7 +241,8 @@ static codec_def_t __codec_defs[] = {
.default_clockrate = 8000, .default_clockrate = 8000,
.default_ptime = 20, .default_ptime = 20,
.packetizer = packetizer_passthrough, .packetizer = packetizer_passthrough,
.type = MT_AUDIO,
.media_type = MT_AUDIO,
.codec_type = &codec_type_avcodec,
}, },
#endif #endif
{ {
@ -215,7 +254,8 @@ static codec_def_t __codec_defs[] = {
.default_bitrate = 6600, .default_bitrate = 6600,
.default_ptime = 20, .default_ptime = 20,
.packetizer = packetizer_passthrough, .packetizer = packetizer_passthrough,
.type = MT_AUDIO,
.media_type = MT_AUDIO,
.codec_type = &codec_type_avcodec,
}, },
{ {
.rtpname = "AMR-WB", .rtpname = "AMR-WB",
@ -226,7 +266,8 @@ static codec_def_t __codec_defs[] = {
.default_bitrate = 14250, .default_bitrate = 14250,
.default_ptime = 20, .default_ptime = 20,
.packetizer = packetizer_passthrough, .packetizer = packetizer_passthrough,
.type = MT_AUDIO,
.media_type = MT_AUDIO,
.codec_type = &codec_type_avcodec,
}, },
// pseudo-codecs // pseudo-codecs
{ {
@ -234,7 +275,7 @@ static codec_def_t __codec_defs[] = {
.avcodec_id = -1, .avcodec_id = -1,
.avcodec_name = NULL, .avcodec_name = NULL,
.packetizer = packetizer_passthrough, .packetizer = packetizer_passthrough,
.type = MT_AUDIO,
.media_type = MT_AUDIO,
}, },
// for file writing // for file writing
{ {
@ -242,14 +283,16 @@ static codec_def_t __codec_defs[] = {
.avcodec_id = AV_CODEC_ID_PCM_S16LE, .avcodec_id = AV_CODEC_ID_PCM_S16LE,
.avcodec_name = NULL, .avcodec_name = NULL,
.packetizer = packetizer_passthrough, .packetizer = packetizer_passthrough,
.type = MT_AUDIO,
.media_type = MT_AUDIO,
.codec_type = &codec_type_avcodec,
}, },
{ {
.rtpname = "MP3", .rtpname = "MP3",
.avcodec_id = AV_CODEC_ID_MP3, .avcodec_id = AV_CODEC_ID_MP3,
.avcodec_name = NULL, .avcodec_name = NULL,
.packetizer = packetizer_passthrough, .packetizer = packetizer_passthrough,
.type = MT_AUDIO,
.media_type = MT_AUDIO,
.codec_type = &codec_type_avcodec,
}, },
}; };
@ -263,7 +306,7 @@ const codec_def_t *codec_find(const str *name, enum media_type type) {
codec_def_t *ret = g_hash_table_lookup(codecs_ht, name); codec_def_t *ret = g_hash_table_lookup(codecs_ht, name);
if (!ret) if (!ret)
return NULL; return NULL;
if (type && type != ret->type)
if (type && type != ret->media_type)
return NULL; return NULL;
return ret; return ret;
} }
@ -282,16 +325,43 @@ enum media_type codec_get_type(const str *type) {
static const char *avc_decoder_init(decoder_t *ret) {
AVCodec *codec = ret->def->decoder;
if (!codec)
return "codec not supported";
ret->u.avc.avcctx = avcodec_alloc_context3(codec);
if (!ret->u.avc.avcctx)
return "failed to alloc codec context";
ret->u.avc.avcctx->channels = ret->in_format.channels;
ret->u.avc.avcctx->sample_rate = ret->in_format.clockrate;
int i = avcodec_open2(ret->u.avc.avcctx, codec, NULL);
if (i)
return "failed to open codec context";
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));
return NULL;
}
decoder_t *decoder_new_fmt(const codec_def_t *def, int clockrate, int channels, const format_t *resample_fmt) { decoder_t *decoder_new_fmt(const codec_def_t *def, int clockrate, int channels, const format_t *resample_fmt) {
const char *err = NULL;
const char *err;
decoder_t *ret = NULL;
if (def->avcodec_id == -1)
return NULL;
err = "codec not supported";
if (!def->codec_type)
goto err;
clockrate *= def->clockrate_mult; clockrate *= def->clockrate_mult;
decoder_t *ret = g_slice_alloc0(sizeof(*ret));
ret = g_slice_alloc0(sizeof(*ret));
ret->def = def;
format_init(&ret->in_format); format_init(&ret->in_format);
ret->in_format.channels = channels; ret->in_format.channels = channels;
ret->in_format.clockrate = clockrate; ret->in_format.clockrate = clockrate;
@ -301,35 +371,11 @@ decoder_t *decoder_new_fmt(const codec_def_t *def, int clockrate, int channels,
ret->out_format = *resample_fmt; ret->out_format = *resample_fmt;
// sample format to be determined later when decoded frames arrive // sample format to be determined later when decoded frames arrive
AVCodec *codec = def->decoder;
// AVCodec *codec = NULL;
// if (def->decoder)
// codec = def->decoder;
// if (!codec && def->avcodec_name)
// codec = avcodec_find_decoder_by_name(def->avcodec_name);
// if (!codec && def->avcodec_id >= 0)
// 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)
err = def->codec_type->decoder_init(ret);
if (err)
goto err; 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);
av_init_packet(&ret->u.avc.avpkt);
ret->pts = (uint64_t) -1LL; ret->pts = (uint64_t) -1LL;
ret->rtp_ts = (unsigned long) -1L; ret->rtp_ts = (unsigned long) -1L;
@ -338,65 +384,44 @@ decoder_t *decoder_new_fmt(const codec_def_t *def, int clockrate, int channels,
return ret; return ret;
err: err:
decoder_close(ret);
if (ret)
decoder_close(ret);
if (err) if (err)
ilog(LOG_ERR, "Error creating media decoder: %s", err);
ilog(LOG_ERR, "Error creating media decoder for codec %s: %s", def->rtpname, err);
return NULL; return NULL;
} }
static void avc_decoder_close(decoder_t *dec) {
#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(56, 1, 0)
avcodec_free_context(&dec->u.avc.avcctx);
#else
avcodec_close(dec->u.avc.avcctx);
av_free(dec->u.avc.avcctx);
#endif
}
void decoder_close(decoder_t *dec) { void decoder_close(decoder_t *dec) {
if (!dec) if (!dec)
return; return;
/// XXX drain inputs and outputs /// 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
if (dec->def && dec->def->codec_type && dec->def->codec_type->decoder_close)
dec->def->codec_type->decoder_close(dec);
resample_shutdown(&dec->resampler); resample_shutdown(&dec->resampler);
resample_shutdown(&dec->mix_resampler); resample_shutdown(&dec->mix_resampler);
g_slice_free1(sizeof(*dec), dec); 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;
if (!data || !data->s || !data->len)
return 0;
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
u_int64_t shift_ts = ts - dec->rtp_ts;
if ((shift_ts * dec->avcctx->time_base.num * 1000) / dec->avcctx->time_base.den
> PACKET_TS_RESET_THRES)
{
ilog(LOG_DEBUG, "Timestamp disconinuity detected, resetting timestamp from "
"%lu to %lu",
dec->rtp_ts, ts);
// XXX handle lost packets here if timestamps don't line up?
dec->pts += dec->avcctx->time_base.den;
}
else
dec->pts += shift_ts;
}
dec->rtp_ts = ts;
static int avc_decoder_input(decoder_t *dec, const str *data, GQueue *out) {
const char *err;
dec->avpkt.data = (unsigned char *) data->s;
dec->avpkt.size = data->len;
dec->avpkt.pts = dec->pts;
dec->u.avc.avpkt.data = (unsigned char *) data->s;
dec->u.avc.avpkt.size = data->len;
dec->u.avc.avpkt.pts = dec->pts;
AVFrame *frame = NULL; AVFrame *frame = NULL;
@ -411,13 +436,13 @@ int decoder_input_data(decoder_t *dec, const str *data, unsigned long ts,
goto err; goto err;
#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57, 36, 0) #if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57, 36, 0)
if (dec->avpkt.size) {
int ret = avcodec_send_packet(dec->avcctx, &dec->avpkt);
if (dec->u.avc.avpkt.size) {
int ret = avcodec_send_packet(dec->u.avc.avcctx, &dec->u.avc.avpkt);
dbg("send packet ret %i", ret); dbg("send packet ret %i", ret);
err = "failed to send packet to avcodec"; err = "failed to send packet to avcodec";
if (ret == 0) { if (ret == 0) {
// consumed the packet // consumed the packet
dec->avpkt.size = 0;
dec->u.avc.avpkt.size = 0;
keep_going = 1; keep_going = 1;
} }
else { else {
@ -428,7 +453,7 @@ int decoder_input_data(decoder_t *dec, const str *data, unsigned long ts,
} }
} }
int ret = avcodec_receive_frame(dec->avcctx, frame);
int ret = avcodec_receive_frame(dec->u.avc.avcctx, frame);
dbg("receive frame ret %i", ret); dbg("receive frame ret %i", ret);
err = "failed to receive frame from avcodec"; err = "failed to receive frame from avcodec";
if (ret == 0) { if (ret == 0) {
@ -444,10 +469,10 @@ int decoder_input_data(decoder_t *dec, const str *data, unsigned long ts,
} }
#else #else
// only do this if we have any input left // only do this if we have any input left
if (dec->avpkt.size == 0)
if (dec->u.avc.avpkt.size == 0)
break; break;
int ret = avcodec_decode_audio4(dec->avcctx, frame, &got_frame, &dec->avpkt);
int ret = avcodec_decode_audio4(dec->u.avc.avcctx, frame, &got_frame, &dec->u.avc.avpkt);
dbg("decode frame ret %i, got frame %i", ret, got_frame); dbg("decode frame ret %i, got frame %i", ret, got_frame);
err = "failed to decode audio packet"; err = "failed to decode audio packet";
if (ret < 0) if (ret < 0)
@ -455,10 +480,10 @@ int decoder_input_data(decoder_t *dec, const str *data, unsigned long ts,
if (ret > 0) { if (ret > 0) {
// consumed some input // consumed some input
err = "invalid return value"; err = "invalid return value";
if (ret > dec->avpkt.size)
if (ret > dec->u.avc.avpkt.size)
goto err; goto err;
dec->avpkt.size -= ret;
dec->avpkt.data += ret;
dec->u.avc.avpkt.size -= ret;
dec->u.avc.avpkt.data += ret;
keep_going = 1; keep_going = 1;
} }
if (got_frame) if (got_frame)
@ -466,23 +491,18 @@ int decoder_input_data(decoder_t *dec, const str *data, unsigned long ts,
#endif #endif
if (got_frame) { if (got_frame) {
dbg("raw frame from decoder pts %llu samples %u", (unsigned long long) frame->pts, frame->nb_samples);
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) #if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(57, 36, 0)
frame->pts = frame->pkt_pts; frame->pts = frame->pkt_pts;
#endif #endif
if (G_UNLIKELY(frame->pts == AV_NOPTS_VALUE)) if (G_UNLIKELY(frame->pts == AV_NOPTS_VALUE))
frame->pts = dec->avpkt.pts;
dec->avpkt.pts += frame->nb_samples;
frame->pts = dec->u.avc.avpkt.pts;
dec->u.avc.avpkt.pts += frame->nb_samples;
err = "resampling failed";
AVFrame *rsmp_frame = resample_frame(&dec->resampler, frame, &dec->out_format);
if (!rsmp_frame)
goto err;
if (callback(dec, rsmp_frame, u1, u2))
return -1;
av_frame_free(&frame);
g_queue_push_tail(out, frame);
frame = NULL;
} }
} while (keep_going); } while (keep_going);
@ -495,6 +515,58 @@ err:
return -1; return -1;
} }
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)
{
GQueue frames = G_QUEUE_INIT;
if (G_UNLIKELY(!dec))
return -1;
if (!data || !data->s || !data->len)
return 0;
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
u_int64_t shift_ts = ts - dec->rtp_ts;
if ((shift_ts * 1000) / dec->in_format.clockrate > PACKET_TS_RESET_THRES) {
ilog(LOG_DEBUG, "Timestamp disconinuity detected, resetting timestamp from "
"%lu to %lu",
dec->rtp_ts, ts);
// XXX handle lost packets here if timestamps don't line up?
dec->pts += dec->in_format.clockrate;
}
else
dec->pts += shift_ts;
}
dec->rtp_ts = ts;
dec->def->codec_type->decoder_input(dec, data, &frames);
AVFrame *frame;
int ret = 0;
while ((frame = g_queue_pop_head(&frames))) {
AVFrame *rsmp_frame = resample_frame(&dec->resampler, frame, &dec->out_format);
if (!rsmp_frame) {
ilog(LOG_ERR, "Resampling failed");
ret = -1;
}
else {
if (callback(dec, rsmp_frame, u1, u2))
ret = -1;
}
av_frame_free(&frame);
}
return ret;
}
static void avlog_ilog(void *ptr, int loglevel, const char *fmt, va_list ap) { static void avlog_ilog(void *ptr, int loglevel, const char *fmt, va_list ap) {
char *msg; char *msg;
@ -526,6 +598,47 @@ static void avlog_ilog(void *ptr, int loglevel, const char *fmt, va_list ap) {
free(msg); free(msg);
} }
} }
static void avc_def_init(codec_def_t *def, int print) {
// look up AVCodec structs
if (def->avcodec_name) {
def->encoder = avcodec_find_encoder_by_name(def->avcodec_name);
def->decoder = avcodec_find_decoder_by_name(def->avcodec_name);
}
if (def->avcodec_id >= 0) {
if (!def->encoder)
def->encoder = avcodec_find_encoder(def->avcodec_id);
if (!def->decoder)
def->decoder = avcodec_find_decoder(def->avcodec_id);
}
// check if we have support if we are supposed to
if (def->avcodec_name || def->avcodec_id >= 0) {
if (print) {
if (def->encoder && def->decoder)
printf("%20s: fully supported\n", def->rtpname);
else if (def->decoder)
printf("%20s: supported for decoding only\n", def->rtpname);
else if (def->encoder)
printf("%20s: supported for encoding only\n", def->rtpname);
else
printf("%20s: not supported\n", def->rtpname);
}
else {
if (!def->encoder && !def->decoder)
ilog(LOG_DEBUG, "Codec %s is not supported by codec library",
def->rtpname);
else if (!def->encoder) {
ilog(LOG_DEBUG, "Codec %s is only supported for decoding "
"by codec library", def->rtpname);
}
else if (!def->decoder)
ilog(LOG_DEBUG, "Codec %s is only supported for encoding "
"by codec library", def->rtpname);
}
}
}
void codeclib_init(int print) { void codeclib_init(int print) {
av_register_all(); av_register_all();
avcodec_register_all(); avcodec_register_all();
@ -559,42 +672,8 @@ void codeclib_init(int print) {
else else
def->rfc_payload_type = -1; def->rfc_payload_type = -1;
// look up AVCodec structs
if (def->avcodec_name) {
def->encoder = avcodec_find_encoder_by_name(def->avcodec_name);
def->decoder = avcodec_find_decoder_by_name(def->avcodec_name);
}
if (def->avcodec_id >= 0) {
if (!def->encoder)
def->encoder = avcodec_find_encoder(def->avcodec_id);
if (!def->decoder)
def->decoder = avcodec_find_decoder(def->avcodec_id);
}
// check if we have support if we are supposed to
if (def->avcodec_name || def->avcodec_id >= 0) {
if (print) {
if (def->encoder && def->decoder)
printf("%20s: fully supported\n", def->rtpname);
else if (def->decoder)
printf("%20s: supported for decoding only\n", def->rtpname);
else if (def->encoder)
printf("%20s: supported for encoding only\n", def->rtpname);
else
printf("%20s: not supported\n", def->rtpname);
}
else {
if (!def->encoder && !def->decoder)
ilog(LOG_DEBUG, "Codec %s is not supported by codec library",
def->rtpname);
else if (!def->encoder) {
ilog(LOG_DEBUG, "Codec %s is only supported for decoding "
"by codec library", def->rtpname);
}
else if (!def->decoder)
ilog(LOG_DEBUG, "Codec %s is only supported for encoding "
"by codec library", def->rtpname);
}
}
if (def->codec_type && def->codec_type->def_init)
def->codec_type->def_init(def, print);
} }
} }
@ -748,11 +827,58 @@ encoder_t *encoder_new() {
return ret; return ret;
} }
static const char *avc_encoder_init(encoder_t *enc) {
enc->u.avc.codec = enc->def->encoder;
if (!enc->u.avc.codec)
return "output codec not found";
enc->u.avc.avcctx = avcodec_alloc_context3(enc->u.avc.codec);
if (!enc->u.avc.avcctx)
return "failed to alloc codec context";
enc->actual_format = enc->requested_format;
enc->actual_format.format = -1;
for (const enum AVSampleFormat *sfmt = enc->u.avc.codec->sample_fmts; sfmt && *sfmt != -1; sfmt++) {
dbg("supported sample format for output codec %s: %s",
enc->u.avc.codec->name, av_get_sample_fmt_name(*sfmt));
if (*sfmt == enc->requested_format.format)
enc->actual_format.format = *sfmt;
}
if (enc->actual_format.format == -1 && enc->u.avc.codec->sample_fmts)
enc->actual_format.format = enc->u.avc.codec->sample_fmts[0];
dbg("using output sample format %s for codec %s",
av_get_sample_fmt_name(enc->actual_format.format), enc->u.avc.codec->name);
enc->u.avc.avcctx->channels = enc->actual_format.channels;
enc->u.avc.avcctx->channel_layout = av_get_default_channel_layout(enc->actual_format.channels);
enc->u.avc.avcctx->sample_rate = enc->actual_format.clockrate;
enc->u.avc.avcctx->sample_fmt = enc->actual_format.format;
enc->u.avc.avcctx->time_base = (AVRational){1,enc->actual_format.clockrate};
enc->u.avc.avcctx->bit_rate = enc->bitrate;
enc->samples_per_frame = enc->actual_format.clockrate * enc->ptime / 1000;
if (enc->u.avc.avcctx->frame_size)
enc->samples_per_frame = enc->u.avc.avcctx->frame_size;
if (enc->def->set_options)
enc->def->set_options(enc);
int i = avcodec_open2(enc->u.avc.avcctx, enc->u.avc.codec, NULL);
if (i)
return "failed to open output context";
return NULL;
}
int encoder_config(encoder_t *enc, const codec_def_t *def, int bitrate, int ptime, int encoder_config(encoder_t *enc, const codec_def_t *def, int bitrate, int ptime,
const format_t *requested_format, format_t *actual_format) const format_t *requested_format, format_t *actual_format)
{ {
const char *err; const char *err;
err = "codec not supported";
if (!def->codec_type)
goto err;
// anything to do? // anything to do?
if (G_LIKELY(format_eq(requested_format, &enc->requested_format))) if (G_LIKELY(format_eq(requested_format, &enc->requested_format)))
goto done; goto done;
@ -760,68 +886,28 @@ int encoder_config(encoder_t *enc, const codec_def_t *def, int bitrate, int ptim
encoder_close(enc); encoder_close(enc);
enc->requested_format = *requested_format; enc->requested_format = *requested_format;
err = "output codec not found";
enc->def = def; enc->def = def;
enc->codec = def->encoder;
// if (codec_name)
// enc->codec = avcodec_find_encoder_by_name(codec_name);
// if (!enc->codec)
// enc->codec = avcodec_find_encoder(codec_id);
if (!enc->codec)
goto err;
ptime /= def->clockrate_mult;
enc->ptime = ptime;
enc->ptime = ptime / def->clockrate_mult;
enc->bitrate = bitrate;
err = "failed to alloc codec context";
enc->avcctx = avcodec_alloc_context3(enc->codec);
if (!enc->avcctx)
goto err;
enc->actual_format = enc->requested_format;
enc->actual_format.format = -1;
for (const enum AVSampleFormat *sfmt = enc->codec->sample_fmts; sfmt && *sfmt != -1; sfmt++) {
dbg("supported sample format for output codec %s: %s", enc->codec->name, av_get_sample_fmt_name(*sfmt));
if (*sfmt == requested_format->format)
enc->actual_format.format = *sfmt;
}
if (enc->actual_format.format == -1 && enc->codec->sample_fmts)
enc->actual_format.format = enc->codec->sample_fmts[0];
dbg("using output sample format %s for codec %s", av_get_sample_fmt_name(enc->actual_format.format), enc->codec->name);
enc->avcctx->channels = enc->actual_format.channels;
enc->avcctx->channel_layout = av_get_default_channel_layout(enc->actual_format.channels);
enc->avcctx->sample_rate = enc->actual_format.clockrate;
enc->avcctx->sample_fmt = enc->actual_format.format;
enc->avcctx->time_base = (AVRational){1,enc->actual_format.clockrate};
enc->avcctx->bit_rate = bitrate;
enc->samples_per_frame = enc->actual_format.clockrate * ptime / 1000;
if (def->set_options)
def->set_options(enc);
err = "failed to open output context";
int i = avcodec_open2(enc->avcctx, enc->codec, NULL);
if (i)
err = def->codec_type->encoder_init(enc);
if (err)
goto err; goto err;
av_init_packet(&enc->avpkt); av_init_packet(&enc->avpkt);
// output frame and fifo // output frame and fifo
enc->frame = av_frame_alloc(); enc->frame = av_frame_alloc();
enc->frame->nb_samples = enc->avcctx->frame_size ? : (enc->samples_per_frame ? : 256);
enc->frame->format = enc->avcctx->sample_fmt;
enc->frame->sample_rate = enc->avcctx->sample_rate;
enc->frame->channel_layout = enc->avcctx->channel_layout;
if (!enc->frame->channel_layout)
enc->frame->channel_layout = av_get_default_channel_layout(enc->avcctx->channels);
enc->frame->nb_samples = enc->samples_per_frame ? : 256;
enc->frame->format = enc->actual_format.format;
enc->frame->sample_rate = enc->actual_format.clockrate;
enc->frame->channel_layout = av_get_default_channel_layout(enc->actual_format.channels);
//if (!enc->frame->channel_layout)
//enc->frame->channel_layout = av_get_default_channel_layout(enc->u.avc.avcctx->channels);
if (av_frame_get_buffer(enc->frame, 0) < 0) if (av_frame_get_buffer(enc->frame, 0) < 0)
abort(); abort();
enc->fifo = av_audio_fifo_alloc(enc->avcctx->sample_fmt, enc->avcctx->channels,
enc->fifo = av_audio_fifo_alloc(enc->frame->format, enc->actual_format.channels,
enc->frame->nb_samples); enc->frame->nb_samples);
ilog(LOG_DEBUG, "Initialized encoder with frame size %u samples", enc->frame->nb_samples); ilog(LOG_DEBUG, "Initialized encoder with frame size %u samples", enc->frame->nb_samples);
@ -833,19 +919,24 @@ done:
err: err:
encoder_close(enc); encoder_close(enc);
ilog(LOG_ERR, "Error configuring media output: %s", err);
ilog(LOG_ERR, "Error configuring media output for codec %s: %s", def->rtpname, err);
return -1; return -1;
} }
static void avc_encoder_close(encoder_t *enc) {
if (enc->u.avc.avcctx) {
avcodec_close(enc->u.avc.avcctx);
avcodec_free_context(&enc->u.avc.avcctx);
}
enc->u.avc.avcctx = NULL;
enc->u.avc.codec = NULL;
}
void encoder_close(encoder_t *enc) { void encoder_close(encoder_t *enc) {
if (!enc) if (!enc)
return; return;
if (enc->avcctx) {
avcodec_close(enc->avcctx);
avcodec_free_context(&enc->avcctx);
}
enc->avcctx = NULL;
enc->codec = NULL;
if (enc->def && enc->def->codec_type && enc->def->codec_type->encoder_close)
enc->def->codec_type->encoder_close(enc);
format_init(&enc->requested_format); format_init(&enc->requested_format);
format_init(&enc->actual_format); format_init(&enc->actual_format);
av_audio_fifo_free(enc->fifo); av_audio_fifo_free(enc->fifo);
@ -859,87 +950,96 @@ void encoder_free(encoder_t *enc) {
g_slice_free1(sizeof(*enc), enc); g_slice_free1(sizeof(*enc), enc);
} }
int encoder_input_data(encoder_t *enc, AVFrame *frame,
int (*callback)(encoder_t *, void *u1, void *u2), void *u1, void *u2)
{
if (G_UNLIKELY(!enc->avcctx))
return -1;
if (G_UNLIKELY(!enc->codec))
return -1;
int keep_going;
int have_frame = 1;
do {
keep_going = 0;
int got_packet = 0;
static int avc_encoder_input(encoder_t *enc, AVFrame **frame) {
int keep_going = 0;
int got_packet = 0;
#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57, 36, 0) #if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57, 36, 0)
if (have_frame) {
int ret = avcodec_send_frame(enc->avcctx, frame);
dbg("send frame ret %i", 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(enc->avcctx, &enc->avpkt);
dbg("receive packet ret %i", ret);
if (*frame) {
int ret = avcodec_send_frame(enc->u.avc.avcctx, *frame);
dbg("send frame ret %i", ret);
if (ret == 0) { if (ret == 0) {
// got some data
// consumed
*frame = NULL;
keep_going = 1; keep_going = 1;
got_packet = 1;
} }
else { else {
if (ret == AVERROR(EAGAIN)) if (ret == AVERROR(EAGAIN))
; // try again if there's still more input
; // check output and maybe try again
else else
return -1; return -1;
} }
#else
if (!have_frame)
break;
}
int ret = avcodec_encode_audio2(enc->avcctx, &enc->avpkt, frame, &got_packet);
dbg("encode frame ret %i, got packet %i", ret, got_packet);
if (ret == 0)
have_frame = 0; // consumed
int ret = avcodec_receive_packet(enc->u.avc.avcctx, &enc->avpkt);
dbg("receive packet ret %i", 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 else
return -1; // error
if (got_packet)
keep_going = 1;
return -1;
}
#else
if (!*frame)
return 0;
int ret = avcodec_encode_audio2(enc->u.avc.avcctx, &enc->avpkt, *frame, &got_packet);
dbg("encode frame ret %i, got packet %i", ret, got_packet);
if (ret == 0)
*frame = NULL; // consumed
else
return -1; // error
if (got_packet)
keep_going = 1;
#endif #endif
if (!got_packet)
continue;
if (!got_packet)
return keep_going;
// dbg("{%s} output avpkt size is %i", output->file_name, (int) enc->avpkt.size);
// dbg("{%s} output pkt pts/dts is %li/%li", output->file_name, (long) enc->avpkt.pts,
// (long) enc->avpkt.dts);
// dbg("{%s} output dts %li", output->file_name, (long) output->mux_dts);
// dbg("{%s} output avpkt size is %i", output->file_name, (int) enc->avpkt.size);
// dbg("{%s} output pkt pts/dts is %li/%li", output->file_name, (long) enc->avpkt.pts,
// (long) enc->avpkt.dts);
// dbg("{%s} output dts %li", output->file_name, (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 (enc->mux_dts > enc->avpkt.dts)
enc->avpkt.dts = enc->mux_dts;
if (enc->avpkt.pts < enc->avpkt.dts)
enc->avpkt.pts = enc->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 (enc->mux_dts > enc->avpkt.dts)
enc->avpkt.dts = enc->mux_dts;
if (enc->avpkt.pts < enc->avpkt.dts)
enc->avpkt.pts = enc->avpkt.dts;
return keep_going;
}
int encoder_input_data(encoder_t *enc, AVFrame *frame,
int (*callback)(encoder_t *, void *u1, void *u2), void *u1, void *u2)
{
enc->avpkt.size = 0;
while (1) {
int ret = enc->def->codec_type->encoder_input(enc, &frame);
if (ret < 0)
return -1;
//av_write_frame(output->fmtctx, &output->avpkt); //av_write_frame(output->fmtctx, &output->avpkt);
callback(enc, u1, u2); callback(enc, u1, u2);
//output->fifo_pts += output->frame->nb_samples;
enc->mux_dts = enc->avpkt.dts + 1; // min next expected dts
if (enc->avpkt.size) {
//output->fifo_pts += output->frame->nb_samples;
enc->mux_dts = enc->avpkt.dts + 1; // min next expected dts
av_packet_unref(&enc->avpkt);
} while (keep_going);
av_packet_unref(&enc->avpkt);
enc->avpkt.size = 0;
}
if (ret == 0)
break;
}
return 0; return 0;
} }
@ -1063,7 +1163,8 @@ static void opus_init(struct rtp_payload_type *pt) {
static void opus_set_options(encoder_t *enc) { static void opus_set_options(encoder_t *enc) {
int ret; int ret;
if ((ret = av_opt_set_int(enc->avcctx, "frame_duration", enc->ptime, 0)))
ilog(LOG_WARN, "Failed to set Opus frame_duration option (error code %i)", ret);
if (enc->ptime)
if ((ret = av_opt_set_int(enc->u.avc.avcctx, "frame_duration", enc->ptime, 0)))
ilog(LOG_WARN, "Failed to set Opus frame_duration option (error code %i)", ret);
// XXX additional opus options // XXX additional opus options
} }

+ 36
- 5
lib/codeclib.h View File

@ -17,6 +17,7 @@ typedef struct codec_def_s codec_def_t;
struct codec_type_s;
struct decoder_s; struct decoder_s;
struct encoder_s; struct encoder_s;
struct format_s; struct format_s;
@ -25,6 +26,7 @@ struct seq_packet_s;
struct packet_sequencer_s; struct packet_sequencer_s;
struct rtp_payload_type; struct rtp_payload_type;
typedef struct codec_type_s codec_type_t;
typedef struct decoder_s decoder_t; typedef struct decoder_s decoder_t;
typedef struct encoder_s encoder_t; typedef struct encoder_s encoder_t;
typedef struct format_s format_t; typedef struct format_s format_t;
@ -46,6 +48,18 @@ enum media_type {
MT_OTHER, MT_OTHER,
}; };
struct codec_type_s {
void (*def_init)(codec_def_t *, int print);
const char *(*decoder_init)(decoder_t *);
int (*decoder_input)(decoder_t *, const str *data, GQueue *);
void (*decoder_close)(decoder_t *);
const char *(*encoder_init)(encoder_t *);
int (*encoder_input)(encoder_t *, AVFrame **);
void (*encoder_close)(encoder_t *);
};
struct codec_def_s { struct codec_def_s {
const char * const rtpname; const char * const rtpname;
int clockrate_mult; int clockrate_mult;
@ -57,7 +71,7 @@ struct codec_def_s {
int default_ptime; int default_ptime;
packetizer_f * const packetizer; packetizer_f * const packetizer;
const int bits_per_sample; const int bits_per_sample;
const enum media_type type;
const enum media_type media_type;
// codec-specific callbacks // codec-specific callbacks
format_init_f *init; format_init_f *init;
@ -66,6 +80,10 @@ struct codec_def_s {
// filled in by codeclib_init() // filled in by codeclib_init()
str rtpname_str; str rtpname_str;
int rfc_payload_type; int rfc_payload_type;
const codec_type_t *codec_type;
// libavcodec
AVCodec *encoder; AVCodec *encoder;
AVCodec *decoder; AVCodec *decoder;
}; };
@ -81,14 +99,21 @@ struct resample_s {
}; };
struct decoder_s { struct decoder_s {
const codec_def_t *def;
format_t in_format, format_t in_format,
out_format; out_format;
resample_t resampler, resample_t resampler,
mix_resampler; // XXX move this out of here - specific to recording-daemon mix_resampler; // XXX move this out of here - specific to recording-daemon
AVCodecContext *avcctx;
AVPacket avpkt;
union {
struct {
AVCodecContext *avcctx;
AVPacket avpkt;
} avc;
} u;
unsigned long rtp_ts; unsigned long rtp_ts;
uint64_t pts; uint64_t pts;
@ -100,12 +125,18 @@ struct encoder_s {
actual_format; actual_format;
const codec_def_t *def; const codec_def_t *def;
AVCodec *codec;
AVCodecContext *avcctx;
union {
struct {
AVCodec *codec;
AVCodecContext *avcctx;
} avc;
} u;
AVPacket avpkt; AVPacket avpkt;
AVAudioFifo *fifo; AVAudioFifo *fifo;
int64_t fifo_pts; // pts of first data in fifo int64_t fifo_pts; // pts of first data in fifo
int ptime; int ptime;
int bitrate;
int samples_per_frame; int samples_per_frame;
AVFrame *frame; // to pull samples from the fifo AVFrame *frame; // to pull samples from the fifo
int64_t mux_dts; // last dts passed to muxer int64_t mux_dts; // last dts passed to muxer


+ 3
- 3
recording-daemon/output.c View File

@ -82,7 +82,7 @@ int output_config(output_t *output, const format_t *requested_format, format_t *
// if (!codec) // if (!codec)
// goto err; // goto err;
err = "failed to alloc output stream"; err = "failed to alloc output stream";
output->avst = avformat_new_stream(output->fmtctx, output->encoder->codec);
output->avst = avformat_new_stream(output->fmtctx, output->encoder->u.avc.codec);
if (!output->avst) if (!output->avst)
goto err; goto err;
//#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57, 0, 0) //#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57, 0, 0)
@ -114,10 +114,10 @@ int output_config(output_t *output, const format_t *requested_format, format_t *
// output->avcctx->sample_fmt = output->actual_format.format; // output->avcctx->sample_fmt = output->actual_format.format;
// output->avcctx->time_base = (AVRational){1,output->actual_format.clockrate}; // output->avcctx->time_base = (AVRational){1,output->actual_format.clockrate};
// output->avcctx->bit_rate = mp3_bitrate; // output->avcctx->bit_rate = mp3_bitrate;
output->avst->time_base = output->encoder->avcctx->time_base;
output->avst->time_base = output->encoder->u.avc.avcctx->time_base;
#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(57, 26, 0) // exact version? present in 57.56 #if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(57, 26, 0) // exact version? present in 57.56
avcodec_parameters_from_context(output->avst->codecpar, output->encoder->avcctx);
avcodec_parameters_from_context(output->avst->codecpar, output->encoder->u.avc.avcctx);
#endif #endif
char full_fn[PATH_MAX*2]; char full_fn[PATH_MAX*2];


Loading…
Cancel
Save