|
|
|
@ -33,6 +33,8 @@ static struct timerthread send_timer_thread; |
|
|
|
static void send_timer_send_nolock(struct send_timer *st, struct codec_packet *cp); |
|
|
|
static void send_timer_send_lock(struct send_timer *st, struct codec_packet *cp); |
|
|
|
|
|
|
|
static void media_player_read_packet(struct media_player *mp); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef WITH_TRANSCODING |
|
|
|
@ -112,6 +114,7 @@ struct media_player *media_player_new(struct call_monologue *ml) { |
|
|
|
|
|
|
|
mp->tt_obj.tt = &media_player_thread; |
|
|
|
mutex_init(&mp->lock); |
|
|
|
mp->run_func = media_player_read_packet; // default |
|
|
|
mp->call = obj_get(ml->call); |
|
|
|
mp->ml = ml; |
|
|
|
mp->seq = random(); |
|
|
|
@ -214,29 +217,9 @@ void send_timer_push(struct send_timer *st, struct codec_packet *cp) { |
|
|
|
|
|
|
|
#ifdef WITH_TRANSCODING |
|
|
|
|
|
|
|
#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(57, 26, 0) |
|
|
|
#define CODECPAR codecpar |
|
|
|
#else |
|
|
|
#define CODECPAR codec |
|
|
|
#endif |
|
|
|
|
|
|
|
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); |
|
|
|
|
|
|
|
int media_player_setup(struct media_player *mp, const struct rtp_payload_type *src_pt) { |
|
|
|
// find suitable output payload type |
|
|
|
struct rtp_payload_type *dst_pt; |
|
|
|
for (GList *l = mp->media->codecs_prefs_send.head; l; l = l->next) { |
|
|
|
@ -260,16 +243,93 @@ found: |
|
|
|
mp->sync_ts += ts_diff_us * dst_pt->clock_rate / 1000000 / dst_pt->codec_def->clockrate_mult; |
|
|
|
} |
|
|
|
|
|
|
|
mp->handler = codec_handler_make_playback(&src_pt, dst_pt, mp->sync_ts); |
|
|
|
mp->handler = codec_handler_make_playback(src_pt, dst_pt, mp->sync_ts); |
|
|
|
if (!mp->handler) |
|
|
|
return -1; |
|
|
|
|
|
|
|
return 0; |
|
|
|
} |
|
|
|
|
|
|
|
#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(57, 26, 0) |
|
|
|
#define CODECPAR codecpar |
|
|
|
#else |
|
|
|
#define CODECPAR codec |
|
|
|
#endif |
|
|
|
|
|
|
|
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); |
|
|
|
|
|
|
|
if (media_player_setup(mp, &src_pt)) |
|
|
|
return -1; |
|
|
|
|
|
|
|
mp->duration = avs->duration * 1000 * avs->time_base.num / avs->time_base.den; |
|
|
|
|
|
|
|
return 0; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// appropriate lock must be held |
|
|
|
void media_player_add_packet(struct media_player *mp, char *buf, size_t len, |
|
|
|
long long us_dur, unsigned long long pts) |
|
|
|
{ |
|
|
|
// synthesise fake RTP header and media_packet context |
|
|
|
|
|
|
|
struct rtp_header rtp = { |
|
|
|
.timestamp = pts, // 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, buf, len); |
|
|
|
packet.payload = packet.raw; |
|
|
|
|
|
|
|
mp->handler->func(mp->handler, &packet); |
|
|
|
|
|
|
|
// as this is timing sensitive and we may have spent some time decoding, |
|
|
|
// update our global "now" timestamp |
|
|
|
gettimeofday(&rtpe_now, NULL); |
|
|
|
|
|
|
|
// keep track of RTP timestamps and real clock. look at the last packet we received |
|
|
|
// and update our sync TS. |
|
|
|
if (packet.packets_out.head) { |
|
|
|
struct codec_packet *p = packet.packets_out.head->data; |
|
|
|
if (p->rtp) { |
|
|
|
mp->sync_ts = ntohl(p->rtp->timestamp); |
|
|
|
mp->sync_ts_tv = p->ttq_entry.when; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
media_packet_encrypt(mp->crypt_handler->out->rtp_crypt, mp->sink, &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); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// appropriate lock must be held |
|
|
|
static void media_player_read_packet(struct media_player *mp) { |
|
|
|
if (!mp->fmtctx) |
|
|
|
@ -317,50 +377,20 @@ static void media_player_read_packet(struct media_player *mp) { |
|
|
|
avs->CODECPAR->sample_rate, |
|
|
|
avs->time_base.num, avs->time_base.den); |
|
|
|
|
|
|
|
// synthesise fake RTP header and media_packet context |
|
|
|
media_player_add_packet(mp, (char *) mp->pkt.data, mp->pkt.size, us_dur, pts_scaled); |
|
|
|
|
|
|
|
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); |
|
|
|
out: |
|
|
|
av_packet_unref(&mp->pkt); |
|
|
|
} |
|
|
|
|
|
|
|
// as this is timing sensitive and we may have spent some time decoding, |
|
|
|
// update our global "now" timestamp |
|
|
|
gettimeofday(&rtpe_now, NULL); |
|
|
|
|
|
|
|
// keep track of RTP timestamps and real clock. look at the last packet we received |
|
|
|
// and update our sync TS. |
|
|
|
if (packet.packets_out.head) { |
|
|
|
struct codec_packet *p = packet.packets_out.head->data; |
|
|
|
if (p->rtp) { |
|
|
|
mp->sync_ts = ntohl(p->rtp->timestamp); |
|
|
|
mp->sync_ts_tv = p->ttq_entry.when; |
|
|
|
} |
|
|
|
// call->master_lock held in W |
|
|
|
void media_player_set_media(struct media_player *mp, struct call_media *media) { |
|
|
|
mp->media = media; |
|
|
|
if (media->streams.head) { |
|
|
|
mp->sink = media->streams.head->data; |
|
|
|
mp->crypt_handler = determine_handler(&transport_protocols[PROTO_RTP_AVP], media, 1); |
|
|
|
} |
|
|
|
|
|
|
|
media_packet_encrypt(mp->crypt_handler->out->rtp_crypt, mp->sink, &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); |
|
|
|
|
|
|
|
out: |
|
|
|
av_packet_unref(&mp->pkt); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@ -386,9 +416,7 @@ found: |
|
|
|
ilog(LOG_ERR, "No suitable SDP section for media playback"); |
|
|
|
return -1; |
|
|
|
} |
|
|
|
mp->media = media; |
|
|
|
mp->sink = media->streams.head->data; |
|
|
|
mp->crypt_handler = determine_handler(&transport_protocols[PROTO_RTP_AVP], media, 1); |
|
|
|
media_player_set_media(mp, media); |
|
|
|
|
|
|
|
return 0; |
|
|
|
} |
|
|
|
@ -629,7 +657,7 @@ static void media_player_run(void *ptr) { |
|
|
|
rwlock_lock_r(&call->master_lock); |
|
|
|
mutex_lock(&mp->lock); |
|
|
|
|
|
|
|
media_player_read_packet(mp); |
|
|
|
mp->run_func(mp); |
|
|
|
|
|
|
|
mutex_unlock(&mp->lock); |
|
|
|
rwlock_unlock_r(&call->master_lock); |
|
|
|
|