static void video_seek(rtmp_t *r, media_pipe_t *mp, media_buf_t **mbp, int64_t pos, const char *txt) { if(pos < 0) pos = 0; TRACE(TRACE_DEBUG, "Video", "seek %s to %.2f", txt, pos / 1000000.0); RTMP_SendSeek(r->r, pos / 1000); r->seekpos_video = pos; r->seekpos_audio = pos; mp->mp_video.mq_seektarget = pos; mp->mp_audio.mq_seektarget = pos; mp_flush(mp); if(mbp != NULL && *mbp != NULL) { media_buf_free_unlocked(mp, *mbp); *mbp = NULL; } prop_set_float(prop_create(mp->mp_prop_root, "seektime"), pos / 1000000.0); }
static int64_t video_seek(rtmp_t *r, media_pipe_t *mp, media_buf_t **mbp, int64_t pos, int backward, const char *txt) { if(pos < 0) pos = 0; TRACE(TRACE_DEBUG, "Video", "seek %s to %.2f", txt, pos / 1000000.0); RTMP_SendSeek(r->r, pos / 1000); r->seekpos = pos; mp->mp_video.mq_seektarget = pos; mp->mp_audio.mq_seektarget = pos; mp_flush(mp, 0); if(mbp != NULL && *mbp != NULL) { media_buf_free(*mbp); *mbp = NULL; } prop_set_float(prop_create(mp->mp_prop_root, "seektime"), pos / 1000000.0); r->lastsubpts = AV_NOPTS_VALUE; return pos; }
int64_t rtmp_read_seek(LibRTMPContext *ctx, int64_t timestamp) { RTMP *r = &ctx->rtmp; if (!RTMP_SendSeek(r, timestamp)) return ERROR_UNKNOWN; return timestamp; }
static int64_t rtmp_read_seek(URLContext *s, int stream_index, int64_t timestamp, int flags) { RTMP *r = s->priv_data; if (flags & AVSEEK_FLAG_BYTE) return AVERROR(ENOSYS); /* seeks are in milliseconds */ if (stream_index < 0) timestamp = av_rescale_rnd(timestamp, 1000, AV_TIME_BASE, flags & AVSEEK_FLAG_BACKWARD ? AV_ROUND_DOWN : AV_ROUND_UP); if (!RTMP_SendSeek(r, timestamp)) return -1; return timestamp; }
static gboolean gst_rtmp_src_do_seek (GstBaseSrc * basesrc, GstSegment * segment) { GstRTMPSrc *src; src = GST_RTMP_SRC (basesrc); if (segment->format != GST_FORMAT_TIME) { GST_LOG_OBJECT (src, "Only time based seeks are supported"); return FALSE; } if (!src->rtmp) { GST_LOG_OBJECT (src, "Not connected yet"); return FALSE; } src->discont = TRUE; /* Initial seek */ if (src->cur_offset == 0 && segment->start == 0) return TRUE; if (!src->seekable) { GST_LOG_OBJECT (src, "Not a seekable stream"); return FALSE; } src->last_timestamp = GST_CLOCK_TIME_NONE; if (!RTMP_SendSeek (src->rtmp, segment->start / GST_MSECOND)) { GST_ERROR_OBJECT (src, "Seeking failed"); src->seekable = FALSE; return FALSE; } GST_DEBUG_OBJECT (src, "Seek to %" GST_TIME_FORMAT " successfull", GST_TIME_ARGS (segment->start)); return TRUE; }
static event_t * rtmp_playvideo(const char *url0, media_pipe_t *mp, char *errbuf, size_t errlen, video_queue_t *vq, struct vsource_list *vsl, const video_args_t *va0) { video_args_t va = *va0; rtmp_t r = {0}; event_t *e; char *url = mystrdupa(url0); mp_set_url(mp, va0->canonical_url, va0->parent_url, va0->parent_title); usage_event("Play video", 1, USAGE_SEG("format", "RTMP")); prop_set(mp->mp_prop_metadata, "format", PROP_SET_STRING, "RTMP"); prop_set(mp->mp_prop_root, "loading", PROP_SET_INT, 1); va.flags |= BACKEND_VIDEO_NO_FS_SCAN; rtmp_log_level = RTMP_LOGINFO; RTMP_LogSetLevel(rtmp_log_level); r.r = RTMP_Alloc(); RTMP_Init(r.r, mp->mp_cancellable); int64_t start = playinfo_get_restartpos(va.canonical_url, va.title, va.resume_mode); if(!RTMP_SetupURL(r.r, url)) { snprintf(errbuf, errlen, "Unable to setup RTMP-session"); rtmp_free(&r); return NULL; } r.r->Link.lFlags |= RTMP_LF_SWFV; if(!RTMP_Connect(r.r, NULL, errbuf, errlen, 5000)) { rtmp_free(&r); return NULL; } if(!RTMP_ConnectStream(r.r, 0)) { snprintf(errbuf, errlen, "Unable to connect RTMP-stream"); rtmp_free(&r); return NULL; } if(start) RTMP_SendSeek(r.r, start); r.mp = mp; mp->mp_audio.mq_stream = 0; mp->mp_video.mq_stream = 0; if(start > 0) { r.seekpos_video = start * 1000; r.seekpos_audio = start * 1000; mp->mp_seek_base = r.seekpos_video; mp->mp_video.mq_seektarget = r.seekpos_video; mp->mp_audio.mq_seektarget = r.seekpos_video; } else { mp->mp_video.mq_seektarget = AV_NOPTS_VALUE; mp->mp_audio.mq_seektarget = AV_NOPTS_VALUE; mp->mp_seek_base = 0; r.seekpos_audio = AV_NOPTS_VALUE; r.seekpos_video = AV_NOPTS_VALUE; } mp_configure(mp, MP_CAN_PAUSE, MP_BUFFER_DEEP, 0, "video"); mp->mp_max_realtime_delay = (r.r->Link.timeout - 1) * 1000000; mp_become_primary(mp); playinfo_register_play(va.canonical_url, 0); r.canonical_url = va.canonical_url; r.restartpos_last = -1; r.url = url; r.va = &va; r.is_loading = 1; e = rtmp_loop(&r, mp, url, errbuf, errlen); if(r.ss) sub_scanner_destroy(r.ss); if(r.total_duration) { int p = mp->mp_seek_base / (r.total_duration * 10); if(p >= video_settings.played_threshold) { TRACE(TRACE_DEBUG, "RTMP", "Playback reached %d%%, counting as played", p); playinfo_register_play(va.canonical_url, 1); playinfo_set_restartpos(va.canonical_url, -1, 0); } else { playinfo_set_restartpos(va.canonical_url, mp->mp_seek_base / 1000, 0); } } mp_shutdown(mp); TRACE(TRACE_DEBUG, "RTMP", "End of playback"); rtmp_free(&r); return e; }
static event_t * rtmp_loop(rtmp_t *r, media_pipe_t *mp, char *url, char *errbuf, size_t errlen) { RTMPPacket p = {0}; int pos = -1, ret; uint32_t dts; event_t *e = NULL; while(1) { if(pos == -1) { mp->mp_eof = 0; ret = RTMP_GetNextMediaPacket(r->r, &p); if(ret == 2) { /* Wait for queues to drain */ mp->mp_eof = 1; again: e = mp_wait_for_empty_queues(mp); if(e != NULL) { e = rtmp_process_event(r, e, NULL); if(e == NULL) goto again; } if(e == NULL) e = event_create_type(EVENT_EOF); break; } if(ret == 0) { int64_t restartpos = r->seekpos_video; if(cancellable_is_cancelled(mp->mp_cancellable)) { snprintf(errbuf, errlen, "Cancelled"); return NULL; } TRACE(TRACE_ERROR, "RTMP", "Disconnected"); sleep(1); if(restartpos == AV_NOPTS_VALUE) { snprintf(errbuf, errlen, "Giving up restart since nothing was decoded"); return NULL; } RTMP_Close(r->r); RTMP_Init(r->r, mp->mp_cancellable); memset(&p, 0, sizeof(p)); TRACE(TRACE_DEBUG, "RTMP", "Reconnecting stream at pos %ld", restartpos); if(!RTMP_SetupURL(r->r, url)) { snprintf(errbuf, errlen, "Unable to setup RTMP session"); return NULL; } if(!RTMP_Connect(r->r, NULL, errbuf, errlen, 5000)) { return NULL; } if(!RTMP_ConnectStream(r->r, 0)) { snprintf(errbuf, errlen, "Unable to stream RTMP session"); return NULL; } if(mp->mp_flags & MP_CAN_SEEK) RTMP_SendSeek(r->r, restartpos / 1000); continue; } dts = p.m_nTimeStamp; switch(p.m_packetType) { case RTMP_PACKET_TYPE_INFO: if(handle_metadata(r, p.m_body, p.m_nBodySize, mp, errbuf, errlen)) { RTMPPacket_Free(&p); return NULL; } break; case RTMP_PACKET_TYPE_VIDEO: e = get_packet_v(r, (void *)p.m_body, p.m_nBodySize, dts, mp); break; case RTMP_PACKET_TYPE_AUDIO: e = get_packet_a(r, (void *)p.m_body, p.m_nBodySize, dts, mp); break; case 0x16: pos = 0; break; default: TRACE(TRACE_DEBUG, "RTMP", "Got unknown packet type %d\n", p.m_packetType); break; } if(pos == -1) RTMPPacket_Free(&p); } if(pos != -1) { if(pos + 11 < p.m_nBodySize) { uint32_t ds = AMF_DecodeInt24(p.m_body + pos + 1); if(pos + 11 + ds + 4 > p.m_nBodySize) { snprintf(errbuf, errlen, "Corrupt stream"); RTMPPacket_Free(&p); return NULL; } dts = AMF_DecodeInt24(p.m_body + pos + 4); dts |= (p.m_body[pos + 7] << 24); if(p.m_body[pos] == RTMP_PACKET_TYPE_INFO) { if(handle_metadata(r, p.m_body, p.m_nBodySize, mp, errbuf, errlen)) { RTMPPacket_Free(&p); return NULL; } } else if(p.m_body[pos] == RTMP_PACKET_TYPE_VIDEO) { e = get_packet_v(r, (void *)p.m_body + pos + 11, ds, dts, mp); } else if(p.m_body[pos] == RTMP_PACKET_TYPE_AUDIO) { e = get_packet_a(r, (void *)p.m_body + pos + 11, ds, dts, mp); } else { TRACE(TRACE_DEBUG, "RTMP", "Got unknown packet type %d\n", p.m_body[pos]); } pos += 11 + ds + 4; } else { pos = -1; RTMPPacket_Free(&p); } } if(e != NULL) break; } return e; }
static event_t * rtmp_playvideo(const char *url0, media_pipe_t *mp, char *errbuf, size_t errlen, video_queue_t *vq, struct vsource_list *vsl, const video_args_t *va0) { video_args_t va = *va0; rtmp_t r = {0}; event_t *e; char *url = mystrdupa(url0); va.flags |= BACKEND_VIDEO_NO_FS_SCAN; prop_set_string(mp->mp_prop_type, "video"); rtmp_log_level = RTMP_LOGINFO; RTMP_LogSetLevel(rtmp_log_level); r.r = RTMP_Alloc(); RTMP_Init(r.r); int64_t start = 0; if(va.flags & BACKEND_VIDEO_RESUME || (video_settings.resume_mode == VIDEO_RESUME_YES && !(va.flags & BACKEND_VIDEO_START_FROM_BEGINNING))) start = video_get_restartpos(va.canonical_url); if(!RTMP_SetupURL(r.r, url)) { snprintf(errbuf, errlen, "Unable to setup RTMP-session"); rtmp_free(&r); return NULL; } r.r->Link.lFlags |= RTMP_LF_SWFV; if(!RTMP_Connect(r.r, NULL)) { snprintf(errbuf, errlen, "Unable to connect RTMP-session"); rtmp_free(&r); return NULL; } if(!RTMP_ConnectStream(r.r, 0)) { snprintf(errbuf, errlen, "Unable to connect RTMP-stream"); rtmp_free(&r); return NULL; } if(start) RTMP_SendSeek(r.r, start); r.mp = mp; mp->mp_audio.mq_stream = 0; mp->mp_video.mq_stream = 0; if(start > 0) { r.seekpos_video = start * 1000; r.seekpos_audio = start * 1000; mp->mp_seek_base = r.seekpos_video; mp->mp_video.mq_seektarget = r.seekpos_video; mp->mp_audio.mq_seektarget = r.seekpos_video; } else { mp->mp_video.mq_seektarget = AV_NOPTS_VALUE; mp->mp_audio.mq_seektarget = AV_NOPTS_VALUE; mp->mp_seek_base = 0; r.seekpos_audio = AV_NOPTS_VALUE; r.seekpos_video = AV_NOPTS_VALUE; } mp_configure(mp, MP_PLAY_CAPS_PAUSE, MP_BUFFER_DEEP, 0); mp->mp_max_realtime_delay = (r.r->Link.timeout - 1) * 1000000; mp_become_primary(mp); metadb_register_play(va.canonical_url, 0, CONTENT_VIDEO); r.canonical_url = va.canonical_url; r.restartpos_last = -1; sub_scanner_t *ss = sub_scanner_create(url, mp->mp_prop_subtitle_tracks, &va, 0); e = rtmp_loop(&r, mp, url, errbuf, errlen); sub_scanner_destroy(ss); if(r.total_duration) { int p = mp->mp_seek_base / (r.total_duration * 10); if(p >= video_settings.played_threshold) { TRACE(TRACE_DEBUG, "RTMP", "Playback reached %d%%, counting as played", p); metadb_register_play(va.canonical_url, 1, CONTENT_VIDEO); metadb_set_video_restartpos(va.canonical_url, -1); } } mp_flush(mp, 0); mp_shutdown(mp); TRACE(TRACE_DEBUG, "RTMP", "End of stream"); rtmp_free(&r); return e; }