TT#50652 add media playback capability

Change-Id: I935812639e4f42cd89c4ecf02f5ae1a9a8243a65
changes/41/27641/7
Richard Fuchs 6 years ago
parent 6ba60d5b54
commit 92b08d899c

@ -78,6 +78,7 @@ struct transcode_packet {
static codec_handler_func handler_func_passthrough_ssrc;
static codec_handler_func handler_func_transcode;
static codec_handler_func handler_func_playback;
static codec_handler_func handler_func_dtmf;
static struct ssrc_entry *__ssrc_handler_transcode_new(void *p);
@ -97,6 +98,9 @@ static struct codec_handler codec_handler_stub_ssrc = {
static void __handler_shutdown(struct codec_handler *handler) {
free_ssrc_hash(&handler->ssrc_hash);
if (handler->ssrc_handler)
obj_put(&handler->ssrc_handler->h);
handler->ssrc_handler = NULL;
handler->kernelize = 0;
handler->transcoder = 0;
}
@ -106,6 +110,9 @@ static void __codec_handler_free(void *pp) {
__handler_shutdown(h);
g_slice_free1(sizeof(*h), h);
}
void codec_handler_free(struct codec_handler *handler) {
__codec_handler_free(handler);
}
static struct codec_handler *__handler_new(struct rtp_payload_type *pt) {
struct codec_handler *handler = g_slice_alloc0(sizeof(*handler));
@ -173,6 +180,21 @@ reset:
STR_FMT(&dest->encoding_with_params));
}
struct codec_handler *codec_handler_make_playback(struct rtp_payload_type *src_pt,
struct rtp_payload_type *dst_pt)
{
struct codec_handler *handler = __handler_new(src_pt);
handler->dest_pt = *dst_pt;
handler->func = handler_func_playback;
handler->ssrc_handler = (void *) __ssrc_handler_transcode_new(handler);
ilog(LOG_DEBUG, "Created media playback context for " STR_FORMAT " -> " STR_FORMAT "",
STR_FMT(&src_pt->encoding_with_params),
STR_FMT(&dst_pt->encoding_with_params));
return handler;
}
static void __ensure_codec_def(struct rtp_payload_type *pt, struct call_media *media) {
if (pt->codec_def)
return;
@ -1075,6 +1097,12 @@ static int handler_func_transcode(struct codec_handler *h, struct media_packet *
return __handler_func_sequencer(h, mp, packet);
}
static int handler_func_playback(struct codec_handler *h, struct media_packet *mp) {
decoder_input_data(h->ssrc_handler->decoder, &mp->payload, mp->rtp->timestamp,
__packet_decoded, h->ssrc_handler, mp);
return 0;
}

@ -7,6 +7,10 @@
#include "timerthread.h"
#include "call.h"
#include "str.h"
#include "rtplib.h"
#include "codec.h"
#include "media_socket.h"
#include "ssrc.h"
@ -20,6 +24,13 @@ static void media_player_shutdown(struct media_player *mp) {
timerthread_obj_deschedule(&mp->tt_obj);
avformat_free_context(mp->fmtctx);
mp->fmtctx = NULL;
mp->media = NULL;
if (mp->handler)
codec_handler_free(mp->handler);
mp->handler = NULL;
if (mp->ssrc_out)
obj_put(&mp->ssrc_out->parent->h);
mp->ssrc_out = NULL;
}
@ -49,6 +60,7 @@ struct media_player *media_player_new(struct call_monologue *ml) {
mutex_init(&mp->lock);
mp->call = obj_get(ml->call);
mp->ml = ml;
mp->seq = random();
av_init_packet(&mp->pkt);
mp->pkt.data = NULL;
@ -58,6 +70,50 @@ struct media_player *media_player_new(struct call_monologue *ml) {
}
static int __ensure_codec_handler(struct media_player *mp, AVStream *avs) {
if (mp->handler)
return 0;
// synthesise rtp payload type
struct rtp_payload_type src_pt = { .payload_type = -1 };
// src_pt.codec_def = codec_find_by_av(avs->codec->codec_id); `codec` is deprecated
src_pt.codec_def = codec_find_by_av(avs->codecpar->codec_id);
if (!src_pt.codec_def) {
ilog(LOG_ERR, "Attempting to play media from an unsupported file format/codec");
return -1;
}
src_pt.encoding = src_pt.codec_def->rtpname_str;
src_pt.channels = avs->codecpar->channels;
src_pt.clock_rate = avs->codecpar->sample_rate;
codec_init_payload_type(&src_pt, mp->media);
// find suitable output payload type
struct rtp_payload_type *dst_pt;
for (GList *l = mp->media->codecs_prefs_send.head; l; l = l->next) {
dst_pt = l->data;
if (dst_pt->codec_def && !dst_pt->codec_def->pseudocodec)
goto found;
}
dst_pt = NULL;
found:
if (!dst_pt) {
ilog(LOG_ERR, "No supported output codec found in SDP");
return -1;
}
ilog(LOG_DEBUG, "Output codec for media playback is " STR_FORMAT,
STR_FMT(&dst_pt->encoding_with_params));
mp->handler = codec_handler_make_playback(&src_pt, dst_pt);
if (!mp->handler)
return -1;
mp->ssrc_out = get_ssrc_ctx(random(), mp->call->ssrc_hash, SSRC_DIR_OUTPUT);
if (!mp->ssrc_out)
return -1;
return 0;
}
// appropriate lock must be held
static void media_player_read_packet(struct media_player *mp) {
int ret = av_read_frame(mp->fmtctx, &mp->pkt);
@ -81,11 +137,50 @@ static void media_player_read_packet(struct media_player *mp) {
goto out;
}
if (__ensure_codec_handler(mp, avs))
goto out;
// scale pts and duration according to sample rate
long long duration_scaled = mp->pkt.duration * avs->codecpar->sample_rate
* avs->time_base.num / avs->time_base.den;
unsigned long long pts_scaled = mp->pkt.pts * avs->codecpar->sample_rate
* avs->time_base.num / avs->time_base.den;
long long us_dur = mp->pkt.duration * 1000000LL * avs->time_base.num / avs->time_base.den;
ilog(LOG_DEBUG, "read media packet: duration %llu (%lli us), time_base %i/%i",
(unsigned long long) mp->pkt.duration, us_dur,
ilog(LOG_DEBUG, "read media packet: pts %llu duration %lli (scaled %llu/%lli, %lli us), "
"sample rate %i, time_base %i/%i",
(unsigned long long) mp->pkt.pts,
(long long) mp->pkt.duration,
pts_scaled,
duration_scaled,
us_dur,
avs->codecpar->sample_rate,
avs->time_base.num, avs->time_base.den);
// synthesise fake RTP header and media_packet context
struct rtp_header rtp = {
.timestamp = pts_scaled, // taken verbatim by handler_func_playback w/o byte swap
.seq_num = htons(mp->seq++),
};
struct media_packet packet = {
.tv = rtpe_now,
.call = mp->call,
.media = mp->media,
.rtp = &rtp,
.ssrc_out = mp->ssrc_out,
};
str_init_len(&packet.raw, (char *) mp->pkt.data, mp->pkt.size);
packet.payload = packet.raw;
mp->handler->func(mp->handler, &packet);
mutex_lock(&mp->sink->out_lock);
if (media_socket_dequeue(&packet, mp->sink))
ilog(LOG_ERR, "Error sending playback media to RTP sink");
mutex_unlock(&mp->sink->out_lock);
timeval_add_usec(&mp->next_run, us_dur);
timerthread_obj_schedule_abs(&mp->tt_obj, &mp->next_run);
@ -98,6 +193,27 @@ out:
int media_player_play_file(struct media_player *mp, const str *file) {
media_player_shutdown(mp);
// find call media suitable for playback
struct call_media *media;
for (GList *l = mp->ml->medias.head; l; l = l->next) {
media = l->data;
if (media->type_id != MT_AUDIO)
continue;
if (!MEDIA_ISSET(media, SEND))
continue;
if (media->streams.length == 0)
continue;
goto found;
}
media = NULL;
found:
if (!media) {
ilog(LOG_ERR, "No suitable SDP section for media playback");
return -1;
}
mp->media = media;
mp->sink = media->streams.head->data;
char file_s[PATH_MAX];
snprintf(file_s, sizeof(file_s), STR_FORMAT, STR_FMT(file));

@ -14,6 +14,7 @@ struct codec_handler;
struct media_packet;
struct ssrc_hash;
struct sdp_ng_flags;
struct codec_ssrc_handler;
typedef int codec_handler_func(struct codec_handler *, struct media_packet *);
@ -27,6 +28,9 @@ struct codec_handler {
int transcoder:1;
struct ssrc_hash *ssrc_hash;
// for media playback
struct codec_ssrc_handler *ssrc_handler;
};
struct codec_packet {
@ -37,6 +41,9 @@ struct codec_packet {
struct codec_handler *codec_handler_get(struct call_media *, int payload_type);
void codec_handlers_free(struct call_media *);
struct codec_handler *codec_handler_make_playback(struct rtp_payload_type *src_pt,
struct rtp_payload_type *dst_pt);
void codec_handler_free(struct codec_handler *handler);
void codec_add_raw_packet(struct media_packet *mp);
void codec_packet_free(void *);

@ -12,6 +12,9 @@
struct call;
struct call_monologue;
struct codec_handler;
struct ssrc_ctx;
struct packet_stream;
struct media_player {
@ -19,11 +22,16 @@ struct media_player {
mutex_t lock;
struct call *call;
struct call_monologue *ml;
struct call_media *media;
struct packet_stream *sink;
struct timeval next_run;
AVFormatContext *fmtctx;
AVPacket pkt;
struct codec_handler *handler;
struct ssrc_ctx *ssrc_out;
unsigned long seq;
};

@ -379,6 +379,16 @@ const codec_def_t *codec_find(const str *name, enum media_type type) {
return ret;
}
const codec_def_t *codec_find_by_av(enum AVCodecID id) {
// XXX hash this
for (int i = 0; i < G_N_ELEMENTS(__codec_defs); i++) {
codec_def_t *def = &__codec_defs[i];
if (def->avcodec_id == id)
return def;
}
return NULL;
}
enum media_type codec_get_type(const str *type) {
if (!type || !type->len)
return MT_UNKNOWN;

@ -191,6 +191,7 @@ void codeclib_init(int);
const codec_def_t *codec_find(const str *name, enum media_type);
const codec_def_t *codec_find_by_av(enum AVCodecID);
enum media_type codec_get_type(const str *type);

Loading…
Cancel
Save