예제 #1
0
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;
}
예제 #2
0
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);
}
예제 #3
0
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);
}