bool FlashStream::process(AMF::ContentType type,UInt32 time,PacketReader& packet,FlashWriter& writer,double lostRate) { if(type==AMF::EMPTY) return writer.state()!=Writer::CLOSED; // if exception, it closes the connection, and print an ERROR message switch(type) { case AMF::INFORMATIONS: { string name; AMFReader reader(packet); reader.readString(name); messageHandler(name,reader,writer); break; } case AMF::INVOCATION_AMF3: case AMF::INVOCATION: { string name; AMFReader reader(packet); reader.readString(name); double number(0); reader.readNumber(number); writer.setCallbackHandle(number); reader.readNull(); messageHandler(name,reader,writer); break; } case AMF::DATA: { AMFReader reader(packet); dataHandler(reader, lostRate); break; } case AMF::AUDIO: audioHandler(time,packet, lostRate); break; case AMF::VIDEO: videoHandler(time,packet, lostRate); break; break; case AMF::ACK: // nothing to do, a ack message says about how many bytes have been gotten by the peer // RTMFP has a complexe ack mechanism and RTMP is TCP based, ack mechanism is in system layer => so useless break; default: rawHandler(type, packet, writer); } writer.setCallbackHandle(0); return writer.state()!=Writer::CLOSED; }
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 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); }