1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
4  * You can obtain one at http://mozilla.org/MPL/2.0/. */
5 
6 #include <vector>
7 
8 #include "ContainerWriter.h"
9 #include "EncodedFrame.h"
10 #include "gtest/gtest.h"
11 #include "gmock/gmock.h"
12 #include "Muxer.h"
13 #include "OpusTrackEncoder.h"
14 #include "WebMWriter.h"
15 
16 using namespace mozilla;
17 using testing::_;
18 using testing::ElementsAre;
19 using testing::Return;
20 using testing::StaticAssertTypeEq;
21 
CreateOpusMetadata(int32_t aChannels,float aSamplingFrequency,size_t aIdHeaderSize,size_t aCommentHeaderSize)22 static RefPtr<TrackMetadataBase> CreateOpusMetadata(int32_t aChannels,
23                                                     float aSamplingFrequency,
24                                                     size_t aIdHeaderSize,
25                                                     size_t aCommentHeaderSize) {
26   auto opusMetadata = MakeRefPtr<OpusMetadata>();
27   opusMetadata->mChannels = aChannels;
28   opusMetadata->mSamplingFrequency = aSamplingFrequency;
29   opusMetadata->mIdHeader.SetLength(aIdHeaderSize);
30   for (size_t i = 0; i < opusMetadata->mIdHeader.Length(); i++) {
31     opusMetadata->mIdHeader[i] = 0;
32   }
33   opusMetadata->mCommentHeader.SetLength(aCommentHeaderSize);
34   for (size_t i = 0; i < opusMetadata->mCommentHeader.Length(); i++) {
35     opusMetadata->mCommentHeader[i] = 0;
36   }
37   return opusMetadata;
38 }
39 
CreateVP8Metadata(int32_t aWidth,int32_t aHeight)40 static RefPtr<TrackMetadataBase> CreateVP8Metadata(int32_t aWidth,
41                                                    int32_t aHeight) {
42   auto vp8Metadata = MakeRefPtr<VP8Metadata>();
43   vp8Metadata->mWidth = aWidth;
44   vp8Metadata->mDisplayWidth = aWidth;
45   vp8Metadata->mHeight = aHeight;
46   vp8Metadata->mDisplayHeight = aHeight;
47   return vp8Metadata;
48 }
49 
CreateFrame(EncodedFrame::FrameType aType,uint64_t aTimeUs,uint64_t aDurationUs,size_t aDataSize)50 static RefPtr<EncodedFrame> CreateFrame(EncodedFrame::FrameType aType,
51                                         uint64_t aTimeUs, uint64_t aDurationUs,
52                                         size_t aDataSize) {
53   auto frame = MakeRefPtr<EncodedFrame>();
54   frame->mTime = aTimeUs;
55   if (aType == EncodedFrame::OPUS_AUDIO_FRAME) {
56     // Opus duration is in samples, so figure out how many samples will put us
57     // closest to aDurationUs without going over.
58     frame->mDuration = UsecsToFrames(aDurationUs, 48000).value();
59   } else {
60     frame->mDuration = aDurationUs;
61   }
62   frame->mFrameType = aType;
63 
64   nsTArray<uint8_t> data;
65   data.SetLength(aDataSize);
66   frame->SwapInFrameData(data);
67   return frame;
68 }
69 
70 namespace testing {
71 namespace internal {
72 // This makes the googletest framework treat nsTArray as an std::vector, so all
73 // the regular Matchers (like ElementsAre) work for it.
74 template <typename Element>
75 class StlContainerView<nsTArray<Element>> {
76  public:
77   typedef GTEST_REMOVE_CONST_(Element) RawElement;
78   typedef std::vector<RawElement> type;
79   typedef const type const_reference;
ConstReference(const nsTArray<Element> & aContainer)80   static const_reference ConstReference(const nsTArray<Element>& aContainer) {
81     StaticAssertTypeEq<Element, RawElement>();
82     return type(aContainer.begin(), aContainer.end());
83   }
Copy(const nsTArray<Element> & aContainer)84   static type Copy(const nsTArray<Element>& aContainer) {
85     return type(aContainer.begin(), aContainer.end());
86   }
87 };
88 }  // namespace internal
89 }  // namespace testing
90 
91 class MockContainerWriter : public ContainerWriter {
92  public:
93   MOCK_METHOD2(WriteEncodedTrack,
94                nsresult(const nsTArray<RefPtr<EncodedFrame>>&, uint32_t));
95   MOCK_METHOD1(SetMetadata,
96                nsresult(const nsTArray<RefPtr<TrackMetadataBase>>&));
97   MOCK_METHOD0(IsWritingComplete, bool());
98   MOCK_METHOD2(GetContainerData,
99                nsresult(nsTArray<nsTArray<uint8_t>>*, uint32_t));
100 };
101 
TEST(MuxerTest,AudioOnly)102 TEST(MuxerTest, AudioOnly)
103 {
104   MockContainerWriter* writer = new MockContainerWriter();
105   Muxer muxer(WrapUnique<ContainerWriter>(writer));
106 
107   // Prepare data
108 
109   auto opusMeta = CreateOpusMetadata(1, 48000, 16, 16);
110   auto audioFrame = CreateFrame(EncodedFrame::OPUS_AUDIO_FRAME, 0, 48000, 4096);
111 
112   // Expectations
113 
114   EXPECT_CALL(*writer, SetMetadata(ElementsAre(opusMeta)))
115       .WillOnce(Return(NS_OK));
116   EXPECT_CALL(*writer, WriteEncodedTrack(ElementsAre(audioFrame),
117                                          ContainerWriter::END_OF_STREAM))
118       .WillOnce(Return(NS_OK));
119   EXPECT_CALL(*writer, GetContainerData(_, ContainerWriter::GET_HEADER))
120       .WillOnce(Return(NS_OK));
121   EXPECT_CALL(*writer, GetContainerData(_, ContainerWriter::FLUSH_NEEDED))
122       .WillOnce(Return(NS_OK));
123   EXPECT_CALL(*writer, IsWritingComplete()).Times(0);
124 
125   // Test
126 
127   EXPECT_EQ(muxer.SetMetadata(nsTArray<RefPtr<TrackMetadataBase>>({opusMeta})),
128             NS_OK);
129   muxer.AddEncodedAudioFrame(audioFrame);
130   muxer.AudioEndOfStream();
131   nsTArray<nsTArray<uint8_t>> buffers;
132   EXPECT_EQ(muxer.GetData(&buffers), NS_OK);
133 }
134 
TEST(MuxerTest,AudioVideo)135 TEST(MuxerTest, AudioVideo)
136 {
137   MockContainerWriter* writer = new MockContainerWriter();
138   Muxer muxer(WrapUnique<ContainerWriter>(writer));
139 
140   // Prepare data
141 
142   auto opusMeta = CreateOpusMetadata(1, 48000, 16, 16);
143   auto vp8Meta = CreateVP8Metadata(640, 480);
144   auto audioFrame = CreateFrame(EncodedFrame::OPUS_AUDIO_FRAME, 0, 48000, 4096);
145   auto videoFrame = CreateFrame(EncodedFrame::VP8_I_FRAME, 0, 50000, 65536);
146 
147   // Expectations
148 
149   EXPECT_CALL(*writer, SetMetadata(ElementsAre(opusMeta, vp8Meta)))
150       .WillOnce(Return(NS_OK));
151   EXPECT_CALL(*writer, WriteEncodedTrack(ElementsAre(videoFrame, audioFrame),
152                                          ContainerWriter::END_OF_STREAM))
153       .WillOnce(Return(NS_OK));
154   EXPECT_CALL(*writer, GetContainerData(_, ContainerWriter::GET_HEADER))
155       .WillOnce(Return(NS_OK));
156   EXPECT_CALL(*writer, GetContainerData(_, ContainerWriter::FLUSH_NEEDED))
157       .WillOnce(Return(NS_OK));
158   EXPECT_CALL(*writer, IsWritingComplete()).Times(0);
159 
160   // Test
161 
162   EXPECT_EQ(muxer.SetMetadata(
163                 nsTArray<RefPtr<TrackMetadataBase>>({opusMeta, vp8Meta})),
164             NS_OK);
165   muxer.AddEncodedAudioFrame(audioFrame);
166   muxer.AudioEndOfStream();
167   muxer.AddEncodedVideoFrame(videoFrame);
168   muxer.VideoEndOfStream();
169   nsTArray<nsTArray<uint8_t>> buffers;
170   EXPECT_EQ(muxer.GetData(&buffers), NS_OK);
171 }
172 
TEST(MuxerTest,AudioVideoOutOfOrder)173 TEST(MuxerTest, AudioVideoOutOfOrder)
174 {
175   MockContainerWriter* writer = new MockContainerWriter();
176   Muxer muxer(WrapUnique<ContainerWriter>(writer));
177 
178   // Prepare data
179 
180   auto opusMeta = CreateOpusMetadata(1, 48000, 16, 16);
181   auto vp8Meta = CreateVP8Metadata(640, 480);
182   auto a0 = CreateFrame(EncodedFrame::OPUS_AUDIO_FRAME, 0, 48, 4096);
183   auto v0 = CreateFrame(EncodedFrame::VP8_I_FRAME, 0, 50, 65536);
184   auto a48 = CreateFrame(EncodedFrame::OPUS_AUDIO_FRAME, 48, 48, 4096);
185   auto v50 = CreateFrame(EncodedFrame::VP8_I_FRAME, 50, 50, 65536);
186 
187   // Expectations
188 
189   EXPECT_CALL(*writer, SetMetadata(ElementsAre(opusMeta, vp8Meta)))
190       .WillOnce(Return(NS_OK));
191   EXPECT_CALL(*writer, WriteEncodedTrack(ElementsAre(v0, a0, a48, v50),
192                                          ContainerWriter::END_OF_STREAM))
193       .WillOnce(Return(NS_OK));
194   EXPECT_CALL(*writer, GetContainerData(_, ContainerWriter::GET_HEADER))
195       .WillOnce(Return(NS_OK));
196   EXPECT_CALL(*writer, GetContainerData(_, ContainerWriter::FLUSH_NEEDED))
197       .WillOnce(Return(NS_OK));
198   EXPECT_CALL(*writer, IsWritingComplete()).Times(0);
199 
200   // Test
201 
202   EXPECT_EQ(muxer.SetMetadata(
203                 nsTArray<RefPtr<TrackMetadataBase>>({opusMeta, vp8Meta})),
204             NS_OK);
205   muxer.AddEncodedAudioFrame(a0);
206   muxer.AddEncodedVideoFrame(v0);
207   muxer.AddEncodedVideoFrame(v50);
208   muxer.VideoEndOfStream();
209   muxer.AddEncodedAudioFrame(a48);
210   muxer.AudioEndOfStream();
211   nsTArray<nsTArray<uint8_t>> buffers;
212   EXPECT_EQ(muxer.GetData(&buffers), NS_OK);
213 }
214