/**
* Streaming helpers
* Copyright (C) 2008 Andreas Ă–man
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include
#include "tvheadend.h"
#include "streaming.h"
#include "packet.h"
#include "atomic.h"
#include "service.h"
void
streaming_pad_init(streaming_pad_t *sp)
{
LIST_INIT(&sp->sp_targets);
}
/**
*
*/
void
streaming_target_init(streaming_target_t *st, st_callback_t *cb, void *opaque,
int reject_filter)
{
st->st_cb = cb;
st->st_opaque = opaque;
st->st_reject_filter = reject_filter;
}
/**
*
*/
static void
streaming_queue_deliver(void *opauqe, streaming_message_t *sm)
{
streaming_queue_t *sq = opauqe;
pthread_mutex_lock(&sq->sq_mutex);
/* queue size protection */
// TODO: would be better to update size as we go, but this would
// require updates elsewhere to ensure all removals from the queue
// are covered (new function)
if (sq->sq_maxsize && streaming_queue_size(&sq->sq_queue) >= sq->sq_maxsize)
streaming_msg_free(sm);
else
TAILQ_INSERT_TAIL(&sq->sq_queue, sm, sm_link);
pthread_cond_signal(&sq->sq_cond);
pthread_mutex_unlock(&sq->sq_mutex);
}
/**
*
*/
void
streaming_queue_init2(streaming_queue_t *sq, int reject_filter, size_t maxsize)
{
streaming_target_init(&sq->sq_st, streaming_queue_deliver, sq, reject_filter);
pthread_mutex_init(&sq->sq_mutex, NULL);
pthread_cond_init(&sq->sq_cond, NULL);
TAILQ_INIT(&sq->sq_queue);
sq->sq_maxsize = maxsize;
}
/**
*
*/
void
streaming_queue_init(streaming_queue_t *sq, int reject_filter)
{
streaming_queue_init2(sq, reject_filter, 0); // 0 = unlimited
}
/**
*
*/
void
streaming_queue_deinit(streaming_queue_t *sq)
{
streaming_queue_clear(&sq->sq_queue);
pthread_mutex_destroy(&sq->sq_mutex);
pthread_cond_destroy(&sq->sq_cond);
}
/**
*
*/
void
streaming_target_connect(streaming_pad_t *sp, streaming_target_t *st)
{
sp->sp_ntargets++;
st->st_pad = sp;
LIST_INSERT_HEAD(&sp->sp_targets, st, st_link);
}
/**
*
*/
void
streaming_target_disconnect(streaming_pad_t *sp, streaming_target_t *st)
{
sp->sp_ntargets--;
st->st_pad = NULL;
LIST_REMOVE(st, st_link);
}
/**
*
*/
streaming_message_t *
streaming_msg_create(streaming_message_type_t type)
{
streaming_message_t *sm = malloc(sizeof(streaming_message_t));
sm->sm_type = type;
return sm;
}
/**
*
*/
streaming_message_t *
streaming_msg_create_pkt(th_pkt_t *pkt)
{
streaming_message_t *sm = streaming_msg_create(SMT_PACKET);
sm->sm_data = pkt;
pkt_ref_inc(pkt);
return sm;
}
/**
*
*/
streaming_message_t *
streaming_msg_create_data(streaming_message_type_t type, void *data)
{
streaming_message_t *sm = streaming_msg_create(type);
sm->sm_data = data;
return sm;
}
/**
*
*/
streaming_message_t *
streaming_msg_create_code(streaming_message_type_t type, int code)
{
streaming_message_t *sm = streaming_msg_create(type);
sm->sm_code = code;
return sm;
}
/**
*
*/
streaming_message_t *
streaming_msg_clone(streaming_message_t *src)
{
streaming_message_t *dst = malloc(sizeof(streaming_message_t));
streaming_start_t *ss;
dst->sm_type = src->sm_type;
switch(src->sm_type) {
case SMT_PACKET:
pkt_ref_inc(src->sm_data);
dst->sm_data = src->sm_data;
break;
case SMT_START:
ss = dst->sm_data = src->sm_data;
atomic_add(&ss->ss_refcount, 1);
break;
case SMT_SIGNAL_STATUS:
dst->sm_data = malloc(sizeof(signal_status_t));
memcpy(dst->sm_data, src->sm_data, sizeof(signal_status_t));
break;
case SMT_STOP:
case SMT_SERVICE_STATUS:
case SMT_NOSTART:
dst->sm_code = src->sm_code;
break;
case SMT_EXIT:
break;
case SMT_MPEGTS:
pktbuf_ref_inc(src->sm_data);
dst->sm_data = src->sm_data;
break;
default:
abort();
}
return dst;
}
/**
*
*/
void
streaming_start_unref(streaming_start_t *ss)
{
int i;
if((atomic_add(&ss->ss_refcount, -1)) != 1)
return;
service_source_info_free(&ss->ss_si);
for(i = 0; i < ss->ss_num_components; i++)
if(ss->ss_components[i].ssc_gh)
pktbuf_ref_dec(ss->ss_components[i].ssc_gh);
free(ss);
}
/**
*
*/
void
streaming_msg_free(streaming_message_t *sm)
{
switch(sm->sm_type) {
case SMT_PACKET:
if(sm->sm_data)
pkt_ref_dec(sm->sm_data);
break;
case SMT_START:
if(sm->sm_data)
streaming_start_unref(sm->sm_data);
break;
case SMT_STOP:
break;
case SMT_EXIT:
break;
case SMT_SERVICE_STATUS:
break;
case SMT_NOSTART:
break;
case SMT_SIGNAL_STATUS:
free(sm->sm_data);
break;
case SMT_MPEGTS:
if(sm->sm_data)
pktbuf_ref_dec(sm->sm_data);
break;
default:
abort();
}
free(sm);
}
/**
*
*/
void
streaming_target_deliver2(streaming_target_t *st, streaming_message_t *sm)
{
if(st->st_reject_filter & SMT_TO_MASK(sm->sm_type))
streaming_msg_free(sm);
else
st->st_cb(st->st_opaque, sm);
}
/**
*
*/
void
streaming_pad_deliver(streaming_pad_t *sp, streaming_message_t *sm)
{
streaming_target_t *st, *next;
for(st = LIST_FIRST(&sp->sp_targets);st; st = next) {
next = LIST_NEXT(st, st_link);
if(st->st_reject_filter & SMT_TO_MASK(sm->sm_type))
continue;
st->st_cb(st->st_opaque, streaming_msg_clone(sm));
}
}
/**
*
*/
int
streaming_pad_probe_type(streaming_pad_t *sp, streaming_message_type_t smt)
{
streaming_target_t *st;
LIST_FOREACH(st, &sp->sp_targets, st_link) {
if(!(st->st_reject_filter & SMT_TO_MASK(smt)))
return 1;
}
return 0;
}
/**
*
*/
void
streaming_queue_clear(struct streaming_message_queue *q)
{
streaming_message_t *sm;
while((sm = TAILQ_FIRST(q)) != NULL) {
TAILQ_REMOVE(q, sm, sm_link);
streaming_msg_free(sm);
}
}
/**
*
*/
size_t streaming_queue_size(struct streaming_message_queue *q)
{
streaming_message_t *sm;
int size = 0;
TAILQ_FOREACH(sm, q, sm_link) {
if (sm->sm_type == SMT_PACKET)
{
th_pkt_t *pkt = sm->sm_data;
if (pkt && pkt->pkt_payload)
{
size += pkt->pkt_payload->pb_size;
}
}
else if (sm->sm_type == SMT_MPEGTS)
{
pktbuf_t *pkt_payload = sm->sm_data;
if (pkt_payload)
{
size += pkt_payload->pb_size;
}
}
}
return size;
}
/**
*
*/
const char *
streaming_code2txt(int code)
{
static __thread char ret[64];
switch(code) {
case SM_CODE_OK: return "OK";
case SM_CODE_SOURCE_RECONFIGURED:
return "Source reconfigured";
case SM_CODE_BAD_SOURCE:
return "Source quality is bad";
case SM_CODE_SOURCE_DELETED:
return "Source deleted";
case SM_CODE_SUBSCRIPTION_OVERRIDDEN:
return "Subscription overridden";
case SM_CODE_NO_HW_ATTACHED:
return "No hardware present";
case SM_CODE_MUX_NOT_ENABLED:
return "Mux not enabled";
case SM_CODE_NOT_FREE:
return "Adapter in use by other subscription";
case SM_CODE_TUNING_FAILED:
return "Tuning failed";
case SM_CODE_SVC_NOT_ENABLED:
return "No service enabled";
case SM_CODE_BAD_SIGNAL:
return "Too bad signal quality";
case SM_CODE_NO_SOURCE:
return "No source available";
case SM_CODE_NO_SERVICE:
return "No service assigned to channel";
case SM_CODE_ABORTED:
return "Aborted by user";
case SM_CODE_NO_DESCRAMBLER:
return "No descrambler";
case SM_CODE_NO_ACCESS:
return "No access";
case SM_CODE_NO_INPUT:
return "No input detected";
default:
snprintf(ret, sizeof(ret), "Unknown reason (%i)", code);
return ret;
}
}
/**
*
*/
streaming_start_t *
streaming_start_copy(const streaming_start_t *src)
{
int i;
size_t siz = sizeof(streaming_start_t) +
sizeof(streaming_start_component_t) * src->ss_num_components;
streaming_start_t *dst = malloc(siz);
memcpy(dst, src, siz);
service_source_info_copy(&dst->ss_si, &src->ss_si);
for(i = 0; i < dst->ss_num_components; i++) {
streaming_start_component_t *ssc = &dst->ss_components[i];
if(ssc->ssc_gh != NULL)
pktbuf_ref_inc(ssc->ssc_gh);
}
dst->ss_refcount = 1;
return dst;
}
/**
*
*/
streaming_start_component_t *
streaming_start_component_find_by_index(streaming_start_t *ss, int idx)
{
int i;
for(i = 0; i < ss->ss_num_components; i++) {
streaming_start_component_t *ssc = &ss->ss_components[i];
if(ssc->ssc_index == idx)
return ssc;
}
return NULL;
}