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