michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this file, michael@0: * You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: michael@0: using namespace std; michael@0: michael@0: #include "mozilla/Scoped.h" michael@0: #include michael@0: #include "nsIEventTarget.h" michael@0: #include "FakeMediaStreamsImpl.h" michael@0: michael@0: #define GTEST_HAS_RTTI 0 michael@0: #include "gtest/gtest.h" michael@0: #include "gtest_utils.h" michael@0: michael@0: #include "mtransport_test_utils.h" michael@0: MtransportTestUtils *test_utils; michael@0: michael@0: //Video Frame Color michael@0: const int COLOR = 0x80; //Gray michael@0: michael@0: //MWC RNG of George Marsaglia michael@0: //taken from xiph.org michael@0: static int32_t Rz, Rw; michael@0: static inline int32_t fast_rand(void) michael@0: { michael@0: Rz=36969*(Rz&65535)+(Rz>>16); michael@0: Rw=18000*(Rw&65535)+(Rw>>16); michael@0: return (Rz<<16)+Rw; michael@0: } michael@0: michael@0: /** michael@0: * Global structure to store video test results. michael@0: */ michael@0: struct VideoTestStats michael@0: { michael@0: int numRawFramesInserted; michael@0: int numFramesRenderedSuccessfully; michael@0: int numFramesRenderedWrongly; michael@0: }; michael@0: michael@0: VideoTestStats vidStatsGlobal={0,0,0}; michael@0: michael@0: michael@0: /** michael@0: * A Dummy Video Conduit Tester. michael@0: * The test-case inserts a 640*480 grey imagerevery 33 milliseconds michael@0: * to the video-conduit for encoding and transporting. michael@0: */ michael@0: michael@0: class VideoSendAndReceive michael@0: { michael@0: public: michael@0: VideoSendAndReceive():width(640), michael@0: height(480) michael@0: { michael@0: } michael@0: michael@0: ~VideoSendAndReceive() michael@0: { michael@0: } michael@0: michael@0: void Init(mozilla::RefPtr aSession) michael@0: { michael@0: mSession = aSession; michael@0: } michael@0: void GenerateAndReadSamples() michael@0: { michael@0: michael@0: int len = ((width * height) * 3 / 2); michael@0: uint8_t* frame = (uint8_t*) PR_MALLOC(len); michael@0: int numFrames = 121; michael@0: memset(frame, COLOR, len); michael@0: michael@0: do michael@0: { michael@0: mSession->SendVideoFrame((unsigned char*)frame, michael@0: len, michael@0: width, michael@0: height, michael@0: mozilla::kVideoI420, michael@0: 0); michael@0: PR_Sleep(PR_MillisecondsToInterval(33)); michael@0: vidStatsGlobal.numRawFramesInserted++; michael@0: numFrames--; michael@0: } while(numFrames >= 0); michael@0: PR_Free(frame); michael@0: } michael@0: michael@0: private: michael@0: mozilla::RefPtr mSession; michael@0: int width, height; michael@0: }; michael@0: michael@0: michael@0: michael@0: /** michael@0: * A Dummy AudioConduit Tester michael@0: * The test reads PCM samples of a standard test file and michael@0: * passws to audio-conduit for encoding, RTPfication and michael@0: * decoding ebery 10 milliseconds. michael@0: * This decoded samples are read-off the conduit for writing michael@0: * into output audio file in PCM format. michael@0: */ michael@0: class AudioSendAndReceive michael@0: { michael@0: public: michael@0: static const unsigned int PLAYOUT_SAMPLE_FREQUENCY; //default is 16000 michael@0: static const unsigned int PLAYOUT_SAMPLE_LENGTH; //default is 160000 michael@0: michael@0: AudioSendAndReceive() michael@0: { michael@0: } michael@0: michael@0: ~AudioSendAndReceive() michael@0: { michael@0: } michael@0: michael@0: void Init(mozilla::RefPtr aSession, michael@0: mozilla::RefPtr aOtherSession, michael@0: std::string fileIn, std::string fileOut) michael@0: { michael@0: michael@0: mSession = aSession; michael@0: mOtherSession = aOtherSession; michael@0: iFile = fileIn; michael@0: oFile = fileOut; michael@0: } michael@0: michael@0: //Kick start the test michael@0: void GenerateAndReadSamples(); michael@0: michael@0: private: michael@0: michael@0: mozilla::RefPtr mSession; michael@0: mozilla::RefPtr mOtherSession; michael@0: std::string iFile; michael@0: std::string oFile; michael@0: michael@0: int WriteWaveHeader(int rate, int channels, FILE* outFile); michael@0: int FinishWaveHeader(FILE* outFile); michael@0: void GenerateMusic(int16_t* buf, int len); michael@0: }; michael@0: michael@0: const unsigned int AudioSendAndReceive::PLAYOUT_SAMPLE_FREQUENCY = 16000; michael@0: const unsigned int AudioSendAndReceive::PLAYOUT_SAMPLE_LENGTH = 160000; michael@0: michael@0: int AudioSendAndReceive::WriteWaveHeader(int rate, int channels, FILE* outFile) michael@0: { michael@0: //Hardcoded for 16 bit samples michael@0: unsigned char header[] = { michael@0: // File header michael@0: 0x52, 0x49, 0x46, 0x46, // 'RIFF' michael@0: 0x00, 0x00, 0x00, 0x00, // chunk size michael@0: 0x57, 0x41, 0x56, 0x45, // 'WAVE' michael@0: // fmt chunk. We always write 16-bit samples. michael@0: 0x66, 0x6d, 0x74, 0x20, // 'fmt ' michael@0: 0x10, 0x00, 0x00, 0x00, // chunk size michael@0: 0x01, 0x00, // WAVE_FORMAT_PCM michael@0: 0xFF, 0xFF, // channels michael@0: 0xFF, 0xFF, 0xFF, 0xFF, // sample rate michael@0: 0x00, 0x00, 0x00, 0x00, // data rate michael@0: 0xFF, 0xFF, // frame size in bytes michael@0: 0x10, 0x00, // bits per sample michael@0: // data chunk michael@0: 0x64, 0x61, 0x74, 0x61, // 'data' michael@0: 0xFE, 0xFF, 0xFF, 0x7F // chunk size michael@0: }; michael@0: michael@0: #define set_uint16le(buffer, value) \ michael@0: (buffer)[0] = (value) & 0xff; \ michael@0: (buffer)[1] = (value) >> 8; michael@0: #define set_uint32le(buffer, value) \ michael@0: set_uint16le( (buffer), (value) & 0xffff ); \ michael@0: set_uint16le( (buffer) + 2, (value) >> 16 ); michael@0: michael@0: // set dynamic header fields michael@0: set_uint16le(header + 22, channels); michael@0: set_uint32le(header + 24, rate); michael@0: set_uint16le(header + 32, channels*2); michael@0: michael@0: size_t written = fwrite(header, 1, sizeof(header), outFile); michael@0: if (written != sizeof(header)) { michael@0: cerr << "Writing WAV header failed" << endl; michael@0: return -1; michael@0: } michael@0: michael@0: return 0; michael@0: } michael@0: michael@0: // Update the WAVE file header with the written length michael@0: int AudioSendAndReceive::FinishWaveHeader(FILE* outFile) michael@0: { michael@0: // Measure how much data we've written michael@0: long end = ftell(outFile); michael@0: if (end < 16) { michael@0: cerr << "Couldn't get output file length" << endl; michael@0: return (end < 0) ? end : -1; michael@0: } michael@0: michael@0: // Update the header michael@0: unsigned char size[4]; michael@0: int err = fseek(outFile, 40, SEEK_SET); michael@0: if (err < 0) { michael@0: cerr << "Couldn't seek to WAV file header." << endl; michael@0: return err; michael@0: } michael@0: set_uint32le(size, (end - 44) & 0xffffffff); michael@0: size_t written = fwrite(size, 1, sizeof(size), outFile); michael@0: if (written != sizeof(size)) { michael@0: cerr << "Couldn't write data size to WAV header" << endl; michael@0: return -1; michael@0: } michael@0: michael@0: // Return to the end michael@0: err = fseek(outFile, 0, SEEK_END); michael@0: if (err < 0) { michael@0: cerr << "Couldn't seek to WAV file end." << endl; michael@0: return err; michael@0: } michael@0: michael@0: return 0; michael@0: } michael@0: michael@0: //Code from xiph.org to generate music of predefined length michael@0: void AudioSendAndReceive::GenerateMusic(short* buf, int len) michael@0: { michael@0: cerr <<" Generating Input Music " << endl; michael@0: int32_t a1,a2,b1,b2; michael@0: int32_t c1,c2,d1,d2; michael@0: int32_t i,j; michael@0: a1=b1=a2=b2=0; michael@0: c1=c2=d1=d2=0; michael@0: j=0; michael@0: /*60ms silence */ michael@0: for(i=0;i<2880;i++) michael@0: { michael@0: buf[i*2]=buf[(i*2)+1]=0; michael@0: } michael@0: for(i=2880;i>12)^((j>>10|j>>12)&26&j>>7)))&128)+128)<<15; michael@0: r=fast_rand();v1+=r&65535;v1-=r>>16; michael@0: r=fast_rand();v2+=r&65535;v2-=r>>16; michael@0: b1=v1-a1+((b1*61+32)>>6);a1=v1; michael@0: b2=v2-a2+((b2*61+32)>>6);a2=v2; michael@0: c1=(30*(c1+b1+d1)+32)>>6;d1=b1; michael@0: c2=(30*(c2+b2+d2)+32)>>6;d2=b2; michael@0: v1=(c1+128)>>8; michael@0: v2=(c2+128)>>8; michael@0: buf[i]=v1>32767?32767:(v1<-32768?-32768:v1); michael@0: buf[i+1]=v2>32767?32767:(v2<-32768?-32768:v2); michael@0: if(i%6==0)j++; michael@0: } michael@0: cerr << "Generating Input Music Done " << endl; michael@0: } michael@0: michael@0: //Hardcoded for 16 bit samples for now michael@0: void AudioSendAndReceive::GenerateAndReadSamples() michael@0: { michael@0: int16_t audioInput[PLAYOUT_SAMPLE_LENGTH]; michael@0: int16_t audioOutput[PLAYOUT_SAMPLE_LENGTH]; michael@0: short* inbuf; michael@0: int sampleLengthDecoded = 0; michael@0: unsigned int SAMPLES = (PLAYOUT_SAMPLE_FREQUENCY * 10); //10 seconds michael@0: int CHANNELS = 1; //mono audio michael@0: int sampleLengthInBytes = sizeof(audioInput); michael@0: //generated audio buffer michael@0: inbuf = (short *)moz_xmalloc(sizeof(short)*SAMPLES*CHANNELS); michael@0: memset(audioInput,0,sampleLengthInBytes); michael@0: memset(audioOutput,0,sampleLengthInBytes); michael@0: MOZ_ASSERT(SAMPLES <= PLAYOUT_SAMPLE_LENGTH); michael@0: michael@0: FILE* inFile = fopen( iFile.c_str(), "wb+"); michael@0: if(!inFile) { michael@0: cerr << "Input File Creation Failed " << endl; michael@0: return; michael@0: } michael@0: michael@0: FILE* outFile = fopen( oFile.c_str(), "wb+"); michael@0: if(!outFile) { michael@0: cerr << "Output File Creation Failed " << endl; michael@0: return; michael@0: } michael@0: michael@0: //Create input file with the music michael@0: WriteWaveHeader(PLAYOUT_SAMPLE_FREQUENCY, 1, inFile); michael@0: GenerateMusic(inbuf, SAMPLES); michael@0: fwrite(inbuf,1,SAMPLES*sizeof(inbuf[0])*CHANNELS,inFile); michael@0: FinishWaveHeader(inFile); michael@0: fclose(inFile); michael@0: michael@0: WriteWaveHeader(PLAYOUT_SAMPLE_FREQUENCY, 1, outFile); michael@0: unsigned int numSamplesReadFromInput = 0; michael@0: do michael@0: { michael@0: if(!memcpy(audioInput, inbuf, sampleLengthInBytes)) michael@0: { michael@0: return; michael@0: } michael@0: michael@0: numSamplesReadFromInput += PLAYOUT_SAMPLE_LENGTH; michael@0: inbuf += PLAYOUT_SAMPLE_LENGTH; michael@0: michael@0: mSession->SendAudioFrame(audioInput, michael@0: PLAYOUT_SAMPLE_LENGTH, michael@0: PLAYOUT_SAMPLE_FREQUENCY,10); michael@0: michael@0: PR_Sleep(PR_MillisecondsToInterval(10)); michael@0: mOtherSession->GetAudioFrame(audioOutput, PLAYOUT_SAMPLE_FREQUENCY, michael@0: 10, sampleLengthDecoded); michael@0: if(sampleLengthDecoded == 0) michael@0: { michael@0: cerr << " Zero length Sample " << endl; michael@0: } michael@0: michael@0: int wrote_ = fwrite (audioOutput, 1 , sampleLengthInBytes, outFile); michael@0: if(wrote_ != sampleLengthInBytes) michael@0: { michael@0: cerr << "Couldn't Write " << sampleLengthInBytes << "bytes" << endl; michael@0: break; michael@0: } michael@0: }while(numSamplesReadFromInput < SAMPLES); michael@0: michael@0: FinishWaveHeader(outFile); michael@0: fclose(outFile); michael@0: } michael@0: michael@0: /** michael@0: * Dummy Video Target for the conduit michael@0: * This class acts as renderer attached to the video conuit michael@0: * As of today we just verify if the frames rendered are exactly michael@0: * the same as frame inserted at the first place michael@0: */ michael@0: class DummyVideoTarget: public mozilla::VideoRenderer michael@0: { michael@0: public: michael@0: DummyVideoTarget() michael@0: { michael@0: } michael@0: michael@0: virtual ~DummyVideoTarget() michael@0: { michael@0: } michael@0: michael@0: michael@0: void RenderVideoFrame(const unsigned char* buffer, michael@0: unsigned int buffer_size, michael@0: uint32_t time_stamp, michael@0: int64_t render_time, michael@0: const mozilla::ImageHandle& handle) michael@0: { michael@0: //write the frame to the file michael@0: if(VerifyFrame(buffer, buffer_size) == 0) michael@0: { michael@0: vidStatsGlobal.numFramesRenderedSuccessfully++; michael@0: } else michael@0: { michael@0: vidStatsGlobal.numFramesRenderedWrongly++; michael@0: } michael@0: } michael@0: michael@0: void FrameSizeChange(unsigned int, unsigned int, unsigned int) michael@0: { michael@0: //do nothing michael@0: } michael@0: michael@0: //This is hardcoded to check if the contents of frame is COLOR michael@0: // as we set while sending. michael@0: int VerifyFrame(const unsigned char* buffer, unsigned int buffer_size) michael@0: { michael@0: int good = 0; michael@0: for(int i=0; i < (int) buffer_size; i++) michael@0: { michael@0: if(buffer[i] == COLOR) michael@0: { michael@0: ++good; michael@0: } michael@0: else michael@0: { michael@0: --good; michael@0: } michael@0: } michael@0: return 0; michael@0: } michael@0: michael@0: }; michael@0: michael@0: /** michael@0: * Fake Audio and Video External Transport Class michael@0: * The functions in this class will be invoked by the conduit michael@0: * when it has RTP/RTCP frame to transmit. michael@0: * For everty RTP/RTCP frame we receive, we pass it back michael@0: * to the conduit for eventual decoding and rendering. michael@0: */ michael@0: class FakeMediaTransport : public mozilla::TransportInterface michael@0: { michael@0: public: michael@0: FakeMediaTransport():numPkts(0), michael@0: mAudio(false), michael@0: mVideo(false) michael@0: { michael@0: } michael@0: michael@0: ~FakeMediaTransport() michael@0: { michael@0: } michael@0: michael@0: virtual nsresult SendRtpPacket(const void* data, int len) michael@0: { michael@0: ++numPkts; michael@0: if(mAudio) michael@0: { michael@0: mOtherAudioSession->ReceivedRTPPacket(data,len); michael@0: } else michael@0: { michael@0: mOtherVideoSession->ReceivedRTPPacket(data,len); michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: virtual nsresult SendRtcpPacket(const void* data, int len) michael@0: { michael@0: if(mAudio) michael@0: { michael@0: mOtherAudioSession->ReceivedRTCPPacket(data,len); michael@0: } else michael@0: { michael@0: mOtherVideoSession->ReceivedRTCPPacket(data,len); michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: //Treat this object as Audio Transport michael@0: void SetAudioSession(mozilla::RefPtr aSession, michael@0: mozilla::RefPtr michael@0: aOtherSession) michael@0: { michael@0: mAudioSession = aSession; michael@0: mOtherAudioSession = aOtherSession; michael@0: mAudio = true; michael@0: } michael@0: michael@0: // Treat this object as Video Transport michael@0: void SetVideoSession(mozilla::RefPtr aSession, michael@0: mozilla::RefPtr michael@0: aOtherSession) michael@0: { michael@0: mVideoSession = aSession; michael@0: mOtherVideoSession = aOtherSession; michael@0: mVideo = true; michael@0: } michael@0: michael@0: private: michael@0: mozilla::RefPtr mAudioSession; michael@0: mozilla::RefPtr mVideoSession; michael@0: mozilla::RefPtr mOtherVideoSession; michael@0: mozilla::RefPtr mOtherAudioSession; michael@0: int numPkts; michael@0: bool mAudio, mVideo; michael@0: }; michael@0: michael@0: michael@0: namespace { michael@0: michael@0: class TransportConduitTest : public ::testing::Test michael@0: { michael@0: public: michael@0: TransportConduitTest() michael@0: { michael@0: //input and output file names michael@0: iAudiofilename = "input.wav"; michael@0: oAudiofilename = "recorded.wav"; michael@0: } michael@0: michael@0: ~TransportConduitTest() michael@0: { michael@0: } michael@0: michael@0: //1. Dump audio samples to dummy external transport michael@0: void TestDummyAudioAndTransport() michael@0: { michael@0: //get pointer to AudioSessionConduit michael@0: int err=0; michael@0: mAudioSession = mozilla::AudioSessionConduit::Create(nullptr); michael@0: if( !mAudioSession ) michael@0: ASSERT_NE(mAudioSession, (void*)nullptr); michael@0: michael@0: mAudioSession2 = mozilla::AudioSessionConduit::Create(nullptr); michael@0: if( !mAudioSession2 ) michael@0: ASSERT_NE(mAudioSession2, (void*)nullptr); michael@0: michael@0: FakeMediaTransport* xport = new FakeMediaTransport(); michael@0: ASSERT_NE(xport, (void*)nullptr); michael@0: xport->SetAudioSession(mAudioSession, mAudioSession2); michael@0: mAudioTransport = xport; michael@0: michael@0: // attach the transport to audio-conduit michael@0: err = mAudioSession->AttachTransport(mAudioTransport); michael@0: ASSERT_EQ(mozilla::kMediaConduitNoError, err); michael@0: err = mAudioSession2->AttachTransport(mAudioTransport); michael@0: ASSERT_EQ(mozilla::kMediaConduitNoError, err); michael@0: michael@0: //configure send and recv codecs on the audio-conduit michael@0: //mozilla::AudioCodecConfig cinst1(124,"PCMU",8000,80,1,64000); michael@0: mozilla::AudioCodecConfig cinst1(124,"opus",48000,960,1,64000); michael@0: mozilla::AudioCodecConfig cinst2(125,"L16",16000,320,1,256000); michael@0: michael@0: michael@0: std::vector rcvCodecList; michael@0: rcvCodecList.push_back(&cinst1); michael@0: rcvCodecList.push_back(&cinst2); michael@0: michael@0: err = mAudioSession->ConfigureSendMediaCodec(&cinst1); michael@0: ASSERT_EQ(mozilla::kMediaConduitNoError, err); michael@0: err = mAudioSession->ConfigureRecvMediaCodecs(rcvCodecList); michael@0: ASSERT_EQ(mozilla::kMediaConduitNoError, err); michael@0: michael@0: err = mAudioSession2->ConfigureSendMediaCodec(&cinst1); michael@0: ASSERT_EQ(mozilla::kMediaConduitNoError, err); michael@0: err = mAudioSession2->ConfigureRecvMediaCodecs(rcvCodecList); michael@0: ASSERT_EQ(mozilla::kMediaConduitNoError, err); michael@0: michael@0: //start generating samples michael@0: audioTester.Init(mAudioSession,mAudioSession2, iAudiofilename,oAudiofilename); michael@0: cerr << " ******************************************************** " << endl; michael@0: cerr << " Generating Audio Samples " << endl; michael@0: cerr << " ******************************************************** " << endl; michael@0: PR_Sleep(PR_SecondsToInterval(2)); michael@0: audioTester.GenerateAndReadSamples(); michael@0: PR_Sleep(PR_SecondsToInterval(2)); michael@0: cerr << " ******************************************************** " << endl; michael@0: cerr << " Input Audio File " << iAudiofilename << endl; michael@0: cerr << " Output Audio File " << oAudiofilename << endl; michael@0: cerr << " ******************************************************** " << endl; michael@0: } michael@0: michael@0: //2. Dump audio samples to dummy external transport michael@0: void TestDummyVideoAndTransport() michael@0: { michael@0: int err = 0; michael@0: //get pointer to VideoSessionConduit michael@0: mVideoSession = mozilla::VideoSessionConduit::Create(nullptr); michael@0: if( !mVideoSession ) michael@0: ASSERT_NE(mVideoSession, (void*)nullptr); michael@0: michael@0: // This session is for other one michael@0: mVideoSession2 = mozilla::VideoSessionConduit::Create(nullptr); michael@0: if( !mVideoSession2 ) michael@0: ASSERT_NE(mVideoSession2,(void*)nullptr); michael@0: michael@0: mVideoRenderer = new DummyVideoTarget(); michael@0: ASSERT_NE(mVideoRenderer, (void*)nullptr); michael@0: michael@0: FakeMediaTransport* xport = new FakeMediaTransport(); michael@0: ASSERT_NE(xport, (void*)nullptr); michael@0: xport->SetVideoSession(mVideoSession,mVideoSession2); michael@0: mVideoTransport = xport; michael@0: michael@0: // attach the transport and renderer to video-conduit michael@0: err = mVideoSession2->AttachRenderer(mVideoRenderer); michael@0: ASSERT_EQ(mozilla::kMediaConduitNoError, err); michael@0: err = mVideoSession->AttachTransport(mVideoTransport); michael@0: ASSERT_EQ(mozilla::kMediaConduitNoError, err); michael@0: err = mVideoSession2->AttachTransport(mVideoTransport); michael@0: ASSERT_EQ(mozilla::kMediaConduitNoError, err); michael@0: michael@0: //configure send and recv codecs on theconduit michael@0: mozilla::VideoCodecConfig cinst1(120, "VP8", 0); michael@0: mozilla::VideoCodecConfig cinst2(124, "I420", 0); michael@0: michael@0: michael@0: std::vector rcvCodecList; michael@0: rcvCodecList.push_back(&cinst1); michael@0: rcvCodecList.push_back(&cinst2); michael@0: michael@0: err = mVideoSession->ConfigureSendMediaCodec(&cinst1); michael@0: ASSERT_EQ(mozilla::kMediaConduitNoError, err); michael@0: michael@0: err = mVideoSession2->ConfigureRecvMediaCodecs(rcvCodecList); michael@0: ASSERT_EQ(mozilla::kMediaConduitNoError, err); michael@0: michael@0: //start generating samples michael@0: cerr << " *************************************************" << endl; michael@0: cerr << " Starting the Video Sample Generation " << endl; michael@0: cerr << " *************************************************" << endl; michael@0: PR_Sleep(PR_SecondsToInterval(2)); michael@0: videoTester.Init(mVideoSession); michael@0: videoTester.GenerateAndReadSamples(); michael@0: PR_Sleep(PR_SecondsToInterval(2)); michael@0: cerr << " **************************************************" << endl; michael@0: cerr << " Done With The Testing " << endl; michael@0: cerr << " VIDEO TEST STATS " << endl; michael@0: cerr << " Num Raw Frames Inserted: "<< michael@0: vidStatsGlobal.numRawFramesInserted << endl; michael@0: cerr << " Num Frames Successfully Rendered: "<< michael@0: vidStatsGlobal.numFramesRenderedSuccessfully << endl; michael@0: cerr << " Num Frames Wrongly Rendered: "<< michael@0: vidStatsGlobal.numFramesRenderedWrongly << endl; michael@0: michael@0: cerr << " Done With The Testing " << endl; michael@0: michael@0: cerr << " **************************************************" << endl; michael@0: ASSERT_EQ(0, vidStatsGlobal.numFramesRenderedWrongly); michael@0: ASSERT_EQ(vidStatsGlobal.numRawFramesInserted, michael@0: vidStatsGlobal.numFramesRenderedSuccessfully); michael@0: } michael@0: michael@0: void TestVideoConduitCodecAPI() michael@0: { michael@0: int err = 0; michael@0: mozilla::RefPtr mVideoSession; michael@0: //get pointer to VideoSessionConduit michael@0: mVideoSession = mozilla::VideoSessionConduit::Create(nullptr); michael@0: if( !mVideoSession ) michael@0: ASSERT_NE(mVideoSession, (void*)nullptr); michael@0: michael@0: //Test Configure Recv Codec APIS michael@0: cerr << " *************************************************" << endl; michael@0: cerr << " Test Receive Codec Configuration API Now " << endl; michael@0: cerr << " *************************************************" << endl; michael@0: michael@0: std::vector rcvCodecList; michael@0: michael@0: //Same APIs michael@0: cerr << " *************************************************" << endl; michael@0: cerr << " 1. Same Codec (VP8) Repeated Twice " << endl; michael@0: cerr << " *************************************************" << endl; michael@0: michael@0: mozilla::VideoCodecConfig cinst1(120, "VP8", 0); michael@0: mozilla::VideoCodecConfig cinst2(120, "VP8", 0); michael@0: rcvCodecList.push_back(&cinst1); michael@0: rcvCodecList.push_back(&cinst2); michael@0: err = mVideoSession->ConfigureRecvMediaCodecs(rcvCodecList); michael@0: EXPECT_NE(err,mozilla::kMediaConduitNoError); michael@0: rcvCodecList.pop_back(); michael@0: rcvCodecList.pop_back(); michael@0: michael@0: michael@0: PR_Sleep(PR_SecondsToInterval(2)); michael@0: cerr << " *************************************************" << endl; michael@0: cerr << " 2. Codec With Invalid Payload Names " << endl; michael@0: cerr << " *************************************************" << endl; michael@0: cerr << " Setting payload 1 with name: I4201234tttttthhhyyyy89087987y76t567r7756765rr6u6676" << endl; michael@0: cerr << " Setting payload 2 with name of zero length" << endl; michael@0: michael@0: mozilla::VideoCodecConfig cinst3(124, "I4201234tttttthhhyyyy89087987y76t567r7756765rr6u6676", 0); michael@0: mozilla::VideoCodecConfig cinst4(124, "", 0); michael@0: michael@0: rcvCodecList.push_back(&cinst3); michael@0: rcvCodecList.push_back(&cinst4); michael@0: michael@0: err = mVideoSession->ConfigureRecvMediaCodecs(rcvCodecList); michael@0: EXPECT_TRUE(err != mozilla::kMediaConduitNoError); michael@0: rcvCodecList.pop_back(); michael@0: rcvCodecList.pop_back(); michael@0: michael@0: michael@0: PR_Sleep(PR_SecondsToInterval(2)); michael@0: cerr << " *************************************************" << endl; michael@0: cerr << " 3. Null Codec Parameter " << endl; michael@0: cerr << " *************************************************" << endl; michael@0: michael@0: rcvCodecList.push_back(0); michael@0: michael@0: err = mVideoSession->ConfigureRecvMediaCodecs(rcvCodecList); michael@0: EXPECT_TRUE(err != mozilla::kMediaConduitNoError); michael@0: rcvCodecList.pop_back(); michael@0: michael@0: cerr << " *************************************************" << endl; michael@0: cerr << " Test Send Codec Configuration API Now " << endl; michael@0: cerr << " *************************************************" << endl; michael@0: michael@0: cerr << " *************************************************" << endl; michael@0: cerr << " 1. Same Codec (VP8) Repeated Twice " << endl; michael@0: cerr << " *************************************************" << endl; michael@0: michael@0: michael@0: err = mVideoSession->ConfigureSendMediaCodec(&cinst1); michael@0: EXPECT_EQ(mozilla::kMediaConduitNoError, err); michael@0: err = mVideoSession->ConfigureSendMediaCodec(&cinst1); michael@0: EXPECT_EQ(mozilla::kMediaConduitCodecInUse, err); michael@0: michael@0: michael@0: cerr << " *************************************************" << endl; michael@0: cerr << " 2. Codec With Invalid Payload Names " << endl; michael@0: cerr << " *************************************************" << endl; michael@0: cerr << " Setting payload with name: I4201234tttttthhhyyyy89087987y76t567r7756765rr6u6676" << endl; michael@0: michael@0: err = mVideoSession->ConfigureSendMediaCodec(&cinst3); michael@0: EXPECT_TRUE(err != mozilla::kMediaConduitNoError); michael@0: michael@0: cerr << " *************************************************" << endl; michael@0: cerr << " 3. Null Codec Parameter " << endl; michael@0: cerr << " *************************************************" << endl; michael@0: michael@0: err = mVideoSession->ConfigureSendMediaCodec(nullptr); michael@0: EXPECT_TRUE(err != mozilla::kMediaConduitNoError); michael@0: michael@0: } michael@0: michael@0: void DumpMaxFs(int orig_width, int orig_height, int max_fs, michael@0: int new_width, int new_height) michael@0: { michael@0: cerr << "Applying max_fs=" << max_fs << " to input resolution " << michael@0: orig_width << "x" << orig_height << endl; michael@0: cerr << "New resolution: " << new_width << "x" << new_height << endl; michael@0: cerr << endl; michael@0: } michael@0: michael@0: // Calculate new resolution for sending video by applying max-fs constraint. michael@0: void GetVideoResolutionWithMaxFs(int orig_width, int orig_height, int max_fs, michael@0: int *new_width, int *new_height) michael@0: { michael@0: int err = 0; michael@0: michael@0: // Get pointer to VideoSessionConduit. michael@0: mVideoSession = mozilla::VideoSessionConduit::Create(nullptr); michael@0: if( !mVideoSession ) michael@0: ASSERT_NE(mVideoSession, (void*)nullptr); michael@0: michael@0: // Configure send codecs on the conduit. michael@0: mozilla::VideoCodecConfig cinst1(120, "VP8", 0, max_fs, 0); michael@0: michael@0: err = mVideoSession->ConfigureSendMediaCodec(&cinst1); michael@0: ASSERT_EQ(mozilla::kMediaConduitNoError, err); michael@0: michael@0: // Send one frame. michael@0: MOZ_ASSERT(!(orig_width & 1)); michael@0: MOZ_ASSERT(!(orig_height & 1)); michael@0: int len = ((orig_width * orig_height) * 3 / 2); michael@0: uint8_t* frame = (uint8_t*) PR_MALLOC(len); michael@0: michael@0: memset(frame, COLOR, len); michael@0: mVideoSession->SendVideoFrame((unsigned char*)frame, michael@0: len, michael@0: orig_width, michael@0: orig_height, michael@0: mozilla::kVideoI420, michael@0: 0); michael@0: PR_Free(frame); michael@0: michael@0: // Get the new resolution as adjusted by the max-fs constraint. michael@0: *new_width = mVideoSession->SendingWidth(); michael@0: *new_height = mVideoSession->SendingHeight(); michael@0: } michael@0: michael@0: void TestVideoConduitMaxFs() michael@0: { michael@0: int orig_width, orig_height, width, height, max_fs; michael@0: michael@0: // No limitation. michael@0: cerr << "Test no max-fs limition" << endl; michael@0: orig_width = 640; michael@0: orig_height = 480; michael@0: max_fs = 0; michael@0: GetVideoResolutionWithMaxFs(orig_width, orig_height, max_fs, &width, &height); michael@0: DumpMaxFs(orig_width, orig_height, max_fs, width, height); michael@0: ASSERT_EQ(width, 640); michael@0: ASSERT_EQ(height, 480); michael@0: michael@0: // VGA to QVGA. michael@0: cerr << "Test resizing from VGA to QVGA" << endl; michael@0: orig_width = 640; michael@0: orig_height = 480; michael@0: max_fs = 300; michael@0: GetVideoResolutionWithMaxFs(orig_width, orig_height, max_fs, &width, &height); michael@0: DumpMaxFs(orig_width, orig_height, max_fs, width, height); michael@0: ASSERT_EQ(width, 320); michael@0: ASSERT_EQ(height, 240); michael@0: michael@0: // Extreme input resolution. michael@0: cerr << "Test extreme input resolution" << endl; michael@0: orig_width = 3072; michael@0: orig_height = 100; michael@0: max_fs = 300; michael@0: GetVideoResolutionWithMaxFs(orig_width, orig_height, max_fs, &width, &height); michael@0: DumpMaxFs(orig_width, orig_height, max_fs, width, height); michael@0: ASSERT_EQ(width, 768); michael@0: ASSERT_EQ(height, 26); michael@0: michael@0: // Small max-fs. michael@0: cerr << "Test small max-fs (case 1)" << endl; michael@0: orig_width = 8; michael@0: orig_height = 32; michael@0: max_fs = 1; michael@0: GetVideoResolutionWithMaxFs(orig_width, orig_height, max_fs, &width, &height); michael@0: DumpMaxFs(orig_width, orig_height, max_fs, width, height); michael@0: ASSERT_EQ(width, 4); michael@0: ASSERT_EQ(height, 16); michael@0: michael@0: // Small max-fs. michael@0: cerr << "Test small max-fs (case 2)" << endl; michael@0: orig_width = 4; michael@0: orig_height = 50; michael@0: max_fs = 1; michael@0: GetVideoResolutionWithMaxFs(orig_width, orig_height, max_fs, &width, &height); michael@0: DumpMaxFs(orig_width, orig_height, max_fs, width, height); michael@0: ASSERT_EQ(width, 2); michael@0: ASSERT_EQ(height, 16); michael@0: michael@0: // Small max-fs. michael@0: cerr << "Test small max-fs (case 3)" << endl; michael@0: orig_width = 872; michael@0: orig_height = 136; michael@0: max_fs = 3; michael@0: GetVideoResolutionWithMaxFs(orig_width, orig_height, max_fs, &width, &height); michael@0: DumpMaxFs(orig_width, orig_height, max_fs, width, height); michael@0: ASSERT_EQ(width, 48); michael@0: ASSERT_EQ(height, 8); michael@0: michael@0: // Small max-fs. michael@0: cerr << "Test small max-fs (case 4)" << endl; michael@0: orig_width = 160; michael@0: orig_height = 8; michael@0: max_fs = 5; michael@0: GetVideoResolutionWithMaxFs(orig_width, orig_height, max_fs, &width, &height); michael@0: DumpMaxFs(orig_width, orig_height, max_fs, width, height); michael@0: ASSERT_EQ(width, 80); michael@0: ASSERT_EQ(height, 4); michael@0: michael@0: // Extremely small width and height(see bug 919979). michael@0: cerr << "Test with extremely small width and height" << endl; michael@0: orig_width = 2; michael@0: orig_height = 2; michael@0: max_fs = 5; michael@0: GetVideoResolutionWithMaxFs(orig_width, orig_height, max_fs, &width, &height); michael@0: DumpMaxFs(orig_width, orig_height, max_fs, width, height); michael@0: ASSERT_EQ(width, 2); michael@0: ASSERT_EQ(height, 2); michael@0: michael@0: // Random values. michael@0: cerr << "Test with random values" << endl; michael@0: for (int i = 0; i < 30; i++) { michael@0: cerr << "."; michael@0: max_fs = rand() % 1000; michael@0: orig_width = ((rand() % 2000) & ~1) + 2; michael@0: orig_height = ((rand() % 2000) & ~1) + 2; michael@0: michael@0: GetVideoResolutionWithMaxFs(orig_width, orig_height, max_fs, michael@0: &width, &height); michael@0: if (max_fs > 0 && michael@0: ceil(width / 16.) * ceil(height / 16.) > max_fs) { michael@0: DumpMaxFs(orig_width, orig_height, max_fs, width, height); michael@0: ADD_FAILURE(); michael@0: } michael@0: if ((width & 1) || (height & 1)) { michael@0: DumpMaxFs(orig_width, orig_height, max_fs, width, height); michael@0: ADD_FAILURE(); michael@0: } michael@0: } michael@0: cerr << endl; michael@0: } michael@0: michael@0: private: michael@0: //Audio Conduit Test Objects michael@0: mozilla::RefPtr mAudioSession; michael@0: mozilla::RefPtr mAudioSession2; michael@0: mozilla::RefPtr mAudioTransport; michael@0: AudioSendAndReceive audioTester; michael@0: michael@0: //Video Conduit Test Objects michael@0: mozilla::RefPtr mVideoSession; michael@0: mozilla::RefPtr mVideoSession2; michael@0: mozilla::RefPtr mVideoRenderer; michael@0: mozilla::RefPtr mVideoTransport; michael@0: VideoSendAndReceive videoTester; michael@0: michael@0: std::string fileToPlay; michael@0: std::string fileToRecord; michael@0: std::string iAudiofilename; michael@0: std::string oAudiofilename; michael@0: }; michael@0: michael@0: michael@0: // Test 1: Test Dummy External Xport michael@0: TEST_F(TransportConduitTest, TestDummyAudioWithTransport) { michael@0: TestDummyAudioAndTransport(); michael@0: } michael@0: michael@0: // Test 2: Test Dummy External Xport michael@0: TEST_F(TransportConduitTest, TestDummyVideoWithTransport) { michael@0: TestDummyVideoAndTransport(); michael@0: } michael@0: michael@0: TEST_F(TransportConduitTest, TestVideoConduitCodecAPI) { michael@0: TestVideoConduitCodecAPI(); michael@0: } michael@0: michael@0: TEST_F(TransportConduitTest, TestVideoConduitMaxFs) { michael@0: TestVideoConduitMaxFs(); michael@0: } michael@0: michael@0: } // end namespace michael@0: michael@0: int main(int argc, char **argv) michael@0: { michael@0: // This test can cause intermittent oranges on the builders michael@0: CHECK_ENVIRONMENT_FLAG("MOZ_WEBRTC_MEDIACONDUIT_TESTS") michael@0: michael@0: test_utils = new MtransportTestUtils(); michael@0: ::testing::InitGoogleTest(&argc, argv); michael@0: int rv = RUN_ALL_TESTS(); michael@0: delete test_utils; michael@0: return rv; michael@0: } michael@0: michael@0: