void FlowStream::messageHandler(const string& action,AMFReader& message) { if(action=="play") { disengage(); _state = PLAYING; message.read((string&)name); // TODO implements completly NetStream.play method, with possible NetStream.play.failed too! double start = -2000; if(message.available()) start = message.readNumber(); BinaryWriter& data = writer.writeRawMessage(true); data.write8(0x0F); data.write32(0x00); data.write8(0x00); AMFWriter amf(data); amf.write("|RtmpSampleAccess"); amf.writeBool(handler.audioSampleAccess); amf.writeBool(handler.videoSampleAccess); writer.writeStatusResponse("Play.Reset","Playing and resetting " + name); writer.writeStatusResponse("Play.Start","Started playing " + name); handler.streams.subscribe(peer,_index,name,writer,start); } else if(action == "closeStream") { disengage(); } else if(action=="publish") { disengage(); string type; message.read((string&)name); if(message.available()) message.read(type); // TODO recording publication feature! if(handler.streams.publish(peer,_index,name)) { writer.writeStatusResponse("Publish.Start",name +" is now published"); _state = PUBLISHING; } else writer.writeStatusResponse("Publish.BadName",name +" is already publishing"); } else if(_state==PUBLISHING) { if(!_pPublication) { Publications::Iterator it = handler.streams.publications(name); if(it!=handler.streams.publications.end()) _pPublication = it->second; else ERROR("Publication %s unfound, related for the %s message",name.c_str(),action.c_str()); } if(_pPublication) _pPublication->pushDataPacket(peer,action,message.reader); } else Flow::messageHandler(action,message); }
void FlowConnection::messageHandler(const std::string& name,AMFReader& message) { if(name=="connect") { AMFObject obj; message.readObject(obj); ((URI&)peer.swfUrl) = obj.getString("swfUrl",""); ((URI&)peer.pageUrl) = obj.getString("pageUrl",""); // Don't support AMF0 forced on NetConnection object because impossible to exchange custome data (ByteArray written impossible) // But it's not a pb because NetConnection RTMFP works since flash player 10.0 only (which supports AMF3) if(obj.getDouble("objectEncoding")==0) throw Exception("ObjectEncoding client must be AMF3 and not AMF0"); // Check if the client is authorized ++((UInt32&)handler.count); ((Peer::PeerState&)peer.state) = Peer::REJECTED; if(!handler.onConnection(peer,writer)) throw Exception("Client rejected"); ((Peer::PeerState&)peer.state) = Peer::ACCEPTED; AMFObjectWriter response(writer.writeSuccessResponse("Connect.Success","Connection succeeded")); response.write("objectEncoding",3); response.write("data",peer.data); } else if(name == "setPeerInfo") { list<Address> address; string addr; while(message.available()) { message.read(addr); // private host address.push_back(addr); } peer.setPrivateAddress(address); BinaryWriter& response(writer.writeRawMessage()); response.write16(0x29); // Unknown! response.write32(handler.keepAliveServer); response.write32(handler.keepAlivePeer); } else if(name == "initStream") { // TODO? } else if(name == "createStream") { AMFWriter& response(writer.writeAMFResult()); response.writeNumber(*_streamIndex.insert(handler.streams.create()).first); } else if(name == "deleteStream") { UInt32 index = (UInt32)message.readNumber(); _streamIndex.erase(index); handler.streams.destroy(index); } else { if(!handler.onMessage(peer,name,message,writer)) writer.writeErrorResponse("Call.Failed","Method '" + name + "' not found"); } }
void FlashStream::messageHandler(const string& name,AMFReader& message,FlashWriter& writer) { if(name=="play") { disengage(&writer); string publication; message.readString(publication); // TODO implements completly NetStream.play method, with possible NetStream.play.failed too! Exception ex; _pListener = invoker.subscribe(ex,peer,publication,writer); if (ex) { writer.writeAMFStatus("NetStream.Play.Failed",ex.error()); return; } if(message.available()) _pListener->setNumber("unbuffered",message.readNumber()==-3000); if(_bufferTime>0) { // To do working the buffertime on receiver side BinaryWriter& raw = writer.writeRaw(); raw.write16(0); raw.write32(id); _pListener->setNumber("bufferTime",_bufferTime); } writer.writeAMFStatus("NetStream.Play.Reset","Playing and resetting " + publication); // for entiere playlist writer.writeAMFStatus("NetStream.Play.Start","Started playing " + publication); // for item } else if(name == "closeStream") { disengage(&writer); } else if(name=="publish") { disengage(&writer); string type,publication; message.readString(publication); size_t query = publication.find('?'); if (query != string::npos) publication = publication.substr(0, query); // TODO use query in Util::UnpackQuery for publication options? if(message.available()) message.readString(type); // TODO support "append" and "appendWithGap" Exception ex; _pPublication = invoker.publish(ex, peer, publication, type == "record" ? Publication::RECORD : Publication::LIVE); if (ex) writer.writeAMFStatus("NetStream.Publish.BadName",ex.error()); else writer.writeAMFStatus("NetStream.Publish.Start",publication +" is now published"); } else if(_pListener && name=="receiveAudio") { _pListener->receiveAudio = message.readBoolean(); } else if(_pListener && name=="receiveVideo") { _pListener->receiveVideo = message.readBoolean(); } else ERROR("RTMFPMessage '",name,"' unknown on stream ",id); }
void FlowStream::messageHandler(const string& action,AMFReader& message) { if(action=="play") { disengage(); message.read((string&)name); // TODO implements completly NetStream.play method, with possible NetStream.play.failed too! double start = -2000; if(message.available()) start = message.readNumber(); try { _pListener = &invoker._streams.subscribe(peer,_index,name,writer,start); _state = PLAYING; } catch(...) {} } else if(action == "closeStream") { disengage(); } else if(action=="publish") { disengage(); string type; message.read((string&)name); if(message.available()) message.read(type); // TODO recording publication feature! try { invoker._streams.publish(peer,_index,name,&writer); _state = PUBLISHING; } catch(...) {} } else if(_state==PUBLISHING) { if(!_pPublication) { Publications::Iterator it = invoker.publications(name); if(it!=invoker.publications.end()) _pPublication = it->second; else ERROR("Publication %s unfound, related for the %s message",name.c_str(),action.c_str()); } if(_pPublication) _pPublication->pushDataPacket(action,message.reader); } else if(_pListener && action=="receiveAudio") _pListener->receiveAudio = message.readBoolean(); else if(_pListener && action=="receiveVideo") _pListener->receiveVideo = message.readBoolean(); else Flow::messageHandler(action,message); }
void FlowStream::messageHandler(const string& action,AMFReader& message) { if(action=="play") { disengage(); message.read((string&)name); // TODO implements completly NetStream.play method, with possible NetStream.play.failed too! double start = -2000; if(message.available()) start = message.readNumber(); if(invoker._streams.subscribe(peer,_index,name,writer,start)) _state = PLAYING; } else if(action == "closeStream") { disengage(); } else if(action=="publish") { disengage(); string type; message.read((string&)name); if(message.available()) message.read(type); // TODO recording publication feature! try { invoker._streams.publish(peer,_index,name); _state = PUBLISHING; writer.writeStatusResponse("Publish.Start",name +" is now published"); } catch(Exception& ex) { writer.writeStatusResponse(ex.code() == Publication::BADNAME ? "Publish.BadName" : "Publish.Failed", ex.displayText()); } } else if(_state==PUBLISHING) { if(!_pPublication) { Publications::Iterator it = invoker.publications(name); if(it!=invoker.publications.end()) _pPublication = it->second; else ERROR("Publication %s unfound, related for the %s message",name.c_str(),action.c_str()); } if(_pPublication) _pPublication->pushDataPacket(action,message.reader); } else Flow::messageHandler(action,message); }
void FlashStream::messageHandler(const string& name, AMFReader& message, FlashWriter& writer) { if (name == "play") { disengage(&writer); string publication; message.readString(publication); // TODO implements completly NetStream.play method, with possible NetStream.play.failed too! Exception ex; _pListener = invoker.subscribe(ex, peer, publication, writer); // ex already log displayed if (!_pListener) { writer.writeAMFStatus("NetStream.Play.Failed", ex.error()); return; } OnStart::raise(id, writer); // stream begin writer.writeAMFStatus("NetStream.Play.Reset", "Playing and resetting " + publication); // for entiere playlist writer.writeAMFStatus("NetStream.Play.Start", "Started playing "+publication); // for item AMFWriter& amf(writer.writeInfos("|RtmpSampleAccess")); amf.writeBoolean(true); // audioSampleAccess amf.writeBoolean(true); // videoSampleAccess if (_bufferTime > 0) _pListener->setNumber("bufferTime", _bufferTime); } else if (name == "closeStream") { disengage(&writer); } else if (name == "publish") { disengage(&writer); string type, publication; message.readString(publication); size_t query = publication.find('?'); if (query != string::npos) publication = publication.substr(0, query); // TODO use query in Util::UnpackQuery for publication options? if (message.available()) message.readString(type); // TODO support "append" and "appendWithGap" Exception ex; _pPublication = invoker.publish(ex, peer, publication, type == "record" ? Publication::RECORD : Publication::LIVE); if (ex) { writer.writeAMFStatus("NetStream.Publish.BadName", ex.error()); _pPublication = NULL; } else writer.writeAMFStatus("NetStream.Publish.Start", publication + " is now published"); } else if (_pListener && name == "receiveAudio") { message.readBoolean(_pListener->receiveAudio); } else if (_pListener && name == "receiveVideo") { message.readBoolean(_pListener->receiveVideo); } else if (_pListener && name == "pause") { bool paused(true); message.readBoolean(paused); // TODO support pause for VOD if (paused) { // useless, client knows it when it calls NetStream::pause method // writer.writeAMFStatus("NetStream.Pause.Notify", _pListener->publication.name() + " paused"); } else { double position; if (message.readNumber(position)) _pListener->seek((UInt32)position); OnStart::raise(id, writer); // stream begin // useless, client knows it when it calls NetStream::resume method // writer.writeAMFStatus("NetStream.Unpause.Notify", _pListener->publication.name() + " resumed"); } } else if (_pListener && name == "seek") { double position; if (message.readNumber(position)) { _pListener->seek((UInt32)position); // TODO support seek for VOD OnStart::raise(id, writer); // stream begin // useless, client knows it when it calls NetStream::seek method, and wait "NetStream.Seek.Complete" rather (raised by client side) // writer.writeAMFStatus("NetStream.Seek.Notify", _pListener->publication.name() + " seek operation"); } else writer.writeAMFStatus("NetStream.Seek.InvalidTime", _pListener->publication.name() + " seek operation must pass in argument a milliseconds position time"); } else if (_pPublication && name == "@setDataFrame") { // metadata _pPublication->writeProperties(message); } else if (_pPublication && name == "@clearDataFrame") { _pPublication->clearProperties(); } else ERROR("Message '",name,"' unknown on stream ",id); }
void FlowConnection::messageHandler(const std::string& name,AMFReader& message) { if(name=="connect") { message.stopReferencing(); AMFSimpleObject obj; message.readSimpleObject(obj); message.startReferencing(); ((URI&)peer.swfUrl) = obj.getString("swfUrl",""); ((URI&)peer.pageUrl) = obj.getString("pageUrl",""); ((string&)peer.flashVersion) = obj.getString("flashVer",""); // Don't support AMF0 forced on NetConnection object because AMFWriter writes in AMF3 format // But it's not a pb because NetConnection RTMFP works since flash player 10.0 only (which supports AMF3) if(obj.getNumber("objectEncoding",0)==0) { writer.writeErrorResponse("Connect.Rejected","ObjectEncoding client must be in a AMF3 format (not AMF0)"); return; } // Check if the client is authorized peer.setFlowWriter(&writer); writer.beginTransaction(); bool accept=true; { AMFObjectWriter response(writer.writeSuccessResponse("Connect.Success","Connection succeeded")); response.write("objectEncoding",3.0); accept = peer.onConnection(message,response); } if(!accept) { writer.endTransaction(1); writer.writeAMFMessage("close"); writer.close(); } else writer.endTransaction(); } else if(name == "setPeerInfo") { peer.addresses.resize(1); string addr; while(message.available()) { message.read(addr); // private host peer.addresses.push_back(addr); } BinaryWriter& response(writer.writeRawMessage()); response.write16(0x29); // Unknown! response.write32(invoker.keepAliveServer); response.write32(invoker.keepAlivePeer); } else if(name == "initStream") { // TODO? } else if(name == "createStream") { AMFWriter& response(writer.writeAMFResult()); response.writeInteger(*_streamIndex.insert(invoker._streams.create()).first); } else if(name == "deleteStream") { UInt32 index = (UInt32)message.readNumber(); _streamIndex.erase(index); invoker._streams.destroy(index); } else { if(!peer.onMessage(name,message)) writer.writeErrorResponse("Call.Failed","Method '" + name + "' not found"); } }