1 /* This Source Code Form is subject to the terms of the Mozilla Public
2  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
3  * You can obtain one at http://mozilla.org/MPL/2.0/. */
4 
5 #include <iostream>
6 #include <string>
7 #include <fstream>
8 #include <unistd.h>
9 #include <vector>
10 #include <math.h>
11 
12 using namespace std;
13 
14 #include "mozilla/SyncRunnable.h"
15 #include "mozilla/UniquePtr.h"
16 #include <MediaConduitInterface.h>
17 #include "GmpVideoCodec.h"
18 #include "nsIEventTarget.h"
19 #include "FakeMediaStreamsImpl.h"
20 #include "FakeLogging.h"
21 #include "nsThreadUtils.h"
22 #include "runnable_utils.h"
23 #include "signaling/src/common/EncodingConstraints.h"
24 
25 #include "FakeIPC.h"
26 #include "FakeIPC.cpp"
27 
28 #define GTEST_HAS_RTTI 0
29 #include "gtest/gtest.h"
30 #include "gtest_utils.h"
31 
32 nsCOMPtr<nsIThread> gMainThread;
33 nsCOMPtr<nsIThread> gGtestThread;
34 bool gTestsComplete = false;
35 
36 #include "mtransport_test_utils.h"
37 MtransportTestUtils *test_utils;
38 
39 //Video Frame Color
40 const int COLOR = 0x80; //Gray
41 
42 //MWC RNG of George Marsaglia
43 //taken from xiph.org
44 static int32_t Rz, Rw;
fast_rand(void)45 static inline int32_t fast_rand(void)
46 {
47   Rz=36969*(Rz&65535)+(Rz>>16);
48   Rw=18000*(Rw&65535)+(Rw>>16);
49   return (Rz<<16)+Rw;
50 }
51 
52 /**
53   * Global structure to store video test results.
54   */
55 struct VideoTestStats
56 {
57  int numRawFramesInserted;
58  int numFramesRenderedSuccessfully;
59  int numFramesRenderedWrongly;
60 };
61 
62 VideoTestStats vidStatsGlobal={0,0,0};
63 
64 /**
65  * A Dummy Video Conduit Tester.
66  * The test-case inserts a 640*480 grey imagerevery 33 milliseconds
67  * to the video-conduit for encoding and transporting.
68  */
69 
70 class VideoSendAndReceive
71 {
72 public:
VideoSendAndReceive()73   VideoSendAndReceive():width(640),
74                         height(480),
75 			rate(30)
76   {
77   }
78 
~VideoSendAndReceive()79   ~VideoSendAndReceive()
80   {
81   }
82 
SetDimensions(int w,int h)83   void SetDimensions(int w, int h)
84   {
85     width = w;
86     height = h;
87   }
SetRate(int r)88   void SetRate(int r) {
89     rate = r;
90   }
Init(RefPtr<mozilla::VideoSessionConduit> aSession)91   void Init(RefPtr<mozilla::VideoSessionConduit> aSession)
92   {
93         mSession = aSession;
94         mLen = ((width * height) * 3 / 2);
95         mFrame = mozilla::MakeUnique<uint8_t[]>(mLen);
96         memset(mFrame.get(), COLOR, mLen);
97         numFrames = 121;
98   }
99 
GenerateAndReadSamples()100   void GenerateAndReadSamples()
101   {
102     do
103     {
104       mSession->SendVideoFrame(reinterpret_cast<unsigned char*>(mFrame.get()),
105                                 mLen,
106                                 width,
107                                 height,
108                                 mozilla::kVideoI420,
109                                 0);
110       PR_Sleep(PR_MillisecondsToInterval(1000/rate));
111       vidStatsGlobal.numRawFramesInserted++;
112       numFrames--;
113     } while(numFrames >= 0);
114   }
115 
116 private:
117 RefPtr<mozilla::VideoSessionConduit> mSession;
118 mozilla::UniquePtr<uint8_t[]> mFrame;
119 int mLen;
120 int width, height;
121 int rate;
122 int numFrames;
123 };
124 
125 
126 
127 /**
128  * A Dummy AudioConduit Tester
129  * The test reads PCM samples of a standard test file and
130  * passws to audio-conduit for encoding, RTPfication and
131  * decoding ebery 10 milliseconds.
132  * This decoded samples are read-off the conduit for writing
133  * into output audio file in PCM format.
134  */
135 class AudioSendAndReceive
136 {
137 public:
138   static const unsigned int PLAYOUT_SAMPLE_FREQUENCY; //default is 16000
139   static const unsigned int PLAYOUT_SAMPLE_LENGTH; //default is 160000
140 
AudioSendAndReceive()141   AudioSendAndReceive()
142   {
143   }
144 
~AudioSendAndReceive()145   ~AudioSendAndReceive()
146   {
147   }
148 
Init(RefPtr<mozilla::AudioSessionConduit> aSession,RefPtr<mozilla::AudioSessionConduit> aOtherSession,std::string fileIn,std::string fileOut)149  void Init(RefPtr<mozilla::AudioSessionConduit> aSession,
150            RefPtr<mozilla::AudioSessionConduit> aOtherSession,
151            std::string fileIn, std::string fileOut)
152   {
153 
154     mSession = aSession;
155     mOtherSession = aOtherSession;
156     iFile = fileIn;
157     oFile = fileOut;
158  }
159 
160   //Kick start the test
161   void GenerateAndReadSamples();
162 
163 private:
164 
165   RefPtr<mozilla::AudioSessionConduit> mSession;
166   RefPtr<mozilla::AudioSessionConduit> mOtherSession;
167   std::string iFile;
168   std::string oFile;
169 
170   int WriteWaveHeader(int rate, int channels, FILE* outFile);
171   int FinishWaveHeader(FILE* outFile);
172   void GenerateMusic(int16_t* buf, int len);
173 };
174 
175 const unsigned int AudioSendAndReceive::PLAYOUT_SAMPLE_FREQUENCY = 16000;
176 const unsigned int AudioSendAndReceive::PLAYOUT_SAMPLE_LENGTH  = 160000;
177 
WriteWaveHeader(int rate,int channels,FILE * outFile)178 int AudioSendAndReceive::WriteWaveHeader(int rate, int channels, FILE* outFile)
179 {
180   //Hardcoded for 16 bit samples
181   unsigned char header[] = {
182     // File header
183     0x52, 0x49, 0x46, 0x46, // 'RIFF'
184     0x00, 0x00, 0x00, 0x00, // chunk size
185     0x57, 0x41, 0x56, 0x45, // 'WAVE'
186     // fmt chunk. We always write 16-bit samples.
187     0x66, 0x6d, 0x74, 0x20, // 'fmt '
188     0x10, 0x00, 0x00, 0x00, // chunk size
189     0x01, 0x00,             // WAVE_FORMAT_PCM
190     0xFF, 0xFF,             // channels
191     0xFF, 0xFF, 0xFF, 0xFF, // sample rate
192     0x00, 0x00, 0x00, 0x00, // data rate
193     0xFF, 0xFF,             // frame size in bytes
194     0x10, 0x00,             // bits per sample
195     // data chunk
196     0x64, 0x61, 0x74, 0x61, // 'data'
197     0xFE, 0xFF, 0xFF, 0x7F  // chunk size
198   };
199 
200 #define set_uint16le(buffer, value) \
201   (buffer)[0] = (value) & 0xff; \
202   (buffer)[1] = (value) >> 8;
203 #define set_uint32le(buffer, value) \
204   set_uint16le( (buffer), (value) & 0xffff ); \
205   set_uint16le( (buffer) + 2, (value) >> 16 );
206 
207   // set dynamic header fields
208   set_uint16le(header + 22, channels);
209   set_uint32le(header + 24, rate);
210   set_uint16le(header + 32, channels*2);
211 
212   size_t written = fwrite(header, 1, sizeof(header), outFile);
213   if (written != sizeof(header)) {
214     cerr << "Writing WAV header failed" << endl;
215     return -1;
216   }
217 
218   return 0;
219 }
220 
221 // Update the WAVE file header with the written length
FinishWaveHeader(FILE * outFile)222 int AudioSendAndReceive::FinishWaveHeader(FILE* outFile)
223 {
224   // Measure how much data we've written
225   long end = ftell(outFile);
226   if (end < 16) {
227     cerr << "Couldn't get output file length" << endl;
228     return (end < 0) ? end : -1;
229   }
230 
231   // Update the header
232   unsigned char size[4];
233   int err = fseek(outFile, 40, SEEK_SET);
234   if (err < 0) {
235     cerr << "Couldn't seek to WAV file header." << endl;
236     return err;
237   }
238   set_uint32le(size, (end - 44) & 0xffffffff);
239   size_t written = fwrite(size, 1, sizeof(size), outFile);
240   if (written != sizeof(size)) {
241     cerr << "Couldn't write data size to WAV header" << endl;
242     return -1;
243   }
244 
245   // Return to the end
246   err = fseek(outFile, 0, SEEK_END);
247   if (err < 0) {
248     cerr << "Couldn't seek to WAV file end." << endl;
249     return err;
250   }
251 
252   return 0;
253 }
254 
255 //Code from xiph.org to generate music of predefined length
GenerateMusic(short * buf,int len)256 void AudioSendAndReceive::GenerateMusic(short* buf, int len)
257 {
258   cerr <<" Generating Input Music " << endl;
259   int32_t a1,a2,b1,b2;
260   int32_t c1,c2,d1,d2;
261   int32_t i,j;
262   a1=b1=a2=b2=0;
263   c1=c2=d1=d2=0;
264   j=0;
265   /*60ms silence */
266   for(i=0;i<2880;i++)
267   {
268     buf[i*2]=buf[(i*2)+1]=0;
269   }
270   for(i=2880;i<len-1;i+=2)
271   {
272     int32_t r;
273     int32_t v1,v2;
274     v1=v2=(((j*((j>>12)^((j>>10|j>>12)&26&j>>7)))&128)+128)<<15;
275     r=fast_rand();v1+=r&65535;v1-=r>>16;
276     r=fast_rand();v2+=r&65535;v2-=r>>16;
277     b1=v1-a1+((b1*61+32)>>6);a1=v1;
278     b2=v2-a2+((b2*61+32)>>6);a2=v2;
279     c1=(30*(c1+b1+d1)+32)>>6;d1=b1;
280     c2=(30*(c2+b2+d2)+32)>>6;d2=b2;
281     v1=(c1+128)>>8;
282     v2=(c2+128)>>8;
283     buf[i]=v1>32767?32767:(v1<-32768?-32768:v1);
284     buf[i+1]=v2>32767?32767:(v2<-32768?-32768:v2);
285     if(i%6==0)j++;
286   }
287   cerr << "Generating Input Music Done " << endl;
288 }
289 
290 //Hardcoded for 16 bit samples for now
GenerateAndReadSamples()291 void AudioSendAndReceive::GenerateAndReadSamples()
292 {
293    auto audioInput = mozilla::MakeUnique<int16_t []>(PLAYOUT_SAMPLE_LENGTH);
294    auto audioOutput = mozilla::MakeUnique<int16_t []>(PLAYOUT_SAMPLE_LENGTH);
295    short* inbuf;
296    int sampleLengthDecoded = 0;
297    unsigned int SAMPLES = (PLAYOUT_SAMPLE_FREQUENCY * 10); //10 seconds
298    int CHANNELS = 1; //mono audio
299    int sampleLengthInBytes = sizeof(int16_t) * PLAYOUT_SAMPLE_LENGTH;
300    //generated audio buffer
301    inbuf = (short *)moz_xmalloc(sizeof(short)*SAMPLES*CHANNELS);
302    memset(audioInput.get(),0,sampleLengthInBytes);
303    memset(audioOutput.get(),0,sampleLengthInBytes);
304    MOZ_ASSERT(SAMPLES <= PLAYOUT_SAMPLE_LENGTH);
305 
306    FILE* inFile = fopen( iFile.c_str(), "wb+");
307    if(!inFile) {
308      cerr << "Input File Creation Failed " << endl;
309      free(inbuf);
310      return;
311    }
312 
313    FILE* outFile = fopen( oFile.c_str(), "wb+");
314    if(!outFile) {
315      cerr << "Output File Creation Failed " << endl;
316      free(inbuf);
317      fclose(inFile);
318      return;
319    }
320 
321    //Create input file with the music
322    WriteWaveHeader(PLAYOUT_SAMPLE_FREQUENCY, 1, inFile);
323    GenerateMusic(inbuf, SAMPLES);
324    fwrite(inbuf,1,SAMPLES*sizeof(inbuf[0])*CHANNELS,inFile);
325    FinishWaveHeader(inFile);
326    fclose(inFile);
327 
328    WriteWaveHeader(PLAYOUT_SAMPLE_FREQUENCY, 1, outFile);
329    unsigned int numSamplesReadFromInput = 0;
330    do
331    {
332     if(!memcpy(audioInput.get(), inbuf, sampleLengthInBytes))
333     {
334       free(inbuf);
335       fclose(outFile);
336       return;
337     }
338 
339     numSamplesReadFromInput += PLAYOUT_SAMPLE_LENGTH;
340     inbuf += PLAYOUT_SAMPLE_LENGTH;
341 
342     mSession->SendAudioFrame(audioInput.get(),
343                              PLAYOUT_SAMPLE_LENGTH,
344                              PLAYOUT_SAMPLE_FREQUENCY,10);
345 
346     PR_Sleep(PR_MillisecondsToInterval(10));
347     mOtherSession->GetAudioFrame(audioOutput.get(), PLAYOUT_SAMPLE_FREQUENCY,
348                                  10, sampleLengthDecoded);
349     if(sampleLengthDecoded == 0)
350     {
351       cerr << " Zero length Sample " << endl;
352     }
353 
354     int wrote_  = fwrite (audioOutput.get(), 1 , sampleLengthInBytes, outFile);
355     if(wrote_ != sampleLengthInBytes)
356     {
357       cerr << "Couldn't Write " << sampleLengthInBytes << "bytes" << endl;
358       break;
359     }
360    }while(numSamplesReadFromInput < SAMPLES);
361 
362    FinishWaveHeader(outFile);
363    free(inbuf);
364    fclose(outFile);
365 }
366 
367 /**
368  * Dummy Video Target for the conduit
369  * This class acts as renderer attached to the video conuit
370  * As of today we just verify if the frames rendered are exactly
371  * the same as frame inserted at the first place
372  */
373 class DummyVideoTarget: public mozilla::VideoRenderer
374 {
375 public:
DummyVideoTarget()376   DummyVideoTarget()
377   {
378   }
379 
~DummyVideoTarget()380   virtual ~DummyVideoTarget()
381   {
382   }
383 
384 
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)385   void RenderVideoFrame(const unsigned char* buffer,
386                         size_t buffer_size,
387                         uint32_t y_stride,
388                         uint32_t cbcr_stride,
389                         uint32_t time_stamp,
390                         int64_t render_time,
391                         const mozilla::ImageHandle& handle) override
392   {
393     RenderVideoFrame(buffer, buffer_size, time_stamp, render_time, handle);
394   }
395 
RenderVideoFrame(const unsigned char * buffer,size_t buffer_size,uint32_t time_stamp,int64_t render_time,const mozilla::ImageHandle & handle)396   void RenderVideoFrame(const unsigned char* buffer,
397                         size_t buffer_size,
398                         uint32_t time_stamp,
399                         int64_t render_time,
400                         const mozilla::ImageHandle& handle) override
401  {
402   //write the frame to the file
403   if(VerifyFrame(buffer, buffer_size) == 0)
404   {
405       vidStatsGlobal.numFramesRenderedSuccessfully++;
406   } else
407   {
408       vidStatsGlobal.numFramesRenderedWrongly++;
409   }
410  }
411 
FrameSizeChange(unsigned int,unsigned int,unsigned int)412  void FrameSizeChange(unsigned int, unsigned int, unsigned int) override
413  {
414     //do nothing
415  }
416 
417  //This is hardcoded to check if the contents of frame is COLOR
418  // as we set while sending.
VerifyFrame(const unsigned char * buffer,unsigned int buffer_size)419  int VerifyFrame(const unsigned char* buffer, unsigned int buffer_size)
420  {
421     int good = 0;
422     for(int i=0; i < (int) buffer_size; i++)
423     {
424       if(buffer[i] == COLOR)
425       {
426         ++good;
427       }
428       else
429       {
430         --good;
431       }
432     }
433    return 0;
434  }
435 
436 };
437 
438 /**
439  *  Webrtc Audio and Video External Transport Class
440  *  The functions in this class will be invoked by the conduit
441  *  when it has RTP/RTCP frame to transmit.
442  *  For everty RTP/RTCP frame we receive, we pass it back
443  *  to the conduit for eventual decoding and rendering.
444  */
445 class WebrtcMediaTransport : public mozilla::TransportInterface
446 {
447 public:
WebrtcMediaTransport()448   WebrtcMediaTransport():numPkts(0),
449                        mAudio(false),
450                        mVideo(false)
451   {
452   }
453 
~WebrtcMediaTransport()454   ~WebrtcMediaTransport()
455   {
456   }
457 
SendRtpPacket(const void * data,int len)458   virtual nsresult SendRtpPacket(const void* data, int len)
459   {
460     ++numPkts;
461     if(mAudio)
462     {
463       mOtherAudioSession->ReceivedRTPPacket(data,len);
464     } else
465     {
466       mOtherVideoSession->ReceivedRTPPacket(data,len);
467     }
468     return NS_OK;
469   }
470 
SendRtcpPacket(const void * data,int len)471   virtual nsresult SendRtcpPacket(const void* data, int len)
472   {
473     if(mAudio)
474     {
475       mOtherAudioSession->ReceivedRTCPPacket(data,len);
476     } else
477     {
478       mOtherVideoSession->ReceivedRTCPPacket(data,len);
479     }
480     return NS_OK;
481   }
482 
483   //Treat this object as Audio Transport
SetAudioSession(RefPtr<mozilla::AudioSessionConduit> aSession,RefPtr<mozilla::AudioSessionConduit> aOtherSession)484   void SetAudioSession(RefPtr<mozilla::AudioSessionConduit> aSession,
485                         RefPtr<mozilla::AudioSessionConduit>
486                         aOtherSession)
487   {
488     mAudioSession = aSession;
489     mOtherAudioSession = aOtherSession;
490     mAudio = true;
491   }
492 
493   // Treat this object as Video Transport
SetVideoSession(RefPtr<mozilla::VideoSessionConduit> aSession,RefPtr<mozilla::VideoSessionConduit> aOtherSession)494   void SetVideoSession(RefPtr<mozilla::VideoSessionConduit> aSession,
495                        RefPtr<mozilla::VideoSessionConduit>
496                        aOtherSession)
497   {
498     mVideoSession = aSession;
499     mOtherVideoSession = aOtherSession;
500     mVideo = true;
501   }
502 
503 private:
504   RefPtr<mozilla::AudioSessionConduit> mAudioSession;
505   RefPtr<mozilla::VideoSessionConduit> mVideoSession;
506   RefPtr<mozilla::VideoSessionConduit> mOtherVideoSession;
507   RefPtr<mozilla::AudioSessionConduit> mOtherAudioSession;
508   int numPkts;
509   bool mAudio, mVideo;
510 };
511 
512 
513 namespace {
514 
515 class TransportConduitTest : public ::testing::Test
516 {
517  public:
518 
TransportConduitTest()519   TransportConduitTest()
520   {
521     //input and output file names
522     iAudiofilename = "input.wav";
523     oAudiofilename = "recorded.wav";
524   }
525 
~TransportConduitTest()526   ~TransportConduitTest()
527   {
528     mozilla::SyncRunnable::DispatchToThread(gMainThread,
529                                             mozilla::WrapRunnable(
530                                                 this,
531                                                 &TransportConduitTest::SelfDestruct));
532   }
533 
SelfDestruct()534   void SelfDestruct() {
535     mAudioSession = nullptr;
536     mAudioSession2 = nullptr;
537     mAudioTransport = nullptr;
538 
539     mVideoSession = nullptr;
540     mVideoSession2 = nullptr;
541     mVideoRenderer = nullptr;
542     mVideoTransport = nullptr;
543   }
544 
545   //1. Dump audio samples to dummy external transport
TestDummyAudioAndTransport()546   void TestDummyAudioAndTransport()
547   {
548     //get pointer to AudioSessionConduit
549     int err=0;
550     mozilla::SyncRunnable::DispatchToThread(gMainThread,
551                                             WrapRunnableNMRet(&mAudioSession,
552                                                 &mozilla::AudioSessionConduit::Create));
553     if( !mAudioSession )
554       ASSERT_NE(mAudioSession, (void*)nullptr);
555 
556     mozilla::SyncRunnable::DispatchToThread(gMainThread,
557                                             WrapRunnableNMRet(&mAudioSession2,
558                                                 &mozilla::AudioSessionConduit::Create));
559     if( !mAudioSession2 )
560       ASSERT_NE(mAudioSession2, (void*)nullptr);
561 
562     WebrtcMediaTransport* xport = new WebrtcMediaTransport();
563     ASSERT_NE(xport, (void*)nullptr);
564     xport->SetAudioSession(mAudioSession, mAudioSession2);
565     mAudioTransport = xport;
566 
567     // attach the transport to audio-conduit
568     err = mAudioSession->SetTransmitterTransport(mAudioTransport);
569     ASSERT_EQ(mozilla::kMediaConduitNoError, err);
570     err = mAudioSession2->SetReceiverTransport(mAudioTransport);
571     ASSERT_EQ(mozilla::kMediaConduitNoError, err);
572 
573     //configure send and recv codecs on the audio-conduit
574     //mozilla::AudioCodecConfig cinst1(124, "PCMU", 8000, 80, 1, 64000, false);
575     mozilla::AudioCodecConfig cinst1(124, "opus", 48000, 960, 1, 64000, false);
576     mozilla::AudioCodecConfig cinst2(125, "L16", 16000, 320, 1, 256000, false);
577 
578     std::vector<mozilla::AudioCodecConfig*> rcvCodecList;
579     rcvCodecList.push_back(&cinst1);
580     rcvCodecList.push_back(&cinst2);
581 
582     err = mAudioSession->ConfigureSendMediaCodec(&cinst1);
583     ASSERT_EQ(mozilla::kMediaConduitNoError, err);
584     err = mAudioSession->StartTransmitting();
585     ASSERT_EQ(mozilla::kMediaConduitNoError, err);
586     err = mAudioSession->ConfigureRecvMediaCodecs(rcvCodecList);
587     ASSERT_EQ(mozilla::kMediaConduitNoError, err);
588 
589     err = mAudioSession2->ConfigureSendMediaCodec(&cinst1);
590     ASSERT_EQ(mozilla::kMediaConduitNoError, err);
591     err = mAudioSession2->StartTransmitting();
592     ASSERT_EQ(mozilla::kMediaConduitNoError, err);
593     err = mAudioSession2->ConfigureRecvMediaCodecs(rcvCodecList);
594     ASSERT_EQ(mozilla::kMediaConduitNoError, err);
595 
596     //start generating samples
597     audioTester.Init(mAudioSession,mAudioSession2, iAudiofilename,oAudiofilename);
598     cerr << "   ******************************************************** " << endl;
599     cerr << "    Generating Audio Samples " << endl;
600     cerr << "   ******************************************************** " << endl;
601     PR_Sleep(PR_SecondsToInterval(2));
602     audioTester.GenerateAndReadSamples();
603     PR_Sleep(PR_SecondsToInterval(2));
604     cerr << "   ******************************************************** " << endl;
605     cerr << "    Input Audio  File                " << iAudiofilename << endl;
606     cerr << "    Output Audio File                " << oAudiofilename << endl;
607     cerr << "   ******************************************************** " << endl;
608   }
609 
610   //2. Dump audio samples to dummy external transport
TestDummyVideoAndTransport(bool send_vp8=true,const char * source_file=nullptr)611   void TestDummyVideoAndTransport(bool send_vp8 = true, const char *source_file = nullptr)
612   {
613     int err = 0;
614     //get pointer to VideoSessionConduit
615     mozilla::SyncRunnable::DispatchToThread(gMainThread,
616                                             WrapRunnableNMRet(&mVideoSession,
617                                                 &mozilla::VideoSessionConduit::Create));
618     if( !mVideoSession )
619       ASSERT_NE(mVideoSession, (void*)nullptr);
620 
621    // This session is for other one
622     mozilla::SyncRunnable::DispatchToThread(gMainThread,
623                                             WrapRunnableNMRet(&mVideoSession2,
624                                                 &mozilla::VideoSessionConduit::Create));
625     if( !mVideoSession2 )
626       ASSERT_NE(mVideoSession2,(void*)nullptr);
627 
628     if (!send_vp8) {
629       SetGmpCodecs();
630     }
631 
632     mVideoRenderer = new DummyVideoTarget();
633     ASSERT_NE(mVideoRenderer, (void*)nullptr);
634 
635     WebrtcMediaTransport* xport = new WebrtcMediaTransport();
636     ASSERT_NE(xport, (void*)nullptr);
637     xport->SetVideoSession(mVideoSession,mVideoSession2);
638     mVideoTransport = xport;
639 
640     // attach the transport and renderer to video-conduit
641     err = mVideoSession2->AttachRenderer(mVideoRenderer);
642     ASSERT_EQ(mozilla::kMediaConduitNoError, err);
643     err = mVideoSession->SetTransmitterTransport(mVideoTransport);
644     ASSERT_EQ(mozilla::kMediaConduitNoError, err);
645     err = mVideoSession2->SetReceiverTransport(mVideoTransport);
646     ASSERT_EQ(mozilla::kMediaConduitNoError, err);
647 
648     mozilla::EncodingConstraints constraints;
649     //configure send and recv codecs on theconduit
650     mozilla::VideoCodecConfig cinst1(120, "VP8", constraints);
651     mozilla::VideoCodecConfig cinst2(124, "I420", constraints);
652 
653 
654     std::vector<mozilla::VideoCodecConfig* > rcvCodecList;
655     rcvCodecList.push_back(&cinst1);
656     rcvCodecList.push_back(&cinst2);
657 
658     err = mVideoSession->ConfigureSendMediaCodec(
659         send_vp8 ? &cinst1 : &cinst2);
660 
661     ASSERT_EQ(mozilla::kMediaConduitNoError, err);
662     err = mVideoSession->StartTransmitting();
663     ASSERT_EQ(mozilla::kMediaConduitNoError, err);
664 
665     err = mVideoSession2->ConfigureSendMediaCodec(
666         send_vp8 ? &cinst1 : &cinst2);
667     err = mVideoSession2->StartTransmitting();
668     ASSERT_EQ(mozilla::kMediaConduitNoError, err);
669 
670     ASSERT_EQ(mozilla::kMediaConduitNoError, err);
671     err = mVideoSession2->ConfigureRecvMediaCodecs(rcvCodecList);
672     ASSERT_EQ(mozilla::kMediaConduitNoError, err);
673 
674     //start generating samples
675     cerr << "   *************************************************" << endl;
676     cerr << "    Starting the Video Sample Generation " << endl;
677     cerr << "   *************************************************" << endl;
678     PR_Sleep(PR_SecondsToInterval(2));
679     videoTester.Init(mVideoSession);
680     videoTester.GenerateAndReadSamples();
681     PR_Sleep(PR_SecondsToInterval(2));
682 
683     cerr << "   **************************************************" << endl;
684     cerr << "    Done With The Testing  " << endl;
685     cerr << "    VIDEO TEST STATS  "  << endl;
686     cerr << "    Num Raw Frames Inserted: "<<
687                                         vidStatsGlobal.numRawFramesInserted << endl;
688     cerr << "    Num Frames Successfully Rendered: "<<
689                                         vidStatsGlobal.numFramesRenderedSuccessfully << endl;
690     cerr << "    Num Frames Wrongly Rendered: "<<
691                                         vidStatsGlobal.numFramesRenderedWrongly << endl;
692 
693     cerr << "    Done With The Testing  " << endl;
694 
695     cerr << "   **************************************************" << endl;
696     ASSERT_EQ(0, vidStatsGlobal.numFramesRenderedWrongly);
697     if (send_vp8) {
698 	ASSERT_EQ(vidStatsGlobal.numRawFramesInserted,
699 		  vidStatsGlobal.numFramesRenderedSuccessfully);
700     }
701     else {
702 	// Allow some fudge because there seems to be some buffering.
703 	// TODO(ekr@rtfm.com): Fix this.
704 	ASSERT_GE(vidStatsGlobal.numRawFramesInserted,
705 		  vidStatsGlobal.numFramesRenderedSuccessfully);
706 	ASSERT_LE(vidStatsGlobal.numRawFramesInserted,
707 		  vidStatsGlobal.numFramesRenderedSuccessfully + 2);
708     }
709   }
710 
TestVideoConduitCodecAPI()711  void TestVideoConduitCodecAPI()
712   {
713     int err = 0;
714     RefPtr<mozilla::VideoSessionConduit> videoSession;
715     //get pointer to VideoSessionConduit
716     mozilla::SyncRunnable::DispatchToThread(gMainThread,
717                                             WrapRunnableNMRet(&videoSession,
718                                                 &mozilla::VideoSessionConduit::Create));
719     if( !videoSession )
720       ASSERT_NE(videoSession, (void*)nullptr);
721 
722     //Test Configure Recv Codec APIS
723     cerr << "   *************************************************" << endl;
724     cerr << "    Test Receive Codec Configuration API Now " << endl;
725     cerr << "   *************************************************" << endl;
726 
727     std::vector<mozilla::VideoCodecConfig* > rcvCodecList;
728 
729     //Same APIs
730     cerr << "   *************************************************" << endl;
731     cerr << "    1. Same Codec (VP8) Repeated Twice " << endl;
732     cerr << "   *************************************************" << endl;
733 
734     mozilla::EncodingConstraints constraints;
735     mozilla::VideoCodecConfig cinst1(120, "VP8", constraints);
736     mozilla::VideoCodecConfig cinst2(120, "VP8", constraints);
737     rcvCodecList.push_back(&cinst1);
738     rcvCodecList.push_back(&cinst2);
739     err = videoSession->ConfigureRecvMediaCodecs(rcvCodecList);
740     EXPECT_NE(err,mozilla::kMediaConduitNoError);
741     rcvCodecList.pop_back();
742     rcvCodecList.pop_back();
743 
744 
745     PR_Sleep(PR_SecondsToInterval(2));
746     cerr << "   *************************************************" << endl;
747     cerr << "    2. Codec With Invalid Payload Names " << endl;
748     cerr << "   *************************************************" << endl;
749     cerr << "   Setting payload 1 with name: I4201234tttttthhhyyyy89087987y76t567r7756765rr6u6676" << endl;
750     cerr << "   Setting payload 2 with name of zero length" << endl;
751 
752     mozilla::VideoCodecConfig cinst3(124, "I4201234tttttthhhyyyy89087987y76t567r7756765rr6u6676", constraints);
753     mozilla::VideoCodecConfig cinst4(124, "", constraints);
754 
755     rcvCodecList.push_back(&cinst3);
756     rcvCodecList.push_back(&cinst4);
757 
758     err = videoSession->ConfigureRecvMediaCodecs(rcvCodecList);
759     EXPECT_TRUE(err != mozilla::kMediaConduitNoError);
760     rcvCodecList.pop_back();
761     rcvCodecList.pop_back();
762 
763 
764     PR_Sleep(PR_SecondsToInterval(2));
765     cerr << "   *************************************************" << endl;
766     cerr << "    3. Null Codec Parameter  " << endl;
767     cerr << "   *************************************************" << endl;
768 
769     rcvCodecList.push_back(0);
770 
771     err = videoSession->ConfigureRecvMediaCodecs(rcvCodecList);
772     EXPECT_TRUE(err != mozilla::kMediaConduitNoError);
773     rcvCodecList.pop_back();
774 
775     cerr << "   *************************************************" << endl;
776     cerr << "    Test Send Codec Configuration API Now " << endl;
777     cerr << "   *************************************************" << endl;
778 
779     cerr << "   *************************************************" << endl;
780     cerr << "    1. Same Codec (VP8) Repeated Twice " << endl;
781     cerr << "   *************************************************" << endl;
782 
783 
784     err = videoSession->ConfigureSendMediaCodec(&cinst1);
785     EXPECT_EQ(mozilla::kMediaConduitNoError, err);
786     err = videoSession->StartTransmitting();
787     ASSERT_EQ(mozilla::kMediaConduitNoError, err);
788     err = videoSession->ConfigureSendMediaCodec(&cinst1);
789     EXPECT_EQ(mozilla::kMediaConduitCodecInUse, err);
790     err = videoSession->StartTransmitting();
791     ASSERT_EQ(mozilla::kMediaConduitNoError, err);
792 
793 
794     cerr << "   *************************************************" << endl;
795     cerr << "    2. Codec With Invalid Payload Names " << endl;
796     cerr << "   *************************************************" << endl;
797     cerr << "   Setting payload with name: I4201234tttttthhhyyyy89087987y76t567r7756765rr6u6676" << endl;
798 
799     err = videoSession->ConfigureSendMediaCodec(&cinst3);
800     EXPECT_TRUE(err != mozilla::kMediaConduitNoError);
801 
802     cerr << "   *************************************************" << endl;
803     cerr << "    3. Null Codec Parameter  " << endl;
804     cerr << "   *************************************************" << endl;
805 
806     err = videoSession->ConfigureSendMediaCodec(nullptr);
807     EXPECT_TRUE(err != mozilla::kMediaConduitNoError);
808 
809     mozilla::SyncRunnable::DispatchToThread(gMainThread,
810                                             WrapRunnable(
811                                                 videoSession.forget().take(),
812                                                 &mozilla::VideoSessionConduit::Release));
813   }
814 
DumpMaxFs(int orig_width,int orig_height,int max_fs,int new_width,int new_height)815   void DumpMaxFs(int orig_width, int orig_height, int max_fs,
816                  int new_width, int new_height)
817   {
818     cerr << "Applying max_fs=" << max_fs << " to input resolution " <<
819                  orig_width << "x" << orig_height << endl;
820     cerr << "New resolution: " << new_width << "x" << new_height << endl;
821     cerr << endl;
822   }
823 
824   // Calculate new resolution for sending video by applying max-fs constraint.
GetVideoResolutionWithMaxFs(int orig_width,int orig_height,int max_fs,int * new_width,int * new_height)825   void GetVideoResolutionWithMaxFs(int orig_width, int orig_height, int max_fs,
826                                    int *new_width, int *new_height)
827   {
828     int err = 0;
829 
830     // Get pointer to VideoSessionConduit.
831     mozilla::SyncRunnable::DispatchToThread(gMainThread,
832                                             WrapRunnableNMRet(&mVideoSession,
833                                                 &mozilla::VideoSessionConduit::Create));
834     if( !mVideoSession )
835       ASSERT_NE(mVideoSession, (void*)nullptr);
836 
837     mozilla::EncodingConstraints constraints;
838     constraints.maxFs = max_fs;
839     // Configure send codecs on the conduit.
840     mozilla::VideoCodecConfig cinst1(120, "VP8", constraints);
841 
842     err = mVideoSession->ConfigureSendMediaCodec(&cinst1);
843     ASSERT_EQ(mozilla::kMediaConduitNoError, err);
844     err = mVideoSession->StartTransmitting();
845     ASSERT_EQ(mozilla::kMediaConduitNoError, err);
846 
847     // Send one frame.
848     MOZ_ASSERT(!(orig_width & 1));
849     MOZ_ASSERT(!(orig_height & 1));
850     int len = ((orig_width * orig_height) * 3 / 2);
851     uint8_t* frame = (uint8_t*) PR_MALLOC(len);
852 
853     memset(frame, COLOR, len);
854     mVideoSession->SendVideoFrame((unsigned char*)frame,
855                                   len,
856                                   orig_width,
857                                   orig_height,
858                                   mozilla::kVideoI420,
859                                   0);
860     PR_Free(frame);
861 
862     // Get the new resolution as adjusted by the max-fs constraint.
863     *new_width = mVideoSession->SendingWidth();
864     *new_height = mVideoSession->SendingHeight();
865   }
866 
TestVideoConduitMaxFs()867   void TestVideoConduitMaxFs()
868   {
869     int orig_width, orig_height, width, height, max_fs;
870 
871     // No limitation.
872     cerr << "Test no max-fs limition" << endl;
873     orig_width = 640;
874     orig_height = 480;
875     max_fs = 0;
876     GetVideoResolutionWithMaxFs(orig_width, orig_height, max_fs, &width, &height);
877     DumpMaxFs(orig_width, orig_height, max_fs, width, height);
878     ASSERT_EQ(width, 640);
879     ASSERT_EQ(height, 480);
880 
881     // VGA to QVGA.
882     cerr << "Test resizing from VGA to QVGA" << endl;
883     orig_width = 640;
884     orig_height = 480;
885     max_fs = 300;
886     GetVideoResolutionWithMaxFs(orig_width, orig_height, max_fs, &width, &height);
887     DumpMaxFs(orig_width, orig_height, max_fs, width, height);
888     ASSERT_EQ(width, 320);
889     ASSERT_EQ(height, 240);
890 
891     // Extreme input resolution.
892     cerr << "Test extreme input resolution" << endl;
893     orig_width = 3072;
894     orig_height = 100;
895     max_fs = 300;
896     GetVideoResolutionWithMaxFs(orig_width, orig_height, max_fs, &width, &height);
897     DumpMaxFs(orig_width, orig_height, max_fs, width, height);
898     ASSERT_EQ(width, 768);
899     ASSERT_EQ(height, 26);
900 
901     // Small max-fs.
902     cerr << "Test small max-fs (case 1)" << endl;
903     orig_width = 8;
904     orig_height = 32;
905     max_fs = 1;
906     GetVideoResolutionWithMaxFs(orig_width, orig_height, max_fs, &width, &height);
907     DumpMaxFs(orig_width, orig_height, max_fs, width, height);
908     ASSERT_EQ(width, 4);
909     ASSERT_EQ(height, 16);
910 
911     // Small max-fs.
912     cerr << "Test small max-fs (case 2)" << endl;
913     orig_width = 4;
914     orig_height = 50;
915     max_fs = 1;
916     GetVideoResolutionWithMaxFs(orig_width, orig_height, max_fs, &width, &height);
917     DumpMaxFs(orig_width, orig_height, max_fs, width, height);
918     ASSERT_EQ(width, 2);
919     ASSERT_EQ(height, 16);
920 
921     // Small max-fs.
922     cerr << "Test small max-fs (case 3)" << endl;
923     orig_width = 872;
924     orig_height = 136;
925     max_fs = 3;
926     GetVideoResolutionWithMaxFs(orig_width, orig_height, max_fs, &width, &height);
927     DumpMaxFs(orig_width, orig_height, max_fs, width, height);
928     ASSERT_EQ(width, 48);
929     ASSERT_EQ(height, 8);
930 
931     // Small max-fs.
932     cerr << "Test small max-fs (case 4)" << endl;
933     orig_width = 160;
934     orig_height = 8;
935     max_fs = 5;
936     GetVideoResolutionWithMaxFs(orig_width, orig_height, max_fs, &width, &height);
937     DumpMaxFs(orig_width, orig_height, max_fs, width, height);
938     ASSERT_EQ(width, 80);
939     ASSERT_EQ(height, 4);
940 
941      // Extremely small width and height(see bug 919979).
942     cerr << "Test with extremely small width and height" << endl;
943     orig_width = 2;
944     orig_height = 2;
945     max_fs = 5;
946     GetVideoResolutionWithMaxFs(orig_width, orig_height, max_fs, &width, &height);
947     DumpMaxFs(orig_width, orig_height, max_fs, width, height);
948     ASSERT_EQ(width, 2);
949     ASSERT_EQ(height, 2);
950 
951     // Random values.
952     cerr << "Test with random values" << endl;
953     for (int i = 0; i < 30; i++) {
954       cerr << ".";
955       max_fs = rand() % 1000;
956       orig_width = ((rand() % 2000) & ~1) + 2;
957       orig_height = ((rand() % 2000) & ~1) + 2;
958 
959       GetVideoResolutionWithMaxFs(orig_width, orig_height, max_fs,
960                                   &width, &height);
961       if (max_fs > 0 &&
962           ceil(width / 16.) * ceil(height / 16.) > max_fs) {
963         DumpMaxFs(orig_width, orig_height, max_fs, width, height);
964         ADD_FAILURE();
965       }
966       if ((width & 1) || (height & 1)) {
967         DumpMaxFs(orig_width, orig_height, max_fs, width, height);
968         ADD_FAILURE();
969       }
970     }
971     cerr << endl;
972  }
973 
SetGmpCodecs()974   void SetGmpCodecs() {
975     mExternalEncoder = mozilla::GmpVideoCodec::CreateEncoder();
976     mExternalDecoder = mozilla::GmpVideoCodec::CreateDecoder();
977     mozilla::EncodingConstraints constraints;
978     mozilla::VideoCodecConfig config(124, "H264", constraints);
979     mVideoSession->SetExternalSendCodec(&config, mExternalEncoder);
980     mVideoSession2->SetExternalRecvCodec(&config, mExternalDecoder);
981   }
982 
983  private:
984   //Audio Conduit Test Objects
985   RefPtr<mozilla::AudioSessionConduit> mAudioSession;
986   RefPtr<mozilla::AudioSessionConduit> mAudioSession2;
987   RefPtr<mozilla::TransportInterface> mAudioTransport;
988   AudioSendAndReceive audioTester;
989 
990   //Video Conduit Test Objects
991   RefPtr<mozilla::VideoSessionConduit> mVideoSession;
992   RefPtr<mozilla::VideoSessionConduit> mVideoSession2;
993   RefPtr<mozilla::VideoRenderer> mVideoRenderer;
994   RefPtr<mozilla::TransportInterface> mVideoTransport;
995   VideoSendAndReceive videoTester;
996 
997   mozilla::VideoEncoder* mExternalEncoder;
998   mozilla::VideoDecoder* mExternalDecoder;
999 
1000   std::string fileToPlay;
1001   std::string fileToRecord;
1002   std::string iAudiofilename;
1003   std::string oAudiofilename;
1004 };
1005 
1006 
1007 // Test 1: Test Dummy External Xport
TEST_F(TransportConduitTest,TestDummyAudioWithTransport)1008 TEST_F(TransportConduitTest, TestDummyAudioWithTransport) {
1009   TestDummyAudioAndTransport();
1010 }
1011 
1012 // Test 2: Test Dummy External Xport
TEST_F(TransportConduitTest,TestDummyVideoWithTransport)1013 TEST_F(TransportConduitTest, TestDummyVideoWithTransport) {
1014   TestDummyVideoAndTransport();
1015  }
1016 
TEST_F(TransportConduitTest,TestVideoConduitExternalCodec)1017 TEST_F(TransportConduitTest, TestVideoConduitExternalCodec) {
1018   TestDummyVideoAndTransport(false);
1019 }
1020 
TEST_F(TransportConduitTest,TestVideoConduitCodecAPI)1021 TEST_F(TransportConduitTest, TestVideoConduitCodecAPI) {
1022   TestVideoConduitCodecAPI();
1023  }
1024 
TEST_F(TransportConduitTest,TestVideoConduitMaxFs)1025 TEST_F(TransportConduitTest, TestVideoConduitMaxFs) {
1026   TestVideoConduitMaxFs();
1027  }
1028 
1029 }  // end namespace
1030 
1031 static int test_result;
1032 bool test_finished = false;
1033 
1034 
1035 
1036 // This exists to send as an event to trigger shutdown.
tests_complete()1037 static void tests_complete() {
1038   gTestsComplete = true;
1039 }
1040 
1041 // The GTest thread runs this instead of the main thread so it can
1042 // do things like ASSERT_TRUE_WAIT which you could not do on the main thread.
gtest_main(int argc,char ** argv)1043 static int gtest_main(int argc, char **argv) {
1044   MOZ_ASSERT(!NS_IsMainThread());
1045 
1046   ::testing::InitGoogleTest(&argc, argv);
1047 
1048   int result = RUN_ALL_TESTS();
1049 
1050   // Set the global shutdown flag and tickle the main thread
1051   // The main thread did not go through Init() so calling Shutdown()
1052   // on it will not work.
1053   gMainThread->Dispatch(mozilla::WrapRunnableNM(tests_complete), NS_DISPATCH_SYNC);
1054 
1055   return result;
1056 }
1057 
main(int argc,char ** argv)1058 int main(int argc, char **argv)
1059 {
1060   // This test can cause intermittent oranges on the builders
1061   CHECK_ENVIRONMENT_FLAG("MOZ_WEBRTC_MEDIACONDUIT_TESTS")
1062 
1063   test_utils = new MtransportTestUtils();
1064 
1065   // Set the main thread global which is this thread.
1066   nsIThread *thread;
1067   NS_GetMainThread(&thread);
1068   gMainThread = thread;
1069 
1070   // Now create the GTest thread and run all of the tests on it
1071   // When it is complete it will set gTestsComplete
1072   NS_NewNamedThread("gtest_thread", &thread);
1073   gGtestThread = thread;
1074 
1075   int result;
1076   gGtestThread->Dispatch(
1077     mozilla::WrapRunnableNMRet(&result, gtest_main, argc, argv), NS_DISPATCH_NORMAL);
1078 
1079   // Here we handle the event queue for dispatches to the main thread
1080   // When the GTest thread is complete it will send one more dispatch
1081   // with gTestsComplete == true.
1082   while (!gTestsComplete && NS_ProcessNextEvent());
1083 
1084   gGtestThread->Shutdown();
1085 
1086   delete test_utils;
1087   return test_result;
1088 }
1089 
1090 
1091 
1092