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