|
|
|
@ -26,7 +26,13 @@ |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#define CODEC_DEF_FULL(ref, codec_id, mult, name, clockrate, channels, bitrate) { \ |
|
|
|
packetizer_f packetizer_passthrough; // pass frames as they arrive in AVPackets |
|
|
|
packetizer_f packetizer_samplestream; // flat stream of samples |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#define CODEC_DEF_FULL(ref, codec_id, mult, name, clockrate, channels, bitrate, ptime, pizer, bps) { \ |
|
|
|
.rtpname = #ref, \ |
|
|
|
.avcodec_id = codec_id, \ |
|
|
|
.clockrate_mult = mult, \ |
|
|
|
@ -34,45 +40,53 @@ |
|
|
|
.default_clockrate = clockrate, \ |
|
|
|
.default_channels = channels, \ |
|
|
|
.default_bitrate = bitrate, \ |
|
|
|
.default_ptime = ptime, \ |
|
|
|
.packetizer = packetizer_ ## pizer, \ |
|
|
|
.bits_per_sample = bps, \ |
|
|
|
} |
|
|
|
#define CODEC_DEF_AVC(ref, id, mult, name, clockrate, channels, bitrate) \ |
|
|
|
CODEC_DEF_FULL(ref, AV_CODEC_ID_ ## id, mult, name, clockrate, channels, bitrate) |
|
|
|
#define CODEC_DEF_MULT_NAME(ref, id, mult, name) CODEC_DEF_AVC(ref, id, mult, name, -1, -1, 0) |
|
|
|
#define CODEC_DEF_MULT_NAME_ENC(ref, id, mult, name, clockrate, channels, bitrate) \ |
|
|
|
CODEC_DEF_AVC(ref, id, mult, name, clockrate, channels, bitrate) |
|
|
|
#define CODEC_DEF_MULT(ref, id, mult) CODEC_DEF_MULT_NAME(ref, id, mult, NULL) |
|
|
|
#define CODEC_DEF_MULT_ENC(ref, id, mult, clockrate, channels) \ |
|
|
|
CODEC_DEF_MULT_NAME_ENC(ref, id, mult, NULL, clockrate, channels, 0) |
|
|
|
#define CODEC_DEF_NAME(ref, id, name) CODEC_DEF_MULT_NAME(ref, id, 1, name) |
|
|
|
#define CODEC_DEF_NAME_ENC(ref, id, name, clockrate, channels, bitrate) \ |
|
|
|
CODEC_DEF_MULT_NAME_ENC(ref, id, 1, name, clockrate, channels, bitrate) |
|
|
|
#define CODEC_DEF(ref, id) CODEC_DEF_MULT(ref, id, 1) |
|
|
|
#define CODEC_DEF_ENC(ref, id, clockrate, channels) CODEC_DEF_MULT_ENC(ref, id, 1, clockrate, channels) |
|
|
|
#define CODEC_DEF_STUB(ref) CODEC_DEF_FULL(ref, -1, 1, ref, -1, -1, 0) |
|
|
|
#define CODEC_DEF_AVC(ref, id, mult, name, clockrate, channels, bitrate, ptime, pizer, bps) \ |
|
|
|
CODEC_DEF_FULL(ref, AV_CODEC_ID_ ## id, mult, name, clockrate, channels, bitrate, ptime, pizer, bps) |
|
|
|
#define CODEC_DEF_MULT_NAME(ref, id, mult, name, pizer, bps) CODEC_DEF_AVC(ref, id, mult, name, -1, -1, 0, 0, pizer, bps) |
|
|
|
#define CODEC_DEF_MULT(ref, id, mult, pizer, bps) CODEC_DEF_MULT_NAME(ref, id, mult, NULL, pizer, bps) |
|
|
|
#define CODEC_DEF_NAME(ref, id, name, pizer, bps) CODEC_DEF_MULT_NAME(ref, id, 1, name, pizer, bps) |
|
|
|
#define CODEC_DEF(ref, id, pizer, bps) CODEC_DEF_MULT(ref, id, 1, pizer, bps) |
|
|
|
|
|
|
|
// _ENC macros provided for codecs not having defaults in the RTP RFC |
|
|
|
#define CODEC_DEF_NAME_ENC(ref, id, name, clockrate, channels, bitrate, ptime, pizer, bps) \ |
|
|
|
CODEC_DEF_MULT_NAME_ENC(ref, id, 1, name, clockrate, channels, bitrate, ptime, pizer, bps) |
|
|
|
#define CODEC_DEF_MULT_ENC(ref, id, mult, clockrate, channels, bitrate, ptime, pizer, bps) \ |
|
|
|
CODEC_DEF_MULT_NAME_ENC(ref, id, mult, NULL, clockrate, channels, bitrate, ptime, pizer, bps) |
|
|
|
#define CODEC_DEF_ENC(ref, id, clockrate, channels, bitrate, ptime, pizer, bps) \ |
|
|
|
CODEC_DEF_MULT_ENC(ref, id, 1, clockrate, channels, bitrate, ptime, pizer, bps) |
|
|
|
#define CODEC_DEF_MULT_NAME_ENC(ref, id, mult, name, clockrate, channels, bitrate, ptime, pizer, bps) \ |
|
|
|
CODEC_DEF_AVC(ref, id, mult, name, clockrate, channels, bitrate, ptime, pizer, bps) |
|
|
|
|
|
|
|
// not real audio codecs |
|
|
|
#define CODEC_DEF_STUB(ref) CODEC_DEF_FULL(ref, -1, 1, ref, -1, -1, 0, 0, passthrough, 0) |
|
|
|
|
|
|
|
static const struct codec_def_s codecs[] = { |
|
|
|
CODEC_DEF(PCMA, PCM_ALAW), |
|
|
|
CODEC_DEF(PCMU, PCM_MULAW), |
|
|
|
CODEC_DEF(G723, G723_1), |
|
|
|
CODEC_DEF_MULT(G722, ADPCM_G722, 2), |
|
|
|
CODEC_DEF(QCELP, QCELP), |
|
|
|
CODEC_DEF(G729, G729), |
|
|
|
CODEC_DEF_ENC(speex, SPEEX, 16000, 1), |
|
|
|
CODEC_DEF(GSM, GSM), |
|
|
|
CODEC_DEF(iLBC, ILBC), |
|
|
|
CODEC_DEF_NAME_ENC(opus, OPUS, libopus, 48000, 2, 24000), |
|
|
|
CODEC_DEF_NAME(vorbis, VORBIS, libvorbis), |
|
|
|
CODEC_DEF(ac3, AC3), |
|
|
|
CODEC_DEF(eac3, EAC3), |
|
|
|
CODEC_DEF(ATRAC3, ATRAC3), |
|
|
|
CODEC_DEF(ATRAC-X, ATRAC3P), |
|
|
|
CODEC_DEF(PCMA, PCM_ALAW, samplestream, 8), |
|
|
|
CODEC_DEF(PCMU, PCM_MULAW, samplestream, 8), |
|
|
|
CODEC_DEF(G723, G723_1, passthrough, 0), |
|
|
|
CODEC_DEF_MULT(G722, ADPCM_G722, 2, samplestream, 8), |
|
|
|
CODEC_DEF(QCELP, QCELP, passthrough, 0), |
|
|
|
CODEC_DEF(G729, G729, passthrough, 0), |
|
|
|
CODEC_DEF_ENC(speex, SPEEX, 16000, 1, 11000, 20, passthrough, 0), |
|
|
|
CODEC_DEF(GSM, GSM, passthrough, 0), |
|
|
|
CODEC_DEF(iLBC, ILBC, passthrough, 0), |
|
|
|
CODEC_DEF_NAME_ENC(opus, OPUS, libopus, 48000, 2, 32000, 20, passthrough, 0), |
|
|
|
CODEC_DEF_NAME(vorbis, VORBIS, libvorbis, passthrough, 0), |
|
|
|
CODEC_DEF(ac3, AC3, passthrough, 0), |
|
|
|
CODEC_DEF(eac3, EAC3, passthrough, 0), |
|
|
|
CODEC_DEF(ATRAC3, ATRAC3, passthrough, 0), |
|
|
|
CODEC_DEF(ATRAC-X, ATRAC3P, passthrough, 0), |
|
|
|
#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57, 0, 0) |
|
|
|
CODEC_DEF(EVRC, EVRC), |
|
|
|
CODEC_DEF(EVRC0, EVRC), |
|
|
|
CODEC_DEF(EVRC1, EVRC), |
|
|
|
CODEC_DEF(EVRC, EVRC, passthrough, 0), |
|
|
|
CODEC_DEF(EVRC0, EVRC, passthrough, 0), |
|
|
|
CODEC_DEF(EVRC1, EVRC, passthrough, 0), |
|
|
|
#endif |
|
|
|
CODEC_DEF_ENC(AMR, AMR_NB, 8000, 1), |
|
|
|
CODEC_DEF_ENC(AMR-WB, AMR_WB, 16000, 1), |
|
|
|
CODEC_DEF_ENC(AMR, AMR_NB, 8000, 1, 6600, 20, passthrough, 0), |
|
|
|
CODEC_DEF_ENC(AMR-WB, AMR_WB, 16000, 1, 14250, 20, passthrough, 0), |
|
|
|
CODEC_DEF_STUB(telephone-event), |
|
|
|
}; |
|
|
|
|
|
|
|
@ -171,6 +185,8 @@ int decoder_input_data(decoder_t *dec, const str *data, unsigned long ts, |
|
|
|
|
|
|
|
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); |
|
|
|
@ -477,8 +493,8 @@ encoder_t *encoder_new() { |
|
|
|
return ret; |
|
|
|
} |
|
|
|
|
|
|
|
int encoder_config(encoder_t *enc, int codec_id, int bitrate, const format_t *requested_format, |
|
|
|
format_t *actual_format) |
|
|
|
int encoder_config(encoder_t *enc, int codec_id, int bitrate, int ptime, |
|
|
|
const format_t *requested_format, format_t *actual_format) |
|
|
|
{ |
|
|
|
const char *err; |
|
|
|
|
|
|
|
@ -526,6 +542,25 @@ int encoder_config(encoder_t *enc, int codec_id, int bitrate, const format_t *re |
|
|
|
|
|
|
|
av_init_packet(&enc->avpkt); |
|
|
|
|
|
|
|
enc->samples_per_frame = enc->actual_format.clockrate * ptime / 1000; |
|
|
|
|
|
|
|
// output frame and fifo |
|
|
|
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); |
|
|
|
if (av_frame_get_buffer(enc->frame, 0) < 0) |
|
|
|
abort(); |
|
|
|
|
|
|
|
enc->fifo = av_audio_fifo_alloc(enc->avcctx->sample_fmt, enc->avcctx->channels, |
|
|
|
enc->frame->nb_samples); |
|
|
|
|
|
|
|
ilog(LOG_DEBUG, "Initialized encoder with frame size %u samples", enc->frame->nb_samples); |
|
|
|
|
|
|
|
|
|
|
|
done: |
|
|
|
*actual_format = enc->actual_format; |
|
|
|
return 0; |
|
|
|
@ -546,7 +581,11 @@ void encoder_close(encoder_t *enc) { |
|
|
|
enc->avcctx = NULL; |
|
|
|
format_init(&enc->requested_format); |
|
|
|
format_init(&enc->actual_format); |
|
|
|
av_audio_fifo_free(enc->fifo); |
|
|
|
av_frame_free(&enc->frame); |
|
|
|
enc->mux_dts = 0; |
|
|
|
enc->fifo = NULL; |
|
|
|
enc->fifo_pts = 0; |
|
|
|
} |
|
|
|
void encoder_free(encoder_t *enc) { |
|
|
|
encoder_close(enc); |
|
|
|
@ -556,6 +595,11 @@ void encoder_free(encoder_t *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 { |
|
|
|
@ -632,3 +676,70 @@ int encoder_input_data(encoder_t *enc, AVFrame *frame, |
|
|
|
|
|
|
|
return 0; |
|
|
|
} |
|
|
|
|
|
|
|
static int encoder_fifo_flush(encoder_t *enc, |
|
|
|
int (*callback)(encoder_t *, void *u1, void *u2), void *u1, void *u2) |
|
|
|
{ |
|
|
|
while (av_audio_fifo_size(enc->fifo) >= enc->frame->nb_samples) { |
|
|
|
|
|
|
|
if (av_audio_fifo_read(enc->fifo, (void **) enc->frame->data, |
|
|
|
enc->frame->nb_samples) <= 0) |
|
|
|
abort(); |
|
|
|
|
|
|
|
dbg("output fifo pts %lu",(unsigned long) enc->fifo_pts); |
|
|
|
enc->frame->pts = enc->fifo_pts; |
|
|
|
|
|
|
|
encoder_input_data(enc, enc->frame, callback, u1, u2); |
|
|
|
|
|
|
|
enc->fifo_pts += enc->frame->nb_samples; |
|
|
|
} |
|
|
|
|
|
|
|
return 0; |
|
|
|
} |
|
|
|
|
|
|
|
int encoder_input_fifo(encoder_t *enc, AVFrame *frame, |
|
|
|
int (*callback)(encoder_t *, void *u1, void *u2), void *u1, void *u2) |
|
|
|
{ |
|
|
|
// fix up output pts |
|
|
|
if (av_audio_fifo_size(enc->fifo) == 0) |
|
|
|
enc->fifo_pts = frame->pts; |
|
|
|
|
|
|
|
if (av_audio_fifo_write(enc->fifo, (void **) frame->extended_data, frame->nb_samples) < 0) |
|
|
|
return -1; |
|
|
|
|
|
|
|
return encoder_fifo_flush(enc, callback, u1, u2); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
int packetizer_passthrough(AVPacket *pkt, GString *buf, str *output) { |
|
|
|
if (!pkt) |
|
|
|
return -1; |
|
|
|
assert(output->len >= pkt->size); |
|
|
|
output->len = pkt->size; |
|
|
|
memcpy(output->s, pkt->data, pkt->size); |
|
|
|
return 0; |
|
|
|
} |
|
|
|
|
|
|
|
// returns: -1 = not enough data, nothing returned; 0 = returned a packet; |
|
|
|
// 1 = returned a packet and there's more |
|
|
|
int packetizer_samplestream(AVPacket *pkt, GString *buf, str *input_output) { |
|
|
|
// avoid moving buffers around if possible: |
|
|
|
// most common case: new input packet has just enough (or more) data as what we need |
|
|
|
if (G_LIKELY(pkt && buf->len == 0 && pkt->size >= input_output->len)) { |
|
|
|
memcpy(input_output->s, pkt->data, input_output->len); |
|
|
|
if (pkt->size > input_output->len) // any leftovers? |
|
|
|
g_string_append_len(buf, (char *) pkt->data + input_output->len, |
|
|
|
pkt->size - input_output->len); |
|
|
|
return buf->len >= input_output->len ? 1 : 0; |
|
|
|
} |
|
|
|
// we have to move data around. append input packet to buffer if we have one |
|
|
|
if (pkt) |
|
|
|
g_string_append_len(buf, (char *) pkt->data, pkt->size); |
|
|
|
// do we have enough? |
|
|
|
if (buf->len < input_output->len) |
|
|
|
return -1; |
|
|
|
// copy requested data into provided output buffer and remove from interim buffer |
|
|
|
memcpy(input_output->s, buf->str, input_output->len); |
|
|
|
g_string_erase(buf, 0, input_output->len); |
|
|
|
return buf->len >= input_output->len ? 1 : 0; |
|
|
|
} |