/** * @brief Tells how many buffers are queued to be seen * * @param consumer The consumer object to check * * @return The number of buffers queued in the producer that have not * been seen. * * @note This function will require exclusive access to the producer, * and will thus lock its mutex. */ gulong bq_consumer_unseen(RTP_session *consumer) { Track *producer = consumer->track; gulong unseen = 0; if (bq_consumer_stopped(consumer)) return unseen; /* Ensure we have the exclusive access */ g_mutex_lock(producer->lock); if ( consumer->queue_serial != producer->queue_serial ) unseen = g_queue_get_length(producer->queue); else if ( producer->queue->head != NULL ) unseen = producer->next_serial - consumer->last_element_serial; /* Leave the exclusive access */ g_mutex_unlock(producer->lock); return unseen; }
/** * @brief Get the next element from the consumer list * * @param consumer The consumer object to get the data from * * @return A pointer to the newly selected element * * @retval NULL No element can be read; this might be due to no data * present in the producer, or if the producer was * stopped. To know which one of the two conditions * happened, @ref bq_consumer_stopped should be called. * * @note This function will require exclusive access to the producer, * and will thus lock its mutex. * * This function marks as seen the previously-selected element, if * any; the new selection is not freed until the cursor is moved or * the consumer is deleted. */ struct MParserBuffer *bq_consumer_get(RTP_session *consumer) { Track *producer = consumer->track; struct MParserBuffer *element = NULL; GList *c_cep; if ( bq_consumer_stopped(consumer) ) return NULL; /* Ensure we have the exclusive access */ g_mutex_lock(producer->lock); bq_debug("C:%p LES:%lu:%u PQHS:%lu:%u PQH:%p pointer %p", consumer, consumer->queue_serial, consumer->last_element_serial, producer->queue_serial, producer->queue->head ? GLIST_TO_BQELEM(producer->queue->head)->seq_no : 0, producer->queue->head, consumer->current_element_pointer); c_cep = bq_consumer_confirm_pointer(consumer); /* If we don't have a queue yet, like for the first read, “move * next” (or rather first). */ if ( c_cep == NULL ) bq_consumer_move_internal(consumer); element = BQ_OBJECT(consumer); bq_debug("C:%p pointer %p object %p seen %lu/%d", consumer, consumer->current_element_pointer, element, element ? element->seen : 0, producer->consumers); /* Leave the exclusive access */ g_mutex_unlock(producer->lock); return element; }
/** * @brief Move to the next element in a consumer * * @param consumer The consumer object to move * * @retval true The move was successful * @retval false The move wasn't successful, the producer may be stopped. * * @note This function will require exclusive access to the producer, * and will thus lock its mutex. * * This is actually just a locking-wrapper around @ref * bq_consumer_move_internal. */ gboolean bq_consumer_move(RTP_session *consumer) { Track *producer = consumer->track; gboolean ret; bq_debug("(before) C:%p pointer %p", consumer, consumer->current_element_pointer); if ( bq_consumer_stopped(consumer) ) return false; /* Ensure we have the exclusive access */ g_mutex_lock(producer->lock); ret = bq_consumer_move_internal(consumer); bq_debug("(after) C:%p pointer %p", consumer, consumer->current_element_pointer); /* Leave the exclusive access */ g_mutex_unlock(producer->lock); return ret; }
/** * Send pending RTP packets to a session. * * @param loop eventloop * @param w contains the session the RTP session for which to send the packets * @todo implement a saner ratecontrol */ static void rtp_write_cb(struct ev_loop *loop, ev_periodic *w, ATTR_UNUSED int revents) { RTP_session *session = w->data; Resource *resource = session->track->parent; MParserBuffer *buffer = NULL; ev_tstamp next_time = w->offset; #ifdef HAVE_METADATA if (session->metadata) cpd_send(session, now); #endif /* If there is no buffer, it means that either the producer * has been stopped (as we reached the end of stream) or that * there is no data for the consumer to read. If that's the * case we just give control back to the main loop for now. */ if ( bq_consumer_stopped(session->consumer) ) { /* If the producer has been stopped, we send the * finishing packets and go away. */ fnc_log(FNC_LOG_INFO, "[rtp] Stream Finished"); rtcp_send_sr(session, BYE); return; } /* Check whether we have enough extra frames to send. If we have * no extra frames we have a problem, since we're going to send * one packet at least. */ if (resource->eor) fnc_log(FNC_LOG_INFO, "[%s] end of resource %d packets to be fetched", session->track->properties.encoding_name, bq_consumer_unseen(session->consumer)); /* Get the current buffer, if there is enough data */ if ( !(buffer = bq_consumer_get(session->consumer)) ) { /* We wait a bit of time to get the data but before it is * expired. */ if (resource->eor) { fnc_log(FNC_LOG_INFO, "[rtp] Stream Finished"); rtcp_send_sr(session, BYE); return; } next_time += 0.01; // assumed to be enough } else { MParserBuffer *next; double delivery = buffer->delivery; double timestamp = buffer->timestamp; double duration = buffer->duration; gboolean marker = buffer->marker; rtp_packet_send(session, buffer); if (session->pkt_count % 29 == 1) rtcp_send_sr(session, SDES); if (bq_consumer_move(session->consumer)) { next = bq_consumer_get(session->consumer); if(delivery != next->delivery) { if (session->track->properties.media_source == MS_live) next_time += next->delivery - delivery; else next_time = session->range->playback_time - session->range->begin_time + next->delivery; } } else { if (marker) next_time += duration; } fnc_log(FNC_LOG_VERBOSE, "[%s] Now: %5.4f, cur %5.4f[%5.4f][%5.4f], next %5.4f %s\n", session->track->properties.encoding_name, ev_now(loop) - session->range->playback_time, delivery, timestamp, duration, next_time - session->range->playback_time, marker? "M" : " "); } ev_periodic_set(w, next_time, 0, NULL); ev_periodic_again(loop, w); rtp_session_fill(session); }
static gboolean rtp_lwrite_cb(GEvent *ev, gint revents, void *user_data) { #define XBUFLEN 256 RTP_session *rtp_s = (RTP_session*)user_data; Resource *resource = rtp_s->track->parent; MParserBuffer *buffer; gchar x[XBUFLEN]; gint n, fd = g_event_fd(ev); ev_tstamp now; while ( TRUE ) { n = read(fd, x, XBUFLEN); if (n > 0) continue; if (n < 0 && errno == EAGAIN) break; rc_log( RC_LOG_WARN, "[rtp] writer stopped, something wrong with rtp->fd[0]." ); rtcp_send_sr(rtp_s, BYE); return FALSE; } if (G_UNLIKELY(bq_consumer_stopped(rtp_s->consumer))) { rc_log(FNC_LOG_INFO, "[rtp] one live stream finished."); rtcp_send_sr(rtp_s, BYE); return FALSE; } while ( TRUE ) { if (rtp_session_would_block(rtp_s)) { break; } if (!(buffer = bq_consumer_get(rtp_s->consumer))) { if (resource->eor) { fnc_log(FNC_LOG_INFO, "[rtp] Stream Finished"); rtp_packet_send_eof(rtp_s, (time_t)now); rtcp_send_sr(rtp_s, BYE); return FALSE; } break; } if (buffer == LS_EOF) { fnc_log(FNC_LOG_INFO, "[rtp] Stream Finished"); rtp_packet_send_eof(rtp_s, (time_t)now); rtcp_send_sr(rtp_s, BYE); return FALSE; } now = g_event_time_now_sync(ev); rtp_packet_send(rtp_s, buffer, (time_t)now); if (!bq_consumer_move(rtp_s->consumer)) break; } return TRUE; }
static gboolean rtp_timer_cb(GEvent *ev, gint revents, void *user_data) { RTP_session *session = (RTP_session*)user_data; Resource *resource = session->track->parent; MParserBuffer *buffer = NULL; ev_tstamp now, sleep_secs; gint to_send = MAX_SND_BLOCKSIZE; now = g_event_time_now_sync(ev); sleep_secs = .01; #ifdef HAVE_METADATA if (session->metadata) cpd_send(session, now); #endif if (!session->audio_rtp && resource->audio_rtp) {//Fixme, race against rtp_session_kill(resource->audio_rtp) session->audio_rtp = rtp_session_ref(resource->audio_rtp); } for (;;) { if (to_send <= 0) { break; } if (bq_consumer_stopped(session->consumer)) { fnc_log(FNC_LOG_INFO, "[rtp] Stream Finished"); rtcp_send_sr(session, BYE); return FALSE; } if (rtp_session_would_block(session)) { break; } if (!(buffer = bq_consumer_get(session->consumer))) { if (resource->eor) { fnc_log(FNC_LOG_INFO, "[rtp] Stream Finished"); rtp_packet_send_eof(session, (time_t)now); rtcp_send_sr(session, BYE); sleep_secs = 1; break; } sleep_secs = .005; break; } if (buffer->mtype != MP_video) { if (session->audio_rtp) { rtp_packet_send_rtp(session->audio_rtp, buffer, (time_t)now); } } else { rtp_packet_send_rtp(session, buffer, (time_t)now); } to_send -= buffer->data_size; if (!bq_consumer_move(session->consumer)) { break; } } g_event_mod_timer_sync(ev, sleep_secs); rtp_session_fill(session); return TRUE; }