tvhdhomerun: initial cleanup of the plugin

- attempt to mirror linuxdvb more closely
- remove bunch of mutex'es
- implement UDP unicast
- attempt to fix the PID filter issues
This commit is contained in:
Beralt 2014-10-14 12:20:19 +02:00 committed by Jaroslav Kysela
parent e7ca66cbe2
commit 9380934c1b
3 changed files with 269 additions and 216 deletions

View file

@ -280,7 +280,6 @@ static void tvhdhomerun_device_create(struct hdhomerun_discover_device_t *dInfo)
hd->hd_override_type = strdup(dvb_type2str(type));
tvhlog(LOG_INFO, "tvheadend","Using Network type : %s", hd->hd_override_type);
/* some sane defaults */
hd->hd_fullmux_ok = 1;
@ -291,8 +290,7 @@ static void tvhdhomerun_device_create(struct hdhomerun_discover_device_t *dInfo)
hdhomerun_tuner = hdhomerun_device_create(dInfo->device_id, dInfo->ip_addr, 0, NULL);
{
const char *deviceModel = hdhomerun_device_get_model_str(hdhomerun_tuner);
if ( deviceModel != NULL )
{
if(deviceModel != NULL) {
hd->hd_info.deviceModel = strdup(deviceModel);
}
hdhomerun_device_destroy(hdhomerun_tuner);
@ -337,8 +335,6 @@ static void tvhdhomerun_device_create(struct hdhomerun_discover_device_t *dInfo)
htsmsg_destroy(conf);
}
static void
tvhdhomerun_discovery_timer_cb(void *aux)
{
@ -348,7 +344,7 @@ tvhdhomerun_discovery_timer_cb(void *aux)
return;
int numDevices = hdhomerun_discover_find_devices_custom(0,
HDHOMERUN_DEVICE_TYPE_TUNER,
HDHOMERUN_DEVICE_TYPE_TUNER,
HDHOMERUN_DEVICE_ID_WILDCARD,
result_list,
MAX_HDHOMERUN_DEVICES);

View file

@ -24,8 +24,7 @@
#include "streaming.h"
#include "tvhdhomerun_private.h"
static void tvhdhomerun_device_update_pid_filter(tvhdhomerun_frontend_t *hfe, mpegts_mux_t *mm);
static void tvhdhomerun_device_open_pid(tvhdhomerun_frontend_t *hfe, mpegts_pid_t *mp);
static mpegts_pid_t * tvhdhomerun_frontend_open_pid( mpegts_input_t *mi, mpegts_mux_t *mm, int pid, int type, void *owner );
@ -73,73 +72,153 @@ tvhdhomerun_frontend_is_enabled ( mpegts_input_t *mi, mpegts_mux_t *mm,
return 1;
}
static void *
tvhdhomerun_frontend_input_thread ( void *aux )
{
tvhdhomerun_frontend_t *hfe = aux;
mpegts_mux_instance_t *mmi = hfe->hf_mmi;
mpegts_mux_instance_t *mmi;
sbuf_t sb;
char buf[256];
char target[64];
uint32_t local_ip;
int sockfd, nfds;
int sock_opt = 1;
int r;
int rx_size = 1024 * 1024;
struct sockaddr_in sock_addr;
socklen_t sockaddr_len = sizeof(sock_addr);
tvhpoll_event_t ev[2];
tvhpoll_t *efd;
tvhdebug("tvhdhomerun", "Starting input-thread.\n");
tvhdebug("tvhdhomerun", "starting input thread");
/* Get MMI */
pthread_mutex_lock(&hfe->hf_input_thread_mutex);
hfe->mi_display_name((mpegts_input_t*)hfe, buf, sizeof(buf));
mmi = LIST_FIRST(&hfe->mi_mux_active);
pthread_cond_signal(&hfe->hf_input_thread_cond);
pthread_mutex_unlock(&hfe->hf_input_thread_mutex);
if (mmi == NULL) return NULL;
PTHREAD_MUTEX_LOCK(&hfe->hf_input_mux_lock);
tvhdebug("tvhdhomerun", "opening client socket");
if (mmi == NULL) {
tvhlog(LOG_ERR, "tvhdhomerun","mmi == 0");
hfe->hf_input_thread_running = 0;
PTHREAD_MUTEX_UNLOCK(&hfe->hf_input_mux_lock);
/* One would like to use libhdhomerun for the streaming details,
* but that library uses threads on its own and the socket is put
* into a ring buffer. That makes it less practical to use here,
* so we do the whole UPD recv() stuff ourselves. And we can assume
* POSIX here ;)
*/
/* local IP */
/* TODO: this is nasty */
local_ip = hdhomerun_device_get_local_machine_addr(hfe->hf_hdhomerun_tuner);
/* first setup a local socket for the device to stream to */
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if(sockfd == -1) {
tvherror("stvhdhomerun", "failed to open socket (%d)", errno);
return NULL;
}
PTHREAD_MUTEX_UNLOCK(&hfe->hf_input_mux_lock);
int r = hdhomerun_device_stream_start(hfe->hf_hdhomerun_tuner);
if ( r == 0 ) {
tvhlog(LOG_ERR, "tvhdhomerun", "Failed to start stream from HDHomeRun device! (Command rejected!)");
if(fcntl(sockfd, F_SETFL, O_NONBLOCK) != 0) {
close(sockfd);
tvherror("stvhdhomerun", "failed to set socket nonblocking (%d)", errno);
return NULL;
} else if ( r == -1 ) {
tvhlog(LOG_ERR, "tvhdhomerun", "Failed to start stream from HDHomeRun device! (Communication error!)");
return NULL;
} else if ( r != 1 ) {
tvhlog(LOG_ERR, "tvhdhomerun", "UNKNOWN ERROR(%d) %s:%s:%u",r ,__FILE__,__FUNCTION__,__LINE__);
}
sbuf_init_fixed(&sb,VIDEO_DATA_BUFFER_SIZE_1S); // Buffersize in libhdhomerun.
/* enable broadcast */
setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, (char *) &sock_opt, sizeof(sock_opt));
setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (char *) &sock_opt, sizeof(sock_opt));
while (tvheadend_running && hfe->hf_input_thread_terminating == 0 ) {
PTHREAD_MUTEX_LOCK(&hfe->hf_input_mux_lock);
if ( tvheadend_running && hfe->hf_input_thread_terminating == 0 ) {
const size_t maxRead = sb.sb_size-sb.sb_ptr; // Available space in sbuf.
size_t readDataSize = 0;
// TODO: Rewrite input-thread to read the udp stream straight of instead of the wrapper-functions
// of libhdhomerun.
uint8_t *readData = hdhomerun_device_stream_recv(hfe->hf_hdhomerun_tuner, maxRead, &readDataSize);
if ( readDataSize > 0 ) {
sbuf_append(&sb, readData, readDataSize);
/* important: we need large rx buffers to accomodate the large amount of traffic */
setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, (char *) &rx_size, sizeof(rx_size));
memset(&sock_addr, 0, sizeof(sock_addr));
sock_addr.sin_family = AF_INET;
sock_addr.sin_addr.s_addr = htonl(INADDR_ANY);
sock_addr.sin_port = 0;
if(bind(sockfd, (struct sockaddr *) &sock_addr, sizeof(sock_addr)) != 0) {
tvhlog(LOG_ERR, "tvhdhomerun", "failed bind socket: %d", errno);
return NULL;
}
memset(&sock_addr, 0, sizeof(sock_addr));
if(getsockname(sockfd, (struct sockaddr *) &sock_addr, &sockaddr_len) != 0) {
tvhlog(LOG_ERR, "tvhdhomerun", "failed to getsockname: %d", errno);
return NULL;
}
/* pretend we are smart and set video_socket; doubt that this is required though */
//hfe->hf_hdhomerun_tuner->vs = sockfd;
/* and tell the device to stream to the local port */
memset(target, 0, sizeof(target));
snprintf(target, sizeof(target), "udp://%u.%u.%u.%u:%u",
(unsigned int)(local_ip >> 24) & 0xFF,
(unsigned int)(local_ip >> 16) & 0xFF,
(unsigned int)(local_ip >> 8) & 0xFF,
(unsigned int)(local_ip >> 0) & 0xFF,
ntohs(sock_addr.sin_port));
tvhdebug("tvhdhomerun", "setting target to: %s", target);
pthread_mutex_lock(&hfe->hf_hdhomerun_device_mutex);
r = hdhomerun_device_set_tuner_target(hfe->hf_hdhomerun_tuner, target);
pthread_mutex_unlock(&hfe->hf_hdhomerun_device_mutex);
if(r < 1) {
tvhlog(LOG_ERR, "tvhdhomerun", "failed to set target: %d", r);
return NULL;
}
/* the poll set includes the sockfd and the pipe for IPC */
efd = tvhpoll_create(2);
memset(ev, 0, sizeof(ev));
ev[0].events = TVHPOLL_IN;
ev[0].fd = ev[0].data.fd = sockfd;
ev[1].events = TVHPOLL_IN;
ev[1].fd = ev[1].data.fd = hfe->hf_input_thread_pipe.rd;
r = tvhpoll_add(efd, ev, 2);
if(r < 0)
tvhlog(LOG_ERR, "tvhdhomerun", "failed to setup poll");
sbuf_init_fixed(&sb, (20000000 / 8));
/* TODO: flush buffer? */
while(tvheadend_running) {
nfds = tvhpoll_wait(efd, ev, 1, -1);
if (nfds < 1) continue;
if (ev[0].data.fd != sockfd) break;
if((r = sbuf_read(&sb, sockfd)) < 0) {
/* whoopsy */
if(ERRNO_AGAIN(errno))
continue;
if(errno == EOVERFLOW) {
tvhlog(LOG_WARNING, "tvhdhomerun", "%s - read() EOVERFLOW", buf);
continue;
}
PTHREAD_MUTEX_UNLOCK(&hfe->hf_input_mux_lock);
if ( readDataSize > 0 ) {
mpegts_input_recv_packets((mpegts_input_t*)hfe, mmi, &sb, NULL, NULL);
}
usleep(125000);
tvhlog(LOG_ERR, "tvhdhomerun", "%s - read() error %d (%s)",
buf, errno, strerror(errno));
break;
}
//if(r != (7*188))
//continue; /* dude; this is not a valid packet */
//tvhdebug("tvhdhomerun", "got r=%d (thats %d)", r, (r == 7*188));
mpegts_input_recv_packets((mpegts_input_t*) hfe, mmi, &sb, NULL, NULL);
}
hdhomerun_device_stream_stop(hfe->hf_hdhomerun_tuner);
tvhdebug("tvhdhomerun", "setting target to none");
pthread_mutex_lock(&hfe->hf_hdhomerun_device_mutex);
hdhomerun_device_set_tuner_target(hfe->hf_hdhomerun_tuner, "none");
pthread_mutex_unlock(&hfe->hf_hdhomerun_device_mutex);
sbuf_free(&sb);
tvhdebug("tvhdhomerun", "Terminating input-thread.\n");
hfe->hf_input_thread_terminating = 0;
PTHREAD_MUTEX_UNLOCK(&hfe->hf_input_mux_lock);
tvhpoll_destroy(efd);
shutdown(sockfd, SHUT_RD);
return NULL;
}
@ -149,14 +228,12 @@ static void tvhdhomerun_frontend_default_tables( tvhdhomerun_frontend_t *hfe, dv
psi_tables_default(mm);
/* ATSC */
if (hfe->hf_type == DVB_TYPE_ATSC) {
if (lm->lm_tuning.dmc_fe_modulation == DVB_MOD_VSB_8)
psi_tables_atsc_t(mm);
else
psi_tables_atsc_c(mm);
/* DVB */
} else {
psi_tables_dvb(mm);
}
@ -168,39 +245,68 @@ tvhdhomerun_frontend_monitor_cb( void *aux )
tvhdhomerun_frontend_t *hfe = aux;
mpegts_mux_instance_t *mmi = LIST_FIRST(&hfe->mi_mux_active);
mpegts_mux_t *mm;
mpegts_pid_t *mp;
streaming_message_t sm;
signal_status_t sigstat;
service_t *svc;
int res;
struct hdhomerun_tuner_status_t tunerStatus;
char *tunerBufferString;
struct hdhomerun_tuner_status_t tuner_status;
char *tuner_status_str;
hdhomerun_device_get_tuner_status(hfe->hf_hdhomerun_tuner, &tunerBufferString, &tunerStatus);
/* Stop timer */
if (!mmi || !hfe->hf_ready) return;
if (mmi == NULL) {
// Not tuned, keep on sending heartbeats to the device to prevent a timeout.
gtimer_arm_ms(&hfe->hf_monitor_timer, tvhdhomerun_frontend_monitor_cb, hfe, 5000);
return;
}
/* re-arm */
gtimer_arm(&hfe->hf_monitor_timer, tvhdhomerun_frontend_monitor_cb, hfe, 1);
/* Get current status */
pthread_mutex_lock(&hfe->hf_hdhomerun_device_mutex);
res = hdhomerun_device_get_tuner_status(hfe->hf_hdhomerun_tuner, &tuner_status_str, &tuner_status);
pthread_mutex_unlock(&hfe->hf_hdhomerun_device_mutex);
if(res < 1)
tvhwarn("tvhdhomerun", "tuner_status (%d): %s", res, tuner_status_str);
if(tuner_status.signal_present)
hfe->hf_status = SIGNAL_GOOD;
else
hfe->hf_status = SIGNAL_NONE;
/* Get current mux */
mm = mmi->mmi_mux;
if ( tunerStatus.signal_present ) {
hfe->hf_status = SIGNAL_GOOD;
} else {
hfe->hf_status = SIGNAL_NONE;
/* wait for a signal_present */
if(!hfe->hf_locked) {
if(tuner_status.signal_present) {
tvhdebug("tvhdhomerun", "locked");
hfe->hf_locked = 1;
/* start input thread */
tvh_pipe(O_NONBLOCK, &hfe->hf_input_thread_pipe);
pthread_mutex_lock(&hfe->hf_input_thread_mutex);
tvhthread_create(&hfe->hf_input_thread, NULL, tvhdhomerun_frontend_input_thread, hfe);
pthread_cond_wait(&hfe->hf_input_thread_cond, &hfe->hf_input_thread_mutex);
pthread_mutex_unlock(&hfe->hf_input_thread_mutex);
/* install table handlers */
tvhdhomerun_frontend_default_tables(hfe, (dvb_mux_t*)mm);
/* open PIDs */
pthread_mutex_lock(&hfe->mi_output_lock);
RB_FOREACH(mp, &mm->mm_pids, mp_link)
tvhdhomerun_device_open_pid(hfe, mp);
pthread_mutex_unlock(&hfe->mi_output_lock);
} else { // quick re-arm the timer to wait for signal lock
gtimer_arm_ms(&hfe->hf_monitor_timer, tvhdhomerun_frontend_monitor_cb, hfe, 50);
}
}
if ( hfe->hf_status == SIGNAL_NONE ) {
mmi->mmi_stats.snr = 0;
if(tuner_status.signal_present) {
mmi->mmi_stats.snr = tuner_status.signal_to_noise_quality;
} else {
mmi->mmi_stats.snr = tunerStatus.signal_to_noise_quality;
tvhdhomerun_frontend_default_tables(hfe, (dvb_mux_t*)mm);
tvhdhomerun_device_update_pid_filter(hfe, mm);
mmi->mmi_stats.snr = 0;
}
mmi->mmi_stats.signal = tunerStatus.signal_strength;
mmi->mmi_stats.signal = tuner_status.signal_strength;
sigstat.status_text = signal2str(hfe->hf_status);
sigstat.snr = mmi->mmi_stats.snr;
@ -209,54 +315,49 @@ tvhdhomerun_frontend_monitor_cb( void *aux )
sigstat.unc = mmi->mmi_stats.unc;
sm.sm_type = SMT_SIGNAL_STATUS;
sm.sm_data = &sigstat;
LIST_FOREACH(svc, &hfe->mi_transports, s_active_link) {
PTHREAD_MUTEX_LOCK(&svc->s_stream_mutex);
streaming_pad_deliver(&svc->s_streaming_pad, &sm);
PTHREAD_MUTEX_UNLOCK(&svc->s_stream_mutex);
}
gtimer_arm_ms(&hfe->hf_monitor_timer, tvhdhomerun_frontend_monitor_cb, hfe, 1000);
}
static void tvhdhomerun_device_update_pid_filter(tvhdhomerun_frontend_t *hfe, mpegts_mux_t *mm) {
char filterBuff[1024];
char tmp[10];
mpegts_pid_t *mp;
static void tvhdhomerun_device_open_pid(tvhdhomerun_frontend_t *hfe, mpegts_pid_t *mp) {
char *pfilter;
char buf[1024];
int res;
memset(filterBuff,0,sizeof(filterBuff));
//tvhdebug("tvhdhomerun", "adding PID 0x%x to pfilter", mp->mp_pid);
PTHREAD_MUTEX_LOCK(&hfe->hf_pid_filter_mutex);
PTHREAD_MUTEX_LOCK(&hfe->mi_output_lock);
RB_FOREACH(mp, &mm->mm_pids, mp_link) {
sprintf(tmp, "0x%X ", mp->mp_pid);
strcat(filterBuff, tmp);
/* get the current filter */
pthread_mutex_lock(&hfe->hf_hdhomerun_device_mutex);
res = hdhomerun_device_get_tuner_filter(hfe->hf_hdhomerun_tuner, &pfilter);
pthread_mutex_unlock(&hfe->hf_hdhomerun_device_mutex);
if(res < 1) {
tvhlog(LOG_ERR, "tvhdhomerun", "failed to get_tuner_filter: %d", res);
return;
}
PTHREAD_MUTEX_UNLOCK(&hfe->mi_output_lock);
if ( strlen(filterBuff) == 0) {
sprintf(filterBuff, "0x00");
tvhdebug("tvhdhomerun", "current pfilter: %s", pfilter);
memset(buf, 0x00, sizeof(buf));
snprintf(buf, sizeof(buf), "0x%04x", mp->mp_pid);
if(strncmp(pfilter, buf, strlen(buf)) != 0) {
memset(buf, 0x00, sizeof(buf));
snprintf(buf, sizeof(buf), "%s 0x%04x", pfilter, mp->mp_pid);
tvhdebug("tvhdhomerun", "setting pfilter to: %s", buf);
pthread_mutex_lock(&hfe->hf_hdhomerun_device_mutex);
res = hdhomerun_device_set_tuner_filter(hfe->hf_hdhomerun_tuner, buf);
pthread_mutex_unlock(&hfe->hf_hdhomerun_device_mutex);
if(res < 1)
tvhlog(LOG_ERR, "tvhdhomerun", "failed to set_tuner_filter: %d", res);
} else {
//tvhdebug("tvhdhomerun", "pid 0x%x already present in pfilter", mp->mp_pid);
return;
}
tvhdebug("tvhdhomerun", "Current pids : %s", filterBuff);
if ( strncmp(hfe->hf_pid_filter_buf, filterBuff,1024) != 0) {
memset(hfe->hf_pid_filter_buf,0,sizeof(hfe->hf_pid_filter_buf));
strcat(hfe->hf_pid_filter_buf, filterBuff);
tvhdebug("tvhdhomerun", " ---- Setting pid-filter to : %s",filterBuff);
int r = hdhomerun_device_set_tuner_filter(hfe->hf_hdhomerun_tuner, filterBuff);
if ( r == 0 ) {
tvhlog(LOG_ERR, "tvhdhomerun", "Command rejected when setting pid filter!");
} else if ( r == -1) {
tvhlog(LOG_ERR, "tvhdhomerun", "Communication failure when setting pid filter!");
} else if ( r != 1 ) {
tvhlog(LOG_ERR, "tvhdhomerun", "UNKNOWN ERROR(%d) %s:%s:%u",r ,__FILE__,__FUNCTION__,__LINE__);
}
}
PTHREAD_MUTEX_UNLOCK(&hfe->hf_pid_filter_mutex);
}
static int tvhdhomerun_frontend_tune(tvhdhomerun_frontend_t *hfe, mpegts_mux_instance_t *mmi)
@ -264,96 +365,52 @@ static int tvhdhomerun_frontend_tune(tvhdhomerun_frontend_t *hfe, mpegts_mux_ins
hfe->hf_status = SIGNAL_NONE;
dvb_mux_t *lm = (dvb_mux_t*)mmi->mmi_mux;
dvb_mux_conf_t *dmc = &lm->lm_tuning;
struct hdhomerun_tuner_status_t tunerStatus;
char channel_buf[64];
int res;
char freqBuf[64];
snprintf(freqBuf, 64, "auto:%u", dmc->dmc_fe_freq);
snprintf(channel_buf, sizeof(channel_buf), "auto:%u", dmc->dmc_fe_freq);
tvhlog(LOG_INFO, "tvhdhomerun", "tuning to %s", channel_buf);
int r = hdhomerun_device_set_tuner_channel(hfe->hf_hdhomerun_tuner, freqBuf);
if ( r == 0 ) {
tvhlog(LOG_ERR, "tvhdhomerun", "Command rejected when tuning to '%s'!",freqBuf);
pthread_mutex_lock(&hfe->hf_hdhomerun_device_mutex);
res = hdhomerun_device_set_tuner_channel(hfe->hf_hdhomerun_tuner, channel_buf);
pthread_mutex_unlock(&hfe->hf_hdhomerun_device_mutex);
if(res < 1) {
tvhlog(LOG_ERR, "tvhdhomerun", "failed to tune to %s", channel_buf);
return SM_CODE_TUNING_FAILED;
} else if ( r == -1) {
tvhlog(LOG_ERR, "tvhdhomerun", "Communication failure when tuning to '%s'!",freqBuf);
return SM_CODE_TUNING_FAILED;
} else if ( r != 1 ) {
tvhlog(LOG_ERR, "tvhdhomerun", "UNKNOWN ERROR(%d) %s:%s:%u",r ,__FILE__,__FUNCTION__,__LINE__);
return SM_CODE_TUNING_FAILED;
}
if ( r == 0 ) {
tvhlog(LOG_ERR, "tvhdhomerun", "Command rejected when setting channel!");
return SM_CODE_TUNING_FAILED;
} else if ( r == -1) {
tvhlog(LOG_ERR, "tvhdhomerun", "Communication failure when setting channel!");
return SM_CODE_TUNING_FAILED;
} else if ( r != 1 ) {
tvhlog(LOG_ERR, "tvhdhomerun", "UNKNOWN ERROR(%d) %s:%s:%u",r ,__FILE__,__FUNCTION__,__LINE__);
return SM_CODE_TUNING_FAILED;
}
hfe->hf_status = SIGNAL_NONE;
hdhomerun_device_wait_for_lock(hfe->hf_hdhomerun_tuner, &tunerStatus);
if ( tunerStatus.signal_present ) {
hfe->hf_status = SIGNAL_GOOD;
} else {
hfe->hf_status = SIGNAL_NONE;
}
/* start the monitoring */
gtimer_arm_ms(&hfe->hf_monitor_timer, tvhdhomerun_frontend_monitor_cb, hfe, 50);
hfe->hf_ready = 1;
return 0;
}
static void tvhdhomerun_frontend_stop_inputthread(tvhdhomerun_frontend_t *hfe) {
if ( hfe->hf_input_thread_running == 1 ) {
hfe->hf_input_thread_terminating = 1;
tvhlog(LOG_INFO, "tvhdhomerun", "Stopping iput thread - wait");
pthread_join(hfe->hf_input_thread, NULL);
hfe->hf_input_thread_running = 0;
hfe->hf_input_thread = 0;
tvhlog(LOG_INFO, "tvhdhomerun", "Stopping input thread - stopped");
}
}
static int
tvhdhomerun_frontend_start_mux
( mpegts_input_t *mi, mpegts_mux_instance_t *mmi )
{
tvhdhomerun_frontend_t *hfe = (tvhdhomerun_frontend_t*)mi;
int res, r;
char buf1[256], buf2[256];
mpegts_mux_instance_t *cur = LIST_FIRST(&hfe->mi_mux_active);
if (cur != NULL) {
// Already tuned to this MUX
if (mmi == cur)
return 0;
// Stop current
cur->mmi_mux->mm_stop(cur->mmi_mux, 1);
}
assert(LIST_FIRST(&hfe->mi_mux_active) == NULL);
PTHREAD_MUTEX_LOCK(&hfe->hf_input_mux_lock);
hfe->hf_mmi = mmi;
mi->mi_display_name(mi, buf1, sizeof(buf1));
mmi->mmi_mux->mm_display_name(mmi->mmi_mux, buf2, sizeof(buf2));
tvhdebug("tvhdhomerun", "%s - stopping %s", buf1, buf2);
tvhdhomerun_frontend_stop_inputthread(hfe);
if ( hfe->hf_input_thread_running == 0 ) {
tvhlog(LOG_INFO, "tvhdhomerun", "Starting input thread.");
hfe->hf_input_thread_running = 1;
tvhthread_create(&hfe->hf_input_thread, NULL,
tvhdhomerun_frontend_input_thread, hfe);
}
int r = tvhdhomerun_frontend_tune(hfe, mmi);
PTHREAD_MUTEX_UNLOCK(&hfe->hf_input_mux_lock);
return r;
mi->mi_display_name(mi, buf1, sizeof(buf1));
mpegts_mux_nice_name(mmi->mmi_mux, buf2, sizeof(buf2));
tvhdebug("tvhdhomerun", "%s - starting %s", buf1, buf2);
/* tune to the right mux */
res = tvhdhomerun_frontend_tune(hfe, mmi);
/* reset the pfilters */
pthread_mutex_lock(&hfe->hf_hdhomerun_device_mutex);
r = hdhomerun_device_set_tuner_filter(hfe->hf_hdhomerun_tuner, "0x0000");
pthread_mutex_unlock(&hfe->hf_hdhomerun_device_mutex);
if(r < 1)
tvhlog(LOG_ERR, "tvhdhomerun", "failed to reset pfilter: %d", r);
return res;
}
static void
@ -362,17 +419,25 @@ tvhdhomerun_frontend_stop_mux
{
tvhdhomerun_frontend_t *hfe = (tvhdhomerun_frontend_t*)mi;
char buf1[256], buf2[256];
PTHREAD_MUTEX_LOCK(&hfe->hf_input_mux_lock);
tvhdhomerun_frontend_stop_inputthread(hfe);
hfe->hf_mmi = NULL;
mi->mi_display_name(mi, buf1, sizeof(buf1));
mmi->mmi_mux->mm_display_name(mmi->mmi_mux, buf2, sizeof(buf2));
mpegts_mux_nice_name(mmi->mmi_mux, buf2, sizeof(buf2));
tvhdebug("tvhdhomerun", "%s - stopping %s", buf1, buf2);
PTHREAD_MUTEX_UNLOCK(&hfe->hf_input_mux_lock);
/* join input thread */
if(hfe->hf_input_thread_pipe.wr > 0) {
tvh_write(hfe->hf_input_thread_pipe.wr, "", 1); // wake input thread
tvhtrace("tvhdhomerun", "%s - waiting for input thread", buf1);
pthread_join(hfe->hf_input_thread, NULL);
tvh_pipe_close(&hfe->hf_input_thread_pipe);
tvhtrace("tvhdhomerun", "%s - input thread stopped", buf1);
}
hfe->hf_locked = 0;
hfe->hf_status = 0;
hfe->hf_ready = 0;
gtimer_arm(&hfe->hf_monitor_timer, tvhdhomerun_frontend_monitor_cb, hfe, 2);
}
static mpegts_pid_t *tvhdhomerun_frontend_open_pid( mpegts_input_t *mi, mpegts_mux_t *mm, int pid, int type, void *owner )
@ -380,30 +445,27 @@ static mpegts_pid_t *tvhdhomerun_frontend_open_pid( mpegts_input_t *mi, mpegts_m
tvhdhomerun_frontend_t *hfe = (tvhdhomerun_frontend_t*)mi;
mpegts_pid_t *mp;
tvhdebug("tvhdhomerun", "Open pid 0x%x\n", pid);
//tvhdebug("tvhdhomerun", "Open pid 0x%x\n", pid);
if (!(mp = mpegts_input_open_pid(mi, mm, pid, type, owner))) {
tvhdebug("tvhdhomerun", "Failed to open pid %d",pid);
tvhdebug("tvhdhomerun", "Failed to open pid %d", pid);
return NULL;
}
// Trigger the monitor to update the pid-filter.
gtimer_arm_ms(&hfe->hf_monitor_timer, tvhdhomerun_frontend_monitor_cb, hfe, 1);
tvhdhomerun_device_open_pid(hfe, mp);
return mp;
}
static void tvhdhomerun_frontend_close_pid( mpegts_input_t *mi, mpegts_mux_t *mm, int pid, int type, void *owner )
{
tvhdhomerun_frontend_t *hfe = (tvhdhomerun_frontend_t*)mi;
//tvhdhomerun_frontend_t *hfe = (tvhdhomerun_frontend_t*)mi;
tvhdebug("tvhdhomerun", "Closing pid 0x%x\n",pid);
tvhdebug("tvhdhomerun", "closing pid 0x%x",pid);
mpegts_input_close_pid(mi, mm, pid, type, owner);
// Trigger the monitor to update the pid-filter.
gtimer_arm_ms(&hfe->hf_monitor_timer, tvhdhomerun_frontend_monitor_cb, hfe, 1);
//tvhdhomerun_device_update_pid_filter(hfe, mm);
}
static idnode_set_t *
@ -515,6 +577,7 @@ tvhdhomerun_frontend_delete ( tvhdhomerun_frontend_t *hfe )
/* Remove from adapter */
TAILQ_REMOVE(&hfe->hf_device->hd_frontends, hfe, hf_link);
pthread_mutex_destroy(&hfe->hf_input_thread_mutex);
pthread_mutex_destroy(&hfe->hf_input_thread_mutex);
pthread_mutex_destroy(&hfe->hf_mutex);
pthread_mutex_destroy(&hfe->hf_pid_filter_mutex);
@ -593,12 +656,6 @@ tvhdhomerun_frontend_create(tvhdhomerun_device_t *hd, struct hdhomerun_discover_
hfe->mi_open_pid = tvhdhomerun_frontend_open_pid;
hfe->mi_close_pid = tvhdhomerun_frontend_close_pid;
/* Adapter link */
hfe->hf_device = hd;
TAILQ_INSERT_TAIL(&hd->hd_frontends, hfe, hf_link);
@ -615,12 +672,13 @@ tvhdhomerun_frontend_create(tvhdhomerun_device_t *hd, struct hdhomerun_discover_
}
}
/* mutex init */
pthread_mutex_init(&hfe->hf_hdhomerun_device_mutex, NULL);
pthread_mutex_init(&hfe->hf_input_thread_mutex, NULL);
pthread_cond_init(&hfe->hf_input_thread_cond, NULL);
// TODO: Need a better heartbeat, or we will need to recreate the hdhomerun-device if something fails.
gtimer_arm_ms(&hfe->hf_monitor_timer, tvhdhomerun_frontend_monitor_cb, hfe, 1);
//gtimer_arm_ms(&hfe->hf_monitor_timer, tvhdhomerun_frontend_monitor_cb, hfe, 1);
return hfe;
}

View file

@ -37,11 +37,11 @@ static struct hdhomerun_debug_t* hdhomerun_debug_obj = 0;
#if 0
#define PTHREAD_MUTEX_LOCK(x) \
printf("LOCK: %s:%s:%u "#x"\n",__FILE__,__FUNCTION__,__LINE__); \
tvhdebug("tvhdhomerun", "lock "#x": %s:%d", __FUNCTION__,__LINE__); \
pthread_mutex_lock(x);
#define PTHREAD_MUTEX_UNLOCK(x) \
printf("UNLOCK: %s:%s:%u "#x"\n",__FILE__,__FUNCTION__,__LINE__); \
tvhdebug("tvhdhomerun", "unlock "#x": %s:%d", __FUNCTION__,__LINE__); \
pthread_mutex_unlock(x);
#else
@ -118,23 +118,22 @@ struct tvhdhomerun_frontend
int hf_tunerNumber;
dvb_fe_type_t hf_type;
pthread_mutex_t hf_mutex; // Anything that is used by both input-thread
// or monitor. Only for quick read/writes.
// libhdhomerun objects.
struct hdhomerun_device_t *hf_hdhomerun_tuner;
// Tuning information
int hf_locked;
int hf_ready;
int hf_status;
pthread_mutex_t hf_input_mux_lock; // Lock to make sure we are not running the input-thread
// reader during a mux start/stop.
// input thread..
pthread_t hf_input_thread;
pthread_mutex_t hf_input_thread_mutex; // Used for sending signals.
pthread_mutex_t hf_input_thread_mutex; /* used in condition signaling */
pthread_cond_t hf_input_thread_cond; /* used in condition signaling */
th_pipe_t hf_input_thread_pipe; /* IPC with input thread */
uint8_t hf_input_thread_running; // Indicates if input_thread is running.
uint8_t hf_input_thread_terminating; // Used for terminating the input_thread.