Line data Source code
1 : /* 2 : * Copyright (C) 2004-2024 Savoir-faire Linux Inc. 3 : * 4 : * Author: Philippe Gorley <philippe.gorley@savoirfairelinux.com> 5 : * 6 : * This program is free software; you can redistribute it and/or modify 7 : * it under the terms of the GNU General Public License as published by 8 : * the Free Software Foundation; either version 3 of the License, or 9 : * (at your option) any later version. 10 : * 11 : * This program is distributed in the hope that it will be useful, 12 : * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 : * GNU General Public License for more details. 15 : * 16 : * You should have received a copy of the GNU General Public License 17 : * along with this program; if not, write to the Free Software 18 : * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 19 : */ 20 : 21 : #include <cppunit/TestAssert.h> 22 : #include <cppunit/TestFixture.h> 23 : #include <cppunit/extensions/HelperMacros.h> 24 : 25 : #include "jami.h" 26 : #include "fileutils.h" 27 : #include "media/libav_deps.h" 28 : #include "media/media_encoder.h" 29 : #include "media/media_io_handle.h" 30 : #include "media/system_codec_container.h" 31 : 32 : #include "../../test_runner.h" 33 : 34 : namespace jami { namespace test { 35 : 36 : class MediaEncoderTest : public CppUnit::TestFixture { 37 : public: 38 2 : static std::string name() { return "media_encoder"; } 39 : 40 : void setUp(); 41 : void tearDown(); 42 : 43 : private: 44 : void testMultiStream(); 45 : 46 2 : CPPUNIT_TEST_SUITE(MediaEncoderTest); 47 1 : CPPUNIT_TEST(testMultiStream); 48 4 : CPPUNIT_TEST_SUITE_END(); 49 : 50 : std::unique_ptr<MediaEncoder> encoder_; 51 : std::vector<std::string> files_; 52 : }; 53 : 54 : CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(MediaEncoderTest, MediaEncoderTest::name()); 55 : 56 : void 57 1 : MediaEncoderTest::setUp() 58 : { 59 1 : libjami::init(libjami::InitFlag(libjami::LIBJAMI_FLAG_DEBUG | libjami::LIBJAMI_FLAG_CONSOLE_LOG)); 60 1 : libav_utils::av_init(); 61 1 : encoder_.reset(new MediaEncoder); 62 1 : files_.push_back("test.mkv"); 63 1 : } 64 : 65 : void 66 1 : MediaEncoderTest::tearDown() 67 : { 68 : // clean up behind ourselves 69 2 : for (const auto& file : files_) 70 1 : dhtnet::fileutils::remove(file); 71 1 : libjami::fini(); 72 1 : } 73 : 74 : static AVFrame* 75 25 : getVideoFrame(int width, int height, int frame_index) 76 : { 77 : int x, y; 78 25 : AVFrame* frame = av_frame_alloc(); 79 25 : if (!frame) 80 0 : return nullptr; 81 : 82 25 : frame->format = AV_PIX_FMT_YUV420P; 83 25 : frame->width = width; 84 25 : frame->height = height; 85 : 86 25 : if (av_frame_get_buffer(frame, 32) < 0) { 87 0 : av_frame_free(&frame); 88 0 : return nullptr; 89 : } 90 : 91 : /* Y */ 92 6025 : for (y = 0; y < height; y++) 93 1926000 : for (x = 0; x < width; x++) 94 1920000 : frame->data[0][y * frame->linesize[0] + x] = x + y + frame_index * 3; 95 : 96 : /* Cb and Cr */ 97 3025 : for (y = 0; y < height / 2; y++) { 98 483000 : for (x = 0; x < width / 2; x++) { 99 480000 : frame->data[1][y * frame->linesize[1] + x] = 128 + y + frame_index * 2; 100 480000 : frame->data[2][y * frame->linesize[2] + x] = 64 + x + frame_index * 5; 101 : } 102 : } 103 : 104 25 : return frame; 105 : } 106 : 107 : static AVFrame* 108 25 : getAudioFrame(int sampleRate, int nbSamples, int nbChannels) 109 : { 110 25 : const constexpr float pi = 3.14159265358979323846264338327950288; // M_PI 111 25 : const float tincr = 2 * pi * 440.0 / sampleRate; 112 25 : float t = 0; 113 25 : AVFrame* frame = av_frame_alloc(); 114 25 : if (!frame) 115 0 : return nullptr; 116 : 117 25 : frame->format = AV_SAMPLE_FMT_S16; 118 25 : av_channel_layout_default(&frame->ch_layout, nbChannels); 119 25 : frame->nb_samples = nbSamples; 120 25 : frame->sample_rate = sampleRate; 121 : 122 25 : if (av_frame_get_buffer(frame, 0) < 0) { 123 0 : av_frame_free(&frame); 124 0 : return nullptr; 125 : } 126 : 127 25 : auto samples = reinterpret_cast<uint16_t*>(frame->data[0]); 128 5025 : for (int i = 0; i < 200; ++i) { 129 4805000 : for (int j = 0; j < nbSamples; ++j) { 130 4800000 : samples[2 * j] = static_cast<int>(sin(t) * 10000); 131 9600000 : for (int k = 1; k < nbChannels; ++k) { 132 4800000 : samples[2 * j + k] = samples[2 * j]; 133 : } 134 4800000 : t += tincr; 135 : } 136 : } 137 : 138 25 : return frame; 139 : } 140 : 141 : void 142 1 : MediaEncoderTest::testMultiStream() 143 : { 144 1 : const constexpr int sampleRate = 48000; 145 1 : const constexpr int nbChannels = 2; 146 1 : const constexpr int width = 320; 147 1 : const constexpr int height = 240; 148 : auto vp8Codec = std::static_pointer_cast<jami::SystemVideoCodecInfo>( 149 2 : getSystemCodecContainer()->searchCodecByName("VP8", jami::MEDIA_VIDEO) 150 1 : ); 151 : auto opusCodec = std::static_pointer_cast<SystemAudioCodecInfo>( 152 2 : getSystemCodecContainer()->searchCodecByName("opus", jami::MEDIA_AUDIO) 153 1 : ); 154 2 : auto v = MediaStream("v", AV_PIX_FMT_YUV420P, rational<int>(1, 30), width, height, 1, 30); 155 2 : auto a = MediaStream("a", AV_SAMPLE_FMT_S16, rational<int>(1, sampleRate), sampleRate, nbChannels, 960); 156 : 157 : try { 158 1 : encoder_->openOutput("test.mkv"); 159 1 : encoder_->setOptions(a); 160 1 : CPPUNIT_ASSERT(encoder_->getStreamCount() == 1); 161 1 : int audioIdx = encoder_->addStream(*opusCodec.get()); 162 1 : CPPUNIT_ASSERT(audioIdx >= 0); 163 1 : encoder_->setOptions(v); 164 1 : CPPUNIT_ASSERT(encoder_->getStreamCount() == 2); 165 1 : int videoIdx = encoder_->addStream(*vp8Codec.get()); 166 1 : CPPUNIT_ASSERT(videoIdx >= 0); 167 1 : CPPUNIT_ASSERT(videoIdx != audioIdx); 168 1 : encoder_->setIOContext(nullptr); 169 1 : int sentSamples = 0; 170 1 : AVFrame* audio = nullptr; 171 1 : AVFrame* video = nullptr; 172 26 : for (int i = 0; i < 25; ++i) { 173 25 : audio = getAudioFrame(sampleRate, 0.02*sampleRate, nbChannels); 174 25 : CPPUNIT_ASSERT(audio); 175 25 : audio->pts = sentSamples; 176 25 : video = getVideoFrame(width, height, i); 177 25 : CPPUNIT_ASSERT(video); 178 25 : video->pts = i; 179 : 180 25 : CPPUNIT_ASSERT(encoder_->encode(audio, audioIdx) >= 0); 181 25 : sentSamples += audio->nb_samples; 182 25 : CPPUNIT_ASSERT(encoder_->encode(video, videoIdx) >= 0); 183 : 184 25 : av_frame_free(&audio); 185 25 : av_frame_free(&video); 186 : } 187 1 : CPPUNIT_ASSERT(encoder_->flush() >= 0); 188 0 : } catch (const MediaEncoderException& e) { 189 0 : CPPUNIT_FAIL(e.what()); 190 0 : } 191 1 : } 192 : 193 : }} // namespace jami::test 194 : 195 1 : RING_TEST_RUNNER(jami::test::MediaEncoderTest::name());