|
|
|
|
@ -20,6 +20,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static struct timerthread media_player_thread;
|
|
|
|
|
static struct timerthread send_timer_thread;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -83,6 +84,85 @@ struct media_player *media_player_new(struct call_monologue *ml) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void __send_timer_free(void *p) {
|
|
|
|
|
struct send_timer *st = p;
|
|
|
|
|
|
|
|
|
|
ilog(LOG_DEBUG, "freeing send_timer");
|
|
|
|
|
|
|
|
|
|
g_queue_clear_full(&st->packets, codec_packet_free);
|
|
|
|
|
mutex_destroy(&st->lock);
|
|
|
|
|
obj_put(st->call);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// call->master_lock held in W
|
|
|
|
|
struct send_timer *send_timer_new(struct packet_stream *ps) {
|
|
|
|
|
ilog(LOG_DEBUG, "creating send_timer");
|
|
|
|
|
|
|
|
|
|
struct send_timer *st = obj_alloc0("send_timer", sizeof(*st), __send_timer_free);
|
|
|
|
|
st->tt_obj.tt = &send_timer_thread;
|
|
|
|
|
mutex_init(&st->lock);
|
|
|
|
|
st->call = obj_get(ps->call);
|
|
|
|
|
st->sink = ps;
|
|
|
|
|
g_queue_init(&st->packets);
|
|
|
|
|
|
|
|
|
|
return st;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// st->stream->out_lock (or call->master_lock/W) must be held already
|
|
|
|
|
static int send_timer_send(struct send_timer *st, struct codec_packet *cp) {
|
|
|
|
|
if (cp->to_send.tv_sec && timeval_cmp(&cp->to_send, &rtpe_now) > 0)
|
|
|
|
|
return -1; // not yet
|
|
|
|
|
|
|
|
|
|
if (!st->sink->selected_sfd)
|
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
|
|
struct rtp_header *rh = (void *) cp->s.s;
|
|
|
|
|
ilog(LOG_DEBUG, "Forward to sink endpoint: %s:%d (RTP seq %u TS %u)",
|
|
|
|
|
sockaddr_print_buf(&st->sink->endpoint.address),
|
|
|
|
|
st->sink->endpoint.port,
|
|
|
|
|
ntohs(rh->seq_num),
|
|
|
|
|
ntohl(rh->timestamp));
|
|
|
|
|
|
|
|
|
|
socket_sendto(&st->sink->selected_sfd->socket,
|
|
|
|
|
cp->s.s, cp->s.len, &st->sink->endpoint);
|
|
|
|
|
|
|
|
|
|
out:
|
|
|
|
|
codec_packet_free(cp);
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// st->stream->out_lock (or call->master_lock/W) must be held already
|
|
|
|
|
void send_timer_push(struct send_timer *st, struct codec_packet *cp) {
|
|
|
|
|
// can we send immediately?
|
|
|
|
|
if (!send_timer_send(st, cp))
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
// queue for sending
|
|
|
|
|
|
|
|
|
|
struct rtp_header *rh = (void *) cp->s.s;
|
|
|
|
|
ilog(LOG_DEBUG, "queuing up packet for delivery at %lu.%06u (RTP seq %u TS %u)",
|
|
|
|
|
(unsigned long) cp->to_send.tv_sec,
|
|
|
|
|
(unsigned int) cp->to_send.tv_usec,
|
|
|
|
|
ntohs(rh->seq_num),
|
|
|
|
|
ntohl(rh->timestamp));
|
|
|
|
|
|
|
|
|
|
mutex_lock(&st->lock);
|
|
|
|
|
unsigned int qlen = st->packets.length;
|
|
|
|
|
// this hands over ownership of cp, so we must copy the timeval out
|
|
|
|
|
struct timeval tv_send = cp->to_send;
|
|
|
|
|
g_queue_push_tail(&st->packets, cp);
|
|
|
|
|
mutex_unlock(&st->lock);
|
|
|
|
|
|
|
|
|
|
// first packet in? we're probably not scheduled yet
|
|
|
|
|
if (!qlen)
|
|
|
|
|
timerthread_obj_schedule_abs(&st->tt_obj, &tv_send);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static int __ensure_codec_handler(struct media_player *mp, AVStream *avs) {
|
|
|
|
|
if (mp->handler)
|
|
|
|
|
return 0;
|
|
|
|
|
@ -189,6 +269,10 @@ static void media_player_read_packet(struct media_player *mp) {
|
|
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
|
|
|
|
|
mutex_lock(&mp->sink->out_lock);
|
|
|
|
|
if (media_socket_dequeue(&packet, mp->sink))
|
|
|
|
|
ilog(LOG_ERR, "Error sending playback media to RTP sink");
|
|
|
|
|
@ -234,6 +318,8 @@ found:
|
|
|
|
|
// call->master_lock held in W
|
|
|
|
|
static void media_player_play_start(struct media_player *mp) {
|
|
|
|
|
mp->next_run = rtpe_now;
|
|
|
|
|
// give ourselves a bit of a head start with decoding
|
|
|
|
|
timeval_add_usec(&mp->next_run, -50000);
|
|
|
|
|
media_player_read_packet(mp);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@ -365,8 +451,44 @@ static void media_player_run(void *ptr) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void send_timer_run(void *ptr) {
|
|
|
|
|
struct send_timer *st = ptr;
|
|
|
|
|
struct call *call = st->call;
|
|
|
|
|
|
|
|
|
|
log_info_call(call);
|
|
|
|
|
|
|
|
|
|
ilog(LOG_DEBUG, "running scheduled send_timer");
|
|
|
|
|
|
|
|
|
|
struct timeval next_send = {0,};
|
|
|
|
|
|
|
|
|
|
rwlock_lock_r(&call->master_lock);
|
|
|
|
|
mutex_lock(&st->lock);
|
|
|
|
|
|
|
|
|
|
while (st->packets.length) {
|
|
|
|
|
struct codec_packet *cp = st->packets.head->data;
|
|
|
|
|
// XXX this could be made lock-free
|
|
|
|
|
if (!send_timer_send(st, cp)) {
|
|
|
|
|
g_queue_pop_head(&st->packets);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
// couldn't send the last one. remember time to schedule
|
|
|
|
|
next_send = cp->to_send;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
mutex_unlock(&st->lock);
|
|
|
|
|
rwlock_unlock_r(&call->master_lock);
|
|
|
|
|
|
|
|
|
|
if (next_send.tv_sec)
|
|
|
|
|
timerthread_obj_schedule_abs(&st->tt_obj, &next_send);
|
|
|
|
|
|
|
|
|
|
log_info_clear();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void media_player_init(void) {
|
|
|
|
|
timerthread_init(&media_player_thread, media_player_run);
|
|
|
|
|
timerthread_init(&send_timer_thread, send_timer_run);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -374,3 +496,7 @@ void media_player_loop(void *p) {
|
|
|
|
|
ilog(LOG_DEBUG, "media_player_loop");
|
|
|
|
|
timerthread_run(&media_player_thread);
|
|
|
|
|
}
|
|
|
|
|
void send_timer_loop(void *p) {
|
|
|
|
|
ilog(LOG_DEBUG, "send_timer_loop");
|
|
|
|
|
timerthread_run(&send_timer_thread);
|
|
|
|
|
}
|
|
|
|
|
|