Exemple #1
0
TEST(AMF0Test, EncodeEcmaArray)
{
	AMFEcmaArray val;
	val.setAssociativeCount(0x1234);
	QByteArray data = val.serialized();
	const char expected[] = {
		0x08, // Marker
		0x00, 0x00, 0x12, 0x34,
		0x00, 0x00,
		0x09 // End marker
	};
	ASSERT_EQ(sizeof(expected), data.size());
	for(int i = 0; i < data.size(); i++)
		ASSERT_EQ(expected[i], data[i]);
}
Exemple #2
0
TEST(AMF0Test, DecodeEcmaArray)
{
	AMFEcmaArray val;
	val.setAssociativeCount(0x1234);
	QByteArray data = val.serialized();
	AMFType *out = NULL;
	uint outSize = AMFType::decode(data.constData(), &out);
	AMFEcmaArray *outVal = out->asEcmaArray();

	ASSERT_FALSE(outVal == NULL);
	EXPECT_EQ(8, outSize);
	EXPECT_TRUE(outVal->isEmpty());
	EXPECT_EQ(val.getAssociativeCount(), outVal->getAssociativeCount());

	delete out;
}
Exemple #3
0
int FLVEncoder::StartEncoding()
{
	Log(">Start encoding FLV [id:%d]\n",id);
	
	//Si estabamos mandando tenemos que parar
	if (encodingAudio || encodingVideo)
		//paramos
		StopEncoding();

	//Set init time
	getUpdDifTime(&first);

	//Check if got old meta
	if (meta)
		//Delete
		delete(meta);

	//Create metadata object
	meta = new RTMPMetaData(0);

	//Set name
	meta->AddParam(new AMFString(L"@setDataFrame"));
	//Set name
	meta->AddParam(new AMFString(L"onMetaData"));

	//Create properties string
	AMFEcmaArray *prop = new AMFEcmaArray();

	//Set audio properties
	switch(audioCodec)
	{
		case AudioCodec::SPEEX16:
			prop->AddProperty(L"audiocodecid"	,(float)RTMPAudioFrame::SPEEX		);	//Number Audio codec ID used in the file (see E.4.2.1 for available SoundFormat values)
			prop->AddProperty(L"audiosamplerate"	,(float)16000.0				);	// Number Frequency at which the audio stream is replayed
			break;
		case AudioCodec::NELLY11:
			prop->AddProperty(L"audiocodecid"	,(float)RTMPAudioFrame::NELLY		);	//Number Audio codec ID used in the file (see E.4.2.1 for available SoundFormat values)
			prop->AddProperty(L"audiosamplerate"	,(float)11025.0				);	// Number Frequency at which the audio stream is replayed
			break;
		case AudioCodec::NELLY8:
			prop->AddProperty(L"audiocodecid"	,(float)RTMPAudioFrame::NELLY8khz	);	//Number Audio codec ID used in the file (see E.4.2.1 for available SoundFormat values)
			prop->AddProperty(L"audiosamplerate"	,(float)8000.0				);	// Number Frequency at which the audio stream is replayed
			break;
	}

	prop->AddProperty(L"stereo"		,new AMFBoolean(false)		);	// Boolean Indicating stereo audio
	prop->AddProperty(L"audiodelay"		,0.0				);	// Number Delay introduced by the audio codec in seconds
	
	//Set video codecs
	if (videoCodec==VideoCodec::SORENSON)
		//Set number
		prop->AddProperty(L"videocodecid"	,(float)RTMPVideoFrame::FLV1	);	// Number Video codec ID used in the file (see E.4.3.1 for available CodecID values)
	else if (videoCodec==VideoCodec::H264)
		//AVC
		prop->AddProperty(L"videocodecid"	,new AMFString(L"avc1")		);	// Number Video codec ID used in the file (see E.4.3.1 for available CodecID values)
	prop->AddProperty(L"framerate"		,(float)fps			);	// Number Number of frames per second
	prop->AddProperty(L"height"		,(float)height			);	// Number Height of the video in pixels
	prop->AddProperty(L"videodatarate"	,(float)bitrate			);	// Number Video bit rate in kilobits per second
	prop->AddProperty(L"width"		,(float)width			);	// Number Width of the video in pixels
	prop->AddProperty(L"canSeekToEnd"	,new AMFBoolean(false)		);	// Boolean Indicating the last video frame is a key frame

	//Add param
	meta->AddParam(prop);

	//Send metadata
	SendMetaData(meta);

	//If got audio
	if (audioInput)
	{
		//We are enconding
		encodingAudio = 1;
		//Start thread
		createPriorityThread(&encodingAudioThread,startEncodingAudio,this,1);
	}
	
	//If got video
	if (videoInput)
	{
		//We are enconding
		encodingVideo = 1;
		//Start thread
		createPriorityThread(&encodingVideoThread,startEncodingVideo,this,1);
	}

	Log("<Stop encoding FLV [%d]\n",encodingAudio);

	return 1;
}
/********************************
 * ProcessCommandMessage
 *
 ************************************/
void RTMPConnection::ProcessCommandMessage(DWORD streamId,RTMPCommandMessage* cmd)
{
	//Get message values
	std::wstring name 	= cmd->GetName();
	QWORD transId 		= cmd->GetTransId();
	AMFData* params 	= cmd->GetParams();	

	//Log
	Log("-ProcessCommandMessage [streamId:%d,name:\"%ls\",transId:%ld]\n",streamId,name.c_str(),transId);

	//Check message Stream
	if (streamId)
	{
		//Check if a stream has been created with that id
		RTMPStreams::iterator it = streams.find(streamId);

		//If not found
		if (it==streams.end())
			//Send error
			return SendCommandError(streamId,transId,NULL,NULL);
		
		//Get media stream
		RTMPStream* stream = it->second;

		//Check command names
		if (name.compare(L"play")==0)
		{
			//Get url to play
			std::wstring url = *(cmd->GetExtra(0));
			//Set played data listener
			stream->SetPlayListener(this);
			//Play
			stream->Play(url);
		//Publish
		} else if (name.compare(L"publish")==0){
			//Get url to play
			std::wstring url = *(cmd->GetExtra(0));
			//Publish
			if (stream->Publish(url))
				//OK
				SendCommandResponse(streamId,L"onStatus",transId,NULL,new RTMPNetStatusEvent(L"NetStream.Publish.Start",L"status",L"Playback started"));
			else
				//Not found
				SendCommandResponse(streamId,L"_error",transId,NULL,new RTMPNetStatusEvent(L"NetStream.Publish.Rejected",L"error",L"Stream not found"));
			
		} else if (name.compare(L"deleteStream")==0) {
			//Close stream just in case
			stream->Close();
			//Delete object
			delete(stream);
			//Remove from streams
			streams.erase(it);
			//And do not send a replay
			return;
		} else if (name.compare(L"seek")==0) {
			//Get timestamp
			double time = *(cmd->GetExtra(0));
			//Play
			stream->Seek(time);
		} else if (name.compare(L"closeStream")==0) {
			//Close stream
			stream->Close();
			//Send response
			SendCommandResponse(streamId,L"onStatus",transId,NULL,new RTMPNetStatusEvent(L"NetStream.Connect.Closed",L"status",L"Netstream closed"));
		} else {
			//Debug
			cmd->Dump();
			//Send command
			stream->PublishCommand(name.c_str(),cmd->GetParams());
		}

	} else 	if (name.compare(L"connect")==0) {
		double objectEncoding = 0;
		//Check if we already have an active media stream
		if (app)
			//Send error
			return SendCommandError(streamId,transId);

		//Check we have params
		if (!params || !params->CheckType(AMFData::Object))
			//Send error
			return SendCommandError(streamId,transId);

		//Get object
		AMFObject *obj = (AMFObject*)params;
		//Check if we have an app
		if (!obj->HasProperty(L"app"))
			//Send error
			return SendCommandError(streamId,transId);

		//Get url to connect
		appName = (std::wstring)obj->GetProperty(L"app");
		//Get peer video capabilities
		if (obj->HasProperty(L"videoCodecs"))
			//Get value
			videoCodecs = (double)obj->GetProperty(L"videoCodecs");
		//Check if we have peer audio capabilities
		if (obj->HasProperty(L"audioCodecs"))
			//Get peer audio capabilities
			audioCodecs = (double)obj->GetProperty(L"audioCodecs");
		//Check
		if (obj->HasProperty(L"objectEncoding"))
			//Get object encoding used by client
			objectEncoding = (double)obj->GetProperty(L"objectEncoding");

		//Call listener
		app = listener->OnConnect(appName);

		//If it is null
		if (!app) 
			//Send error
			return SendCommandError(streamId,transId);

		//Send start stream
		SendControlMessage(RTMPMessage::UserControlMessage,RTMPUserControlMessage::CreateStreamBegin(0));
		//Send window acknoledgement
		SendControlMessage(RTMPMessage::WindowAcknowledgementSize, RTMPWindowAcknowledgementSize::Create(512000));
		//Send client bandwitdh
		SendControlMessage(RTMPMessage::SetPeerBandwidth, RTMPSetPeerBandWidth::Create(512000,2));
		//Increase chunk size
		maxOutChunkSize = 1028;
		//Send client bandwitdh
		SendControlMessage(RTMPMessage::SetChunkSize, RTMPSetChunkSize::Create(maxOutChunkSize));

		//Create params & extra info
		AMFObject* params = new AMFObject();
		AMFObject* extra = new AMFObject();
		AMFEcmaArray* data = new AMFEcmaArray();	
		objectEncoding = 3;
		//Add properties
		params->AddProperty(L"fmsVer"		,L"FMS/3,5,1,525");
		params->AddProperty(L"capabilities"	,31.0);
		params->AddProperty(L"mode"		,1.0);
		extra->AddProperty(L"level"		,L"status");
		extra->AddProperty(L"code"		,L"NetConnection.Connect.Success");
		extra->AddProperty(L"description"	,L"Connection succeded");
		extra->AddProperty(L"data"		,data);
		extra->AddProperty(L"objectEncoding"	,objectEncoding);
		data->AddProperty(L"version"           	,L"3,5,1,525");
		//Create 
		SendCommandResult(streamId,transId,params,extra);

	} else if (name.compare(L"createStream")==0 || name.compare(L"initStream")==0) {
		//Check if we have an application
		if (!app)
			//Send error
			return SendCommandError(streamId,transId);

		//Assign the media string id
		DWORD mediaStreamId = maxStreamId++;

		//Call the application to create the stream
		RTMPStream *stream = app->CreateStream(mediaStreamId,appName,audioCodecs,videoCodecs);

		//Check if it was created correctly
		if (!stream)
			//Send error
			return SendCommandError(streamId,transId);

		//Add to the streams vector
		streams[mediaStreamId] = stream;
	
		//Create 
		SendCommandResult(streamId,transId,new AMFNull(),new AMFNumber((double)mediaStreamId));

/*
	} else if (name.compare(L"FCPublish")==0) {
		//Check if we have an application
		if (!app)
			//Send error
			return SendCommandResponse(messageStreamId,new RTMPCommandError(transId,L"Not connected"));
		//Get url to play
		std::wstring url = *(cmd->GetExtra(0));
		//Create return code
		AMFString* params = new AMFString();
		//Create params & extra info
		AMFObject* extra = new AMFObject();
		//Check publish
		extra->AddProperty(L"code",L"NetStream.Publish.Start");
		extra->AddProperty(L"code",L"NetStream.Publish.BadName");
		//Create 
		SendCommandResponse(messageStreamId, new RTMPCommandResponse(L"onFCPublish",transId,NULL,extra));
*/
	} else {
		//Send
		SendCommandError(streamId,transId);
	}
}
bool RTMPMP4Stream::Play(std::wstring& url)
{
	char filename[1024];

	//Print it
	snprintf(filename,1024,"%ls",url.c_str());

	//Open it and play
	if (!streamer.Open(filename))
	{
		//Send error comand
		SendCommand(L"onStatus", new RTMPNetStatusEvent(L"NetStream.Play.StreamNotFound",L"error",L"Stream not found"));
		//Error
		return Error("Error opening mp4 file [path:\"%ls\"",filename);
	}
	
	//Send play comand
	SendCommand(L"onStatus", new RTMPNetStatusEvent(L"NetStream.Play.Reset",L"status",L"Playback reset") );

	//Send play comand
	SendCommand(L"onStatus", new RTMPNetStatusEvent(L"NetStream.Play.Start",L"status",L"Playback started") );

	//Create metadata object
	RTMPMetaData *meta = new RTMPMetaData(0);

	//Set name
	meta->AddParam(new AMFString(L"onMetaData"));

	//Create properties string
	AMFEcmaArray *prop = new AMFEcmaArray();

	//Add default properties
	if (streamer.HasAudioTrack())
	{
		switch (streamer.GetAudioCodec())
		{
			case AudioCodec::PCMU:
				//Set properties
				prop->AddProperty(L"audiocodecid"	,(float)RTMPAudioFrame::SPEEX	);	//Number Audio codec ID used in the file (see E.4.2.1 for available SoundFormat values)
				prop->AddProperty(L"audiodatarate"	,(float)16000			);	// Number Audio bit rate in kilobits per second
				//Set decoder
				decoder = new PCMUCodec();
				//Set encode
				encoder = new SpeexCodec();
				break;
			case AudioCodec::PCMA:
				prop->AddProperty(L"audiocodecid"	,(float)RTMPAudioFrame::SPEEX	);	//Number Audio codec ID used in the file (see E.4.2.1 for available SoundFormat values)
				prop->AddProperty(L"audiodatarate"	,(float)16000			);	// Number Audio bit rate in kilobits per second
				//Set decoder
				decoder = new PCMACodec();
				//Set encode
				encoder = new SpeexCodec();
				break;
		}
		//prop->AddProperty(L"stereo"		,new AMFBoolean(false)	);	// Boolean Indicating stereo audio
		prop->AddProperty(L"audiodelay"		,0.0	);	// Number Delay introduced by the audio codec in seconds
		prop->AddProperty(L"audiosamplerate"	,8000.0	);	// Number Frequency at which the audio stream is replayed
		prop->AddProperty(L"audiosamplesize"	,160.0	);	// Number Resolution of a single audio sample
	}

	//If ti has video track
	if (streamer.HasVideoTrack())
	{
		switch (streamer.GetVideoCodec())
		{
			case VideoCodec::H263_1996:
			case VideoCodec::H263_1998:
				prop->AddProperty(L"videocodecid"	,(float)RTMPVideoFrame::FLV1	);	// Number Video codec ID used in the file (see E.4.3.1 for available CodecID values)
				break;
			case VideoCodec::H264:
				prop->AddProperty(L"videocodecid"	,new AMFString(L"avc1")		);	// Number Video codec ID used in the file (see E.4.3.1 for available CodecID values)
				break;
		}

		prop->AddProperty(L"framerate"		,(float)streamer.GetVideoFramerate()	);	// Number Number of frames per second
		prop->AddProperty(L"height"		,(float)streamer.GetVideoHeight()	);	// Number Height of the video in pixels
		prop->AddProperty(L"videodatarate"	,(float)streamer.GetVideoBitrate()/1024	);	// Number Video bit rate in kilobits per second
		prop->AddProperty(L"width"		,(float)streamer.GetVideoWidth()	);	// Number Width of the video in pixels
	}

	prop->AddProperty(L"canSeekToEnd"	,0.0	);			// Boolean Indicating the last video frame is a key frame
	prop->AddProperty(L"duration"		,(float)streamer.GetDuration());	// Number Total duration of the file in seconds

	//Add param
	meta->AddParam(prop);

	//Send metadata
	PlayMetaData(meta);

	//Get AVC descriptor if any
	desc = streamer.GetAVCDescriptor();

	//If we have one
	if (desc)
	{
		//Create the frame
		RTMPVideoFrame fdesc(0,desc);
		//Play it
		PlayMediaFrame(&fdesc);
	}

	//Play it
	if (!streamer.Play())
	{
		//Close it
		streamer.Close();
		//Send error comand
		SendCommand(L"onStatus", new RTMPNetStatusEvent(L"NetStream.Play.Error",L"error",L"Error openeing stream"));
		//Error
		return Error("Error starting playback of mp4 file [path:\"%ls\"",url.c_str());
	}
	
	return true;
}