diff --git a/rtmp/rtmp.go b/rtmp/rtmp.go index 7ebe337f..a851aace 100644 --- a/rtmp/rtmp.go +++ b/rtmp/rtmp.go @@ -1909,122 +1909,180 @@ func C_HandleClientBW(r *C.RTMP, packet *C.RTMPPacket) { // int HandleInvoke(RTMP* r, const char* body, unsigned int nBodySize); // rtmp.c +2912 func C_HandleInvoke(r *C.RTMP, body *byte, nBodySize uint32) int32 { - var obj C.AMFObject - var method C.AVal - var txn float32 - var ret int32 = 0 - var nRes int32 + AMFObject obj; + AVal method; + double txn; + int ret = 0, nRes; + if (body[0] != 0x02){ + //RTMP_Log(RTMP_LOGWARNING, "%s, Sanity failed. no string method in invoke packet", + //__FUNCTION__); + return 0; + } - if *body != 0x02 { - // TODO use new logger here - // RTMP_Log(RTMP_LOGWARNING, "%s, Sanity failed. no string method in invoke packet", - //__FUNCTION__); - return 0 - } + nRes = AMF_Decode(&obj, body, nBodySize, FALSE); + if (nRes < 0){ + RTMP_Log(RTMP_LOGERROR, "%s, error decoding invoke packet", __FUNCTION__); + return 0; + } - nRes = C_AMF_Decode(&obj, body, nBodySize, 0) + AMF_Dump(&obj); + AMFProp_GetString(AMF_GetProp(&obj, NULL, 0), &method); + txn = AMFProp_GetNumber(AMF_GetProp(&obj, NULL, 1)); + RTMP_Log(RTMP_LOGDEBUG, "%s, server invoking <%s>", __FUNCTION__, method.av_val); - if nRes < 0 { - // TODO use new logger here - //RTMP_Log(RTMP_LOGERROR, "%s, error decoding invoke packet", __FUNCTION__); - return 0 - } + switch{ + case AVMATCH(&method, &av__result): + AVal methodInvoked = {0}; + int i; - // TODO port this - C.AMF_Dump(&obj) - // TODO port this - C.AMFProp_GetString(C.AMF_GetProp(&obj, nil, 0),&method) - // TODO port this - txn = C.AMFProp_getNumber(C.AMF_GetProp(&obj, nil,1)) - // TODO use new logger here - // RTMP_Log(RTMP_LOGDEBUG, "%s, server invoking <%s>", __FUNCTION__, method.av_val); - - // TODO port this - switch { - case C.AVMATCH(&method, &av__result): - var methodInvoked C.AVal - - for i := 0; i < r.m_numCalls; i++ { - if r.m_methodCalls[i].num == int32(txn) { - methodInvoked = r.m_methodCalls[i].name - // TODO port this - C.AV_erase(r.m_methodCalls, &r.m_numCalls, i, 0) - break + for i=0; im_numCalls; i++ { + if (r->m_methodCalls[i].num == (int)txn) { + methodInvoked = r->m_methodCalls[i].name; + AV_erase(r->m_methodCalls, &r->m_numCalls, i, FALSE); + break; } - } - if methodInvoked.av_val == 0 { - // TODO use new logger here - // RTMP_Log(RTMP_LOGDEBUG, "%s, received result id %f without matching request", - //__FUNCTION__, txn); - goto leave - } - // TODO use new logger here - // RTMP_Log(RTMP_LOGDEBUG, "%s, received result for method call <%s>", __FUNCTION__, - //methodInvoked.av_val); - // TODO port AVMATCH + } + if methodInvoked.av_val == 0 { + RTMP_Log(RTMP_LOGDEBUG, "%s, received result id %f without matching request", + __FUNCTION__, txn); + goto leave; + } + + RTMP_Log(RTMP_LOGDEBUG, "%s, received result for method call <%s>", __FUNCTION__, + methodInvoked.av_val); switch { - case AVMATCH(&methodInvoked, &av_connect) != 0: - if r.Link.token.av_len != 0 { - var p C.AMFOBjectProperty - // TODO port this - if C.RTMP_FindFirstMatchingProperty(&obj, &av_secureToken, &p) { - // TODO port This - C.DecodeTEA(&r.Link.token, &p.p_vu.p_aval) - // TODO port this - C.SendSecureTokenResponse(r, &p.p_vu.p_aval) + case AVMATCH(&methodInvoked, &av_connect): + if (r->Link.token.av_len){ + AMFObjectProperty p; + if (RTMP_FindFirstMatchingProperty(&obj, &av_secureToken, &p)){ + DecodeTEA(&r->Link.token, &p.p_vu.p_aval); + SendSecureTokenResponse(r, &p.p_vu.p_aval); } - } - if r.Link.protocol & RTMP_FEATURE_WRITE { - // TODO port this - C.SendReleaseStream(r) - // TODO port this - C.SendFCPublish(r) - } else { - // TODO port this - C.RTMP_SendServerBW(r) - // TODO port this - C.RTMP_SendCtrl(r, 3, 0, 300) - } - // TODO port this - C.RTMP_SendCreateStream(r) + } + if (r->Link.protocol & RTMP_FEATURE_WRITE){ + SendReleaseStream(r); + SendFCPublish(r); + }else{ + RTMP_SendServerBW(r); + RTMP_SendCtrl(r, 3, 0, 300); + } + RTMP_SendCreateStream(r); - if (r.Link.protocol & RTMP_FEATURE_WRITE) == 0 { - if r.Link.usherToken.av_len != 0 { - // TODO port this - C.SendUsherToken(r, &r.Link.usherToken) + if (!(r->Link.protocol & RTMP_FEATURE_WRITE)){ + /* Authenticate on Justin.tv legacy servers before sending FCSubscribe */ + if (r->Link.usherToken.av_len){ + SendUsherToken(r, &r->Link.usherToken); } - switch { - case r.Link.subscribepath.av_len != 0: - // TODO port this - C.SendFCSubscribe(r, &r.Link.subscribepath) - case r.Link.lFlags & RTMP_LF_LIVE: - C.SendFCSubscribe(r, &r.Link.playpath) + /* Send the FCSubscribe if live stream or if subscribepath is set */ + switch{ + case r->Link.subscribepath.av_len: + SendFCSubscribe(r, &r->Link.subscribepath); + case r->Link.lFlags & RTMP_LF_LIV: + SendFCSubscribe(r, &r->Link.playpath); } - } - case AVMATCH(&methodInvoked, &av_createStream) != 0: - r->m_stream_id = (int)AMFProp_GetNumber(AMF_GetProp(&obj, NULL, 3)); + } + case AVMATCH(&methodInvoked, &av_createStream): + r->m_stream_id = (int)AMFProp_GetNumber(AMF_GetProp(&obj, NULL, 3)); - if (r->Link.protocol & RTMP_FEATURE_WRITE) - { - SendPublish(r); - } - else - { - if (r->Link.lFlags & RTMP_LF_PLST) - SendPlaylist(r); - SendPlay(r); - RTMP_SendCtrl(r, 3, r->m_stream_id, r->m_nBufferMS); - } - case + if (r->Link.protocol & RTMP_FEATURE_WRITE){ + SendPublish(r); + } else { + if (r->Link.lFlags & RTMP_LF_PLST){ + SendPlaylist(r); + } + SendPlay(r); + RTMP_SendCtrl(r, 3, r->m_stream_id, r->m_nBufferMS); + } + case AVMATCH(&methodInvoked, &av_play) || + AVMATCH(&methodInvoked, &av_publish): + r->m_bPlaying = TRUE; } - case AVMATCH(&method, &av_onBWDone): - case AVMATCH(&method, &av_onFCSubscribe): - case AVMATCH(&method, &av_onFCUnsubscribe): - case AVMATCH(&method, &av_ping): - case AVMATCH(&method, &av__onbwcheck): - case AVMATCH(&method, &av__onbwdone): - case AVMATCH(&method, &av__error): + free(methodInvoked.av_val); + case (AVMATCH(&method, &av_onBWDone)): + if (!r->m_nBWCheckCounter){ + SendCheckBW(r); + } + case AVMATCH(&method, &av_onFCSubscribe): + /* SendOnFCSubscribe(); */ + case AVMATCH(&method, &av_onFCUnsubscribe): + RTMP_Close(r); + ret = 1; + case AVMATCH(&method, &av_ping): + SendPong(r, txn); + case AVMATCH(&method, &av__onbwcheck): + SendCheckBWResult(r, txn); + case AVMATCH(&method, &av__onbwdone): + int i; + for (i = 0; i < r->m_numCalls; i++){ + if (AVMATCH(&r->m_methodCalls[i].name, &av__checkbw)){ + AV_erase(r->m_methodCalls, &r->m_numCalls, i, TRUE); + break; + } + } + case AVMATCH(&method, &av_close): + RTMP_Log(RTMP_LOGERROR, "rtmp server requested close"); + RTMP_Close(r); + case AVMATCH(&method, &av_onStatus): + AMFObject obj2; + AVal code, level; + AMFProp_GetObject(AMF_GetProp(&obj, NULL, 3), &obj2); + AMFProp_GetString(AMF_GetProp(&obj2, &av_code, -1), &code); + AMFProp_GetString(AMF_GetProp(&obj2, &av_level, -1), &level); + + RTMP_Log(RTMP_LOGDEBUG, "%s, onStatus: %s", __FUNCTION__, code.av_val); + switch { + case AVMATCH(&code, &av_NetStream_Failed) + || AVMATCH(&code, &av_NetStream_Play_Failed) + || AVMATCH(&code, &av_NetStream_Play_StreamNotFound) + || AVMATCH(&code, &av_NetConnection_Connect_InvalidApp): + r->m_stream_id = -1; + RTMP_Close(r); + RTMP_Log(RTMP_LOGERROR, "Closing connection: %s", code.av_val); + case AVMATCH(&code, &av_NetStream_Play_Start) + || AVMATCH(&code, &av_NetStream_Play_PublishNotify): + int i; + r->m_bPlaying = TRUE; + for (i = 0; i < r->m_numCalls; i++){ + if (AVMATCH(&r->m_methodCalls[i].name, &av_play)){ + AV_erase(r->m_methodCalls, &r->m_numCalls, i, TRUE); + break; + } + } + case AVMATCH(&code, &av_NetStream_Publish_Start): + int i; + r->m_bPlaying = TRUE; + for (i = 0; i < r->m_numCalls; i++){ + if (AVMATCH(&r->m_methodCalls[i].name, &av_publish)){ + AV_erase(r->m_methodCalls, &r->m_numCalls, i, TRUE); + break; + } + } + case AVMATCH(&code, &av_NetStream_Play_Complete) + || AVMATCH(&code, &av_NetStream_Play_Stop) + || AVMATCH(&code, &av_NetStream_Play_UnpublishNotify): + RTMP_Close(r); + ret = 1; + case AVMATCH(&code, &av_NetStream_Seek_Notify): + r->m_read.flags &= ~RTMP_READ_SEEKING; + case AVMATCH(&code, &av_NetStream_Pause_Notify): + if (r->m_pausing == 1 || r->m_pausing == 2){ + RTMP_SendPause(r, FALSE, r->m_pauseStamp); + r->m_pausing = 3; + } + } + case AVMATCH(&method, &av_playlist_ready): + int i; + for (i = 0; i < r->m_numCalls; i++){ + if (AVMATCH(&r->m_methodCalls[i].name, &av_set_playlist)){ + AV_erase(r->m_methodCalls, &r->m_numCalls, i, TRUE); + break; + } + } + default: } +leave: + AMF_Reset(&obj); + return ret; }