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