/* * Connected */ static int iptv_rtsp_header ( http_client_t *hc ) { iptv_mux_t *im = hc->hc_aux; rtsp_priv_t *rp; int r; if (im == NULL) { /* teardown (or teardown timeout) */ if (hc->hc_cmd == RTSP_CMD_TEARDOWN) { pthread_mutex_lock(&global_lock); mtimer_arm_rel(&hc->hc_close_timer, iptv_rtsp_close_cb, hc, 0); pthread_mutex_unlock(&global_lock); } return 0; } if (hc->hc_cmd == RTSP_CMD_GET_PARAMETER && hc->hc_code != HTTP_STATUS_OK) { tvhtrace("iptv", "GET_PARAMETER command returned invalid error code %d for '%s', " "fall back to OPTIONS in keep alive loop.", hc->hc_code, im->mm_iptv_url_raw); hc->hc_rtsp_keep_alive_cmd = RTSP_CMD_OPTIONS; return 0; } if (hc->hc_code != HTTP_STATUS_OK) { tvherror("iptv", "invalid error code %d for '%s'", hc->hc_code, im->mm_iptv_url_raw); return 0; } rp = im->im_data; switch (hc->hc_cmd) { case RTSP_CMD_SETUP: r = rtsp_setup_decode(hc, 0); if (r >= 0) { rtsp_play(hc, rp->path, rp->query); rp->play = 1; } break; case RTSP_CMD_PLAY: // Now let's set peer port for RTCP // Use the HTTP host for sending RTCP reports, NOT the hc_rtp_dest (which is where the stream is sent) if (udp_connect(rp->rtcp_info->connection, "rtcp", hc->hc_host, hc->hc_rtcp_server_port)) { tvhlog(LOG_WARNING, "rtsp", "Can't connect to remote, RTCP receiver reports won't be sent"); } hc->hc_cmd = HTTP_CMD_NONE; pthread_mutex_lock(&global_lock); iptv_input_mux_started(hc->hc_aux); mtimer_arm_rel(&rp->alive_timer, iptv_rtsp_alive_cb, im, sec2mono(MAX(1, (hc->hc_rtp_timeout / 2) - 1))); pthread_mutex_unlock(&global_lock); break; default: break; } return 0; }
static void * mtimer_thread(void *aux) { mtimer_t *mti; mti_callback_t *cb; int64_t now, next; #if ENABLE_GTIMER_CHECK int64_t mtm; const char *id; const char *fcn; #endif while (tvheadend_is_running()) { now = mdispatch_clock_update(); /* Global monoclock timers */ pthread_mutex_lock(&global_lock); next = now + sec2mono(3600); while((mti = LIST_FIRST(&mtimers)) != NULL) { if (mti->mti_expire > now) { next = mti->mti_expire; break; } #if ENABLE_GTIMER_CHECK mtm = getmonoclock(); id = mti->mti_id; fcn = mti->mti_fcn; #endif cb = mti->mti_callback; LIST_REMOVE(mti, mti_link); mti->mti_callback = NULL; cb(mti->mti_opaque); #if ENABLE_GTIMER_CHECK mtm = getmonoclock() - mtm; if (mtm > 10000) tvhtrace("mtimer", "%s:%s duration %"PRId64"us", id, fcn, mtm); #endif } /* Periodic updates */ if (next > mtimer_periodic) next = mtimer_periodic; /* Wait */ tvh_cond_timedwait(&mtimer_cond, &global_lock, next); pthread_mutex_unlock(&global_lock); } return NULL; }
static void * mtimer_thread(void *aux) { mtimer_t *mti; mti_callback_t *cb; int64_t now, next; const char *id; pthread_mutex_lock(&global_lock); while (tvheadend_is_running() && atomic_get(&tvheadend_mainloop) == 0) tvh_cond_wait(&mtimer_cond, &global_lock); pthread_mutex_unlock(&global_lock); while (tvheadend_is_running()) { now = mdispatch_clock_update(); /* Global monoclock timers */ pthread_mutex_lock(&global_lock); next = now + sec2mono(3600); while((mti = LIST_FIRST(&mtimers)) != NULL) { if (mti->mti_expire > now) { next = mti->mti_expire; break; } #if ENABLE_GTIMER_CHECK id = mti->mti_id; #else id = NULL; #endif tprofile_start(&mtimer_profile, id); cb = mti->mti_callback; LIST_REMOVE(mti, mti_link); mti->mti_callback = NULL; cb(mti->mti_opaque); tprofile_finish(&mtimer_profile); } /* Periodic updates */ if (next > mtimer_periodic) next = mtimer_periodic; /* Wait */ tvh_cond_timedwait(&mtimer_cond, &global_lock, next); pthread_mutex_unlock(&global_lock); } return NULL; }
void mpegts_network_scan_timer_cb ( void *p ) { mpegts_network_t *mn = p; mpegts_mux_t *mm, *nxt = NULL; int r; /* Process Q */ for (mm = TAILQ_FIRST(&mn->mn_scan_pend); mm != NULL; mm = nxt) { nxt = TAILQ_NEXT(mm, mm_scan_link); assert(mm->mm_scan_state == MM_SCAN_STATE_PEND || mm->mm_scan_state == MM_SCAN_STATE_ACTIVE); /* Don't try to subscribe already tuned muxes */ if (mm->mm_active) continue; /* Attempt to tune */ r = mpegts_mux_subscribe(mm, NULL, "scan", mm->mm_scan_weight, mm->mm_scan_flags | SUBSCRIPTION_ONESHOT | SUBSCRIPTION_TABLES); /* Started */ if (!r) { assert(mm->mm_scan_state == MM_SCAN_STATE_ACTIVE); continue; } assert(mm->mm_scan_state == MM_SCAN_STATE_PEND); /* No free tuners - stop */ if (r == SM_CODE_NO_FREE_ADAPTER || r == SM_CODE_NO_ADAPTERS) break; /* No valid tuners (subtly different, might be able to tuner a later * mux) */ if (r == SM_CODE_NO_VALID_ADAPTER && mm->mm_is_enabled(mm)) continue; /* Failed */ TAILQ_REMOVE(&mn->mn_scan_pend, mm, mm_scan_link); if (mm->mm_scan_result != MM_SCAN_FAIL) { mm->mm_scan_result = MM_SCAN_FAIL; idnode_changed(&mm->mm_id); } mm->mm_scan_state = MM_SCAN_STATE_IDLE; mm->mm_scan_weight = 0; mpegts_network_scan_notify(mm); } /* Re-arm timer. Really this is just a safety measure as we'd normally * expect the timer to be forcefully triggered on finish of a mux scan */ mtimer_arm_rel(&mn->mn_scan_timer, mpegts_network_scan_timer_cb, mn, sec2mono(120)); }
/* * Read thread */ static void * iptv_file_thread ( void *aux ) { iptv_mux_t *im = aux; file_priv_t *fp = im->im_data; ssize_t r; int fd = fp->fd, pause = 0; char buf[32*1024]; off_t off = 0; int64_t mono; int e; #if defined(PLATFORM_DARWIN) fcntl(fd, F_NOCACHE, 1); #endif pthread_mutex_lock(&iptv_lock); while (!fp->shutdown && fd > 0) { while (!fp->shutdown && pause) { mono = mclk() + sec2mono(1); do { e = tvh_cond_timedwait(&fp->cond, &iptv_lock, mono); if (e == ETIMEDOUT) break; } while (ERRNO_AGAIN(e)); } if (fp->shutdown) break; pause = 0; pthread_mutex_unlock(&iptv_lock); r = read(fd, buf, sizeof(buf)); pthread_mutex_lock(&iptv_lock); if (r == 0) break; if (r < 0) { if (ERRNO_AGAIN(errno)) continue; break; } sbuf_append(&im->mm_iptv_buffer, buf, r); if (iptv_input_recv_packets(im, r) == 1) pause = 1; #ifndef PLATFORM_DARWIN #if !ENABLE_ANDROID posix_fadvise(fd, off, r, POSIX_FADV_DONTNEED); #endif #endif off += r; } pthread_mutex_unlock(&iptv_lock); return NULL; }
static void epggrab_ota_kick ( int delay ) { /* next round is pending? queue rest of ota muxes */ if (epggrab_ota_pending_flag) { epggrab_ota_pending_flag = 0; epggrab_ota_requeue(); } if (TAILQ_EMPTY(&epggrab_ota_pending)) return; mtimer_arm_rel(&epggrab_ota_kick_timer, epggrab_ota_kick_cb, NULL, sec2mono(delay)); }
/* * Alive timeout */ static void iptv_rtsp_alive_cb ( void *aux ) { iptv_mux_t *im = aux; rtsp_priv_t *rp = im->im_data; if(rp->hc->hc_rtsp_keep_alive_cmd == RTSP_CMD_GET_PARAMETER) rtsp_get_parameter(rp->hc, "position"); else if(rp->hc->hc_rtsp_keep_alive_cmd == RTSP_CMD_OPTIONS) rtsp_send(rp->hc, RTSP_CMD_OPTIONS, rp->path, rp->query, NULL); else return; mtimer_arm_rel(&rp->alive_timer, iptv_rtsp_alive_cb, im, sec2mono(MAX(1, (rp->hc->hc_rtp_timeout / 2) - 1))); }
void mpegts_network_scan_queue_add ( mpegts_mux_t *mm, int weight, int flags, int delay ) { int reload = 0; char buf[256], buf2[256];; mpegts_network_t *mn = mm->mm_network; if (!mm->mm_is_enabled(mm)) return; if (weight <= 0) return; if (weight > mm->mm_scan_weight) { mm->mm_scan_weight = weight; reload = 1; } /* Already active */ if (mm->mm_scan_state == MM_SCAN_STATE_ACTIVE) return; /* Remove entry (or ignore) */ if (mm->mm_scan_state == MM_SCAN_STATE_PEND) { if (!reload) return; TAILQ_REMOVE(&mn->mn_scan_pend, mm, mm_scan_link); } mpegts_mux_nice_name(mm, buf, sizeof(buf)); mn->mn_display_name(mn, buf2, sizeof(buf2)); tvhdebug(LS_MPEGTS, "%s - adding mux %s to scan queue weight %d flags %04X", buf2, buf, weight, flags); /* Add new entry */ mm->mm_scan_state = MM_SCAN_STATE_PEND; mm->mm_scan_flags |= flags; if (mm->mm_scan_flags == 0) mm->mm_scan_flags = SUBSCRIPTION_IDLESCAN; TAILQ_INSERT_SORTED_R(&mn->mn_scan_pend, mpegts_mux_queue, mm, mm_scan_link, mm_cmp); mtimer_arm_rel(&mn->mn_scan_timer, mpegts_network_scan_timer_cb, mn, sec2mono(delay)); mpegts_network_scan_notify(mm); }
/* Finished */ static inline void mpegts_network_scan_mux_done0 ( mpegts_mux_t *mm, mpegts_mux_scan_result_t result, int weight ) { mpegts_network_t *mn = mm->mm_network; mpegts_mux_scan_state_t state = mm->mm_scan_state; /* prevent double del: */ /* mpegts_mux_stop -> mpegts_network_scan_mux_cancel */ mm->mm_scan_state = MM_SCAN_STATE_IDLE; mpegts_mux_unsubscribe_by_name(mm, "scan"); mm->mm_scan_state = state; if (state == MM_SCAN_STATE_PEND) { if (weight || mn->mn_idlescan) { if (!weight) mm->mm_scan_weight = SUBSCRIPTION_PRIO_SCAN_IDLE; TAILQ_REMOVE(&mn->mn_scan_pend, mm, mm_scan_link); TAILQ_INSERT_SORTED_R(&mn->mn_scan_pend, mpegts_mux_queue, mm, mm_scan_link, mm_cmp); mtimer_arm_rel(&mn->mn_scan_timer, mpegts_network_scan_timer_cb, mn, sec2mono(10)); weight = 0; } else { mpegts_network_scan_queue_del(mm); } } else { if (!weight && mn->mn_idlescan) weight = SUBSCRIPTION_PRIO_SCAN_IDLE; mpegts_network_scan_queue_del(mm); } if (result != MM_SCAN_NONE && mm->mm_scan_result != result) { mm->mm_scan_result = result; idnode_changed(&mm->mm_id); } /* Re-enable? */ if (weight > 0) mpegts_network_scan_queue_add(mm, weight, mm->mm_scan_flags, 10); }
int tvh_write(int fd, const void *buf, size_t len) { int64_t limit = mclk() + sec2mono(25); ssize_t c; while (len) { c = write(fd, buf, len); if (c < 0) { if (ERRNO_AGAIN(errno)) { if (mclk() > limit) break; tvh_safe_usleep(100); continue; } break; } len -= c; buf += c; } return len ? 1 : 0; }
static void * tvhdhomerun_device_discovery_thread( void *aux ) { struct hdhomerun_discover_device_t result_list[MAX_HDHOMERUN_DEVICES]; int numDevices, brk; while (tvheadend_is_running()) { numDevices = hdhomerun_discover_find_devices_custom(0, HDHOMERUN_DEVICE_TYPE_TUNER, HDHOMERUN_DEVICE_ID_WILDCARD, result_list, MAX_HDHOMERUN_DEVICES); if (numDevices > 0) { while (numDevices > 0 ) { numDevices--; struct hdhomerun_discover_device_t* cDev = &result_list[numDevices]; if ( cDev->device_type == HDHOMERUN_DEVICE_TYPE_TUNER ) { pthread_mutex_lock(&global_lock); tvhdhomerun_device_t *existing = tvhdhomerun_device_find(cDev->device_id); if ( tvheadend_is_running() ) { if ( !existing ) { tvhinfo(LS_TVHDHOMERUN,"Found HDHomerun device %08x with %d tuners", cDev->device_id, cDev->tuner_count); tvhdhomerun_device_create(cDev); } else if ( ((struct sockaddr_in *)&existing->hd_info.ip_address)->sin_addr.s_addr != htonl(cDev->ip_addr) ) { struct sockaddr_storage detected_dev_addr; memset(&detected_dev_addr, 0, sizeof(detected_dev_addr)); detected_dev_addr.ss_family = AF_INET; ((struct sockaddr_in *)&detected_dev_addr)->sin_addr.s_addr = htonl(cDev->ip_addr); char existing_ip[64]; tcp_get_str_from_ip(&existing->hd_info.ip_address, existing_ip, sizeof(existing_ip)); char detected_ip[64]; tcp_get_str_from_ip(&detected_dev_addr, detected_ip, sizeof(detected_ip)); tvhinfo(LS_TVHDHOMERUN,"HDHomerun device %08x switched IPs from %s to %s, updating", cDev->device_id, existing_ip, detected_ip); tvhdhomerun_device_destroy(existing); tvhdhomerun_device_create(cDev); } } pthread_mutex_unlock(&global_lock); } } } pthread_mutex_lock(&tvhdhomerun_discovery_lock); brk = 0; if (tvheadend_is_running()) { brk = tvh_cond_timedwait(&tvhdhomerun_discovery_cond, &tvhdhomerun_discovery_lock, mclk() + sec2mono(15)); brk = !ERRNO_AGAIN(brk) && brk != ETIMEDOUT; } pthread_mutex_unlock(&tvhdhomerun_discovery_lock); if (brk) break; } return NULL; }
/* * Timeshift thread */ void *timeshift_reader ( void *p ) { timeshift_t *ts = p; int nfds, end, run = 1, wait = -1, state; timeshift_seek_t *seek = &ts->seek; timeshift_file_t *tmp_file; int cur_speed = 100, keyframe_mode = 0; int64_t mono_now, mono_play_time = 0, mono_last_status = 0; int64_t deliver, deliver0, pause_time = 0, last_time = 0, skip_time = 0; int64_t i64; streaming_message_t *sm = NULL, *ctrl = NULL; streaming_skip_t *skip = NULL; tvhpoll_t *pd; tvhpoll_event_t ev = { 0 }; pd = tvhpoll_create(1); ev.fd = ts->rd_pipe.rd; ev.events = TVHPOLL_IN; tvhpoll_add(pd, &ev, 1); /* Output */ while (run) { // Note: Previously we allowed unlimited wait, but we now must wake periodically // to output status message if (wait < 0 || wait > 1000) wait = 1000; /* Wait for data */ if(wait) nfds = tvhpoll_wait(pd, &ev, 1, wait); else nfds = 0; wait = -1; end = 0; skip = NULL; mono_now = getfastmonoclock(); /* Control */ pthread_mutex_lock(&ts->state_mutex); if (nfds == 1) { if (_read_msg(NULL, ts->rd_pipe.rd, &ctrl) > 0) { /* Exit */ if (ctrl->sm_type == SMT_EXIT) { tvhtrace(LS_TIMESHIFT, "ts %d read exit request", ts->id); run = 0; streaming_msg_free(ctrl); ctrl = NULL; /* Speed */ } else if (ctrl->sm_type == SMT_SPEED) { int speed = ctrl->sm_code; int keyframe; /* Bound it */ if (speed > 3200) speed = 3200; if (speed < -3200) speed = -3200; /* Ignore negative */ if (!ts->dobuf && (speed < 0)) speed = seek->file ? speed : 0; /* Process */ if (cur_speed != speed) { /* Live playback */ state = ts->state; if (state == TS_LIVE) { /* Reject */ if (speed >= 100) { tvhdebug(LS_TIMESHIFT, "ts %d reject 1x+ in live mode", ts->id); speed = 100; /* Set position */ } else { tvhdebug(LS_TIMESHIFT, "ts %d enter timeshift mode", ts->id); ts->dobuf = 1; _seek_reset(seek); tmp_file = timeshift_filemgr_newest(ts); if (tmp_file != NULL) { i64 = tmp_file->last; timeshift_file_put(tmp_file); } else { i64 = ts->buf_time; } seek->file = timeshift_filemgr_get(ts, i64); if (seek->file != NULL) { seek->file->roff = seek->file->size; pause_time = seek->file->last; last_time = pause_time; } else { pause_time = i64; last_time = pause_time; } } } /* Check keyframe mode */ keyframe = (speed < 0) || (speed > 400); if (keyframe != keyframe_mode) { tvhdebug(LS_TIMESHIFT, "using keyframe mode? %s", keyframe ? "yes" : "no"); keyframe_mode = keyframe; if (keyframe) seek->frame = NULL; } /* Update */ if (speed != 100 || state != TS_LIVE) { ts->state = speed == 0 ? TS_PAUSE : TS_PLAY; tvhtrace(LS_TIMESHIFT, "reader - set %s", speed == 0 ? "TS_PAUSE" : "TS_PLAY"); } if ((ts->state == TS_PLAY && state != TS_PLAY) || (speed != cur_speed)) { mono_play_time = mono_now; tvhtrace(LS_TIMESHIFT, "update play time TS_LIVE - %"PRId64" play buffer from %"PRId64, mono_now, pause_time); if (speed != cur_speed) pause_time = last_time; } else if (ts->state == TS_PAUSE && state != TS_PAUSE) { pause_time = last_time; } cur_speed = speed; tvhdebug(LS_TIMESHIFT, "ts %d change speed %d", ts->id, speed); } /* Send on the message */ ctrl->sm_code = speed; streaming_target_deliver2(ts->output, ctrl); ctrl = NULL; /* Skip/Seek */ } else if (ctrl->sm_type == SMT_SKIP) { skip = ctrl->sm_data; switch (skip->type) { case SMT_SKIP_LIVE: if (ts->state != TS_LIVE) { /* Reset */ if (ts->full) { timeshift_filemgr_flush(ts, NULL); _seek_reset(seek); ts->full = 0; } /* Release */ if (sm) streaming_msg_free(sm); /* Find end */ skip_time = 0x7fffffffffffffffLL; // TODO: change this sometime! } break; case SMT_SKIP_ABS_TIME: /* -fallthrough */ case SMT_SKIP_REL_TIME: /* Convert */ skip_time = ts_rescale(skip->time, 1000000); tvhdebug(LS_TIMESHIFT, "ts %d skip %"PRId64" requested %"PRId64, ts->id, skip_time, skip->time); /* Live playback (stage1) */ if (ts->state == TS_LIVE) { _seek_reset(seek); tmp_file = timeshift_filemgr_newest(ts); if (tmp_file) { i64 = tmp_file->last; timeshift_file_put(tmp_file); } if (tmp_file && (seek->file = timeshift_filemgr_get(ts, i64)) != NULL) { seek->file->roff = seek->file->size; last_time = seek->file->last; } else { last_time = ts->buf_time; } } /* May have failed */ if (skip->type == SMT_SKIP_REL_TIME) skip_time += last_time; tvhdebug(LS_TIMESHIFT, "ts %d skip time %"PRId64, ts->id, skip_time); /* Live (stage2) */ if (ts->state == TS_LIVE) { if (skip_time >= ts->buf_time - TIMESHIFT_PLAY_BUF) { tvhdebug(LS_TIMESHIFT, "ts %d skip ignored, already live", ts->id); skip = NULL; } else { ts->state = TS_PLAY; ts->dobuf = 1; tvhtrace(LS_TIMESHIFT, "reader - set TS_PLAY"); } } /* OK */ if (skip) { /* seek */ seek->frame = NULL; end = _timeshift_do_skip(ts, skip_time, last_time, seek); if (seek->frame) { pause_time = seek->frame->time; tvhtrace(LS_TIMESHIFT, "ts %d skip - play buffer from %"PRId64" last_time %"PRId64, ts->id, pause_time, last_time); /* Adjust time */ if (mono_play_time != mono_now) tvhtrace(LS_TIMESHIFT, "ts %d update play time skip - %"PRId64, ts->id, mono_now); mono_play_time = mono_now; /* Clear existing packet */ if (sm) { streaming_msg_free(sm); sm = NULL; } } else { skip = NULL; } } break; default: tvherror(LS_TIMESHIFT, "ts %d invalid/unsupported skip type: %d", ts->id, skip->type); skip = NULL; break; } /* Error */ if (!skip) { ((streaming_skip_t*)ctrl->sm_data)->type = SMT_SKIP_ERROR; streaming_target_deliver2(ts->output, ctrl); ctrl = NULL; } /* Ignore */ } else { streaming_msg_free(ctrl); ctrl = NULL; } } } /* Done */ if (!run || !seek->file || ((ts->state != TS_PLAY && !skip))) { if (mono_now >= (mono_last_status + sec2mono(1))) { timeshift_status(ts, last_time); mono_last_status = mono_now; } pthread_mutex_unlock(&ts->state_mutex); continue; } /* Calculate delivery time */ deliver0 = (mono_now - mono_play_time) + TIMESHIFT_PLAY_BUF; deliver = (deliver0 * cur_speed) / 100; deliver = (deliver + pause_time); tvhtrace(LS_TIMESHIFT, "speed %d now %"PRId64" play_time %"PRId64" deliver %"PRId64" deliver0 %"PRId64, cur_speed, mono_now, mono_play_time, deliver, deliver0); /* Determine next packet */ if (!sm) { /* Rewind or Fast forward (i-frame only) */ if (skip || keyframe_mode) { int64_t req_time; /* Time */ if (!skip) req_time = last_time + ((cur_speed < 0) ? -1 : 1); else req_time = skip_time; end = _timeshift_do_skip(ts, req_time, last_time, seek); } /* Clear old message */ if (sm) { streaming_msg_free(sm); sm = NULL; } /* Find packet */ if (_timeshift_read(ts, seek, &sm, &wait) == -1) { pthread_mutex_unlock(&ts->state_mutex); break; } } /* Send skip response */ if (skip) { if (sm) { /* Status message */ skip->time = ts_rescale_inv(sm->sm_time, 1000000); skip->type = SMT_SKIP_ABS_TIME; tvhdebug(LS_TIMESHIFT, "ts %d skip to pts %"PRId64" ok", ts->id, sm->sm_time); /* Update timeshift status */ timeshift_fill_status(ts, &skip->timeshift, sm->sm_time); mono_last_status = mono_now; } else { /* Report error */ skip->type = SMT_SKIP_ERROR; skip = NULL; tvhdebug(LS_TIMESHIFT, "ts %d skip failed (%d)", ts->id, sm ? sm->sm_type : -1); } streaming_target_deliver2(ts->output, ctrl); } else { streaming_msg_free(ctrl); } ctrl = NULL; /* Deliver */ if (sm && (skip || (((cur_speed < 0) && (sm->sm_time >= deliver)) || ((cur_speed > 0) && (sm->sm_time <= deliver))))) { last_time = sm->sm_time; if (!skip && keyframe_mode) /* always send status on keyframe mode */ timeshift_status(ts, last_time); timeshift_packet_log("out", ts, sm); streaming_target_deliver2(ts->output, sm); sm = NULL; wait = 0; } else if (sm) { if (cur_speed > 0) wait = (sm->sm_time - deliver) / 1000; else wait = (deliver - sm->sm_time) / 1000; if (wait == 0) wait = 1; tvhtrace(LS_TIMESHIFT, "ts %d wait %d speed %d sm_time %"PRId64" deliver %"PRId64, ts->id, wait, cur_speed, sm->sm_time, deliver); } /* Periodic timeshift status */ if (mono_now >= (mono_last_status + sec2mono(1))) { timeshift_status(ts, last_time); mono_last_status = mono_now; } /* Terminate */ if (!seek->file || end != 0) { /* Back to live (unless buffer is full) */ if ((end == 1 && !ts->full) || !seek->file) { tvhdebug(LS_TIMESHIFT, "ts %d eob revert to live mode", ts->id); cur_speed = 100; ctrl = streaming_msg_create_code(SMT_SPEED, cur_speed); streaming_target_deliver2(ts->output, ctrl); ctrl = NULL; tvhtrace(LS_TIMESHIFT, "reader - set TS_LIVE"); /* Flush timeshift buffer to live */ if (_timeshift_flush_to_live(ts, seek, &wait) == -1) { pthread_mutex_unlock(&ts->state_mutex); break; } ts->state = TS_LIVE; /* Close file (if open) */ _read_close(seek); /* Pause */ } else { if (cur_speed <= 0) { cur_speed = 0; tvhtrace(LS_TIMESHIFT, "reader - set TS_PAUSE"); ts->state = TS_PAUSE; } else { cur_speed = 100; tvhtrace(LS_TIMESHIFT, "reader - set TS_PLAY"); if (ts->state != TS_PLAY) { ts->state = TS_PLAY; ts->dobuf = 1; if (mono_play_time != mono_now) tvhtrace(LS_TIMESHIFT, "update play time (pause) - %"PRId64, mono_now); mono_play_time = mono_now; } } tvhdebug(LS_TIMESHIFT, "ts %d sob speed %d last time %"PRId64, ts->id, cur_speed, last_time); pause_time = last_time; ctrl = streaming_msg_create_code(SMT_SPEED, cur_speed); streaming_target_deliver2(ts->output, ctrl); ctrl = NULL; } } pthread_mutex_unlock(&ts->state_mutex); } /* Cleanup */ tvhpoll_destroy(pd); _read_close(seek); if (sm) streaming_msg_free(sm); if (ctrl) streaming_msg_free(ctrl); tvhtrace(LS_TIMESHIFT, "ts %d exit reader thread", ts->id); return NULL; }
int h264_decode_slice_header(elementary_stream_t *st, bitstream_t *bs, int *pkttype, int *isfield) { h264_private_t *p; h264_pps_t *pps; h264_sps_t *sps; uint32_t slice_type, pps_id, width, height, v; uint64_t d; *pkttype = 0; *isfield = 0; if ((p = st->es_priv) == NULL) return -1; read_golomb_ue(bs); /* first_mb_in_slice */ slice_type = read_golomb_ue(bs); if (slice_type > 4) slice_type -= 5; /* Fixed slice type per frame */ pps_id = read_golomb_ue(bs); if (pps_id >= MAX_PPS_COUNT) return -1; pps = &p->pps[pps_id]; if (!pps->valid) return -1; sps = &p->sps[pps->sps_id]; if (!sps->valid) return -1; if (!sps->max_frame_num_bits) return -1; switch(slice_type) { case 0: *pkttype = PKT_P_FRAME; break; case 1: *pkttype = PKT_B_FRAME; break; case 2: *pkttype = PKT_I_FRAME; break; default: return -1; } skip_bits(bs, sps->max_frame_num_bits); if (!sps->mbs_only_flag) if (read_bits1(bs)) { skip_bits1(bs); // bottom field *isfield = 1; } d = 0; if (sps->time_scale) d = 180000 * (uint64_t)sps->units_in_tick / (uint64_t)sps->time_scale; if (d == 0 && st->es_frame_duration < 2 && p->start + sec2mono(4) < mclk()) { tvhwarn("parser", "H264 stream has not timing information, using 30fps"); d = 3000; /* 90000/30 = 3000 : 30fps */ } #if 0 if (sps->cbpsize) st->es_vbv_size = sps->cbpsize; #endif width = sps->width; height = sps->height * (2 - sps->mbs_only_flag); if (width && height && d) parser_set_stream_vparam(st, width, height, d); if (sps->aspect_num && sps->aspect_den) { width *= sps->aspect_num; height *= sps->aspect_den; if (width && height) { v = gcdU32(width, height); st->es_aspect_num = width / v; st->es_aspect_den = height / v; } } else { st->es_aspect_num = 0; st->es_aspect_den = 1; } return 0; }