static int try_connect(struct rtmp_stream *stream) { #ifndef FILE_TEST blog(LOG_INFO, "Connecting to RTMP URL %s...", stream->path.array); if (!RTMP_SetupURL2(&stream->rtmp, stream->path.array, stream->key.array)) return OBS_OUTPUT_BAD_PATH; RTMP_EnableWrite(&stream->rtmp); set_rtmp_dstr(&stream->rtmp.Link.pubUser, &stream->username); set_rtmp_dstr(&stream->rtmp.Link.pubPasswd, &stream->password); stream->rtmp.Link.swfUrl = stream->rtmp.Link.tcUrl; set_rtmp_str(&stream->rtmp.Link.flashVer, "FMLE/3.0 (compatible; FMSc/1.0)"); stream->rtmp.m_outChunkSize = 4096; stream->rtmp.m_bSendChunkSizeInfo = true; stream->rtmp.m_bUseNagle = true; if (!RTMP_Connect(&stream->rtmp, NULL)) return OBS_OUTPUT_CONNECT_FAILED; if (!RTMP_ConnectStream(&stream->rtmp, 0)) return OBS_OUTPUT_INVALID_STREAM; blog(LOG_INFO, "Connection to %s successful", stream->path.array); #endif return init_send(stream); }
static int rtmp_probe(const char *url0, char *errbuf, size_t errlen, int timeout_ms) { RTMP *r; char *url = mystrdupa(url0); r = RTMP_Alloc(); RTMP_Init(r, NULL); if(!RTMP_SetupURL(r, url)) { snprintf(errbuf, errlen, "Unable to setup RTMP-session"); RTMP_Free(r); return BACKEND_PROBE_FAIL; } if(!RTMP_Connect(r, NULL, errbuf, errlen, timeout_ms)) { RTMP_Close(r); RTMP_Free(r); return BACKEND_PROBE_FAIL; } RTMP_SetReadTimeout(r, timeout_ms); if(!RTMP_ConnectStream(r, 0)) { snprintf(errbuf, errlen, "Unable to connect RTMP-stream"); RTMP_Close(r); RTMP_Free(r); return BACKEND_PROBE_FAIL; } RTMP_Close(r); RTMP_Free(r); return BACKEND_PROBE_OK; }
/* * Class: net_butterflytv_rtmp_client_RtmpClient * Method: open * Signature: (Ljava/lang/String;)I */ JNIEXPORT jint JNICALL Java_net_ossrs_sea_RtmpClient_open (JNIEnv * env, jobject thiz, jstring url_, jboolean isPublishMode) { const char *url = (*env)->GetStringUTFChars(env, url_, 0); rtmp = RTMP_Alloc(); if (rtmp == NULL) { return -1; } RTMP_Init(rtmp); int ret = RTMP_SetupURL(rtmp, url); if (!ret) { RTMP_Free(rtmp); return -2; } if (isPublishMode) { RTMP_EnableWrite(rtmp); } ret = RTMP_Connect(rtmp, NULL); if (!ret) { RTMP_Free(rtmp); return -3; } ret = RTMP_ConnectStream(rtmp, 0); if (!ret) { return -4; } (*env)->ReleaseStringUTFChars(env, url_, url); return 1; }
/** * Open RTMP connection and verify that the stream can be played. * * URL syntax: rtmp://server[:port][/app][/playpath][ keyword=value]... * where 'app' is first one or two directories in the path * (e.g. /ondemand/, /flash/live/, etc.) * and 'playpath' is a file name (the rest of the path, * may be prefixed with "mp4:") * * Additional RTMP library options may be appended as * space-separated key-value pairs. */ static int rtmp_open(URLContext *s, const char *uri, int flags) { LibRTMPContext *ctx = s->priv_data; RTMP *r = &ctx->rtmp; int rc = 0, level; char *filename = s->filename; switch (av_log_get_level()) { default: case AV_LOG_FATAL: level = RTMP_LOGCRIT; break; case AV_LOG_ERROR: level = RTMP_LOGERROR; break; case AV_LOG_WARNING: level = RTMP_LOGWARNING; break; case AV_LOG_INFO: level = RTMP_LOGINFO; break; case AV_LOG_VERBOSE: level = RTMP_LOGDEBUG; break; case AV_LOG_DEBUG: level = RTMP_LOGDEBUG2; break; } RTMP_LogSetLevel(level); RTMP_LogSetCallback(rtmp_log); if (ctx->app || ctx->playpath) { int len = strlen(s->filename) + 1; if (ctx->app) len += strlen(ctx->app) + sizeof(" app="); if (ctx->playpath) len += strlen(ctx->playpath) + sizeof(" playpath="); if (!(filename = av_malloc(len))) return AVERROR(ENOMEM); av_strlcpy(filename, s->filename, len); if (ctx->app) { av_strlcat(filename, " app=", len); av_strlcat(filename, ctx->app, len); } if (ctx->playpath) { av_strlcat(filename, " playpath=", len); av_strlcat(filename, ctx->playpath, len); } } RTMP_Init(r); if (!RTMP_SetupURL(r, filename)) { rc = AVERROR_UNKNOWN; goto fail; } if (flags & AVIO_FLAG_WRITE) RTMP_EnableWrite(r); if (!RTMP_Connect(r, NULL) || !RTMP_ConnectStream(r, 0)) { rc = AVERROR_UNKNOWN; goto fail; } s->is_streamed = 1; rc = 0; fail: if (filename != s->filename) av_freep(&filename); return rc; }
/** * Open RTMP connection and verify that the stream can be played. * * URL syntax: rtmp://server[:port][/app][/playpath][ keyword=value]... * where 'app' is first one or two directories in the path * (e.g. /ondemand/, /flash/live/, etc.) * and 'playpath' is a file name (the rest of the path, * may be prefixed with "mp4:") * * Additional RTMP library options may be appended as * space-separated key-value pairs. */ static int rtmp_open(URLContext *s, const char *uri, int flags) { RTMP *r; int rc; r = av_mallocz(sizeof(RTMP)); if (!r) return AVERROR(ENOMEM); switch (av_log_get_level()) { default: case AV_LOG_FATAL: rc = RTMP_LOGCRIT; break; case AV_LOG_ERROR: rc = RTMP_LOGERROR; break; case AV_LOG_WARNING: rc = RTMP_LOGWARNING; break; case AV_LOG_INFO: rc = RTMP_LOGINFO; break; case AV_LOG_VERBOSE: rc = RTMP_LOGDEBUG; break; case AV_LOG_DEBUG: rc = RTMP_LOGDEBUG2; break; } RTMP_LogSetLevel(rc); RTMP_LogSetCallback(rtmp_log); RTMP_Init(r); if (!RTMP_SetupURL(r, s->filename)) { rc = -1; goto fail; } if (flags & AVIO_WRONLY) RTMP_EnableWrite(r); if (!RTMP_Connect(r, NULL) || !RTMP_ConnectStream(r, 0)) { rc = -1; goto fail; } s->priv_data = r; s->is_streamed = 1; return 0; fail: av_free(r); return rc; }
static GstFlowReturn gst_rtmp_sink_render (GstBaseSink * bsink, GstBuffer * buf) { GstRTMPSink *sink = GST_RTMP_SINK (bsink); GstBuffer *reffed_buf = NULL; if (sink->first) { /* open the connection */ if (!RTMP_IsConnected (sink->rtmp)) { if (!RTMP_Connect (sink->rtmp, NULL) || !RTMP_ConnectStream (sink->rtmp, 0)) { GST_ELEMENT_ERROR (sink, RESOURCE, OPEN_WRITE, (NULL), ("Could not connect to RTMP stream \"%s\" for writing", sink->uri)); RTMP_Free (sink->rtmp); sink->rtmp = NULL; g_free (sink->rtmp_uri); sink->rtmp_uri = NULL; return GST_FLOW_ERROR; } GST_DEBUG_OBJECT (sink, "Opened connection to %s", sink->rtmp_uri); } /* FIXME: Parse the first buffer and see if it contains a header plus a packet instead * of just assuming it's only the header */ GST_LOG_OBJECT (sink, "Caching first buffer of size %d for concatenation", GST_BUFFER_SIZE (buf)); gst_buffer_replace (&sink->cache, buf); sink->first = FALSE; return GST_FLOW_OK; } if (sink->cache) { GST_LOG_OBJECT (sink, "Joining 2nd buffer of size %d to cached buf", GST_BUFFER_SIZE (buf)); gst_buffer_ref (buf); reffed_buf = buf = gst_buffer_join (sink->cache, buf); sink->cache = NULL; } GST_LOG_OBJECT (sink, "Sending %d bytes to RTMP server", GST_BUFFER_SIZE (buf)); if (!RTMP_Write (sink->rtmp, (char *) GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf))) { GST_ELEMENT_ERROR (sink, RESOURCE, WRITE, (NULL), ("Failed to write data")); if (reffed_buf) gst_buffer_unref (reffed_buf); return GST_FLOW_ERROR; } if (reffed_buf) gst_buffer_unref (reffed_buf); return GST_FLOW_OK; }
static event_t * rtmp_playvideo(const char *url0, media_pipe_t *mp, int flags, int priority, char *errbuf, size_t errlen, const char *mimetype) { rtmp_t r = {0}; event_t *e; char *url = mystrdupa(url0); prop_set_string(mp->mp_prop_type, "video"); RTMP_LogSetLevel(RTMP_LOGINFO); r.r = RTMP_Alloc(); RTMP_Init(r.r); if(!RTMP_SetupURL(r.r, url)) { snprintf(errbuf, errlen, "Unable to setup RTMP-session"); rtmp_free(&r); return NULL; } 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; } mp->mp_audio.mq_stream = 0; mp->mp_video.mq_stream = 0; mp_configure(mp, MP_PLAY_CAPS_PAUSE, MP_BUFFER_DEEP); mp->mp_max_realtime_delay = (r.r->Link.timeout - 1) * 1000000; mp_become_primary(mp); e = rtmp_loop(&r, mp, url, errbuf, errlen); mp_flush(mp, 0); mp_shutdown(mp); TRACE(TRACE_DEBUG, "RTMP", "End of stream"); rtmp_free(&r); return e; }
/* open the file, do stuff necessary to go to PAUSED state */ static gboolean gst_rtmp_src_start (GstBaseSrc * basesrc) { GstRTMPSrc *src; src = GST_RTMP_SRC (basesrc); if (!src->uri) { GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ, (NULL), ("No filename given")); return FALSE; } src->cur_offset = 0; src->last_timestamp = 0; src->discont = TRUE; src->rtmp = RTMP_Alloc (); if (!src->rtmp) { GST_ERROR_OBJECT (src, "Could not allocate librtmp's RTMP context"); goto error; } RTMP_Init (src->rtmp); if (!RTMP_SetupURL (src->rtmp, src->uri)) { GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ, (NULL), ("Failed to setup URL '%s'", src->uri)); goto error; } src->seekable = !(src->rtmp->Link.lFlags & RTMP_LF_LIVE); GST_INFO_OBJECT (src, "seekable %d", src->seekable); /* open if required */ if (!RTMP_IsConnected (src->rtmp)) { if (!RTMP_Connect (src->rtmp, NULL)) { GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ, (NULL), ("Could not connect to RTMP stream \"%s\" for reading", src->uri)); goto error; } } return TRUE; error: if (src->rtmp) { RTMP_Free (src->rtmp); src->rtmp = NULL; } return FALSE; }
static int try_connect(struct rtmp_stream *stream) { if (dstr_is_empty(&stream->path)) { warn("URL is empty"); return OBS_OUTPUT_BAD_PATH; } info("Connecting to RTMP URL %s...", stream->path.array); memset(&stream->rtmp.Link, 0, sizeof(stream->rtmp.Link)); if (!RTMP_SetupURL(&stream->rtmp, stream->path.array)) return OBS_OUTPUT_BAD_PATH; RTMP_EnableWrite(&stream->rtmp); set_rtmp_dstr(&stream->rtmp.Link.pubUser, &stream->username); set_rtmp_dstr(&stream->rtmp.Link.pubPasswd, &stream->password); stream->rtmp.Link.swfUrl = stream->rtmp.Link.tcUrl; set_rtmp_str(&stream->rtmp.Link.flashVer, "FMLE/3.0 (compatible; OBS Studio; FMSc/1.0)"); RTMP_AddStream(&stream->rtmp, stream->key.array); for (size_t idx = 1;; idx++) { obs_encoder_t *encoder = obs_output_get_audio_encoder( stream->output, idx); const char *encoder_name; if (!encoder) break; encoder_name = obs_encoder_get_name(encoder); RTMP_AddStream(&stream->rtmp, encoder_name); } stream->rtmp.m_outChunkSize = 4096; stream->rtmp.m_bSendChunkSizeInfo = true; stream->rtmp.m_bUseNagle = true; if (!RTMP_Connect(&stream->rtmp, NULL)) return OBS_OUTPUT_CONNECT_FAILED; if (!RTMP_ConnectStream(&stream->rtmp, 0)) return OBS_OUTPUT_INVALID_STREAM; info("Connection to %s successful", stream->path.array); return init_send(stream); }
static gboolean gst_rtmp_sink_start (GstBaseSink * basesink) { GstRTMPSink *sink = GST_RTMP_SINK (basesink); if (!sink->uri) { GST_ELEMENT_ERROR (sink, RESOURCE, OPEN_WRITE, ("Please set URI for RTMP output"), ("No URI set before starting")); return FALSE; } sink->rtmp_uri = g_strdup (sink->uri); sink->rtmp = RTMP_Alloc (); RTMP_Init (sink->rtmp); if (!RTMP_SetupURL (sink->rtmp, sink->rtmp_uri)) { GST_ELEMENT_ERROR (sink, RESOURCE, OPEN_WRITE, (NULL), ("Failed to setup URL '%s'", sink->uri)); RTMP_Free (sink->rtmp); sink->rtmp = NULL; g_free (sink->rtmp_uri); sink->rtmp_uri = NULL; return FALSE; } GST_DEBUG_OBJECT (sink, "Created RTMP object"); /* Mark this as an output connection */ RTMP_EnableWrite (sink->rtmp); /* open the connection */ if (!RTMP_IsConnected (sink->rtmp)) { if (!RTMP_Connect (sink->rtmp, NULL) || !RTMP_ConnectStream (sink->rtmp, 0)) { GST_ELEMENT_ERROR (sink, RESOURCE, OPEN_WRITE, (NULL), ("Could not connect to RTMP stream \"%s\" for writing", sink->uri)); RTMP_Free (sink->rtmp); sink->rtmp = NULL; g_free (sink->rtmp_uri); sink->rtmp_uri = NULL; return FALSE; } GST_DEBUG_OBJECT (sink, "Opened connection to %s", sink->rtmp_uri); } sink->first = TRUE; return TRUE; }
Rtmp::Rtmp(QUrl url, QObject *parent) : QObject(parent) { m_rtmp = RTMP_Alloc(); RTMP_Init(m_rtmp); qDebug() << "Connecting to" << url; RTMP_SetupURL(m_rtmp, MY_URL ); RTMP_EnableWrite(m_rtmp); RTMP_Connect(m_rtmp, NULL); RTMP_ConnectStream(m_rtmp, 0); memset(&m_rtmpPacket, 0, sizeof(RTMPPacket)); qDebug() << RTMP_IsConnected(m_rtmp); }
/* open the file, do stuff necessary to go to PAUSED state */ static gboolean gst_rtmp_src_start (GstBaseSrc * basesrc) { GstRTMPSrc *src; gchar *uri_copy; src = GST_RTMP_SRC (basesrc); if (!src->uri) { GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ, (NULL), ("No filename given")); return FALSE; } src->cur_offset = 0; src->last_timestamp = 0; src->seekable = TRUE; src->discont = TRUE; uri_copy = g_strdup (src->uri); src->rtmp = RTMP_Alloc (); RTMP_Init (src->rtmp); if (!RTMP_SetupURL (src->rtmp, uri_copy)) { GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ, (NULL), ("Failed to setup URL '%s'", src->uri)); g_free (uri_copy); RTMP_Free (src->rtmp); src->rtmp = NULL; return FALSE; } /* open if required */ if (!RTMP_IsConnected (src->rtmp)) { if (!RTMP_Connect (src->rtmp, NULL)) { GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ, (NULL), ("Could not connect to RTMP stream \"%s\" for reading", src->uri)); RTMP_Free (src->rtmp); src->rtmp = NULL; return FALSE; } } return TRUE; }
bool LibRtmp::Open(const char* url) { streming_url_ = (char*)calloc(strlen(url)+1, sizeof(char)); strcpy(streming_url_, url); std::string tmp(url); stream_name_ = tmp.substr(tmp.rfind("/")+1); //AVal flashver = AVC("flashver"); //AVal flashver_arg = AVC("WIN 9,0,115,0"); AVal swfUrl = AVC("swfUrl"); AVal swfUrl_arg = AVC("http://localhost/librtmp.swf"); AVal pageUrl = AVC("pageUrl"); AVal pageUrl_arg = AVC("http://localhost/librtmp.html"); //RTMP_SetOpt(rtmp_, &flashver, &flashver_arg); RTMP_SetOpt(rtmp_, &swfUrl, &swfUrl_arg); RTMP_SetOpt(rtmp_, &pageUrl, &pageUrl_arg); if (is_need_record_) { AVal record = AVC("record"); AVal record_arg = AVC("on"); RTMP_SetOpt(rtmp_, &record, &record_arg); } int err = RTMP_SetupURL(rtmp_, streming_url_); if (err <= 0) return false; RTMP_EnableWrite(rtmp_); err = RTMP_Connect(rtmp_, NULL); if (err <= 0) return false; err = RTMP_ConnectStream(rtmp_, 0); if (err <= 0) return false; rtmp_->m_outChunkSize = 2*1024*1024; SendSetChunkSize(rtmp_->m_outChunkSize); return true; }
static int download(char *osis, int chapter) { struct stat sb; int size, pipe; char pageurl[256]; char playpath[256]; char path[256]; double duration = 0; RTMP_Init(rtmp); rtmp->Link.protocol = RTMP_PROTOCOL_RTMP; rtmp->Link.hostname = (AVal)AVC(HOSTNAME); rtmp->Link.port = 1935; rtmp->Link.app = (AVal)AVC(APP); rtmp->Link.flashVer = (AVal)AVC(FLASHVER); rtmp->Link.swfUrl = (AVal)AVC(SWFURL); rtmp->Link.tcUrl = (AVal)AVC(TCURL); sprintf(pageurl, PAGEURL "%s/%s/%s.%d", author, version, osis, chapter); rtmp->Link.pageUrl = (AVal){pageurl, strlen(pageurl)}; sprintf(playpath, "%s-%s/%s.%d", version, author, osis, chapter); rtmp->Link.playpath = (AVal){playpath, strlen(playpath)}; printf("downloading %s .....", playpath); fflush(stdout); sprintf(path, "%s.mp3", playpath); if (stat(path, &sb) == 0 && sb.st_size == 0) { unlink(path); } pipe = open(path, O_WRONLY | O_CREAT | O_EXCL, 0644); if (pipe < 0) { perror("open"); return errno; } size = RTMP_Connect(rtmp, NULL); if (size == FALSE) { perror("rtmp_conect"); return EINVAL; } do { size = RTMP_Read(rtmp, buffer, BUFSIZ); if (duration <= 0) { duration = RTMP_GetDuration(rtmp); } if (rtmp->m_read.status == RTMP_READ_COMPLETE) { size = 0; break; } if (!RTMP_IsConnected(rtmp)) { size = -1; break; } if (size > 0) { write(pipe, buffer, size); if (duration > 0) { printf("\b\b\b\b\b%04.1f%%", ((double)rtmp->m_read.timestamp) / (duration * 10.0)); fflush(stdout); } } } while (size >= 0); close(pipe); RTMP_Close(rtmp); if (size != 0 && stat(path, &sb) == 0) { if (sb.st_size == 0) { unlink(path); } else { sprintf(buffer, "%s.failed", path); rename(path, buffer); } } printf("\b\b\b\b\b%s\n", size == 0 ? "completed" : "incomplete"); return size; }
/** * Open RTMP connection and verify that the stream can be played. * * URL syntax: rtmp://server[:port][/app][/playpath][ keyword=value]... * where 'app' is first one or two directories in the path * (e.g. /ondemand/, /flash/live/, etc.) * and 'playpath' is a file name (the rest of the path, * may be prefixed with "mp4:") * * Additional RTMP library options may be appended as * space-separated key-value pairs. */ static int rtmp_open(URLContext *s, const char *uri, int flags) { LibRTMPContext *ctx = s->priv_data; RTMP *r = &ctx->rtmp; int rc = 0, level; char *filename = s->filename; int len = strlen(s->filename) + 1; switch (av_log_get_level()) { default: case AV_LOG_FATAL: level = RTMP_LOGCRIT; break; case AV_LOG_ERROR: level = RTMP_LOGERROR; break; case AV_LOG_WARNING: level = RTMP_LOGWARNING; break; case AV_LOG_INFO: level = RTMP_LOGINFO; break; case AV_LOG_VERBOSE: level = RTMP_LOGDEBUG; break; case AV_LOG_DEBUG: level = RTMP_LOGDEBUG2; break; } RTMP_LogSetLevel(level); RTMP_LogSetCallback(rtmp_log); if (ctx->app) len += strlen(ctx->app) + sizeof(" app="); if (ctx->tcurl) len += strlen(ctx->tcurl) + sizeof(" tcUrl="); if (ctx->pageurl) len += strlen(ctx->pageurl) + sizeof(" pageUrl="); if (ctx->flashver) len += strlen(ctx->flashver) + sizeof(" flashver="); if (ctx->conn) { char *sep, *p = ctx->conn; int options = 0; while (p) { options++; p += strspn(p, " "); if (!*p) break; sep = strchr(p, ' '); if (sep) p = sep + 1; else break; } len += options * sizeof(" conn="); len += strlen(ctx->conn); } if (ctx->playpath) len += strlen(ctx->playpath) + sizeof(" playpath="); if (ctx->live) len += sizeof(" live=1"); if (ctx->subscribe) len += strlen(ctx->subscribe) + sizeof(" subscribe="); if (ctx->client_buffer_time) len += strlen(ctx->client_buffer_time) + sizeof(" buffer="); if (ctx->swfurl || ctx->swfverify) { len += sizeof(" swfUrl="); if (ctx->swfverify) len += strlen(ctx->swfverify) + sizeof(" swfVfy=1"); else len += strlen(ctx->swfurl); } if (!(ctx->temp_filename = filename = av_malloc(len))) return AVERROR(ENOMEM); av_strlcpy(filename, s->filename, len); if (ctx->app) { av_strlcat(filename, " app=", len); av_strlcat(filename, ctx->app, len); } if (ctx->tcurl) { av_strlcat(filename, " tcUrl=", len); av_strlcat(filename, ctx->tcurl, len); } if (ctx->pageurl) { av_strlcat(filename, " pageUrl=", len); av_strlcat(filename, ctx->pageurl, len); } if (ctx->swfurl) { av_strlcat(filename, " swfUrl=", len); av_strlcat(filename, ctx->swfurl, len); } if (ctx->flashver) { av_strlcat(filename, " flashVer=", len); av_strlcat(filename, ctx->flashver, len); } if (ctx->conn) { char *sep, *p = ctx->conn; while (p) { av_strlcat(filename, " conn=", len); p += strspn(p, " "); if (!*p) break; sep = strchr(p, ' '); if (sep) *sep = '\0'; av_strlcat(filename, p, len); if (sep) p = sep + 1; else break; } } if (ctx->playpath) { av_strlcat(filename, " playpath=", len); av_strlcat(filename, ctx->playpath, len); } if (ctx->live) av_strlcat(filename, " live=1", len); if (ctx->subscribe) { av_strlcat(filename, " subscribe=", len); av_strlcat(filename, ctx->subscribe, len); } if (ctx->client_buffer_time) { av_strlcat(filename, " buffer=", len); av_strlcat(filename, ctx->client_buffer_time, len); } if (ctx->swfurl || ctx->swfverify) { av_strlcat(filename, " swfUrl=", len); if (ctx->swfverify) { av_strlcat(filename, ctx->swfverify, len); av_strlcat(filename, " swfVfy=1", len); } else { av_strlcat(filename, ctx->swfurl, len); } } RTMP_Init(r); if (!RTMP_SetupURL(r, filename)) { rc = AVERROR_UNKNOWN; goto fail; } if (flags & AVIO_FLAG_WRITE) RTMP_EnableWrite(r); if (!RTMP_Connect(r, NULL) || !RTMP_ConnectStream(r, 0)) { rc = AVERROR_UNKNOWN; goto fail; } #if CONFIG_NETWORK if (ctx->buffer_size >= 0 && (flags & AVIO_FLAG_WRITE)) { int tmp = ctx->buffer_size; setsockopt(r->m_sb.sb_socket, SOL_SOCKET, SO_SNDBUF, &tmp, sizeof(tmp)); } #endif s->is_streamed = 1; return 0; fail: av_freep(&ctx->temp_filename); if (rc) RTMP_Close(r); return rc; }
DWORD WINAPI RTMPPublisher::CreateConnectionThread(RTMPPublisher *publisher) { //------------------------------------------------------ // set up URL bool bRetry = false; bool bSuccess = false; bool bCanRetry = false; String failReason; String strBindIP; int serviceID = AppConfig->GetInt (TEXT("Publish"), TEXT("Service")); String strURL = AppConfig->GetString(TEXT("Publish"), TEXT("URL")); String strPlayPath = AppConfig->GetString(TEXT("Publish"), TEXT("PlayPath")); strURL.KillSpaces(); strPlayPath.KillSpaces(); LPSTR lpAnsiURL = NULL, lpAnsiPlaypath = NULL; RTMP *rtmp = NULL; //-------------------------------- // unbelievably disgusting hack for elgato devices String strOldDirectory; UINT dirSize = GetCurrentDirectory(0, 0); strOldDirectory.SetLength(dirSize); GetCurrentDirectory(dirSize, strOldDirectory.Array()); OSSetCurrentDirectory(API->GetAppPath()); //-------------------------------- if(!strURL.IsValid()) { failReason = TEXT("No server specified to connect to"); goto end; } if(serviceID != 0) { XConfig serverData; if(!serverData.Open(TEXT("services.xconfig"))) { failReason = TEXT("Could not open services.xconfig"); goto end; } XElement *services = serverData.GetElement(TEXT("services")); if(!services) { failReason = TEXT("Could not find any services in services.xconfig"); goto end; } XElement *service = NULL; DWORD numServices = services->NumElements(); for(UINT i=0; i<numServices; i++) { XElement *curService = services->GetElementByID(i); if(curService->GetInt(TEXT("id")) == serviceID) { service = curService; break; } } if(!service) { failReason = TEXT("Could not find the service specified in services.xconfig"); goto end; } XElement *servers = service->GetElement(TEXT("servers")); if(!servers) { failReason = TEXT("Could not find any servers for the service specified in services.xconfig"); goto end; } XDataItem *item = servers->GetDataItem(strURL); if(!item) item = servers->GetDataItemByID(0); strURL = item->GetData(); Log(TEXT("Using RTMP service: %s"), service->GetName()); Log(TEXT(" Server selection: %s"), strURL.Array()); } //------------------------------------------------------ // now back to the elgato directory if it needs the directory changed still to function *sigh* OSSetCurrentDirectory(strOldDirectory); //------------------------------------------------------ rtmp = RTMP_Alloc(); RTMP_Init(rtmp); RTMP_LogSetCallback(librtmpErrorCallback); //RTMP_LogSetLevel(RTMP_LOGERROR); lpAnsiURL = strURL.CreateUTF8String(); lpAnsiPlaypath = strPlayPath.CreateUTF8String(); if(!RTMP_SetupURL2(rtmp, lpAnsiURL, lpAnsiPlaypath)) { failReason = Str("Connection.CouldNotParseURL"); goto end; } char *rtmpUser = AppConfig->GetString(TEXT("Publish"), TEXT("Username")).CreateUTF8String(); char *rtmpPass = AppConfig->GetString(TEXT("Publish"), TEXT("Password")).CreateUTF8String(); if (rtmpUser) { rtmp->Link.pubUser.av_val = rtmpUser; rtmp->Link.pubUser.av_len = (int)strlen(rtmpUser); } if (rtmpPass) { rtmp->Link.pubPasswd.av_val = rtmpPass; rtmp->Link.pubPasswd.av_len = (int)strlen(rtmpPass); } RTMP_EnableWrite(rtmp); //set it to publish rtmp->Link.swfUrl.av_len = rtmp->Link.tcUrl.av_len; rtmp->Link.swfUrl.av_val = rtmp->Link.tcUrl.av_val; /*rtmp->Link.pageUrl.av_len = rtmp->Link.tcUrl.av_len; rtmp->Link.pageUrl.av_val = rtmp->Link.tcUrl.av_val;*/ rtmp->Link.flashVer.av_val = "FMLE/3.0 (compatible; FMSc/1.0)"; rtmp->Link.flashVer.av_len = (int)strlen(rtmp->Link.flashVer.av_val); //----------------------------------------- UINT tcpBufferSize = AppConfig->GetInt(TEXT("Publish"), TEXT("TCPBufferSize"), 64*1024); if(tcpBufferSize < 8192) tcpBufferSize = 8192; else if(tcpBufferSize > 1024*1024) tcpBufferSize = 1024*1024; rtmp->m_outChunkSize = 4096;//RTMP_DEFAULT_CHUNKSIZE;// rtmp->m_bSendChunkSizeInfo = TRUE; rtmp->m_bUseNagle = TRUE; strBindIP = AppConfig->GetString(TEXT("Publish"), TEXT("BindToIP"), TEXT("Default")); if (scmp(strBindIP, TEXT("Default"))) { rtmp->m_bindIP.addr.sin_family = AF_INET; rtmp->m_bindIP.addrLen = sizeof(rtmp->m_bindIP.addr); if (WSAStringToAddress(strBindIP.Array(), AF_INET, NULL, (LPSOCKADDR)&rtmp->m_bindIP.addr, &rtmp->m_bindIP.addrLen) == SOCKET_ERROR) { // no localization since this should rarely/never happen failReason = TEXT("WSAStringToAddress: Could not parse address"); goto end; } } LogInterfaceType(rtmp); //----------------------------------------- if(!RTMP_Connect(rtmp, NULL)) { failReason = Str("Connection.CouldNotConnect"); failReason << TEXT("\r\n\r\n") << RTMPPublisher::GetRTMPErrors(); bCanRetry = true; goto end; } if(!RTMP_ConnectStream(rtmp, 0)) { failReason = Str("Connection.InvalidStream"); failReason << TEXT("\r\n\r\n") << RTMPPublisher::GetRTMPErrors(); bCanRetry = true; goto end; } //----------------------------------------- OSDebugOut(TEXT("Connected: %u\r\n"), OSGetTime()); publisher->RequestKeyframe(1000); //----------------------------------------- bSuccess = true; end: if (lpAnsiURL) Free(lpAnsiURL); if (lpAnsiPlaypath) Free(lpAnsiPlaypath); if(!bSuccess) { if(rtmp) { RTMP_Close(rtmp); RTMP_Free(rtmp); } if(failReason.IsValid()) App->SetStreamReport(failReason); if(!publisher->bStopping) PostMessage(hwndMain, OBS_REQUESTSTOP, bCanRetry ? 0 : 1, 0); Log(TEXT("Connection to %s failed: %s"), strURL.Array(), failReason.Array()); publisher->bStopping = true; } else { publisher->Init(rtmp, tcpBufferSize); publisher->bConnected = true; publisher->bConnecting = false; } return 0; }
void QRtmp::run() { bool first = true; int retries = 0; if(!m_destFile.fileName().isEmpty()) if(!m_destFile.open(QIODevice::WriteOnly)) { setError(QString("Can't open %1 for writing").arg(m_destFile.fileName())); return; } while(!m_stop) { RTMP_Log(RTMP_LOGDEBUG, "Setting buffer time to: %dms", m_bufferTime); RTMP_SetBufferMS(m_rtmp, m_bufferTime); if(first) { first = false; RTMP_LogPrintf("Connecting ...\n"); if(!RTMP_Connect(m_rtmp, NULL)) { setError("RTMP_Connect failed"); break; } RTMP_Log(RTMP_LOGINFO, "Connected..."); // User defined seek offset if(dStartOffset > 0) { // Don't need the start offset if resuming an existing file if(m_bResume) { RTMP_Log(RTMP_LOGWARNING, "Can't seek a resumed stream, ignoring --start option"); dStartOffset = 0; } else dSeek = dStartOffset; } // Calculate the length of the stream to still play if(dStopOffset > 0) { // Quit if start seek is past required stop offset if(dStopOffset <= dSeek) { RTMP_LogPrintf("Already Completed\n"); break; } } if(!RTMP_ConnectStream(m_rtmp, dSeek)) { setError("RTMP_ConnectStream failed"); break; } } else { if(retries) { RTMP_Log(RTMP_LOGERROR, "Failed to resume the stream\n\n"); if(!RTMP_IsTimedout(m_rtmp)) setError("RTMP_IsTimedout failed"); else setError("RTMP_IsTimedout RD_INCOMPLETE"); break; } RTMP_Log(RTMP_LOGINFO, "Connection timed out, trying to resume.\n\n"); /* Did we already try pausing, and it still didn't work? */ if(m_rtmp->m_pausing == 3) { /* Only one try at reconnecting... */ retries = 1; dSeek = m_rtmp->m_pauseStamp; if(dStopOffset > 0) { if(dStopOffset <= dSeek) { RTMP_LogPrintf("Already Completed\n"); break; } } if(!RTMP_ReconnectStream(m_rtmp, dSeek)) { RTMP_Log(RTMP_LOGERROR, "Failed to resume the stream\n\n"); if(!RTMP_IsTimedout(m_rtmp)) setError("RTMP_IsTimedout failed"); else setError("RTMP_IsTimedout RD_INCOMPLETE"); break; } } else if(!RTMP_ToggleStream(m_rtmp)) { RTMP_Log(RTMP_LOGERROR, "Failed to resume the stream\n\n"); if(!RTMP_IsTimedout(m_rtmp)) setError("RTMP_IsTimedout failed"); else setError("RTMP_IsTimedout RD_INCOMPLETE"); break; } m_bResume = true; } int nStatus = download(); if(nStatus != RD_INCOMPLETE || !RTMP_IsTimedout(m_rtmp) || m_bLiveStream) break; } RTMP_Log(RTMP_LOGDEBUG, "Closing connection.\n"); RTMP_Close(m_rtmp); if(m_destFile.isOpen()) m_destFile.close(); setStreamIsRunning(false); }
int main(int argc, char **argv) { extern char *optarg; int nStatus = RD_SUCCESS; int bStdoutMode = TRUE; // if true print the stream directly to stdout, messages go to stderr int bLiveStream = TRUE; // is it a live stream? then we can't seek/resume long int timeout = DEF_TIMEOUT; // timeout connection after 120 seconds RTMP rtmp = { 0 }; AVal fullUrl = { 0, 0 }; RTMP_debuglevel = RTMP_LOGINFO; char *flvFile = 0; signal(SIGINT, sigIntHandler); signal(SIGTERM, sigIntHandler); signal(SIGHUP, sigIntHandler); signal(SIGPIPE, sigIntHandler); signal(SIGQUIT, sigIntHandler); RTMP_LogPrintf("RTMPDump %s\n", RTMPDUMP_VERSION); RTMP_LogPrintf("(c) 2010 Andrej Stepanchuk, Howard Chu, The Flvstreamer Team; license: GPL\n"); int opt; struct option longopts[] = { {"help", 0, NULL, 'h'}, {"url", 1, NULL, 'i'}, {"rtmp", 1, NULL, 'r'}, {"live", 0, NULL, 'v'}, {"timeout", 1, NULL, 'm'}, {"hashes", 0, NULL, '#'}, {"quiet", 0, NULL, 'q'}, {"verbose", 0, NULL, 'V'}, {0, 0, 0, 0} }; while ((opt = getopt_long(argc, argv, "hVvqzRr:s:t:i:p:a:b:f:o:u:C:n:c:l:y:Ym:k:d:A:B:T:w:x:W:X:S:#j:", longopts, NULL)) != -1) { switch (opt) { case 'h': usage(argv[0]); return RD_SUCCESS; case 'v': bLiveStream = TRUE; // no seeking or resuming possible! break; case 'i': STR2AVAL(fullUrl, optarg); break; case 'o': flvFile = optarg; if (strcmp(flvFile, "-")) bStdoutMode = FALSE; break; case 'm': timeout = atoi(optarg); break; case 'q': RTMP_debuglevel = RTMP_LOGCRIT; break; case 'V': RTMP_debuglevel = RTMP_LOGDEBUG; break; case 'z': RTMP_debuglevel = RTMP_LOGALL; break; default: RTMP_LogPrintf("unknown option: %c\n", opt); usage(argv[0]); return RD_FAILED; break; } } if (flvFile == 0) { RTMP_Log(RTMP_LOGWARNING, "You haven't specified an output file (-o filename), using stdout"); bStdoutMode = TRUE; } if (!file) { if (bStdoutMode) { file = stdout; SET_BINMODE(file); } else { file = fopen(flvFile, "w+b"); if (file == 0) { RTMP_LogPrintf("Failed to open file! %s\n", flvFile); return RD_FAILED; } } } //////////////////////////////////////////////////////////////////////////////////////// RTMP_Init(&rtmp);//åå§åRTMPåę° //ęå®äŗ-i åę°ļ¼ē“ę„č®¾ē½®URL if (RTMP_SetupURL(&rtmp, fullUrl.av_val) == FALSE) { RTMP_Log(RTMP_LOGERROR, "Couldn't parse URL: %s", fullUrl.av_val); return RD_FAILED; } rtmp.Link.timeout = timeout ; /* Try to keep the stream moving if it pauses on us */ if (!bLiveStream ) rtmp.Link.lFlags |= RTMP_LF_BUFX; else { rtmp.Link.lFlags |= RTMP_LF_LIVE ; } printf("aaaaa:%d\n", rtmp.Link.lFlags ) ; while (!RTMP_ctrlC) { RTMP_Log(RTMP_LOGDEBUG, "Setting buffer time to: %dms", DEF_BUFTIME); RTMP_SetBufferMS(&rtmp, 2000);//åčÆęå”åØåø®ęē¼åå¤ä¹ RTMP_LogPrintf("Connecting ...\n"); if (!RTMP_Connect(&rtmp, NULL)) {//å»ŗē«čæę„,åé"connect" nStatus = RD_NO_CONNECT; break; } RTMP_Log(RTMP_LOGINFO, "Connected..."); //å¤ēęå”ē«Æčæåēåē§ę§å¶ę¶ęÆå ļ¼ęÆå¦ę¶å°connectēresultåå°±čæč”createStreamļ¼ä»„ååéplay(test)ę¶ęÆ if (!RTMP_ConnectStream(&rtmp, 0)) {//äøę¦čæåļ¼č”Øē¤ŗęå”ē«Æå¼å§åéę°ę®äŗ.åÆ仄playäŗ nStatus = RD_FAILED; break; } nStatus = Download(&rtmp, file, bStdoutMode, bLiveStream ); if (nStatus != RD_INCOMPLETE || !RTMP_IsTimedout(&rtmp) || bLiveStream) break; } RTMP_LogPrintf("Download complete\n"); //clean: RTMP_Log(RTMP_LOGDEBUG, "Closing connection.\n"); RTMP_Close(&rtmp); if (file != 0) fclose(file); return nStatus; }
int main(int argc, char **argv) { extern char *optarg; int nStatus = RD_SUCCESS; double percent = 0; double duration = 0.0; int nSkipKeyFrames = DEF_SKIPFRM; // skip this number of keyframes when resuming int bOverrideBufferTime = FALSE; // if the user specifies a buffer time override this is true int bStdoutMode = TRUE; // if true print the stream directly to stdout, messages go to stderr int bResume = FALSE; // true in resume mode uint32_t dSeek = 0; // seek position in resume mode, 0 otherwise uint32_t bufferTime = DEF_BUFTIME; // meta header and initial frame for the resume mode (they are read from the file and compared with // the stream we are trying to continue char *metaHeader = 0; uint32_t nMetaHeaderSize = 0; // video keyframe for matching char *initialFrame = 0; uint32_t nInitialFrameSize = 0; int initialFrameType = 0; // tye: audio or video AVal hostname = { 0, 0 }; AVal playpath = { 0, 0 }; AVal subscribepath = { 0, 0 }; AVal usherToken = { 0, 0 }; //Justin.tv auth token int port = -1; int protocol = RTMP_PROTOCOL_UNDEFINED; int retries = 0; int bLiveStream = FALSE; // is it a live stream? then we can't seek/resume int bRealtimeStream = FALSE; // If true, disable the BUFX hack (be patient) int bHashes = FALSE; // display byte counters not hashes by default long int timeout = DEF_TIMEOUT; // timeout connection after 120 seconds uint32_t dStartOffset = 0; // seek position in non-live mode uint32_t dStopOffset = 0; RTMP rtmp = { 0 }; FILE *pLogFile; AVal fullUrl = { 0, 0 }; AVal swfUrl = { 0, 0 }; AVal tcUrl = { 0, 0 }; AVal pageUrl = { 0, 0 }; AVal app = { 0, 0 }; AVal auth = { 0, 0 }; AVal swfHash = { 0, 0 }; uint32_t swfSize = 0; AVal flashVer = { 0, 0 }; AVal sockshost = { 0, 0 }; #ifdef CRYPTO int swfAge = 30; /* 30 days for SWF cache by default */ int swfVfy = 0; unsigned char hash[RTMP_SWF_HASHLEN]; #endif char *flvFile = 0; signal(SIGINT, sigIntHandler); signal(SIGTERM, sigIntHandler); #ifndef WIN32 signal(SIGHUP, sigIntHandler); signal(SIGPIPE, sigIntHandler); signal(SIGQUIT, sigIntHandler); #endif RTMP_debuglevel = RTMP_LOGALL; //pLogFile = fopen("log.txt", "w"); //RTMP_LogSetOutput(pLogFile); // Check for --quiet option before printing any output int index = 0; while (index < argc) { if (strcmp(argv[index], "--quiet") == 0 || strcmp(argv[index], "-q") == 0) RTMP_debuglevel = RTMP_LOGCRIT; index++; } #define RTMPDUMP_VERSION "2.4" RTMP_LogPrintf("RTMPDump %s\n", RTMPDUMP_VERSION); RTMP_LogPrintf ("(c) 2010 Andrej Stepanchuk, Howard Chu, The Flvstreamer Team; license: GPL\n"); if (!InitSockets()) { RTMP_Log(RTMP_LOGERROR, "Couldn't load sockets support on your platform, exiting!"); return RD_FAILED; } /* sleep(30); */ RTMP_Init(&rtmp); int opt; /* struct option longopts[] = { {"help", 0, NULL, 'h'}, {"host", 1, NULL, 'n'}, {"port", 1, NULL, 'c'}, {"socks", 1, NULL, 'S'}, {"protocol", 1, NULL, 'l'}, {"playpath", 1, NULL, 'y'}, {"playlist", 0, NULL, 'Y'}, {"rtmp", 1, NULL, 'r'}, {"swfUrl", 1, NULL, 's'}, {"tcUrl", 1, NULL, 't'}, {"pageUrl", 1, NULL, 'p'}, {"app", 1, NULL, 'a'}, {"auth", 1, NULL, 'u'}, {"conn", 1, NULL, 'C'}, #ifdef CRYPTO {"swfhash", 1, NULL, 'w'}, {"swfsize", 1, NULL, 'x'}, {"swfVfy", 1, NULL, 'W'}, {"swfAge", 1, NULL, 'X'}, #endif {"flashVer", 1, NULL, 'f'}, {"live", 0, NULL, 'v'}, {"flv", 1, NULL, 'o'}, {"resume", 0, NULL, 'e'}, {"timeout", 1, NULL, 'm'}, {"buffer", 1, NULL, 'b'}, {"skip", 1, NULL, 'k'}, {"subscribe", 1, NULL, 'd'}, {"start", 1, NULL, 'A'}, {"stop", 1, NULL, 'B'}, {"token", 1, NULL, 'T'}, {"hashes", 0, NULL, '#'}, {"debug", 0, NULL, 'z'}, {"quiet", 0, NULL, 'q'}, {"verbose", 0, NULL, 'V'}, {0, 0, 0, 0} };*/ while ((opt = getopt/*_long*/(argc, argv, "hVveqzr:s:t:p:a:b:f:o:u:C:n:c:l:y:Ym:k:d:A:B:T:w:x:W:X:S:#"/*, longopts, NULL*/)) != -1) { switch (opt) { case 'h': usage(argv[0]); return RD_SUCCESS; #ifdef CRYPTO case 'w': { int res = hex2bin(optarg, &swfHash.av_val); if (res != RTMP_SWF_HASHLEN) { swfHash.av_val = NULL; RTMP_Log(RTMP_LOGWARNING, "Couldn't parse swf hash hex string, not hexstring or not %d bytes, ignoring!", RTMP_SWF_HASHLEN); } swfHash.av_len = RTMP_SWF_HASHLEN; break; } case 'x': { int size = atoi(optarg); if (size <= 0) { RTMP_Log(RTMP_LOGERROR, "SWF Size must be at least 1, ignoring\n"); } else { swfSize = size; } break; } case 'W': STR2AVAL(swfUrl, optarg); swfVfy = 1; break; case 'X': { int num = atoi(optarg); if (num < 0) { RTMP_Log(RTMP_LOGERROR, "SWF Age must be non-negative, ignoring\n"); } else { swfAge = num; } } break; #endif case 'k': nSkipKeyFrames = atoi(optarg); if (nSkipKeyFrames < 0) { RTMP_Log(RTMP_LOGERROR, "Number of keyframes skipped must be greater or equal zero, using zero!"); nSkipKeyFrames = 0; } else { RTMP_Log(RTMP_LOGDEBUG, "Number of skipped key frames for resume: %d", nSkipKeyFrames); } break; case 'b': { int32_t bt = atol(optarg); if (bt < 0) { RTMP_Log(RTMP_LOGERROR, "Buffer time must be greater than zero, ignoring the specified value %d!", bt); } else { bufferTime = bt; bOverrideBufferTime = TRUE; } break; } case 'v': bLiveStream = TRUE; // no seeking or resuming possible! break; case 'R': bRealtimeStream = TRUE; // seeking and resuming is still possible break; case 'd': STR2AVAL(subscribepath, optarg); break; case 'n': STR2AVAL(hostname, optarg); break; case 'c': port = atoi(optarg); break; case 'l': protocol = atoi(optarg); if (protocol < RTMP_PROTOCOL_RTMP || protocol > RTMP_PROTOCOL_RTMPTS) { RTMP_Log(RTMP_LOGERROR, "Unknown protocol specified: %d", protocol); return RD_FAILED; } break; case 'y': STR2AVAL(playpath, optarg); break; case 'Y': RTMP_SetOpt(&rtmp, &av_playlist, (AVal *)&av_true); break; case 'r': { AVal parsedHost, parsedApp, parsedPlaypath; unsigned int parsedPort = 0; int parsedProtocol = RTMP_PROTOCOL_UNDEFINED; if (!RTMP_ParseURL (optarg, &parsedProtocol, &parsedHost, &parsedPort, &parsedPlaypath, &parsedApp)) { RTMP_Log(RTMP_LOGWARNING, "Couldn't parse the specified url (%s)!", optarg); } else { if (!hostname.av_len) hostname = parsedHost; if (port == -1) port = parsedPort; if (playpath.av_len == 0 && parsedPlaypath.av_len) { playpath = parsedPlaypath; } if (protocol == RTMP_PROTOCOL_UNDEFINED) protocol = parsedProtocol; if (app.av_len == 0 && parsedApp.av_len) { app = parsedApp; } } break; } case 'i': STR2AVAL(fullUrl, optarg); break; case 's': STR2AVAL(swfUrl, optarg); break; case 't': STR2AVAL(tcUrl, optarg); break; case 'p': STR2AVAL(pageUrl, optarg); break; case 'a': STR2AVAL(app, optarg); break; case 'f': STR2AVAL(flashVer, optarg); break; case 'o': flvFile = optarg; if (strcmp(flvFile, "-")) bStdoutMode = FALSE; break; case 'e': bResume = TRUE; break; case 'u': STR2AVAL(auth, optarg); break; case 'C': { AVal av; STR2AVAL(av, optarg); if (!RTMP_SetOpt(&rtmp, &av_conn, &av)) { RTMP_Log(RTMP_LOGERROR, "Invalid AMF parameter: %s", optarg); return RD_FAILED; } } break; case 'm': timeout = atoi(optarg); break; case 'A': dStartOffset = (int) (atof(optarg) * 1000.0); break; case 'B': dStopOffset = (int) (atof(optarg) * 1000.0); break; case 'T': { AVal token; STR2AVAL(token, optarg); RTMP_SetOpt(&rtmp, &av_token, &token); } break; case '#': bHashes = TRUE; break; case 'q': RTMP_debuglevel = RTMP_LOGCRIT; break; case 'V': RTMP_debuglevel = RTMP_LOGDEBUG; break; case 'z': RTMP_debuglevel = RTMP_LOGALL; break; case 'S': STR2AVAL(sockshost, optarg); break; case 'j': STR2AVAL(usherToken, optarg); break; default: RTMP_LogPrintf("unknown option: %c\n", opt); usage(argv[0]); return RD_FAILED; break; } } if (!hostname.av_len && !fullUrl.av_len) { RTMP_Log(RTMP_LOGERROR, "You must specify a hostname (--host) or url (-r \"rtmp://host[:port]/playpath\") containing a hostname"); return RD_FAILED; } if (playpath.av_len == 0 && !fullUrl.av_len) { RTMP_Log(RTMP_LOGERROR, "You must specify a playpath (--playpath) or url (-r \"rtmp://host[:port]/playpath\") containing a playpath"); return RD_FAILED; } if (protocol == RTMP_PROTOCOL_UNDEFINED && !fullUrl.av_len) { RTMP_Log(RTMP_LOGWARNING, "You haven't specified a protocol (--protocol) or rtmp url (-r), using default protocol RTMP"); protocol = RTMP_PROTOCOL_RTMP; } if (port == -1 && !fullUrl.av_len) { RTMP_Log(RTMP_LOGWARNING, "You haven't specified a port (--port) or rtmp url (-r), using default port 1935"); port = 0; } if (port == 0 && !fullUrl.av_len) { if (protocol & RTMP_FEATURE_SSL) port = 443; else if (protocol & RTMP_FEATURE_HTTP) port = 80; else port = 1935; } if (flvFile == 0) { RTMP_Log(RTMP_LOGWARNING, "You haven't specified an output file (-o filename), using stdout"); bStdoutMode = TRUE; } if (bStdoutMode && bResume) { RTMP_Log(RTMP_LOGWARNING, "Can't resume in stdout mode, ignoring --resume option"); bResume = FALSE; } if (bLiveStream && bResume) { RTMP_Log(RTMP_LOGWARNING, "Can't resume live stream, ignoring --resume option"); bResume = FALSE; } #ifdef CRYPTO if (swfVfy) { if (RTMP_HashSWF(swfUrl.av_val, (unsigned int *)&swfSize, hash, swfAge) == 0) { swfHash.av_val = (char *)hash; swfHash.av_len = RTMP_SWF_HASHLEN; } } if (swfHash.av_len == 0 && swfSize > 0) { RTMP_Log(RTMP_LOGWARNING, "Ignoring SWF size, supply also the hash with --swfhash"); swfSize = 0; } if (swfHash.av_len != 0 && swfSize == 0) { RTMP_Log(RTMP_LOGWARNING, "Ignoring SWF hash, supply also the swf size with --swfsize"); swfHash.av_len = 0; swfHash.av_val = NULL; } #endif if (tcUrl.av_len == 0) { tcUrl.av_len = strlen(RTMPProtocolStringsLower[protocol]) + hostname.av_len + app.av_len + sizeof("://:65535/"); tcUrl.av_val = (char *) malloc(tcUrl.av_len); if (!tcUrl.av_val) return RD_FAILED; tcUrl.av_len = snprintf(tcUrl.av_val, tcUrl.av_len, "%s://%.*s:%d/%.*s", RTMPProtocolStringsLower[protocol], hostname.av_len, hostname.av_val, port, app.av_len, app.av_val); } int first = 1; // User defined seek offset if (dStartOffset > 0) { // Live stream if (bLiveStream) { RTMP_Log(RTMP_LOGWARNING, "Can't seek in a live stream, ignoring --start option"); dStartOffset = 0; } } if (!fullUrl.av_len) { RTMP_SetupStream(&rtmp, protocol, &hostname, port, &sockshost, &playpath, &tcUrl, &swfUrl, &pageUrl, &app, &auth, &swfHash, swfSize, &flashVer, &subscribepath, &usherToken, dSeek, dStopOffset, bLiveStream, timeout); } else { if (RTMP_SetupURL(&rtmp, fullUrl.av_val) == FALSE) { RTMP_Log(RTMP_LOGERROR, "Couldn't parse URL: %s", fullUrl.av_val); return RD_FAILED; } } /* Try to keep the stream moving if it pauses on us */ if (!bLiveStream && !bRealtimeStream && !(protocol & RTMP_FEATURE_HTTP)) rtmp.Link.lFlags |= RTMP_LF_BUFX; off_t size = 0; // ok, we have to get the timestamp of the last keyframe (only keyframes are seekable) / last audio frame (audio only streams) if (bResume) { nStatus = OpenResumeFile(flvFile, &file, &size, &metaHeader, &nMetaHeaderSize, &duration); if (nStatus == RD_FAILED) goto clean; if (!file) { // file does not exist, so go back into normal mode bResume = FALSE; // we are back in fresh file mode (otherwise finalizing file won't be done) } else { nStatus = GetLastKeyframe(file, nSkipKeyFrames, &dSeek, &initialFrame, &initialFrameType, &nInitialFrameSize); if (nStatus == RD_FAILED) { RTMP_Log(RTMP_LOGDEBUG, "Failed to get last keyframe."); goto clean; } if (dSeek == 0) { RTMP_Log(RTMP_LOGDEBUG, "Last keyframe is first frame in stream, switching from resume to normal mode!"); bResume = FALSE; } } } if (!file) { if (bStdoutMode) { file = stdout; SET_BINMODE(file); } else { file = fopen(flvFile, "w+b"); if (file == 0) { RTMP_LogPrintf("Failed to open file! %s\n", flvFile); return RD_FAILED; } } } #ifdef _DEBUG netstackdump = fopen("netstackdump", "wb"); netstackdump_read = fopen("netstackdump_read", "wb"); #endif while (!RTMP_ctrlC) { RTMP_Log(RTMP_LOGDEBUG, "Setting buffer time to: %dms", bufferTime); RTMP_SetBufferMS(&rtmp, bufferTime); if (first) { first = 0; RTMP_LogPrintf("Connecting ...\n"); if (!RTMP_Connect(&rtmp, NULL)) { nStatus = RD_NO_CONNECT; break; } RTMP_Log(RTMP_LOGINFO, "Connected..."); // User defined seek offset if (dStartOffset > 0) { // Don't need the start offset if resuming an existing file if (bResume) { RTMP_Log(RTMP_LOGWARNING, "Can't seek a resumed stream, ignoring --start option"); dStartOffset = 0; } else { dSeek = dStartOffset; } } // Calculate the length of the stream to still play if (dStopOffset > 0) { // Quit if start seek is past required stop offset if (dStopOffset <= dSeek) { RTMP_LogPrintf("Already Completed\n"); nStatus = RD_SUCCESS; break; } } if (!RTMP_ConnectStream(&rtmp, dSeek)) { nStatus = RD_FAILED; break; } } else { nInitialFrameSize = 0; if (retries) { RTMP_Log(RTMP_LOGERROR, "Failed to resume the stream\n\n"); if (!RTMP_IsTimedout(&rtmp)) nStatus = RD_FAILED; else nStatus = RD_INCOMPLETE; break; } RTMP_Log(RTMP_LOGINFO, "Connection timed out, trying to resume.\n\n"); /* Did we already try pausing, and it still didn't work? */ if (rtmp.m_pausing == 3) { /* Only one try at reconnecting... */ retries = 1; dSeek = rtmp.m_pauseStamp; if (dStopOffset > 0) { if (dStopOffset <= dSeek) { RTMP_LogPrintf("Already Completed\n"); nStatus = RD_SUCCESS; break; } } if (!RTMP_ReconnectStream(&rtmp, dSeek)) { RTMP_Log(RTMP_LOGERROR, "Failed to resume the stream\n\n"); if (!RTMP_IsTimedout(&rtmp)) nStatus = RD_FAILED; else nStatus = RD_INCOMPLETE; break; } } else if (!RTMP_ToggleStream(&rtmp)) { RTMP_Log(RTMP_LOGERROR, "Failed to resume the stream\n\n"); if (!RTMP_IsTimedout(&rtmp)) nStatus = RD_FAILED; else nStatus = RD_INCOMPLETE; break; } bResume = TRUE; } nStatus = Download(&rtmp, file, dSeek, dStopOffset, duration, bResume, metaHeader, nMetaHeaderSize, initialFrame, initialFrameType, nInitialFrameSize, nSkipKeyFrames, bStdoutMode, bLiveStream, bRealtimeStream, bHashes, bOverrideBufferTime, bufferTime, &percent); free(initialFrame); initialFrame = NULL; /* If we succeeded, we're done. */ if (nStatus != RD_INCOMPLETE || !RTMP_IsTimedout(&rtmp) || bLiveStream) break; } if (nStatus == RD_SUCCESS) { RTMP_LogPrintf("Download complete\n"); } else if (nStatus == RD_INCOMPLETE) { RTMP_LogPrintf ("Download may be incomplete (downloaded about %.2f%%), try resuming\n", percent); } clean: RTMP_Log(RTMP_LOGDEBUG, "Closing connection.\n"); RTMP_Close(&rtmp); if (file != 0) fclose(file); CleanupSockets(); #ifdef _DEBUG if (netstackdump != 0) fclose(netstackdump); if (netstackdump_read != 0) fclose(netstackdump_read); #endif fclose(pLogFile); return nStatus; }
int main (int argc, char **argv) { char *url; int brate = 32; int c; while ((c = getopt(argc, argv, "u:b:")) != -1) switch (c) { case 'u': url = strdup(optarg); break; case 'b': brate = atoi(optarg); break; default: abort(); } // libev loop = ev_default_loop(0); ev_signal_init(&sigint_sig, sigint_cb, SIGINT); ev_signal_start (loop, &sigint_sig); ev_async_init(&async, send_cb); ev_async_start(loop, &async); // jack jack_status_t status; ringbuf = jack_ringbuffer_create(buffer_size); framebuf = malloc(buffer_samples * sizeof(jack_default_audio_sample_t)); client = jack_client_open("jack2rtmp", JackNullOption, &status); if (client == NULL) { fprintf(stderr, "jack server not running?\n"); return EXIT_FAILURE; } jack_on_shutdown(client, jack_shutdown, NULL); jack_set_process_callback(client, jack_callback, NULL); //jack_set_latency_callback(client, jack_latency_callback, NULL); output_port = jack_port_register(client, "1", JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0); //jack_port_register(client, "2", JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0); if (jack_activate(client)) { fprintf(stderr, "cannot activate client"); } // lame mp3 = lame_init(); lame_set_num_channels(mp3, 1); lame_set_mode(mp3, MONO); lame_set_in_samplerate(mp3, 48000); lame_set_out_samplerate(mp3, 44100); lame_set_quality(mp3, 2); // 0=best (very slow), 9=worst. lame_set_brate(mp3, brate); lame_init_params(mp3); // rtmp rtmp_o = RTMP_Alloc(); if (rtmp_o == NULL) { fprintf(stderr, "RTMP_Alloc\n"); return EXIT_FAILURE; } RTMP_Init(rtmp_o); RTMP_SetupURL(rtmp_o, url); RTMP_EnableWrite(rtmp_o); if (!RTMP_Connect(rtmp_o, NULL) || !RTMP_ConnectStream(rtmp_o, 0)) { RTMP_Free(rtmp_o); fprintf(stderr, "Can not connect.\n"); return EXIT_FAILURE; } else { fprintf(stderr, "Connected.\n"); } /* char *rtmp_buffer = malloc(4096); char *rtmp_top = rtmp_buffer + RTMP_MAX_HEADER_SIZE; char *outend = rtmp_buffer + 4096; *rtmp_top = (uint8_t) AMF_STRING; rtmp_top++; rtmp_top = put_amf_string(rtmp_top, "@setDataFrame"); *rtmp_top = (uint8_t) AMF_STRING; rtmp_top++; rtmp_top = put_amf_string(rtmp_top, "onMetaData"); // Š¾ŠæŠøŃŠ°Š½ŠøŠµ Š¾Š±ŃŠµŠŗŃŠ° *rtmp_top = (uint8_t) 8; rtmp_top++; rtmp_top = AMF_EncodeInt32(rtmp_top, outend, 8); // ŃŠ¾Š“ŠµŃŠ¶ŠøŠ¼Š¾Šµ Š¾Š±ŃŠµŠŗŃŠ° rtmp_top = put_amf_string(rtmp_top, "duration"); rtmp_top = AMF_EncodeNumber(rtmp_top, outend, 0); rtmp_top = put_amf_string(rtmp_top, "audiodatarate"); rtmp_top = AMF_EncodeNumber(rtmp_top, outend, brate); rtmp_top = put_amf_string(rtmp_top, "audiosamplerate"); rtmp_top = AMF_EncodeNumber(rtmp_top, outend, 44100); rtmp_top = put_amf_string(rtmp_top, "audiosamplesize"); rtmp_top = AMF_EncodeNumber(rtmp_top, outend, 16); rtmp_top = put_amf_string(rtmp_top, "stereo"); rtmp_top = AMF_EncodeBoolean(rtmp_top, outend, FALSE); rtmp_top = put_amf_string(rtmp_top, "audiocodecid"); rtmp_top = AMF_EncodeNumber(rtmp_top, outend, 2); rtmp_top = put_amf_string(rtmp_top, "encoder"); *rtmp_top = (uint8_t) AMF_STRING; rtmp_top++; rtmp_top = put_amf_string(rtmp_top, "Lavf53.21.1"); rtmp_top = put_amf_string(rtmp_top, "filesize"); rtmp_top = AMF_EncodeNumber(rtmp_top, outend, 0); // ŠŗŠ¾Š½ŠµŃ Š¾Š±ŃŠµŠŗŃŠ° rtmp_top = put_amf_string(rtmp_top, ""); *rtmp_top = (uint8_t) AMF_OBJECT_END; rtmp_top++; rtmp_w.m_headerType = RTMP_PACKET_SIZE_LARGE; rtmp_w.m_nChannel = 0x04; // source channel rtmp_w.m_nBodySize = rtmp_top - rtmp_buffer - RTMP_MAX_HEADER_SIZE; rtmp_w.m_packetType = RTMP_PACKET_TYPE_INFO; rtmp_w.m_nInfoField2 = rtmp_o->m_stream_id; rtmp_w.m_body = rtmp_buffer + RTMP_MAX_HEADER_SIZE; if ( !RTMP_SendPacket(rtmp_o, &rtmp_w, TRUE) ) { fprintf(stderr, "RTMP_SendPacket error\n"); } free(rtmp_buffer); */ int off = 0; setsockopt(RTMP_Socket(rtmp_o), SOL_TCP, TCP_NODELAY, &off, sizeof(off)); rtmp_i = TRUE; ev_loop(loop, 0); jack_ringbuffer_free(ringbuf); //jack_client_close(client); return EXIT_SUCCESS; }
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 int try_connect(struct rtmp_stream *stream) { if (dstr_is_empty(&stream->path)) { warn("URL is empty"); return OBS_OUTPUT_BAD_PATH; } info("Connecting to RTMP URL %s...", stream->path.array); memset(&stream->rtmp.Link, 0, sizeof(stream->rtmp.Link)); if (!RTMP_SetupURL(&stream->rtmp, stream->path.array)) return OBS_OUTPUT_BAD_PATH; RTMP_EnableWrite(&stream->rtmp); dstr_copy(&stream->encoder_name, "FMLE/3.0 (compatible; FMSc/1.0)"); set_rtmp_dstr(&stream->rtmp.Link.pubUser, &stream->username); set_rtmp_dstr(&stream->rtmp.Link.pubPasswd, &stream->password); set_rtmp_dstr(&stream->rtmp.Link.flashVer, &stream->encoder_name); stream->rtmp.Link.swfUrl = stream->rtmp.Link.tcUrl; if (dstr_is_empty(&stream->bind_ip) || dstr_cmp(&stream->bind_ip, "default") == 0) { memset(&stream->rtmp.m_bindIP, 0, sizeof(stream->rtmp.m_bindIP)); } else { bool success = netif_str_to_addr(&stream->rtmp.m_bindIP.addr, &stream->rtmp.m_bindIP.addrLen, stream->bind_ip.array); if (success) info("Binding to IP"); } RTMP_AddStream(&stream->rtmp, stream->key.array); for (size_t idx = 1;; idx++) { obs_encoder_t *encoder = obs_output_get_audio_encoder( stream->output, idx); const char *encoder_name; if (!encoder) break; encoder_name = obs_encoder_get_name(encoder); RTMP_AddStream(&stream->rtmp, encoder_name); } stream->rtmp.m_outChunkSize = 4096; stream->rtmp.m_bSendChunkSizeInfo = true; stream->rtmp.m_bUseNagle = true; #ifdef _WIN32 win32_log_interface_type(stream); #endif if (!RTMP_Connect(&stream->rtmp, NULL)) return OBS_OUTPUT_CONNECT_FAILED; if (!RTMP_ConnectStream(&stream->rtmp, 0)) return OBS_OUTPUT_INVALID_STREAM; info("Connection to %s successful", stream->path.array); return init_send(stream); }
//Publish using RTMP_SendPacket() int publish_using_packet(){ RTMP *rtmp=NULL; RTMPPacket *packet=NULL; uint32_t start_time=0; uint32_t now_time=0; //the timestamp of the previous frame long pre_frame_time=0; long lasttime=0; int bNextIsKey=1; uint32_t preTagsize=0; //packet attributes uint32_t type=0; uint32_t datalength=0; uint32_t timestamp=0; uint32_t streamid=0; FILE*fp=NULL; fp=fopen("../live.flv","rb"); if (!fp){ RTMP_LogPrintf("Open File Error.\n"); // CleanupSockets(); return -1; } /* set log level */ //RTMP_LogLevel loglvl=RTMP_LOGDEBUG; //RTMP_LogSetLevel(loglvl); // if (!InitSockets()){ // RTMP_LogPrintf("Init Socket Err\n"); // return -1; // } rtmp=RTMP_Alloc(); RTMP_Init(rtmp); //set connection timeout,default 30s rtmp->Link.timeout=5; if(!RTMP_SetupURL(rtmp,"rtmp://101.251.251.93:1935/myapp/mystream")) { RTMP_Log(RTMP_LOGERROR,"SetupURL Err\n"); RTMP_Free(rtmp); // CleanupSockets(); return -1; } //if unable,the AMF command would be 'play' instead of 'publish' RTMP_EnableWrite(rtmp); if (!RTMP_Connect(rtmp,NULL)){ RTMP_Log(RTMP_LOGERROR,"Connect Err\n"); RTMP_Free(rtmp); // CleanupSockets(); return -1; } if (!RTMP_ConnectStream(rtmp,0)){ RTMP_Log(RTMP_LOGERROR,"ConnectStream Err\n"); RTMP_Close(rtmp); RTMP_Free(rtmp); // CleanupSockets(); return -1; } packet=(RTMPPacket*)malloc(sizeof(RTMPPacket)); RTMPPacket_Alloc(packet,1024*64); RTMPPacket_Reset(packet); packet->m_hasAbsTimestamp = 0; packet->m_nChannel = 0x04; packet->m_nInfoField2 = rtmp->m_stream_id; RTMP_LogPrintf("Start to send data ...\n"); //jump over FLV Header fseek(fp,9,SEEK_SET); //jump over previousTagSizen fseek(fp,4,SEEK_CUR); start_time=RTMP_GetTime(); while(1) { if((((now_time=RTMP_GetTime())-start_time) <(pre_frame_time)) && bNextIsKey){ //wait for 1 sec if the send process is too fast //this mechanism is not very good,need some improvement if(pre_frame_time>lasttime){ RTMP_LogPrintf("TimeStamp:%8lu ms\n",pre_frame_time); lasttime=pre_frame_time; } sleep(1); continue; } //not quite the same as FLV spec if(!ReadU8(&type,fp)) break; if(!ReadU24(&datalength,fp)) break; if(!ReadTime(×tamp,fp)) break; if(!ReadU24(&streamid,fp)) break; if (type!=0x08&&type!=0x09){ //jump over non_audio and non_video frameļ¼ //jump over next previousTagSizen at the same time fseek(fp,datalength+4,SEEK_CUR); continue; } if(fread(packet->m_body,1,datalength,fp)!=datalength) break; packet->m_headerType = RTMP_PACKET_SIZE_LARGE; packet->m_nTimeStamp = timestamp; packet->m_packetType = type; packet->m_nBodySize = datalength; pre_frame_time=timestamp; if (!RTMP_IsConnected(rtmp)){ RTMP_Log(RTMP_LOGERROR,"rtmp is not connect\n"); break; } if (!RTMP_SendPacket(rtmp,packet,0)){ RTMP_Log(RTMP_LOGERROR,"Send Error\n"); break; } if(!ReadU32(&preTagsize,fp)) break; if(!PeekU8(&type,fp)) break; if(type==0x09){ if(fseek(fp,11,SEEK_CUR)!=0) break; if(!PeekU8(&type,fp)){ break; } if(type==0x17) bNextIsKey=1; else bNextIsKey=0; fseek(fp,-11,SEEK_CUR); } } RTMP_LogPrintf("\nSend Data Over\n"); if(fp) fclose(fp); if (rtmp!=NULL){ RTMP_Close(rtmp); RTMP_Free(rtmp); rtmp=NULL; } if (packet!=NULL){ RTMPPacket_Free(packet); free(packet); packet=NULL; } // CleanupSockets(); return 0; }
//Publish using RTMP_Write() int publish_using_write(){ uint32_t start_time=0; uint32_t now_time=0; uint32_t pre_frame_time=0; uint32_t lasttime=0; int bNextIsKey=0; char* pFileBuf=NULL; //read from tag header uint32_t type=0; uint32_t datalength=0; uint32_t timestamp=0; RTMP *rtmp=NULL; FILE*fp=NULL; fp=fopen("cuc_ieschool.flv","rb"); if (!fp){ RTMP_LogPrintf("Open File Error.\n"); // CleanupSockets(); return -1; } /* set log level */ //RTMP_LogLevel loglvl=RTMP_LOGDEBUG; //RTMP_LogSetLevel(loglvl); // if (!InitSockets()){ // RTMP_LogPrintf("Init Socket Err\n"); // return -1; // } rtmp=RTMP_Alloc(); RTMP_Init(rtmp); //set connection timeout,default 30s rtmp->Link.timeout=5; if(!RTMP_SetupURL(rtmp,"rtmp://localhost/publishlive/livestream")) { RTMP_Log(RTMP_LOGERROR,"SetupURL Err\n"); RTMP_Free(rtmp); // CleanupSockets(); return -1; } RTMP_EnableWrite(rtmp); //1hour RTMP_SetBufferMS(rtmp, 3600*1000); if (!RTMP_Connect(rtmp,NULL)){ RTMP_Log(RTMP_LOGERROR,"Connect Err\n"); RTMP_Free(rtmp); // CleanupSockets(); return -1; } if (!RTMP_ConnectStream(rtmp,0)){ RTMP_Log(RTMP_LOGERROR,"ConnectStream Err\n"); RTMP_Close(rtmp); RTMP_Free(rtmp); //CleanupSockets(); return -1; } printf("Start to send data ...\n"); //jump over FLV Header fseek(fp,9,SEEK_SET); //jump over previousTagSizen fseek(fp,4,SEEK_CUR); start_time=RTMP_GetTime(); while(1) { if((((now_time=RTMP_GetTime())-start_time) <(pre_frame_time)) && bNextIsKey){ //wait for 1 sec if the send process is too fast //this mechanism is not very good,need some improvement if(pre_frame_time>lasttime){ RTMP_LogPrintf("TimeStamp:%8lu ms\n", pre_frame_time); lasttime=pre_frame_time; } sleep(1); continue; } //jump over type fseek(fp,1,SEEK_CUR); if(!ReadU24(&datalength,fp)) break; if(!ReadTime(×tamp,fp)) break; //jump back fseek(fp,-8,SEEK_CUR); pFileBuf=(char*)malloc(11+datalength+4); memset(pFileBuf,0,11+datalength+4); if(fread(pFileBuf,1,11+datalength+4,fp)!=(11+datalength+4)) break; pre_frame_time=timestamp; if (!RTMP_IsConnected(rtmp)){ RTMP_Log(RTMP_LOGERROR,"rtmp is not connect\n"); break; } if (!RTMP_Write(rtmp,pFileBuf,11+datalength+4)){ RTMP_Log(RTMP_LOGERROR,"Rtmp Write Error\n"); break; } free(pFileBuf); pFileBuf=NULL; if(!PeekU8(&type,fp)) break; if(type==0x09){ if(fseek(fp,11,SEEK_CUR)!=0) break; if(!PeekU8(&type,fp)){ break; } if(type==0x17) bNextIsKey=1; else bNextIsKey=0; fseek(fp,-11,SEEK_CUR); } } RTMP_LogPrintf("\nSend Data Over\n"); if(fp) fclose(fp); if (rtmp!=NULL){ RTMP_Close(rtmp); RTMP_Free(rtmp); rtmp=NULL; } if(pFileBuf){ free(pFileBuf); pFileBuf=NULL; } //CleanupSockets(); return 0; }
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; }
static int try_connect(struct rtmp_stream *stream) { if (dstr_is_empty(&stream->path)) { warn("URL is empty"); return OBS_OUTPUT_BAD_PATH; } info("Connecting to RTMP URL %s...", stream->path.array); memset(&stream->rtmp.Link, 0, sizeof(stream->rtmp.Link)); if (!RTMP_SetupURL(&stream->rtmp, stream->path.array)) return OBS_OUTPUT_BAD_PATH; RTMP_EnableWrite(&stream->rtmp); dstr_copy(&stream->encoder_name, "FMLE/3.0 (compatible; obs-studio/"); #ifdef HAVE_OBSCONFIG_H dstr_cat(&stream->encoder_name, OBS_VERSION); #else dstr_catf(&stream->encoder_name, "%d.%d.%d", LIBOBS_API_MAJOR_VER, LIBOBS_API_MINOR_VER, LIBOBS_API_PATCH_VER); #endif dstr_cat(&stream->encoder_name, "; FMSc/1.0)"); set_rtmp_dstr(&stream->rtmp.Link.pubUser, &stream->username); set_rtmp_dstr(&stream->rtmp.Link.pubPasswd, &stream->password); set_rtmp_dstr(&stream->rtmp.Link.flashVer, &stream->encoder_name); stream->rtmp.Link.swfUrl = stream->rtmp.Link.tcUrl; RTMP_AddStream(&stream->rtmp, stream->key.array); for (size_t idx = 1;; idx++) { obs_encoder_t *encoder = obs_output_get_audio_encoder( stream->output, idx); const char *encoder_name; if (!encoder) break; encoder_name = obs_encoder_get_name(encoder); RTMP_AddStream(&stream->rtmp, encoder_name); } stream->rtmp.m_outChunkSize = 4096; stream->rtmp.m_bSendChunkSizeInfo = true; stream->rtmp.m_bUseNagle = true; #ifdef _WIN32 win32_log_interface_type(stream); #endif if (!RTMP_Connect(&stream->rtmp, NULL)) return OBS_OUTPUT_CONNECT_FAILED; if (!RTMP_ConnectStream(&stream->rtmp, 0)) return OBS_OUTPUT_INVALID_STREAM; info("Connection to %s successful", stream->path.array); return init_send(stream); }
static GstFlowReturn gst_rtmp_sink_render (GstBaseSink * bsink, GstBuffer * buf) { GstRTMPSink *sink = GST_RTMP_SINK (bsink); GstBuffer *reffed_buf = NULL; GstMapInfo map; if (sink->first) { /* open the connection */ if (!RTMP_IsConnected (sink->rtmp)) { if (!RTMP_Connect (sink->rtmp, NULL) || !RTMP_ConnectStream (sink->rtmp, 0)) { GST_ELEMENT_ERROR (sink, RESOURCE, OPEN_WRITE, (NULL), ("Could not connect to RTMP stream \"%s\" for writing", sink->uri)); RTMP_Free (sink->rtmp); sink->rtmp = NULL; g_free (sink->rtmp_uri); sink->rtmp_uri = NULL; return GST_FLOW_ERROR; } GST_DEBUG_OBJECT (sink, "Opened connection to %s", sink->rtmp_uri); } /* FIXME: Parse the first buffer and see if it contains a header plus a packet instead * of just assuming it's only the header */ GST_LOG_OBJECT (sink, "Caching first buffer of size %" G_GSIZE_FORMAT " for concatenation", gst_buffer_get_size (buf)); gst_buffer_replace (&sink->cache, buf); sink->first = FALSE; return GST_FLOW_OK; } if (sink->cache) { GST_LOG_OBJECT (sink, "Joining 2nd buffer of size %" G_GSIZE_FORMAT " to cached buf", gst_buffer_get_size (buf)); gst_buffer_ref (buf); reffed_buf = buf = gst_buffer_append (sink->cache, buf); sink->cache = NULL; } GST_LOG_OBJECT (sink, "Sending %" G_GSIZE_FORMAT " bytes to RTMP server", gst_buffer_get_size (buf)); gst_buffer_map (buf, &map, GST_MAP_READ); if (RTMP_Write (sink->rtmp, (char *) map.data, map.size) <= 0) goto write_failed; gst_buffer_unmap (buf, &map); if (reffed_buf) gst_buffer_unref (reffed_buf); return GST_FLOW_OK; /* ERRORS */ write_failed: { GST_ELEMENT_ERROR (sink, RESOURCE, WRITE, (NULL), ("Failed to write data")); gst_buffer_unmap (buf, &map); if (reffed_buf) gst_buffer_unref (reffed_buf); return GST_FLOW_ERROR; } }
void processTCPrequest(STREAMING_SERVER * server, // server socket and state (our listening socket) int sockfd // client connection socket ) { char buf[512] = { 0 }; // answer buffer char header[2048] = { 0 }; // request header char *filename = NULL; // GET request: file name //512 not enuf char *buffer = NULL; // stream buffer char *ptr = NULL; // header pointer int len; size_t nRead = 0; RTMP rtmp = { 0 }; uint32_t dSeek = 0; // can be used to start from a later point in the stream // reset RTMP options to defaults specified upon invokation of streams RTMP_REQUEST req; char srvhead[] = "\r\nServer: HTTP-RTMP Stream Server " RTMPDUMP_VERSION "\r\n"; // timeout for http requests fd_set fds; struct timeval tv; char *status = "404 Not Found"; server->state = STREAMING_IN_PROGRESS; memcpy(&req, &defaultRTMPRequest, sizeof(RTMP_REQUEST)); memset(&tv, 0, sizeof(struct timeval)); tv.tv_sec = 5; // go through request lines //do { FD_ZERO(&fds); FD_SET(sockfd, &fds); if (select(sockfd + 1, &fds, NULL, NULL, &tv) <= 0) { RTMP_Log(RTMP_LOGERROR, "Request timeout/select failed, ignoring request"); goto quit; } else { nRead = recv(sockfd, header, 2047, 0); header[2047] = '\0'; RTMP_Log(RTMP_LOGDEBUG, "%s: header: %s", __FUNCTION__, header); if (strstr(header, "Range: bytes=") != 0) { // TODO check range starts from 0 and asking till the end. RTMP_LogPrintf("%s, Range request not supported\n", __FUNCTION__); len = sprintf(buf, "HTTP/1.0 416 Requested Range Not Satisfiable%s\r\n", srvhead); send(sockfd, buf, len, 0); goto quit; } if (strncmp(header, "GET", 3) == 0 && nRead > 4) { char *p = filename; filename = header + 4; // filter " HTTP/..." from end of request while (*p != '\0') { if (*p == ' ') { *p = '\0'; break; } p++; } } } //} while(!isHTTPRequestEOF(header, nRead)); // if we got a filename from the GET method if (filename != NULL) { RTMP_Log(RTMP_LOGDEBUG, "%s: Request header: %s", __FUNCTION__, filename); if (filename[0] == '/') { // if its not empty, is it /? ptr = filename + 1; // parse parameters if (*ptr == '?') { int len; ptr++; len = strlen(ptr); while (len >= 2) { // get position of the next '&' char *temp; char ich = *ptr; unsigned int nArgLen; char *arg; ptr++; if (*ptr != '=') goto filenotfound; // long parameters not (yet) supported ptr++; len -= 2; nArgLen = len; if ((temp = strstr(ptr, "&")) != 0) { nArgLen = temp - ptr; } arg = (char *) malloc((nArgLen + 1) * sizeof(char)); memcpy(arg, ptr, nArgLen * sizeof(char)); arg[nArgLen] = '\0'; //RTMP_Log(RTMP_LOGDEBUG, "%s: unescaping parameter: %s", __FUNCTION__, arg); http_unescape(arg); RTMP_Log(RTMP_LOGDEBUG, "%s: parameter: %c, arg: %s", __FUNCTION__, ich, arg); ptr += nArgLen + 1; len -= nArgLen + 1; if (!ParseOption(ich, arg, &req)) { status = "400 unknown option"; goto filenotfound; } } } } else { goto filenotfound; } } else { RTMP_LogPrintf("%s: No request header received/unsupported method\n", __FUNCTION__); } // do necessary checks right here to make sure the combined request of default values and GET parameters is correct if (!req.hostname.av_len) { RTMP_Log(RTMP_LOGERROR, "You must specify a hostname (--host) or url (-r \"rtmp://host[:port]/playpath\") containing a hostname"); status = "400 Missing Hostname"; goto filenotfound; } if (req.playpath.av_len == 0) { RTMP_Log(RTMP_LOGERROR, "You must specify a playpath (--playpath) or url (-r \"rtmp://host[:port]/playpath\") containing a playpath"); status = "400 Missing Playpath"; goto filenotfound;; } if (req.protocol == RTMP_PROTOCOL_UNDEFINED) { RTMP_Log(RTMP_LOGWARNING, "You haven't specified a protocol (--protocol) or rtmp url (-r), using default protocol RTMP"); req.protocol = RTMP_PROTOCOL_RTMP; } if (req.rtmpport == -1) { RTMP_Log(RTMP_LOGWARNING, "You haven't specified a port (--port) or rtmp url (-r), using default port"); req.rtmpport = 0; } if (req.rtmpport == 0) { if (req.protocol & RTMP_FEATURE_SSL) req.rtmpport = 443; else if (req.protocol & RTMP_FEATURE_HTTP) req.rtmpport = 80; else req.rtmpport = 1935; } if (req.tcUrl.av_len == 0) { char str[512] = { 0 }; req.tcUrl.av_len = snprintf(str, 511, "%s://%.*s:%d/%.*s", RTMPProtocolStringsLower[req.protocol], req.hostname.av_len, req.hostname.av_val, req.rtmpport, req.app.av_len, req.app.av_val); req.tcUrl.av_val = (char *) malloc(req.tcUrl.av_len + 1); strcpy(req.tcUrl.av_val, str); } if (req.swfVfy) { #ifdef CRYPTO if (RTMP_HashSWF(req.swfUrl.av_val, &req.swfSize, req.hash, req.swfAge) == 0) { req.swfHash.av_val = (char *)req.hash; req.swfHash.av_len = RTMP_SWF_HASHLEN; } #endif } // after validation of the http request send response header len = sprintf(buf, "HTTP/1.0 200 OK%sContent-Type: video/flv\r\n\r\n", srvhead); send(sockfd, buf, len, 0); // send the packets buffer = (char *) calloc(PACKET_SIZE, 1); // User defined seek offset if (req.dStartOffset > 0) { if (req.bLiveStream) RTMP_Log(RTMP_LOGWARNING, "Can't seek in a live stream, ignoring --seek option"); else dSeek += req.dStartOffset; } if (dSeek != 0) { RTMP_LogPrintf("Starting at TS: %d ms\n", dSeek); } RTMP_Log(RTMP_LOGDEBUG, "Setting buffer time to: %dms", req.bufferTime); RTMP_Init(&rtmp); RTMP_SetBufferMS(&rtmp, req.bufferTime); RTMP_SetupStream(&rtmp, req.protocol, &req.hostname, req.rtmpport, &req.sockshost, &req.playpath, &req.tcUrl, &req.swfUrl, &req.pageUrl, &req.app, &req.auth, &req.swfHash, req.swfSize, &req.flashVer, &req.subscribepath, dSeek, req.dStopOffset, req.bLiveStream, req.timeout); /* backward compatibility, we always sent this as true before */ if (req.auth.av_len) rtmp.Link.lFlags |= RTMP_LF_AUTH; rtmp.Link.extras = req.extras; rtmp.Link.token = req.token; rtmp.m_read.timestamp = dSeek; RTMP_LogPrintf("Connecting ... port: %d, app: %s\n", req.rtmpport, req.app); if (!RTMP_Connect(&rtmp, NULL)) { RTMP_LogPrintf("%s, failed to connect!\n", __FUNCTION__); } else { unsigned long size = 0; double percent = 0; double duration = 0.0; int nWritten = 0; int nRead = 0; do { nRead = RTMP_Read(&rtmp, buffer, PACKET_SIZE); if (nRead > 0) { if ((nWritten = send(sockfd, buffer, nRead, 0)) < 0) { RTMP_Log(RTMP_LOGERROR, "%s, sending failed, error: %d", __FUNCTION__, GetSockError()); goto cleanup; // we are in STREAMING_IN_PROGRESS, so we'll go to STREAMING_ACCEPTING } size += nRead; //RTMP_LogPrintf("write %dbytes (%.1f KB)\n", nRead, nRead/1024.0); if (duration <= 0) // if duration unknown try to get it from the stream (onMetaData) duration = RTMP_GetDuration(&rtmp); if (duration > 0) { percent = ((double) (dSeek + rtmp.m_read.timestamp)) / (duration * 1000.0) * 100.0; percent = ((double) (int) (percent * 10.0)) / 10.0; RTMP_LogStatus("\r%.3f KB / %.2f sec (%.1f%%)", (double) size / 1024.0, (double) (rtmp.m_read.timestamp) / 1000.0, percent); } else { RTMP_LogStatus("\r%.3f KB / %.2f sec", (double) size / 1024.0, (double) (rtmp.m_read.timestamp) / 1000.0); } } #ifdef _DEBUG else { RTMP_Log(RTMP_LOGDEBUG, "zero read!"); } #endif } while (server->state == STREAMING_IN_PROGRESS && nRead > -1 && RTMP_IsConnected(&rtmp) && nWritten >= 0); } cleanup: RTMP_LogPrintf("Closing connection... "); RTMP_Close(&rtmp); RTMP_LogPrintf("done!\n\n"); quit: if (buffer) { free(buffer); buffer = NULL; } if (sockfd) closesocket(sockfd); if (server->state == STREAMING_IN_PROGRESS) server->state = STREAMING_ACCEPTING; return; filenotfound: RTMP_LogPrintf("%s, %s, %s\n", __FUNCTION__, status, filename); len = sprintf(buf, "HTTP/1.0 %s%s\r\n", status, srvhead); send(sockfd, buf, len, 0); goto quit; }
int do_rtmp(int port,int protocol,char* playpath_arg,char* host,char* swfVfy_arg,char* tcUrl_arg,char* app_arg,char* pageUrl_arg,char* flashVer_arg,char* conn,char* outfile) { int nStatus = RD_SUCCESS; double percent = 0; double duration = 0.0; int nSkipKeyFrames = DEF_SKIPFRM; // skip this number of keyframes when resuming int bOverrideBufferTime = FALSE; // if the user specifies a buffer time override this is true int bResume = FALSE; // true in resume mode uint32_t dSeek = 0; // seek position in resume mode, 0 otherwise uint32_t bufferTime = DEF_BUFTIME; // meta header and initial frame for the resume mode (they are read from the file and compared with // the stream we are trying to continue char *metaHeader = 0; uint32_t nMetaHeaderSize = 0; // video keyframe for matching char *initialFrame = 0; uint32_t nInitialFrameSize = 0; int initialFrameType = 0; // tye: audio or video AVal hostname = { 0, 0 }; AVal playpath = { 0, 0 }; AVal subscribepath = { 0, 0 }; AVal usherToken = { 0, 0 }; //Justin.tv auth token int retries = 0; int bLiveStream = FALSE; // is it a live stream? then we can't seek/resume int bHashes = FALSE; // display byte counters not hashes by default long int timeout = DEF_TIMEOUT; // timeout connection after 120 seconds uint32_t dStopOffset = 0; RTMP rtmp; AVal swfUrl = { 0, 0 }; AVal tcUrl = { 0, 0 }; AVal pageUrl = { 0, 0 }; AVal app = { 0, 0 }; AVal auth = { 0, 0 }; AVal swfHash = { 0, 0 }; uint32_t swfSize = 0; AVal flashVer = { 0, 0 }; AVal sockshost = { 0, 0 }; #ifdef CRYPTO int swfAge = 30; /* 30 days for SWF cache by default */ int swfVfy = 0; unsigned char hash[RTMP_SWF_HASHLEN]; #endif char *flvFile = 0; signal(SIGINT, sigIntHandler); signal(SIGTERM, sigIntHandler); signal(SIGHUP, sigIntHandler); signal(SIGPIPE, sigIntHandler); signal(SIGQUIT, sigIntHandler); RTMP_debuglevel = RTMP_LOGINFO; RTMP_Init(&rtmp); STR2AVAL(&swfUrl,swfVfy_arg); swfVfy = 1; if (host) { STR2AVAL(&hostname, host); } if (playpath_arg) { STR2AVAL(&playpath, playpath_arg); } if (tcUrl_arg) { STR2AVAL(&tcUrl, tcUrl_arg); } if (pageUrl_arg) { STR2AVAL(&pageUrl, pageUrl_arg); } if (app_arg) { STR2AVAL(&app, app_arg); } if (flashVer_arg) { STR2AVAL(&flashVer, flashVer_arg); } if (conn) { AVal av; STR2AVAL(&av, conn); if (!RTMP_SetOpt(&rtmp, &av_conn, &av)) { RTMP_Log(RTMP_LOGERROR, "Invalid AMF parameter: %s", conn); return RD_FAILED; } } flvFile = outfile; file = fopen(flvFile, "w+b"); if (file == 0) { RTMP_LogPrintf("Failed to open file! %s\n", flvFile); return RD_FAILED; } if (port == 0) { if (protocol & RTMP_FEATURE_SSL) port = 443; else if (protocol & RTMP_FEATURE_HTTP) port = 80; else port = 1935; } #ifdef CRYPTO if (swfVfy) { if (RTMP_HashSWF(swfUrl.av_val, &swfSize, hash, swfAge) == 0) { swfHash.av_val = (char *)hash; swfHash.av_len = RTMP_SWF_HASHLEN; } } if (swfHash.av_len == 0 && swfSize > 0) { RTMP_Log(RTMP_LOGWARNING, "Ignoring SWF size, supply also the hash with --swfhash"); swfSize = 0; } if (swfHash.av_len != 0 && swfSize == 0) { RTMP_Log(RTMP_LOGWARNING, "Ignoring SWF hash, supply also the swf size with --swfsize"); swfHash.av_len = 0; swfHash.av_val = NULL; } #endif RTMP_SetupStream(&rtmp, protocol, &hostname, port, &sockshost, &playpath, &tcUrl, &swfUrl, &pageUrl, &app, &auth, &swfHash, swfSize, &flashVer, &subscribepath, &usherToken, dSeek, dStopOffset, bLiveStream, timeout); /* Try to keep the stream moving if it pauses on us */ if (!bLiveStream && !(protocol & RTMP_FEATURE_HTTP)) rtmp.Link.lFlags |= RTMP_LF_BUFX; int first = 1; while (!RTMP_ctrlC) { RTMP_Log(RTMP_LOGDEBUG, "Setting buffer time to: %dms", bufferTime); RTMP_SetBufferMS(&rtmp, bufferTime); if (first) { first = 0; RTMP_LogPrintf("Connecting ...\n"); if (!RTMP_Connect(&rtmp, NULL)) { nStatus = RD_NO_CONNECT; break; } RTMP_Log(RTMP_LOGINFO, "Connected..."); if (!RTMP_ConnectStream(&rtmp, dSeek)) { nStatus = RD_FAILED; break; } } else { nInitialFrameSize = 0; if (retries) { RTMP_Log(RTMP_LOGERROR, "Failed to resume the stream\n\n"); if (!RTMP_IsTimedout(&rtmp)) nStatus = RD_FAILED; else nStatus = RD_INCOMPLETE; break; } RTMP_Log(RTMP_LOGINFO, "Connection timed out, trying to resume.\n\n"); /* Did we already try pausing, and it still didn't work? */ if (rtmp.m_pausing == 3) { /* Only one try at reconnecting... */ retries = 1; dSeek = rtmp.m_pauseStamp; if (dStopOffset > 0) { if (dStopOffset <= dSeek) { RTMP_LogPrintf("Already Completed\n"); nStatus = RD_SUCCESS; break; } } if (!RTMP_ReconnectStream(&rtmp, dSeek)) { RTMP_Log(RTMP_LOGERROR, "Failed to resume the stream\n\n"); if (!RTMP_IsTimedout(&rtmp)) nStatus = RD_FAILED; else nStatus = RD_INCOMPLETE; break; } } else if (!RTMP_ToggleStream(&rtmp)) { RTMP_Log(RTMP_LOGERROR, "Failed to resume the stream\n\n"); if (!RTMP_IsTimedout(&rtmp)) nStatus = RD_FAILED; else nStatus = RD_INCOMPLETE; break; } bResume = TRUE; } nStatus = Download(&rtmp, file, dSeek, dStopOffset, duration, bResume, metaHeader, nMetaHeaderSize, initialFrame, initialFrameType, nInitialFrameSize, nSkipKeyFrames, bLiveStream, bHashes, bOverrideBufferTime, bufferTime, &percent); free(initialFrame); initialFrame = NULL; /* If we succeeded, we're done. */ if (nStatus != RD_INCOMPLETE || !RTMP_IsTimedout(&rtmp) || bLiveStream) break; } if (nStatus == RD_SUCCESS) { RTMP_LogPrintf("Download complete\n"); } else if (nStatus == RD_INCOMPLETE) { RTMP_LogPrintf ("Download may be incomplete (downloaded about %.2f%%), try resuming\n", percent); } RTMP_Log(RTMP_LOGDEBUG, "Closing connection.\n"); RTMP_Close(&rtmp); if (file != 0) fclose(file); return nStatus; }