int ExecuteRPCResult(RTMP *r, AMFObject *obj, YLENGStream *yle, int *redirected) { AVal rpcKind; AVal mediaxml; char *playurl, *tvpayOnly; AVal parsedHost = {NULL, 0}; AVal parsedPlaypath = {NULL, 0}; AVal parsedApp = {NULL, 0}; *redirected = FALSE; AMFProp_GetString(AMF_GetProp(obj, NULL, 3), &rpcKind); if (!AVMATCH(&rpcKind, &av_e0)) return TRUE; AMFProp_GetString(AMF_GetProp(obj, NULL, 4), &mediaxml); RTMP_Log(RTMP_LOGDEBUG, "clip data:\n%.*s", mediaxml.av_len, mediaxml.av_val); playurl = GetXMLNodeContent(mediaxml.av_val, "url"); if (!playurl) return FALSE; if (!ParseYLEPlaypath(playurl, &parsedHost, &parsedApp, &parsedPlaypath)) { RTMP_Log(RTMP_LOGERROR, "Couldn't parse stream url %s!", playurl); free(playurl); return FALSE; } // FIXME: old r->Link.playpath may be leaked r->Link.playpath.av_len = parsedPlaypath.av_len; r->Link.playpath.av_val = malloc(parsedPlaypath.av_len*sizeof(char)); strncpy(r->Link.playpath.av_val, parsedPlaypath.av_val, r->Link.playpath.av_len); RTMP_Log(RTMP_LOGDEBUG, "New playpath : %.*s", r->Link.playpath.av_len, r->Link.playpath.av_val); if (!AVMATCH(&parsedHost, &r->Link.hostname)) { RTMP_Log(RTMP_LOGDEBUG, "Redirected to another server: %.*s", parsedHost.av_len, parsedHost.av_val); // FIXME: old value may be leaked r->Link.hostname.av_val = malloc(parsedHost.av_len*sizeof(char)); r->Link.hostname.av_len = parsedHost.av_len; memcpy(r->Link.hostname.av_val, parsedHost.av_val, parsedHost.av_len); *redirected = TRUE; } tvpayOnly = GetXMLNodeContent(mediaxml.av_val, "tvpayOnly"); if (tvpayOnly) { yle->tvFeeRequired = (strcmp(tvpayOnly, "false")!=0); free(tvpayOnly); } free(playurl); return RTMP_SendCreateStream(r); }
static int handle_metadata0(rtmp_t *r, AMFObject *obj, media_pipe_t *mp, char *errstr, size_t errlen) { AVal metastring; AMFObjectProperty prop; prop_t *m = mp->mp_prop_metadata; AMFProp_GetString(AMF_GetProp(obj, NULL, 0), &metastring); if(!AVMATCH(&metastring, &av_onMetaData)) { snprintf(errstr, errlen, "No metadata in metadata packet"); return -1; } if(RTMP_FindFirstMatchingProperty(obj, &av_duration, &prop) && prop.p_type == AMF_NUMBER && prop.p_vu.p_number > 0) { prop_set_float(prop_create(m, "duration"), prop.p_vu.p_number); r->total_duration = prop.p_vu.p_number * 1000; mp_set_duration(mp, r->total_duration * 1000LL); mp_set_clr_flags(mp, MP_CAN_SEEK, 0); if(r->ss == NULL && !(r->va->flags & BACKEND_VIDEO_NO_SUBTITLE_SCAN)) r->ss = sub_scanner_create(r->url, mp->mp_prop_subtitle_tracks, r->va, 0); } else { r->total_duration = 0; mp_set_duration(mp, AV_NOPTS_VALUE); mp_set_clr_flags(mp, 0, MP_CAN_SEEK); } if((RTMP_FindFirstMatchingProperty(obj, &av_videoframerate, &prop) && RTMP_FindFirstMatchingProperty(obj, &av_framerate, &prop)) && prop.p_type == AMF_NUMBER) { r->vframeduration = 1000000.0 / prop.p_vu.p_number; mp->mp_framerate.num = 1000000; mp->mp_framerate.den = prop.p_vu.p_number; } r->width = r->height = 0; if(RTMP_FindFirstMatchingProperty(obj, &av_width, &prop) && prop.p_type == AMF_NUMBER) r->width = prop.p_vu.p_number; if(RTMP_FindFirstMatchingProperty(obj, &av_height, &prop) && prop.p_type == AMF_NUMBER) r->height = prop.p_vu.p_number; if(r->width && r->height) TRACE(TRACE_DEBUG, "RTMP", "Video size %d x %d", r->width, r->height); return 0; }
static int handle_metadata0(rtmp_t *r, AMFObject *obj, media_pipe_t *mp, char *errstr, size_t errlen) { AVal metastring; AMFObjectProperty prop; prop_t *m = mp->mp_prop_metadata; AMFProp_GetString(AMF_GetProp(obj, NULL, 0), &metastring); if(!AVMATCH(&metastring, &av_onMetaData)) { snprintf(errstr, errlen, "No metadata in metadata packet"); return -1; } if(!RTMP_FindFirstMatchingProperty(obj, &av_duration, &prop) || prop.p_type != AMF_NUMBER) { snprintf(errstr, errlen, "Unable to parse total duration"); return -1; } prop_set_float(prop_create(m, "duration"), prop.p_vu.p_number); r->duration = prop.p_vu.p_number; if(!RTMP_FindFirstMatchingProperty(obj, &av_videoframerate, &prop) || prop.p_type != AMF_NUMBER) { if(!RTMP_FindFirstMatchingProperty(obj, &av_framerate, &prop) || prop.p_type != AMF_NUMBER) { snprintf(errstr, errlen, "Unable to parse video framerate"); return -1; } } r->vframeduration = 1000000.0 / prop.p_vu.p_number; r->width = r->height = 0; if(RTMP_FindFirstMatchingProperty(obj, &av_width, &prop) && prop.p_type == AMF_NUMBER) r->width = prop.p_vu.p_number; if(RTMP_FindFirstMatchingProperty(obj, &av_height, &prop) && prop.p_type == AMF_NUMBER) r->height = prop.p_vu.p_number; if(r->width && r->height) TRACE(TRACE_DEBUG, "RTMP", "Video size %d x %d", r->width, r->height); return 0; }
int OpenResumeFile(const char *flvFile, // file name [in] FILE ** file, // opened file [out] off_t * size, // size of the file [out] char **metaHeader, // meta data read from the file [out] uint32_t * nMetaHeaderSize, // length of metaHeader [out] double *duration) // duration of the stream in ms [out] { size_t bufferSize = 0; char hbuf[16], *buffer = NULL; *nMetaHeaderSize = 0; *size = 0; *file = fopen(flvFile, "r+b"); if (!*file) return RD_SUCCESS; // RD_SUCCESS, because we go to fresh file mode instead of quiting fseek(*file, 0, SEEK_END); *size = ftello(*file); fseek(*file, 0, SEEK_SET); if (*size > 0) { // verify FLV format and read header uint32_t prevTagSize = 0; // check we've got a valid FLV file to continue! if (fread(hbuf, 1, 13, *file) != 13) { RTMP_Log(RTMP_LOGERROR, "Couldn't read FLV file header!"); return RD_FAILED; } if (hbuf[0] != 'F' || hbuf[1] != 'L' || hbuf[2] != 'V' || hbuf[3] != 0x01) { RTMP_Log(RTMP_LOGERROR, "Invalid FLV file!"); return RD_FAILED; } if ((hbuf[4] & 0x05) == 0) { RTMP_Log(RTMP_LOGERROR, "FLV file contains neither video nor audio, aborting!"); return RD_FAILED; } uint32_t dataOffset = AMF_DecodeInt32(hbuf + 5); fseek(*file, dataOffset, SEEK_SET); if (fread(hbuf, 1, 4, *file) != 4) { RTMP_Log(RTMP_LOGERROR, "Invalid FLV file: missing first prevTagSize!"); return RD_FAILED; } prevTagSize = AMF_DecodeInt32(hbuf); if (prevTagSize != 0) { RTMP_Log(RTMP_LOGWARNING, "First prevTagSize is not zero: prevTagSize = 0x%08X", prevTagSize); } // go through the file to find the meta data! off_t pos = dataOffset + 4; int bFoundMetaHeader = FALSE; while (pos < *size - 4 && !bFoundMetaHeader) { fseeko(*file, pos, SEEK_SET); if (fread(hbuf, 1, 4, *file) != 4) break; uint32_t dataSize = AMF_DecodeInt24(hbuf + 1); if (hbuf[0] == 0x12) { if (dataSize > bufferSize) { /* round up to next page boundary */ bufferSize = dataSize + 4095; bufferSize ^= (bufferSize & 4095); free(buffer); buffer = (char *)malloc(bufferSize); if (!buffer) return RD_FAILED; } fseeko(*file, pos + 11, SEEK_SET); if (fread(buffer, 1, dataSize, *file) != dataSize) break; AMFObject metaObj; int nRes = AMF_Decode(&metaObj, buffer, dataSize, FALSE); if (nRes < 0) { RTMP_Log(RTMP_LOGERROR, "%s, error decoding meta data packet", __FUNCTION__); break; } AVal metastring; AMFProp_GetString(AMF_GetProp(&metaObj, NULL, 0), &metastring); if (AVMATCH(&metastring, &av_onMetaData)) { AMF_Dump(&metaObj); *nMetaHeaderSize = dataSize; if (*metaHeader) free(*metaHeader); *metaHeader = (char *) malloc(*nMetaHeaderSize); memcpy(*metaHeader, buffer, *nMetaHeaderSize); // get duration AMFObjectProperty prop; if (RTMP_FindFirstMatchingProperty (&metaObj, &av_duration, &prop)) { *duration = AMFProp_GetNumber(&prop); RTMP_Log(RTMP_LOGDEBUG, "File has duration: %f", *duration); } bFoundMetaHeader = TRUE; break; } //metaObj.Reset(); //delete obj; } pos += (dataSize + 11 + 4); } free(buffer); if (!bFoundMetaHeader) RTMP_Log(RTMP_LOGWARNING, "Couldn't locate meta data!"); } return RD_SUCCESS; }
// Returns 0 for OK/Failed/error, 1 for 'Stop or Complete' int ServeInvoke(STREAMING_SERVER *server, RTMP * r, RTMPPacket *packet, unsigned int offset) { const char *body; unsigned int nBodySize; int ret = 0, nRes; body = packet->m_body + offset; nBodySize = packet->m_nBodySize - offset; if (body[0] != 0x02) // make sure it is a string method name we start with { RTMP_Log(RTMP_LOGWARNING, "%s, Sanity failed. no string method in invoke packet", __FUNCTION__); return 0; } AMFObject obj; nRes = AMF_Decode(&obj, body, nBodySize, FALSE); if (nRes < 0) { RTMP_Log(RTMP_LOGERROR, "%s, error decoding invoke packet", __FUNCTION__); return 0; } AMF_Dump(&obj); AVal method; AMFProp_GetString(AMF_GetProp(&obj, NULL, 0), &method); double txn = AMFProp_GetNumber(AMF_GetProp(&obj, NULL, 1)); RTMP_Log(RTMP_LOGDEBUG, "%s, client invoking <%s>", __FUNCTION__, method.av_val); if (AVMATCH(&method, &av_connect)) { AMFObject cobj; AVal pname, pval; int i; server->connect = packet->m_body; packet->m_body = NULL; AMFProp_GetObject(AMF_GetProp(&obj, NULL, 2), &cobj); for (i=0; i<cobj.o_num; i++) { pname = cobj.o_props[i].p_name; pval.av_val = NULL; pval.av_len = 0; if (cobj.o_props[i].p_type == AMF_STRING) pval = cobj.o_props[i].p_vu.p_aval; if (AVMATCH(&pname, &av_app)) { r->Link.app = pval; pval.av_val = NULL; if (!r->Link.app.av_val) r->Link.app.av_val = ""; server->arglen += 6 + pval.av_len; server->argc += 2; } else if (AVMATCH(&pname, &av_flashVer)) { r->Link.flashVer = pval; pval.av_val = NULL; server->arglen += 6 + pval.av_len; server->argc += 2; } else if (AVMATCH(&pname, &av_swfUrl)) { r->Link.swfUrl = pval; pval.av_val = NULL; server->arglen += 6 + pval.av_len; server->argc += 2; } else if (AVMATCH(&pname, &av_tcUrl)) { r->Link.tcUrl = pval; pval.av_val = NULL; server->arglen += 6 + pval.av_len; server->argc += 2; } else if (AVMATCH(&pname, &av_pageUrl)) { r->Link.pageUrl = pval; pval.av_val = NULL; server->arglen += 6 + pval.av_len; server->argc += 2; } else if (AVMATCH(&pname, &av_audioCodecs)) { r->m_fAudioCodecs = cobj.o_props[i].p_vu.p_number; } else if (AVMATCH(&pname, &av_videoCodecs)) { r->m_fVideoCodecs = cobj.o_props[i].p_vu.p_number; } else if (AVMATCH(&pname, &av_objectEncoding)) { r->m_fEncoding = cobj.o_props[i].p_vu.p_number; } } /* Still have more parameters? Copy them */ if (obj.o_num > 3) { int i = obj.o_num - 3; r->Link.extras.o_num = i; r->Link.extras.o_props = malloc(i*sizeof(AMFObjectProperty)); memcpy(r->Link.extras.o_props, obj.o_props+3, i*sizeof(AMFObjectProperty)); obj.o_num = 3; server->arglen += countAMF(&r->Link.extras, &server->argc); } SendConnectResult(r, txn); } else if (AVMATCH(&method, &av_createStream)) { SendResultNumber(r, txn, ++server->streamID); } else if (AVMATCH(&method, &av_getStreamLength)) { SendResultNumber(r, txn, 10.0); } else if (AVMATCH(&method, &av_play)) { char *file, *p, *q, *cmd, *ptr; AVal *argv, av; int len, argc; uint32_t now; RTMPPacket pc = {0}; AMFProp_GetString(AMF_GetProp(&obj, NULL, 3), &r->Link.playpath); /* r->Link.seekTime = AMFProp_GetNumber(AMF_GetProp(&obj, NULL, 4)); if (obj.o_num > 5) r->Link.length = AMFProp_GetNumber(AMF_GetProp(&obj, NULL, 5)); */ if (r->Link.tcUrl.av_len) { len = server->arglen + r->Link.playpath.av_len + 4 + sizeof("rtmpdump") + r->Link.playpath.av_len + 12; server->argc += 5; cmd = malloc(len + server->argc * sizeof(AVal)); ptr = cmd; argv = (AVal *)(cmd + len); argv[0].av_val = cmd; argv[0].av_len = sizeof("rtmpdump")-1; ptr += sprintf(ptr, "rtmpdump"); argc = 1; argv[argc].av_val = ptr + 1; argv[argc++].av_len = 2; argv[argc].av_val = ptr + 5; ptr += sprintf(ptr," -r \"%s\"", r->Link.tcUrl.av_val); argv[argc++].av_len = r->Link.tcUrl.av_len; if (r->Link.app.av_val) { argv[argc].av_val = ptr + 1; argv[argc++].av_len = 2; argv[argc].av_val = ptr + 5; ptr += sprintf(ptr, " -a \"%s\"", r->Link.app.av_val); argv[argc++].av_len = r->Link.app.av_len; } if (r->Link.flashVer.av_val) { argv[argc].av_val = ptr + 1; argv[argc++].av_len = 2; argv[argc].av_val = ptr + 5; ptr += sprintf(ptr, " -f \"%s\"", r->Link.flashVer.av_val); argv[argc++].av_len = r->Link.flashVer.av_len; } if (r->Link.swfUrl.av_val) { argv[argc].av_val = ptr + 1; argv[argc++].av_len = 2; argv[argc].av_val = ptr + 5; ptr += sprintf(ptr, " -W \"%s\"", r->Link.swfUrl.av_val); argv[argc++].av_len = r->Link.swfUrl.av_len; } if (r->Link.pageUrl.av_val) { argv[argc].av_val = ptr + 1; argv[argc++].av_len = 2; argv[argc].av_val = ptr + 5; ptr += sprintf(ptr, " -p \"%s\"", r->Link.pageUrl.av_val); argv[argc++].av_len = r->Link.pageUrl.av_len; } if (r->Link.extras.o_num) { ptr = dumpAMF(&r->Link.extras, ptr, argv, &argc); AMF_Reset(&r->Link.extras); } argv[argc].av_val = ptr + 1; argv[argc++].av_len = 2; argv[argc].av_val = ptr + 5; ptr += sprintf(ptr, " -y \"%.*s\"", r->Link.playpath.av_len, r->Link.playpath.av_val); argv[argc++].av_len = r->Link.playpath.av_len; av = r->Link.playpath; /* strip trailing URL parameters */ q = memchr(av.av_val, '?', av.av_len); if (q) { if (q == av.av_val) { av.av_val++; av.av_len--; } else { av.av_len = q - av.av_val; } } /* strip leading slash components */ for (p=av.av_val+av.av_len-1; p>=av.av_val; p--) if (*p == '/') { p++; av.av_len -= p - av.av_val; av.av_val = p; break; } /* skip leading dot */ if (av.av_val[0] == '.') { av.av_val++; av.av_len--; } file = malloc(av.av_len+5); memcpy(file, av.av_val, av.av_len); file[av.av_len] = '\0'; for (p=file; *p; p++) if (*p == ':') *p = '_'; /* Add extension if none present */ if (file[av.av_len - 4] != '.') { av.av_len += 4; } /* Always use flv extension, regardless of original */ if (strcmp(file+av.av_len-4, ".flv")) { strcpy(file+av.av_len-4, ".flv"); } argv[argc].av_val = ptr + 1; argv[argc++].av_len = 2; argv[argc].av_val = file; argv[argc].av_len = av.av_len; ptr += sprintf(ptr, " -o %s", file); now = RTMP_GetTime(); if (now - server->filetime < DUPTIME && AVMATCH(&argv[argc], &server->filename)) { printf("Duplicate request, skipping.\n"); free(file); } else { printf("\n%s\n\n", cmd); fflush(stdout); server->filetime = now; free(server->filename.av_val); server->filename = argv[argc++]; spawn_dumper(argc, argv, cmd); } free(cmd); } pc.m_body = server->connect; server->connect = NULL; RTMPPacket_Free(&pc); ret = 1; RTMP_SendCtrl(r, 0, 1, 0); SendPlayStart(r); RTMP_SendCtrl(r, 1, 1, 0); SendPlayStop(r); } AMF_Reset(&obj); return ret; }