tvheadend/src/input/mpegts/tsfile/tsfile_input.c
Jaroslav Kysela c07824a410 Graceful exit
It's necessary to do serious checks for the memory leaks using
tools like valgrind. This patch tries to implement a graceful
exit for all tvheadend components and free allocated memory.

Also, some memory leaks were fixed.
2014-02-17 13:13:14 +01:00

264 lines
7 KiB
C

/*
* Tvheadend - TS file input system
*
* Copyright (C) 2013 Adam Sutton
*
* 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 <http://www.gnu.org/licenses/>.
*/
#include "tvheadend.h"
#include "tsfile_private.h"
#include "input.h"
#include "input/mpegts/dvb.h"
#include "tvhpoll.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <assert.h>
#include <sched.h>
extern const idclass_t mpegts_input_class;
static void *
tsfile_input_thread ( void *aux )
{
int pos = 0, fd = -1, nfds;
size_t len, rem;
ssize_t c;
tvhpoll_t *efd;
tvhpoll_event_t ev;
struct stat st;
uint8_t tsb[188*10];
int64_t pcr, pcr_last = PTS_UNSET;
#if PLATFORM_LINUX
int64_t pcr_last_realtime = 0;
#endif
mpegts_input_t *mi = aux;
mpegts_mux_instance_t *mmi;
tsfile_mux_instance_t *tmi;
/* Open file */
pthread_mutex_lock(&global_lock);
if ((mmi = LIST_FIRST(&mi->mi_mux_active))) {
tmi = (tsfile_mux_instance_t*)mmi;
fd = tvh_open(tmi->mmi_tsfile_path, O_RDONLY | O_NONBLOCK, 0);
if (fd == -1)
tvhlog(LOG_ERR, "tsfile", "open(%s) failed %d (%s)",
tmi->mmi_tsfile_path, errno, strerror(errno));
else
tvhtrace("tsfile", "adapter %d opened %s", mi->mi_instance, tmi->mmi_tsfile_path);
}
pthread_mutex_unlock(&global_lock);
if (fd == -1) return NULL;
/* Polling */
memset(&ev, 0, sizeof(ev));
efd = tvhpoll_create(2);
ev.events = TVHPOLL_IN;
ev.fd = ev.data.fd = mi->mi_thread_pipe.rd;
tvhpoll_add(efd, &ev, 1);
/* Get file length */
if (fstat(fd, &st)) {
tvhlog(LOG_ERR, "tsfile", "stat() failed %d (%s)",
errno, strerror(errno));
goto exit;
}
/* Check for extra (incomplete) packet at end */
rem = st.st_size % 188;
len = 0;
tvhtrace("tsfile", "adapter %d file size %"PRIoff_t " rem %"PRIsize_t,
mi->mi_instance, st.st_size, rem);
/* Process input */
while (1) {
/* Find PCR PID */
if (!tmi->mmi_tsfile_pcr_pid) {
mpegts_service_t *s;
pthread_mutex_lock(&tsfile_lock);
LIST_FOREACH(s, &tmi->mmi_mux->mm_services, s_dvb_mux_link) {
if (s->s_pcr_pid)
tmi->mmi_tsfile_pcr_pid = s->s_pcr_pid;
}
pthread_mutex_unlock(&tsfile_lock);
}
/* Check for terminate */
nfds = tvhpoll_wait(efd, &ev, 1, 0);
if (nfds == 1) break;
/* Read */
c = read(fd, tsb+pos, sizeof(tsb)-pos);
if (c < 0) {
if (errno == EAGAIN || errno == EINTR)
continue;
tvhlog(LOG_ERR, "tsfile", "read() error %d (%s)",
errno, strerror(errno));
break;
}
len += c;
/* Reset */
if (len == st.st_size) {
len = 0;
c -= rem;
tvhtrace("tsfile", "adapter %d reached eof, resetting", mi->mi_instance);
lseek(fd, 0, SEEK_SET);
pcr_last = PTS_UNSET;
}
/* Process */
if (c >= 0) {
pcr = PTS_UNSET;
pos = mpegts_input_recv_packets(mi, mmi, tsb, c+pos, &pcr,
&tmi->mmi_tsfile_pcr_pid, "tsfile");
/* Delay */
if (pcr != PTS_UNSET) {
if (pcr_last != PTS_UNSET) {
struct timespec slp;
int64_t delta;
delta = pcr - pcr_last;
if (delta < 0)
delta = 0;
else if (delta > 90000)
delta = 90000;
delta *= 11;
#if PLATFORM_LINUX
delta += pcr_last_realtime;
slp.tv_sec = (delta / 1000000);
slp.tv_nsec = (delta % 1000000) * 1000;
clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &slp, NULL);
#else
slp.tv_sec = (delta / 1000000);
slp.tv_nsec = (delta % 1000000) * 1000;
nanosleep(&slp, NULL);
#endif
}
pcr_last = pcr;
#if PLATFORM_LINUX
pcr_last_realtime = getmonoclock();
#endif
}
}
sched_yield();
}
exit:
tvhpoll_destroy(efd);
close(fd);
return NULL;
}
static void
tsfile_input_stop_mux ( mpegts_input_t *mi, mpegts_mux_instance_t *mmi )
{
int err;
/* Stop thread */
if (mi->mi_thread_pipe.rd != -1) {
tvhtrace("tsfile", "adapter %d stopping thread", mi->mi_instance);
err = tvh_write(mi->mi_thread_pipe.wr, "", 1);
assert(err != -1);
pthread_join(mi->mi_thread_id, NULL);
tvh_pipe_close(&mi->mi_thread_pipe);
tvhtrace("tsfile", "adapter %d stopped thread", mi->mi_instance);
}
mmi->mmi_mux->mm_active = NULL;
LIST_REMOVE(mmi, mmi_active_link);
}
static int
tsfile_input_start_mux ( mpegts_input_t *mi, mpegts_mux_instance_t *t )
{
struct stat st;
mpegts_mux_t *mm = t->mmi_mux;
tsfile_mux_instance_t *mmi = (tsfile_mux_instance_t*)t;
tvhtrace("tsfile", "adapter %d starting mmi %p", mi->mi_instance, mmi);
/* Already tuned */
if (mmi->mmi_mux->mm_active == t) {
tvhtrace("tsfile", "mmi %p is already active", mmi);
return 0;
}
assert(mmi->mmi_mux->mm_active == NULL);
assert(LIST_FIRST(&mi->mi_mux_active) == NULL);
/* Check file is accessible */
if (lstat(mmi->mmi_tsfile_path, &st)) {
tvhlog(LOG_ERR, "tsfile", "mmi %p could not stat %s",
mmi, mmi->mmi_tsfile_path);
mmi->mmi_tune_failed = 1;
return SM_CODE_TUNING_FAILED;
}
/* Start thread */
if (mi->mi_thread_pipe.rd == -1) {
if (tvh_pipe(O_NONBLOCK, &mi->mi_thread_pipe)) {
mmi->mmi_tune_failed = 1;
tvhlog(LOG_ERR, "tsfile", "failed to create thread pipe");
return SM_CODE_TUNING_FAILED;
}
tvhtrace("tsfile", "adapter %d starting thread", mi->mi_instance);
tvhthread_create(&mi->mi_thread_id, NULL, tsfile_input_thread, mi, 0);
}
/* Current */
mmi->mmi_mux->mm_active = t;
/* Install table handlers */
psi_tables_default(mm);
psi_tables_dvb(mm);
psi_tables_atsc_t(mm);
psi_tables_atsc_c(mm);
return 0;
}
mpegts_input_t *
tsfile_input_create ( int idx )
{
mpegts_input_t *mi;
/* Create object */
mi = mpegts_input_create1(NULL, NULL);
mi->mi_instance = idx;
mi->mi_enabled = 1;
mi->mi_start_mux = tsfile_input_start_mux;
mi->mi_stop_mux = tsfile_input_stop_mux;
LIST_INSERT_HEAD(&tsfile_inputs, mi, mi_global_link);
if (!mi->mi_name)
mi->mi_name = strdup("TSFile");
/* Start table thread */
mpegts_input_table_thread_start(mi);
return mi;
}
/******************************************************************************
* Editor Configuration
*
* vim:sts=2:ts=2:sw=2:et
*****************************************************************************/