/* * Output packet */ static int _timeshift_read ( timeshift_t *ts, timeshift_file_t **cur_file, off_t *cur_off, int *fd, streaming_message_t **sm, int *wait ) { if (*cur_file) { /* Open file */ if (*fd == -1) { tvhtrace("timeshift", "ts %d open file %s", ts->id, (*cur_file)->path); *fd = open((*cur_file)->path, O_RDONLY); } tvhtrace("timeshift", "ts %d seek to %"PRIoff_t, ts->id, *cur_off); lseek(*fd, *cur_off, SEEK_SET); /* Read msg */ ssize_t r = _read_msg(*fd, sm); if (r < 0) { streaming_message_t *e = streaming_msg_create_code(SMT_STOP, SM_CODE_UNDEFINED_ERROR); streaming_target_deliver2(ts->output, e); tvhlog(LOG_ERR, "timeshift", "ts %d could not read buffer", ts->id); return -1; } tvhtrace("timeshift", "ts %d read msg %p (%"PRIssize_t")", ts->id, *sm, r); /* Incomplete */ if (r == 0) { lseek(*fd, *cur_off, SEEK_SET); return 0; } /* Update */ *cur_off += r; /* Special case - EOF */ if (r == sizeof(size_t) || *cur_off > (*cur_file)->size) { close(*fd); *fd = -1; pthread_mutex_lock(&ts->rdwr_mutex); *cur_file = timeshift_filemgr_next(*cur_file, NULL, 0); pthread_mutex_unlock(&ts->rdwr_mutex); *cur_off = 0; // reset *wait = 0; /* Check SMT_START index */ } else { streaming_message_t *ssm = _timeshift_find_sstart(*cur_file, (*sm)->sm_time); if (ssm && ssm->sm_data != ts->smt_start) { streaming_target_deliver2(ts->output, streaming_msg_clone(ssm)); if (ts->smt_start) streaming_start_unref(ts->smt_start); ts->smt_start = ssm->sm_data; atomic_add(&ts->smt_start->ss_refcount, 1); } } } return 0; }
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); assert(next != st); if(st->st_reject_filter & SMT_TO_MASK(sm->sm_type)) continue; st->st_cb(st->st_opaque, streaming_msg_clone(sm)); } }
/* * Receive data */ static void timeshift_input ( void *opaque, streaming_message_t *sm ) { int exit = 0; timeshift_t *ts = opaque; pthread_mutex_lock(&ts->state_mutex); /* Control */ if (sm->sm_type == SMT_SKIP) { if (ts->state >= TS_LIVE) timeshift_write_skip(ts->rd_pipe.wr, sm->sm_data); } else if (sm->sm_type == SMT_SPEED) { if (ts->state >= TS_LIVE) timeshift_write_speed(ts->rd_pipe.wr, sm->sm_code); } else { /* Start */ if (sm->sm_type == SMT_START && ts->state == TS_INIT) { ts->state = TS_LIVE; } /* Pass-thru */ if (ts->state <= TS_LIVE) { streaming_target_deliver2(ts->output, streaming_msg_clone(sm)); } /* Check for exit */ if (sm->sm_type == SMT_EXIT || (sm->sm_type == SMT_STOP && sm->sm_code == 0)) exit = 1; /* Buffer to disk */ if ((ts->state > TS_LIVE) || (!ts->ondemand && (ts->state == TS_LIVE))) { sm->sm_time = getmonoclock(); streaming_target_deliver2(&ts->wr_queue.sq_st, sm); } else streaming_msg_free(sm); /* Exit/Stop */ if (exit) { timeshift_write_exit(ts->rd_pipe.wr); ts->state = TS_EXIT; } } pthread_mutex_unlock(&ts->state_mutex); }
void streaming_pad_deliver(streaming_pad_t *sp, streaming_message_t *sm) { streaming_target_t *st, *next, *run = NULL; for (st = LIST_FIRST(&sp->sp_targets); st; st = next) { next = LIST_NEXT(st, st_link); assert(next != st); if (st->st_reject_filter & SMT_TO_MASK(sm->sm_type)) continue; if (run) streaming_target_deliver(run, streaming_msg_clone(sm)); run = st; } if (run) streaming_target_deliver(run, sm); else streaming_msg_free(sm); }
void timeshift_packets_clone ( timeshift_t *ts, struct streaming_message_queue *dst, int delivered ) { streaming_message_t *lowest, *sm, *sm2; struct streaming_message_queue *sq, *sq2, *backlogs; th_pkt_t *pkt; int i; lock_assert(&ts->state_mutex); /* init temporary queues and copy the backlog data */ backlogs = alloca(ts->backlog_max * sizeof(*backlogs)); for (i = 0; i < ts->backlog_max; i++) { sq = &backlogs[i]; sq2 = &ts->backlog[i]; TAILQ_INIT(sq); TAILQ_FOREACH(sm, sq2, sm_link) { if (!delivered) { pkt = sm->sm_data; if (pkt->pkt_delivered) continue; } sm2 = streaming_msg_clone(sm); TAILQ_INSERT_TAIL(sq, sm2, sm_link); } } /* push to destination (pts sorted) */ while (1) { lowest = NULL; for (i = 0; i < ts->backlog_max; i++) { sq = &backlogs[i]; sm = TAILQ_FIRST(sq); if (sm && (lowest == NULL || lowest->sm_time > sm->sm_time)) lowest = sm; } if (!lowest) break; TAILQ_REMOVE(sq, lowest, sm_link); TAILQ_INSERT_TAIL(dst, lowest, sm_link); } }
static inline ssize_t _process_msg0 ( timeshift_t *ts, timeshift_file_t *tsf, streaming_message_t *sm ) { ssize_t err; if (sm->sm_type == SMT_START) { err = 0; _handle_sstart(ts, tsf, streaming_msg_clone(sm)); } else if (sm->sm_type == SMT_SIGNAL_STATUS) err = timeshift_write_sigstat(tsf, sm->sm_time, sm->sm_data); else if (sm->sm_type == SMT_PACKET) { err = timeshift_write_packet(tsf, sm->sm_time, sm->sm_data); if (err > 0) { th_pkt_t *pkt = sm->sm_data; /* Index video iframes */ if (pkt->pkt_componentindex == ts->vididx && pkt->pkt_frametype == PKT_I_FRAME) { timeshift_index_iframe_t *ti = calloc(1, sizeof(timeshift_index_iframe_t)); memoryinfo_alloc(×hift_memoryinfo, sizeof(*ti)); ti->pos = tsf->size; ti->time = sm->sm_time; TAILQ_INSERT_TAIL(&tsf->iframes, ti, link); } } } else if (sm->sm_type == SMT_MPEGTS) { err = timeshift_write_mpegts(tsf, sm->sm_time, sm->sm_data); } else err = 0; /* OK */ if (err > 0) { tsf->last = sm->sm_time; tsf->size += err; atomic_add_u64(×hift_total_size, err); if (tsf->ram) atomic_add_u64(×hift_total_ram_size, err); } return err; }
/* * Receive data */ static void timeshift_input ( void *opaque, streaming_message_t *sm ) { int exit = 0; timeshift_t *ts = opaque; th_pkt_t *pkt = sm->sm_data; pthread_mutex_lock(&ts->state_mutex); /* Control */ if (sm->sm_type == SMT_SKIP) { if (ts->state >= TS_LIVE) timeshift_write_skip(ts->rd_pipe.wr, sm->sm_data); } else if (sm->sm_type == SMT_SPEED) { if (ts->state >= TS_LIVE) timeshift_write_speed(ts->rd_pipe.wr, sm->sm_code); } else { /* Start */ if (sm->sm_type == SMT_START && ts->state == TS_INIT) { ts->state = TS_LIVE; } if (sm->sm_type == SMT_PACKET) { tvhtrace("timeshift", "ts %d pkt in - stream %d type %c pts %10"PRId64 " dts %10"PRId64" dur %10d len %zu", ts->id, pkt->pkt_componentindex, pkt_frametype_to_char(pkt->pkt_frametype), ts_rescale(pkt->pkt_pts, 1000000), ts_rescale(pkt->pkt_dts, 1000000), pkt->pkt_duration, pktbuf_len(pkt->pkt_payload)); } /* Pass-thru */ if (ts->state <= TS_LIVE) { if (sm->sm_type == SMT_START) { if (ts->smt_start) streaming_start_unref(ts->smt_start); ts->smt_start = sm->sm_data; atomic_add(&ts->smt_start->ss_refcount, 1); } streaming_target_deliver2(ts->output, streaming_msg_clone(sm)); } /* Check for exit */ if (sm->sm_type == SMT_EXIT || (sm->sm_type == SMT_STOP && sm->sm_code == 0)) exit = 1; /* Record (one-off) PTS delta */ if (sm->sm_type == SMT_PACKET && ts->pts_delta == PTS_UNSET) timeshift_set_pts_delta(ts, pkt->pkt_pts); /* Buffer to disk */ if ((ts->state > TS_LIVE) || (!ts->ondemand && (ts->state == TS_LIVE))) { sm->sm_time = getmonoclock(); if (sm->sm_type == SMT_PACKET) { tvhtrace("timeshift", "ts %d pkt buf - stream %d type %c pts %10"PRId64 " dts %10"PRId64" dur %10d len %zu", ts->id, pkt->pkt_componentindex, pkt_frametype_to_char(pkt->pkt_frametype), ts_rescale(pkt->pkt_pts, 1000000), ts_rescale(pkt->pkt_dts, 1000000), pkt->pkt_duration, pktbuf_len(pkt->pkt_payload)); } streaming_target_deliver2(&ts->wr_queue.sq_st, sm); } else streaming_msg_free(sm); /* Exit/Stop */ if (exit) { timeshift_write_exit(ts->rd_pipe.wr); ts->state = TS_EXIT; } } pthread_mutex_unlock(&ts->state_mutex); }
/* * Output packet */ static int _timeshift_read ( timeshift_t *ts, timeshift_file_t **cur_file, streaming_message_t **sm, int *wait ) { timeshift_file_t *tsf = *cur_file; ssize_t r; off_t off, ooff; if (tsf) { /* Open file */ if (tsf->rfd < 0 && !tsf->ram) { tsf->rfd = open(tsf->path, O_RDONLY); tvhtrace("timeshift", "ts %d open file %s (fd %i)", ts->id, tsf->path, tsf->rfd); if (tsf->rfd < 0) return -1; } if (tsf->rfd >= 0) if ((off = lseek(tsf->rfd, tsf->roff, SEEK_SET)) != tsf->roff) tvherror("timeshift", "ts %d seek to %s failed (off %"PRId64" != %"PRId64"): %s", ts->id, tsf->path, (int64_t)tsf->roff, (int64_t)off, strerror(errno)); /* Read msg */ ooff = tsf->roff; r = _read_msg(tsf, -1, sm); if (r < 0) { streaming_message_t *e = streaming_msg_create_code(SMT_STOP, SM_CODE_UNDEFINED_ERROR); streaming_target_deliver2(ts->output, e); tvhtrace("timeshift", "ts %d seek to %jd (fd %i)", ts->id, (intmax_t)tsf->roff, tsf->rfd); tvhlog(LOG_ERR, "timeshift", "ts %d could not read buffer", ts->id); return -1; } tvhtrace("timeshift", "ts %d seek to %jd (fd %i) read msg %p (%"PRId64")", ts->id, (intmax_t)tsf->roff, tsf->rfd, *sm, (int64_t)r); /* Incomplete */ if (r == 0) { if (tsf->rfd >= 0) { tvhtrace("timeshift", "ts %d seek to %jd (fd %i) (incomplete)", ts->id, (intmax_t)tsf->roff, tsf->rfd); if ((off = lseek(tsf->rfd, ooff, SEEK_SET)) != ooff) tvherror("timeshift", "seek to %s failed (off %"PRId64" != %"PRId64"): %s", tsf->path, (int64_t)ooff, (int64_t)off, strerror(errno)); } tsf->roff = ooff; return 0; } /* Special case - EOF */ if (r == sizeof(size_t) || tsf->roff > tsf->size) { if (tsf->rfd >= 0) close(tsf->rfd); tsf->rfd = -1; pthread_mutex_lock(&ts->rdwr_mutex); *cur_file = timeshift_filemgr_next(tsf, NULL, 0); pthread_mutex_unlock(&ts->rdwr_mutex); tsf->roff = 0; // reset *wait = 0; /* Check SMT_START index */ } else { streaming_message_t *ssm = _timeshift_find_sstart(*cur_file, (*sm)->sm_time); if (ssm && ssm->sm_data != ts->smt_start) { streaming_target_deliver2(ts->output, streaming_msg_clone(ssm)); if (ts->smt_start) streaming_start_unref(ts->smt_start); ts->smt_start = ssm->sm_data; atomic_add(&ts->smt_start->ss_refcount, 1); } } } return 0; }
/* * Receive data */ static void timeshift_input ( void *opaque, streaming_message_t *sm ) { int exit = 0; timeshift_t *ts = opaque; pthread_mutex_lock(&ts->state_mutex); /* Control */ if (sm->sm_type == SMT_SKIP) { if (ts->state >= TS_LIVE) timeshift_write_skip(ts->rd_pipe.wr, sm->sm_data); } else if (sm->sm_type == SMT_SPEED) { if (ts->state >= TS_LIVE) timeshift_write_speed(ts->rd_pipe.wr, sm->sm_code); } else { /* Start */ if (sm->sm_type == SMT_START && ts->state == TS_INIT) { ts->state = TS_LIVE; } /* Pass-thru */ if (ts->state <= TS_LIVE) { if (sm->sm_type == SMT_START) { if (ts->smt_start) streaming_start_unref(ts->smt_start); ts->smt_start = sm->sm_data; atomic_add(&ts->smt_start->ss_refcount, 1); } streaming_target_deliver2(ts->output, streaming_msg_clone(sm)); } /* Check for exit */ if (sm->sm_type == SMT_EXIT || (sm->sm_type == SMT_STOP && sm->sm_code == 0)) exit = 1; /* Record (one-off) PTS delta */ if (sm->sm_type == SMT_PACKET && ts->pts_delta == PTS_UNSET) { th_pkt_t *pkt = sm->sm_data; if (pkt->pkt_pts != PTS_UNSET) ts->pts_delta = getmonoclock() - ts_rescale(pkt->pkt_pts, 1000000); } /* Buffer to disk */ if ((ts->state > TS_LIVE) || (!ts->ondemand && (ts->state == TS_LIVE))) { sm->sm_time = getmonoclock(); streaming_target_deliver2(&ts->wr_queue.sq_st, sm); } else streaming_msg_free(sm); /* Exit/Stop */ if (exit) { timeshift_write_exit(ts->rd_pipe.wr); ts->state = TS_EXIT; } } pthread_mutex_unlock(&ts->state_mutex); }
/* * Get current / new file */ timeshift_file_t *timeshift_filemgr_get ( timeshift_t *ts, int create ) { int fd; struct timespec tp; timeshift_file_t *tsf_tl, *tsf_hd, *tsf_tmp; timeshift_index_data_t *ti; char path[512]; time_t time; /* Return last file */ if (!create) return timeshift_filemgr_newest(ts); /* No space */ if (ts->full) return NULL; /* Store to file */ clock_gettime(CLOCK_MONOTONIC_COARSE, &tp); time = tp.tv_sec / TIMESHIFT_FILE_PERIOD; tsf_tl = TAILQ_LAST(&ts->files, timeshift_file_list); if (!tsf_tl || tsf_tl->time != time) { tsf_hd = TAILQ_FIRST(&ts->files); /* Close existing */ if (tsf_tl && tsf_tl->fd != -1) timeshift_filemgr_close(tsf_tl); /* Check period */ if (ts->max_time && tsf_hd && tsf_tl) { time_t d = (tsf_tl->time - tsf_hd->time) * TIMESHIFT_FILE_PERIOD; if (d > (ts->max_time+5)) { if (!tsf_hd->refcount) { timeshift_filemgr_remove(ts, tsf_hd, 0); tsf_hd = NULL; } else { tvhlog(LOG_DEBUG, "timeshift", "ts %d buffer full", ts->id); ts->full = 1; } } } /* Check size */ if (!timeshift_unlimited_size && atomic_pre_add_u64(×hift_total_size, 0) >= timeshift_max_size) { /* Remove the last file (if we can) */ if (tsf_hd && !tsf_hd->refcount) { timeshift_filemgr_remove(ts, tsf_hd, 0); /* Full */ } else { tvhlog(LOG_DEBUG, "timeshift", "ts %d buffer full", ts->id); ts->full = 1; } } /* Create new file */ tsf_tmp = NULL; if (!ts->full) { /* Create directories */ if (!ts->path) { if (timeshift_filemgr_makedirs(ts->id, path, sizeof(path))) return NULL; ts->path = strdup(path); } /* Create File */ snprintf(path, sizeof(path), "%s/tvh-%"PRItime_t, ts->path, time); tvhtrace("timeshift", "ts %d create file %s", ts->id, path); if ((fd = open(path, O_WRONLY | O_CREAT, 0600)) > 0) { tsf_tmp = calloc(1, sizeof(timeshift_file_t)); tsf_tmp->time = time; tsf_tmp->fd = fd; tsf_tmp->path = strdup(path); tsf_tmp->refcount = 0; tsf_tmp->last = getmonoclock(); TAILQ_INIT(&tsf_tmp->iframes); TAILQ_INIT(&tsf_tmp->sstart); TAILQ_INSERT_TAIL(&ts->files, tsf_tmp, link); /* Copy across last start message */ if (tsf_tl && (ti = TAILQ_LAST(&tsf_tl->sstart, timeshift_index_data_list))) { tvhtrace("timeshift", "ts %d copy smt_start to new file", ts->id); timeshift_index_data_t *ti2 = calloc(1, sizeof(timeshift_index_data_t)); ti2->data = streaming_msg_clone(ti->data); TAILQ_INSERT_TAIL(&tsf_tmp->sstart, ti2, link); } } } tsf_tl = tsf_tmp; } if (tsf_tl) tsf_tl->refcount++; return tsf_tl; }
/* * Get current / new file */ timeshift_file_t *timeshift_filemgr_get ( timeshift_t *ts, int create ) { int fd; struct timespec tp; timeshift_file_t *tsf_tl, *tsf_hd, *tsf_tmp; timeshift_index_data_t *ti; char path[512]; /* Return last file */ if (!create) return TAILQ_LAST(&ts->files, timeshift_file_list); /* No space */ if (ts->full) return NULL; /* Store to file */ clock_gettime(CLOCK_MONOTONIC_COARSE, &tp); tsf_tl = TAILQ_LAST(&ts->files, timeshift_file_list); if (!tsf_tl || tsf_tl->time != tp.tv_sec) { tsf_hd = TAILQ_FIRST(&ts->files); /* Close existing */ if (tsf_tl && tsf_tl->fd != -1) timeshift_filemgr_close(tsf_tl); /* Check period */ if (ts->max_time && tsf_hd && tsf_tl) { time_t d = tsf_tl->time - tsf_hd->time; if (d > (ts->max_time+5)) { if (!tsf_hd->refcount) { timeshift_filemgr_remove(ts, tsf_hd, 0); } else { #ifdef TSHFT_TRACE tvhlog(LOG_DEBUG, "timeshift", "ts %d buffer full", ts->id); #endif ts->full = 1; } } } /* Check size */ // TODO: need to implement this /* Create new file */ tsf_tmp = NULL; if (!ts->full) { snprintf(path, sizeof(path), "%s/tvh-%"PRItime_t, ts->path, tp.tv_sec); #ifdef TSHFT_TRACE tvhlog(LOG_DEBUG, "timeshift", "ts %d create file %s", ts->id, path); #endif if ((fd = open(path, O_WRONLY | O_CREAT, 0600)) > 0) { tsf_tmp = calloc(1, sizeof(timeshift_file_t)); tsf_tmp->time = tp.tv_sec; tsf_tmp->fd = fd; tsf_tmp->path = strdup(path); tsf_tmp->refcount = 0; tsf_tmp->last = getmonoclock(); TAILQ_INIT(&tsf_tmp->iframes); TAILQ_INIT(&tsf_tmp->sstart); TAILQ_INSERT_TAIL(&ts->files, tsf_tmp, link); /* Copy across last start message */ if (tsf_tl && (ti = TAILQ_LAST(&tsf_tl->sstart, timeshift_index_data_list))) { #ifdef TSHFT_TRACE tvhlog(LOG_DEBUG, "timeshift", "ts %d copy smt_start to new file", ts->id); #endif timeshift_index_data_t *ti2 = calloc(1, sizeof(timeshift_index_data_t)); ti2->data = streaming_msg_clone(ti->data); TAILQ_INSERT_TAIL(&tsf_tmp->sstart, ti2, link); } } } tsf_tl = tsf_tmp; } return tsf_tl; }
/* * Get current / new file */ timeshift_file_t *timeshift_filemgr_get ( timeshift_t *ts, int create ) { int fd; struct timespec tp; timeshift_file_t *tsf_tl, *tsf_hd, *tsf_tmp; timeshift_index_data_t *ti; char path[PATH_MAX]; time_t time; /* Return last file */ if (!create) return timeshift_filemgr_newest(ts); /* No space */ if (ts->full) return NULL; /* Store to file */ clock_gettime(CLOCK_MONOTONIC_COARSE, &tp); time = tp.tv_sec / TIMESHIFT_FILE_PERIOD; tsf_tl = TAILQ_LAST(&ts->files, timeshift_file_list); if (!tsf_tl || tsf_tl->time != time || (tsf_tl->ram && tsf_tl->woff >= timeshift_conf.ram_segment_size)) { tsf_hd = TAILQ_FIRST(&ts->files); /* Close existing */ if (tsf_tl) timeshift_filemgr_close(tsf_tl); /* Check period */ if (!timeshift_conf.unlimited_period && ts->max_time && tsf_hd && tsf_tl) { time_t d = (tsf_tl->time - tsf_hd->time) * TIMESHIFT_FILE_PERIOD; if (d > (ts->max_time+5)) { if (!tsf_hd->refcount) { timeshift_filemgr_remove(ts, tsf_hd, 0); tsf_hd = NULL; } else { tvhlog(LOG_DEBUG, "timeshift", "ts %d buffer full", ts->id); ts->full = 1; } } } /* Check size */ if (!timeshift_conf.unlimited_size && atomic_pre_add_u64(×hift_conf.total_size, 0) >= timeshift_conf.max_size) { /* Remove the last file (if we can) */ if (tsf_hd && !tsf_hd->refcount) { timeshift_filemgr_remove(ts, tsf_hd, 0); /* Full */ } else { tvhlog(LOG_DEBUG, "timeshift", "ts %d buffer full", ts->id); ts->full = 1; } } /* Create new file */ tsf_tmp = NULL; if (!ts->full) { tvhtrace("timeshift", "ts %d RAM total %"PRId64" requested %"PRId64" segment %"PRId64, ts->id, atomic_pre_add_u64(×hift_total_ram_size, 0), timeshift_conf.ram_size, timeshift_conf.ram_segment_size); if (timeshift_conf.ram_size >= 8*1024*1024 && atomic_pre_add_u64(×hift_total_ram_size, 0) < timeshift_conf.ram_size + (timeshift_conf.ram_segment_size / 2)) { tsf_tmp = timeshift_filemgr_file_init(ts, time); tsf_tmp->ram_size = MIN(16*1024*1024, timeshift_conf.ram_segment_size); tsf_tmp->ram = malloc(tsf_tmp->ram_size); if (!tsf_tmp->ram) { free(tsf_tmp); tsf_tmp = NULL; } else { tvhtrace("timeshift", "ts %d create RAM segment with %"PRId64" bytes (time %li)", ts->id, tsf_tmp->ram_size, (long)time); } } if (!tsf_tmp && !timeshift_conf.ram_only) { /* Create directories */ if (!ts->path) { if (timeshift_filemgr_makedirs(ts->id, path, sizeof(path))) return NULL; ts->path = strdup(path); } /* Create File */ snprintf(path, sizeof(path), "%s/tvh-%"PRItime_t, ts->path, time); tvhtrace("timeshift", "ts %d create file %s", ts->id, path); if ((fd = open(path, O_WRONLY | O_CREAT, 0600)) > 0) { tsf_tmp = timeshift_filemgr_file_init(ts, time); tsf_tmp->wfd = fd; tsf_tmp->path = strdup(path); } } if (tsf_tmp) { /* Copy across last start message */ if (tsf_tl && (ti = TAILQ_LAST(&tsf_tl->sstart, timeshift_index_data_list))) { tvhtrace("timeshift", "ts %d copy smt_start to new file", ts->id); timeshift_index_data_t *ti2 = calloc(1, sizeof(timeshift_index_data_t)); ti2->data = streaming_msg_clone(ti->data); TAILQ_INSERT_TAIL(&tsf_tmp->sstart, ti2, link); } } } tsf_tl = tsf_tmp; } if (tsf_tl) tsf_tl->refcount++; return tsf_tl; }
/* * Receive data */ static void timeshift_input ( void *opaque, streaming_message_t *sm ) { int exit = 0, type = sm->sm_type; timeshift_t *ts = opaque; th_pkt_t *pkt = sm->sm_data, *pkt2; pthread_mutex_lock(&ts->state_mutex); /* Control */ if (type == SMT_SKIP) { if (ts->state >= TS_LIVE) timeshift_write_skip(ts->rd_pipe.wr, sm->sm_data); streaming_msg_free(sm); } else if (type == SMT_SPEED) { if (ts->state >= TS_LIVE) timeshift_write_speed(ts->rd_pipe.wr, sm->sm_code); streaming_msg_free(sm); } else { /* Start */ if (type == SMT_START && ts->state == TS_INIT) ts->state = TS_LIVE; /* Change PTS/DTS offsets */ if (ts->packet_mode && ts->start_pts && type == SMT_PACKET) { pkt2 = pkt_copy_shallow(pkt); pkt_ref_dec(pkt); sm->sm_data = pkt = pkt2; pkt->pkt_pts += ts->start_pts; pkt->pkt_dts += ts->start_pts; } /* Pass-thru */ if (ts->state <= TS_LIVE) { if (type == SMT_START) { if (ts->smt_start) streaming_start_unref(ts->smt_start); ts->smt_start = sm->sm_data; atomic_add(&ts->smt_start->ss_refcount, 1); if (ts->packet_mode) { timeshift_packet_flush(ts, ts->last_time + MAX_TIME_DELTA + 1000, ts->dobuf); if (ts->last_time) ts->start_pts = ts->last_time + 1000; } } streaming_target_deliver2(ts->output, streaming_msg_clone(sm)); } /* Check for exit */ if (type == SMT_EXIT || (type == SMT_STOP && sm->sm_code != SM_CODE_SOURCE_RECONFIGURED)) exit = 1; if (type == SMT_MPEGTS) ts->packet_mode = 0; /* Buffer to disk */ if ((ts->state > TS_LIVE) || (ts->dobuf && (ts->state == TS_LIVE))) { if (ts->packet_mode) { sm->sm_time = ts->last_time; if (type == SMT_PACKET) { timeshift_packet(ts, pkt, 1); goto msg_free; } } else { if (ts->ref_time == 0) { ts->ref_time = getmonoclock(); sm->sm_time = 0; } else { sm->sm_time = getmonoclock() - ts->ref_time; } } streaming_target_deliver2(&ts->wr_queue.sq_st, sm); } else { if (type == SMT_PACKET) { timeshift_packet(ts, pkt, 0); tvhtrace("timeshift", "ts %d pkt in - stream %d type %c pts %10"PRId64 " dts %10"PRId64" dur %10d len %6zu", ts->id, pkt->pkt_componentindex, pkt_frametype_to_char(pkt->pkt_frametype), ts_rescale(pkt->pkt_pts, 1000000), ts_rescale(pkt->pkt_dts, 1000000), pkt->pkt_duration, pktbuf_len(pkt->pkt_payload)); } msg_free: streaming_msg_free(sm); } /* Exit/Stop */ if (exit) { timeshift_write_exit(ts->rd_pipe.wr); ts->state = TS_EXIT; } } pthread_mutex_unlock(&ts->state_mutex); }
static void _process_msg ( timeshift_t *ts, streaming_message_t *sm, int *run ) { int err; timeshift_file_t *tsf; /* Process */ switch (sm->sm_type) { /* Terminate */ case SMT_EXIT: if (run) *run = 0; break; case SMT_STOP: if (sm->sm_code != SM_CODE_SOURCE_RECONFIGURED && run) *run = 0; goto live; /* Timeshifting */ case SMT_SKIP: case SMT_SPEED: break; /* Status */ case SMT_GRACE: case SMT_NOSTART: case SMT_NOSTART_WARN: case SMT_SERVICE_STATUS: case SMT_TIMESHIFT_STATUS: case SMT_DESCRAMBLE_INFO: goto live; /* Store */ case SMT_SIGNAL_STATUS: case SMT_START: case SMT_MPEGTS: case SMT_PACKET: pthread_mutex_lock(&ts->state_mutex); ts->buf_time = sm->sm_time; if (ts->state == TS_LIVE) { streaming_target_deliver2(ts->output, streaming_msg_clone(sm)); if (sm->sm_type == SMT_PACKET) timeshift_packet_log("liv", ts, sm); } if (sm->sm_type == SMT_START) _update_smt_start(ts, (streaming_start_t *)sm->sm_data); if (ts->dobuf) { if ((tsf = timeshift_filemgr_get(ts, sm->sm_time)) != NULL) { if (tsf->wfd >= 0 || tsf->ram) { if ((err = _process_msg0(ts, tsf, sm)) < 0) { timeshift_filemgr_close(tsf); tsf->bad = 1; ts->full = 1; ///< Stop any more writing } else { timeshift_packet_log("sav", ts, sm); } } timeshift_file_put(tsf); } } pthread_mutex_unlock(&ts->state_mutex); break; } /* Next */ streaming_msg_free(sm); return; live: pthread_mutex_lock(&ts->state_mutex); if (ts->state == TS_LIVE) streaming_target_deliver2(ts->output, sm); else streaming_msg_free(sm); pthread_mutex_unlock(&ts->state_mutex); }