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