/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "gtest/gtest.h" #include "VorbisTrackEncoder.h" #include "WebMWriter.h" #include "MediaStreamGraph.h" using namespace mozilla; class TestVorbisTrackEncoder : public VorbisTrackEncoder { public: // Return true if it has successfully initialized the vorbis encoder. bool TestVorbisCreation(int aChannels, int aSamplingRate) { if (Init(aChannels, aSamplingRate) == NS_OK) { return true; } return false; } }; static bool TestVorbisInit(int aChannels, int aSamplingRate) { TestVorbisTrackEncoder encoder; return encoder.TestVorbisCreation(aChannels, aSamplingRate); } static int ReadLacing(const uint8_t* aInput, uint32_t aInputLength, uint32_t& aReadBytes) { aReadBytes = 0; int packetSize = 0; while (aReadBytes < aInputLength) { if (aInput[aReadBytes] == 255) { packetSize += 255; aReadBytes++; } else { // the last byte packetSize += aInput[aReadBytes]; aReadBytes++; break; } } return packetSize; } static bool parseVorbisMetadata(nsTArray& aData, int aChannels, int aRate) { uint32_t offset = 0; // the first byte should be 2. if (aData.ElementAt(0) != 2) { return false; } offset = 1; // Read the length of header and header_comm uint32_t readbytes; ogg_packet header; ogg_packet header_comm; ogg_packet header_code; memset(&header, 0, sizeof(ogg_packet)); memset(&header_comm, 0, sizeof(ogg_packet)); memset(&header_code, 0, sizeof(ogg_packet)); int header_length; int header_comm_length; int header_code_length; EXPECT_TRUE(offset < aData.Length()); header_length = ReadLacing(aData.Elements()+offset, aData.Length()-offset, readbytes); offset += readbytes; EXPECT_TRUE(offset < aData.Length()); header_comm_length = ReadLacing(aData.Elements()+offset, aData.Length()-offset, readbytes); offset += readbytes; EXPECT_TRUE(offset < aData.Length()); // The rest length is header_code. header_code_length = aData.Length() - offset - header_length - header_comm_length; EXPECT_TRUE(header_code_length >= 32); // Verify the three header packets by vorbis_synthesis_headerin. // Raise the b_o_s (begin of stream) flag. header.b_o_s = true; header.packet = aData.Elements() + offset; header.bytes = header_length; offset += header_length; header_comm.packet = aData.Elements() + offset; header_comm.bytes = header_comm_length; offset += header_comm_length; header_code.packet = aData.Elements() + offset; header_code.bytes = header_code_length; vorbis_info vi; vorbis_comment vc; vorbis_info_init(&vi); vorbis_comment_init(&vc); EXPECT_TRUE(0 == vorbis_synthesis_headerin(&vi, &vc, &header)); EXPECT_TRUE(0 == vorbis_synthesis_headerin(&vi, &vc, &header_comm)); EXPECT_TRUE(0 == vorbis_synthesis_headerin(&vi, &vc, &header_code)); EXPECT_TRUE(vi.channels == aChannels); EXPECT_TRUE(vi.rate == aRate); vorbis_info_clear(&vi); vorbis_comment_clear(&vc); return true; } // Test init function TEST(VorbisTrackEncoder, Init) { // Channel number range test // Expect false with 0 or negative channels of input signal. EXPECT_FALSE(TestVorbisInit(0, 16000)); EXPECT_FALSE(TestVorbisInit(-1, 16000)); EXPECT_FALSE(TestVorbisInit(8 + 1, 16000)); // Sample rate and channel range test. for (int i = 1; i <= 8; i++) { EXPECT_FALSE(TestVorbisInit(i, -1)); EXPECT_FALSE(TestVorbisInit(i, 2000)); EXPECT_FALSE(TestVorbisInit(i, 4000)); EXPECT_FALSE(TestVorbisInit(i, 7999)); EXPECT_TRUE(TestVorbisInit(i, 8000)); EXPECT_TRUE(TestVorbisInit(i, 11000)); EXPECT_TRUE(TestVorbisInit(i, 16000)); EXPECT_TRUE(TestVorbisInit(i, 22050)); EXPECT_TRUE(TestVorbisInit(i, 32000)); EXPECT_TRUE(TestVorbisInit(i, 44100)); EXPECT_TRUE(TestVorbisInit(i, 48000)); EXPECT_TRUE(TestVorbisInit(i, 96000)); EXPECT_TRUE(TestVorbisInit(i, 192000)); EXPECT_FALSE(TestVorbisInit(i, 192001)); EXPECT_FALSE(TestVorbisInit(i, 200000 + 1)); } } // Test metadata TEST(VorbisTrackEncoder, Metadata) { // Initiate vorbis encoder. TestVorbisTrackEncoder encoder; int channels = 1; int rate = 44100; encoder.TestVorbisCreation(channels, rate); nsRefPtr meta = encoder.GetMetadata(); nsRefPtr vorbisMetadata(static_cast(meta.get())); // According to initialization parameters, verify the correctness // of vorbisMetadata. EXPECT_TRUE(vorbisMetadata->mChannels == channels); EXPECT_TRUE(vorbisMetadata->mSamplingFrequency == rate); EXPECT_TRUE(parseVorbisMetadata(vorbisMetadata->mData, channels, rate)); } // Test encode function TEST(VorbisTrackEncoder, EncodedFrame) { // Initiate vorbis encoder TestVorbisTrackEncoder encoder; int channels = 1; int rate = 44100; encoder.TestVorbisCreation(channels, rate); // Generate 1 second samples. // Reference PeerConnectionMedia.h::Fake_AudioGenerator nsRefPtr samples = mozilla::SharedBuffer::Create(rate * sizeof(AudioDataValue)); AudioDataValue* data = static_cast(samples->Data()); for (int i = 0; i < rate; i++) { data[i] = ((i%8)*4000) - (7*4000)/2; } nsAutoTArray channelData; channelData.AppendElement(data); AudioSegment segment; segment.AppendFrames(samples.forget(), channelData, 44100); // Track change notification. encoder.NotifyQueuedTrackChanges(nullptr, 0, 0, 0, 0, segment); // Pull Encoded data back from encoder and verify encoded samples. EncodedFrameContainer container; EXPECT_TRUE(NS_SUCCEEDED(encoder.GetEncodedTrack(container))); // Should have some encoded data. EXPECT_TRUE(container.GetEncodedFrames().Length() > 0); EXPECT_TRUE(container.GetEncodedFrames().ElementAt(0)->GetFrameData().Length() > 0); EXPECT_TRUE(container.GetEncodedFrames().ElementAt(0)->GetFrameType() == EncodedFrame::FrameType::VORBIS_AUDIO_FRAME); // Encoded data doesn't have duration and timestamp. EXPECT_TRUE(container.GetEncodedFrames().ElementAt(0)->GetDuration() == 0); EXPECT_TRUE(container.GetEncodedFrames().ElementAt(0)->GetTimeStamp() == 0); } // EOS test TEST(VorbisTrackEncoder, EncodeComplete) { // Initiate vorbis encoder TestVorbisTrackEncoder encoder; int channels = 1; int rate = 44100; encoder.TestVorbisCreation(channels, rate); // Track end notification. AudioSegment segment; encoder.NotifyQueuedTrackChanges(nullptr, 0, 0, 0, MediaStreamListener::TRACK_EVENT_ENDED, segment); // Pull Encoded Data back from encoder. Since we had send out // EOS to encoder, encoder.GetEncodedTrack should return // NS_OK immidiately. EncodedFrameContainer container; EXPECT_TRUE(NS_SUCCEEDED(encoder.GetEncodedTrack(container))); }