From 33c3e14740cf8d909530a1fd9dd326527b62f0f4 Mon Sep 17 00:00:00 2001 From: lastpeony Date: Wed, 26 Jun 2024 16:27:19 +0300 Subject: [PATCH 01/12] change hwScalingEnabled app setting default value from true to false --- src/main/java/io/antmedia/AppSettings.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/io/antmedia/AppSettings.java b/src/main/java/io/antmedia/AppSettings.java index 9342bea3d..17d2b424d 100644 --- a/src/main/java/io/antmedia/AppSettings.java +++ b/src/main/java/io/antmedia/AppSettings.java @@ -2064,8 +2064,8 @@ public boolean isWriteStatsToDatastore() { @Value("${sendAudioLevelToViewers:true}") private boolean sendAudioLevelToViewers = true; - @Value("${hwScalingEnabled:${"+SETTINGS_HW_SCALING_ENABLED+":true}}") - private boolean hwScalingEnabled = true; + @Value("${hwScalingEnabled:${"+SETTINGS_HW_SCALING_ENABLED+":false}}") + private boolean hwScalingEnabled = false; /** * Firebase Service Account Key JSON to send push notification From b9dbbc4f504db1491291bb2df2ddda60dded8920 Mon Sep 17 00:00:00 2001 From: mekya Date: Sun, 30 Jun 2024 21:09:09 +0300 Subject: [PATCH 02/12] Try disabling one test that worked well --- .github/workflows/test.yml | 2 +- src/test/java/io/antmedia/test/StreamFetcherUnitTest.java | 3 +++ .../io/antmedia/test/rest/BroadcastRestServiceV2UnitTest.java | 2 +- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 3505622fe..5b5c074e6 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -66,7 +66,7 @@ jobs: run: | export RELEASE_VERSION="$(mvn -q -Dexec.executable="echo" -Dexec.args='${project.version}' --non-recursive exec:exec)" echo $RELEASE_VERSION - mvn clean org.jacoco:jacoco-maven-plugin:prepare-agent package -Dtest=!*/integration/*,!MuxerUnitTest -Dorg.bytedeco.javacpp.logger.debug=true org.jacoco:jacoco-maven-plugin:report sonar:sonar -Dmaven.javadoc.skip=true --quiet + mvn clean org.jacoco:jacoco-maven-plugin:prepare-agent package -Dtest=!*/integration/*,!ConsoleRestV2UnitTest -Dorg.bytedeco.javacpp.logger.debug=true org.jacoco:jacoco-maven-plugin:report sonar:sonar -Dmaven.javadoc.skip=true --quiet - name: Show MongoDB Log, Crash Log and Servis Status on failure if: failure() diff --git a/src/test/java/io/antmedia/test/StreamFetcherUnitTest.java b/src/test/java/io/antmedia/test/StreamFetcherUnitTest.java index cf67f656e..3ba6d0dd5 100644 --- a/src/test/java/io/antmedia/test/StreamFetcherUnitTest.java +++ b/src/test/java/io/antmedia/test/StreamFetcherUnitTest.java @@ -35,6 +35,7 @@ import java.util.concurrent.ConcurrentSkipListSet; import java.util.concurrent.TimeUnit; +import org.apache.commons.lang3.RandomStringUtils; import org.apache.commons.lang3.StringUtils; import org.awaitility.Awaitility; import org.bytedeco.ffmpeg.avcodec.AVCodecParameters; @@ -672,6 +673,8 @@ public void testStreamFetcherBuffer() { Broadcast newCam = new Broadcast("streamSource", "127.0.0.1:8080", "admin", "admin", "src/test/resources/test_video_360p.flv", AntMediaApplicationAdapter.STREAM_SOURCE); + + newCam.setStreamId("stream_id_" + RandomStringUtils.randomAlphanumeric(12)); assertNotNull(newCam.getStreamUrl()); diff --git a/src/test/java/io/antmedia/test/rest/BroadcastRestServiceV2UnitTest.java b/src/test/java/io/antmedia/test/rest/BroadcastRestServiceV2UnitTest.java index 818d211ac..4f524d385 100644 --- a/src/test/java/io/antmedia/test/rest/BroadcastRestServiceV2UnitTest.java +++ b/src/test/java/io/antmedia/test/rest/BroadcastRestServiceV2UnitTest.java @@ -2381,7 +2381,7 @@ public void testSearchOnvifDevices() { // PAY ATTENTION //TODO: We should enable below assertion to make sure onvif discovery works //however there is a problem in CI. We need to check it on a linux box later. - //assertEquals(1, result.length); + assertEquals(1, result.length); //***************************************************************************** //***************************************************************************** From 9314e8a01741fc3d3cf0d7cb24dd76ea1c34c9f3 Mon Sep 17 00:00:00 2001 From: mekya Date: Sun, 30 Jun 2024 21:19:48 +0300 Subject: [PATCH 03/12] Revert back to previous test --- .../io/antmedia/test/rest/BroadcastRestServiceV2UnitTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/io/antmedia/test/rest/BroadcastRestServiceV2UnitTest.java b/src/test/java/io/antmedia/test/rest/BroadcastRestServiceV2UnitTest.java index 4f524d385..818d211ac 100644 --- a/src/test/java/io/antmedia/test/rest/BroadcastRestServiceV2UnitTest.java +++ b/src/test/java/io/antmedia/test/rest/BroadcastRestServiceV2UnitTest.java @@ -2381,7 +2381,7 @@ public void testSearchOnvifDevices() { // PAY ATTENTION //TODO: We should enable below assertion to make sure onvif discovery works //however there is a problem in CI. We need to check it on a linux box later. - assertEquals(1, result.length); + //assertEquals(1, result.length); //***************************************************************************** //***************************************************************************** From 82e29356cedd72dcf33c6c1770781ca8aae78f37 Mon Sep 17 00:00:00 2001 From: mekya Date: Sun, 30 Jun 2024 21:40:10 +0300 Subject: [PATCH 04/12] Disable MuxerUnitTest --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 5b5c074e6..56a5c50d7 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -66,7 +66,7 @@ jobs: run: | export RELEASE_VERSION="$(mvn -q -Dexec.executable="echo" -Dexec.args='${project.version}' --non-recursive exec:exec)" echo $RELEASE_VERSION - mvn clean org.jacoco:jacoco-maven-plugin:prepare-agent package -Dtest=!*/integration/*,!ConsoleRestV2UnitTest -Dorg.bytedeco.javacpp.logger.debug=true org.jacoco:jacoco-maven-plugin:report sonar:sonar -Dmaven.javadoc.skip=true --quiet + mvn clean org.jacoco:jacoco-maven-plugin:prepare-agent package -Dtest=!*/integration/*,!ConsoleRestV2UnitTest,!MuxerUnitTest -Dorg.bytedeco.javacpp.logger.debug=true org.jacoco:jacoco-maven-plugin:report sonar:sonar -Dmaven.javadoc.skip=true --quiet - name: Show MongoDB Log, Crash Log and Servis Status on failure if: failure() From a1e8606bbdd7b997777434fa4bc33ffefcce2a7f Mon Sep 17 00:00:00 2001 From: Murat Ugur Eminoglu Date: Sun, 30 Jun 2024 22:23:30 +0300 Subject: [PATCH 05/12] Add SSL configuration for Docker container --- src/main/server/enable_ssl.sh | 29 +++++++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/src/main/server/enable_ssl.sh b/src/main/server/enable_ssl.sh index fe062059d..d51158848 100755 --- a/src/main/server/enable_ssl.sh +++ b/src/main/server/enable_ssl.sh @@ -102,11 +102,28 @@ get_password() { done } + +is_docker_container() { + # /.dockerenv dosyasını kontrol et + if [ -f /.dockerenv ]; then + apt-get install iptables dnsutils -y + return 0 + fi + + return 1 +} + + SUDO="sudo" if ! [ -x "$(command -v sudo)" ]; then SUDO="" fi +if is_docker_container; then + SUDO="" +fi + + output() { OUT=$? if [ $OUT -ne 0 ]; then @@ -138,8 +155,8 @@ wait_for_dns_validation() { # Install jq install_jq() { if ! [ command -v jq &> /dev/null ]; then - sudo apt update -qq - sudo apt install -y jq + $SUDO apt update -qq + $SUDO apt install -y jq fi } @@ -469,7 +486,11 @@ ipt_restore echo "" -$SUDO service antmedia restart +if is_docker_container; then + kill -HUP 1 +else + $SUDO service antmedia restart +fi output @@ -478,4 +499,4 @@ echo "Https port: 5443" echo "You can use this url: https://$domain:5443/" #remove temp dir -$SUDO rm -rf $TEMP_DIR +$SUDO rm -rf $TEMP_DIR \ No newline at end of file From 21b57c461ba8eae496b830e779d2c628f25aecce Mon Sep 17 00:00:00 2001 From: Murat Ugur Eminoglu Date: Mon, 1 Jul 2024 08:23:46 +0300 Subject: [PATCH 06/12] Add comment --- src/main/server/enable_ssl.sh | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/main/server/enable_ssl.sh b/src/main/server/enable_ssl.sh index d51158848..cc6c19e62 100755 --- a/src/main/server/enable_ssl.sh +++ b/src/main/server/enable_ssl.sh @@ -102,11 +102,10 @@ get_password() { done } - +# Check if there is a Container and install necessary packages is_docker_container() { - # /.dockerenv dosyasını kontrol et if [ -f /.dockerenv ]; then - apt-get install iptables dnsutils -y + apt-get install iptables dnsutils -y return 0 fi @@ -499,4 +498,4 @@ echo "Https port: 5443" echo "You can use this url: https://$domain:5443/" #remove temp dir -$SUDO rm -rf $TEMP_DIR \ No newline at end of file +$SUDO rm -rf $TEMP_DIR From 0d38869c3d995262c95c2e55c1e4eef9d0463694 Mon Sep 17 00:00:00 2001 From: mekya Date: Mon, 1 Jul 2024 14:06:14 +0300 Subject: [PATCH 07/12] Simplify one test that may cause problem --- src/test/java/io/antmedia/test/StreamFetcherUnitTest.java | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/test/java/io/antmedia/test/StreamFetcherUnitTest.java b/src/test/java/io/antmedia/test/StreamFetcherUnitTest.java index 3ba6d0dd5..a0bee1408 100644 --- a/src/test/java/io/antmedia/test/StreamFetcherUnitTest.java +++ b/src/test/java/io/antmedia/test/StreamFetcherUnitTest.java @@ -931,13 +931,8 @@ public void testSeekTime() public void testBugUnexpectedStream() throws InterruptedException { - AVFormatContext inputFormatContext = avformat.avformat_alloc_context(); - - AVStream stream = avformat.avformat_new_stream(inputFormatContext, null); AVCodecParameters pars = new AVCodecParameters(); - stream.codecpar(pars); pars.codec_type(AVMEDIA_TYPE_DATA); - stream.codecpar(pars); Mp4Muxer mp4Muxer = Mockito.spy(new Mp4Muxer(null, null, "streams")); @@ -950,11 +945,9 @@ public void testBugUnexpectedStream() throws InterruptedException Mockito.verify(mp4Muxer, Mockito.never()).avNewStream(Mockito.any()); - //Close the codec parameters to not let collect it by garbage collector that may cause double free error because it's released in av_format_free_context as well pars.close(); pars = null; - avformat.avformat_free_context(inputFormatContext); } From e6fb6885e819a3b2f9c508bee5003221caf7db3f Mon Sep 17 00:00:00 2001 From: mekya Date: Mon, 1 Jul 2024 14:40:46 +0300 Subject: [PATCH 08/12] Comment out one test --- src/test/java/io/antmedia/test/StreamSchedularUnitTest.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/test/java/io/antmedia/test/StreamSchedularUnitTest.java b/src/test/java/io/antmedia/test/StreamSchedularUnitTest.java index 5b5e87eb7..6d20d710d 100644 --- a/src/test/java/io/antmedia/test/StreamSchedularUnitTest.java +++ b/src/test/java/io/antmedia/test/StreamSchedularUnitTest.java @@ -1067,7 +1067,8 @@ public void testIPTVStream() { * */ - @Test + //TODO: Comment out the test because it is not compatible with latest version + //@Test public void testBandwidth() { boolean deleteHLSFilesOnExit = getAppSettings().isDeleteHLSFilesOnEnded(); From f8103a6ab696bf38c3f4308d89613e23d2509a67 Mon Sep 17 00:00:00 2001 From: mekya Date: Mon, 1 Jul 2024 16:07:22 +0300 Subject: [PATCH 09/12] Implement testBandwidth in another way in testStreamSpeed --- .github/workflows/test.yml | 9 +- .../java/io/antmedia/muxer/MuxAdaptor.java | 40 +- .../java/io/antmedia/test/MuxerUnitTest.java | 537 ++++++++++++------ .../test/StreamSchedularUnitTest.java | 133 +---- 4 files changed, 389 insertions(+), 330 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 56a5c50d7..6a198db42 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -224,7 +224,13 @@ jobs: deploy: runs-on: ubuntu-22.04 - needs: [run-unit-tests, check-vulnerabilities, run-integration-tests] + needs: [run-unit-tests, check-vulnerabilities, run-integration-tests] + env: + CI_DEPLOY_USERNAME: ${{ secrets.CI_DEPLOY_USERNAME }} + CI_DEPLOY_PASSWORD: ${{ secrets.CI_DEPLOY_PASSWORD }} + GPG_KEY_NAME: ${{ secrets.GPG_KEY_NAME }} + GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} + steps: - name: Checkout code uses: actions/checkout@v4 @@ -235,6 +241,7 @@ jobs: distribution: 'adopt' #openjdk java-version: '17' cache: 'maven' + gpg-private-key: ${{ secrets.GPG_PRIVATE_KEY }} - name: Build projects # there is low probability that if somethings has pushed to the related branch while running test and before this job, it may build something not exactly same uses: ./.github/actions/build-projects diff --git a/src/main/java/io/antmedia/muxer/MuxAdaptor.java b/src/main/java/io/antmedia/muxer/MuxAdaptor.java index 98ced57dd..8758d151a 100644 --- a/src/main/java/io/antmedia/muxer/MuxAdaptor.java +++ b/src/main/java/io/antmedia/muxer/MuxAdaptor.java @@ -1,26 +1,39 @@ package io.antmedia.muxer; import static io.antmedia.muxer.IAntMediaStreamHandler.BROADCAST_STATUS_BROADCASTING; -import static org.bytedeco.ffmpeg.global.avcodec.*; -import static org.bytedeco.ffmpeg.global.avutil.*; +import static org.bytedeco.ffmpeg.global.avcodec.AV_CODEC_ID_AAC; +import static org.bytedeco.ffmpeg.global.avcodec.AV_CODEC_ID_H264; +import static org.bytedeco.ffmpeg.global.avcodec.AV_CODEC_ID_PNG; +import static org.bytedeco.ffmpeg.global.avcodec.AV_PKT_FLAG_KEY; +import static org.bytedeco.ffmpeg.global.avutil.AVMEDIA_TYPE_ATTACHMENT; +import static org.bytedeco.ffmpeg.global.avutil.AVMEDIA_TYPE_AUDIO; +import static org.bytedeco.ffmpeg.global.avutil.AVMEDIA_TYPE_DATA; +import static org.bytedeco.ffmpeg.global.avutil.AVMEDIA_TYPE_SUBTITLE; +import static org.bytedeco.ffmpeg.global.avutil.AVMEDIA_TYPE_VIDEO; +import static org.bytedeco.ffmpeg.global.avutil.AV_PIX_FMT_YUV420P; +import static org.bytedeco.ffmpeg.global.avutil.AV_SAMPLE_FMT_FLTP; +import static org.bytedeco.ffmpeg.global.avutil.av_channel_layout_default; +import static org.bytedeco.ffmpeg.global.avutil.av_free; +import static org.bytedeco.ffmpeg.global.avutil.av_malloc; +import static org.bytedeco.ffmpeg.global.avutil.av_rescale_q; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.Collections; +import java.util.Deque; import java.util.Iterator; -import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.NoSuchElementException; +import java.util.Queue; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentLinkedDeque; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.ConcurrentSkipListSet; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; -import io.antmedia.logger.LoggerUtils; - import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.exception.ExceptionUtils; import org.apache.mina.core.buffer.IoBuffer; @@ -61,6 +74,7 @@ import io.antmedia.datastore.db.IDataStoreFactory; import io.antmedia.datastore.db.types.Broadcast; import io.antmedia.datastore.db.types.Endpoint; +import io.antmedia.logger.LoggerUtils; import io.antmedia.muxer.parser.AACConfigParser; import io.antmedia.muxer.parser.AACConfigParser.AudioObjectTypes; import io.antmedia.muxer.parser.SpsParser; @@ -163,7 +177,7 @@ public class MuxAdaptor implements IRecordingListener, IEndpointStatusListener { protected static boolean isStreamSource = false; private int previewCreatePeriod; - private double oldspeed; + private double latestSpeed; private long lastQualityUpdateTime = 0; private Broadcast broadcast; protected AppSettings appSettings; @@ -183,7 +197,7 @@ public class MuxAdaptor implements IRecordingListener, IEndpointStatusListener { * Value is the system time at that moment * */ - private LinkedList packetTimeList = new LinkedList(); + private Deque packetTimeList = new ConcurrentLinkedDeque<>(); public boolean addID3Data(String data) { for (Muxer muxer : muxerList) { @@ -895,6 +909,7 @@ public void prepareMuxerIO() public void updateStreamQualityParameters(String streamId, String quality, double speed, int inputQueueSize) { long now = System.currentTimeMillis(); + latestSpeed = speed; //increase updating time to STAT_UPDATE_PERIOD_MS seconds because it may cause some issues in mongodb updates //or //update before STAT_UPDATE_PERIOD_MS if speed something meaningful @@ -917,8 +932,13 @@ public void updateStreamQualityParameters(String streamId, String quality, doubl getStreamHandler().setQualityParameters(streamId, quality, speed, inputQueueSize, System.currentTimeMillis()); oldQuality = quality; - oldspeed = speed; } + + + } + + public double getLatestSpeed() { + return latestSpeed; } public IAntMediaStreamHandler getStreamHandler() { @@ -1312,7 +1332,7 @@ public void updateQualityParameters(long pts, AVRational timebase, long packetSi if (packetTimeList.size() > 300) { //limit the size. - packetTimeList.remove(0); + packetTimeList.removeFirst(); } PacketTime firstPacket = packetTimeList.getFirst(); @@ -2286,7 +2306,7 @@ public void setBufferingFinishTimeMs(long bufferingFinishTimeMs) { this.bufferingFinishTimeMs = bufferingFinishTimeMs; } - public LinkedList getPacketTimeList() { + public Queue getPacketTimeList() { return packetTimeList; } diff --git a/src/test/java/io/antmedia/test/MuxerUnitTest.java b/src/test/java/io/antmedia/test/MuxerUnitTest.java index 715d75d4d..344437d9e 100644 --- a/src/test/java/io/antmedia/test/MuxerUnitTest.java +++ b/src/test/java/io/antmedia/test/MuxerUnitTest.java @@ -488,34 +488,34 @@ public void testAACAudio() { assertTrue(aacAudio.addData(result)); assertNull(aacAudio.getDecoderConfiguration()); } - - + + @Test public void testInitBitstreamFilter() { AVFormatContext inputFormatContext = avformat.avformat_alloc_context(); //AVInputFormat findInputFormat = avformat.av_find_input_format("mp4"); if (avformat_open_input(inputFormatContext, (String) "src/test/resources/test_hevc.ts", null, (AVDictionary) null) < 0) { - - logger.error("cannot open input format"); - fail("cannot open input format"); + + logger.error("cannot open input format"); + fail("cannot open input format"); } - + int ret = avformat_find_stream_info(inputFormatContext, (AVDictionary) null); if (ret < 0) { fail("cannot find stream info"); - } - + } + + + - - HLSMuxer hlsMuxer = new HLSMuxer(vertx, Mockito.mock(StorageClient.class), "streams", 7, null, false); if (appScope == null) { appScope = (WebScope) applicationContext.getBean("web.scope"); logger.debug("Application / web scope: {}", appScope); assertTrue(appScope.getDepth() == 1); } - + String streamId = "stream_name_" + (int) (Math.random() * 10000); hlsMuxer.setHlsParameters("5", "2", "event", null, null, "fmp4"); @@ -523,34 +523,34 @@ public void testInitBitstreamFilter() { //init hlsMuxer.init(appScope, streamId, 0, null, 0); - + AVBSFContext initVideoBitstreamFilter = hlsMuxer.initVideoBitstreamFilter("h264_mp4toannexb", inputFormatContext.streams(0).codecpar(), inputFormatContext.streams(0).time_base()); assertNull(initVideoBitstreamFilter); - - + + initVideoBitstreamFilter = hlsMuxer.initVideoBitstreamFilter("not exists", inputFormatContext.streams(0).codecpar(), inputFormatContext.streams(0).time_base()); assertNull(initVideoBitstreamFilter); - + avformat_close_input(inputFormatContext); } - + @Test public void testHEVCHLSMuxingInFMP4() { - + AVFormatContext inputFormatContext = avformat.avformat_alloc_context(); //AVInputFormat findInputFormat = avformat.av_find_input_format("mp4"); if (avformat_open_input(inputFormatContext, (String) "src/test/resources/test_hevc.ts", null, (AVDictionary) null) < 0) { - - logger.error("cannot open input format"); - fail("cannot open input format"); + + logger.error("cannot open input format"); + fail("cannot open input format"); } - + int ret = avformat_find_stream_info(inputFormatContext, (AVDictionary) null); if (ret < 0) { fail("cannot find stream info"); - } - + } + vertx = Vertx.vertx(); HLSMuxer hlsMuxer = new HLSMuxer(vertx, Mockito.mock(StorageClient.class), "streams", 7, null, false); @@ -559,60 +559,60 @@ public void testHEVCHLSMuxingInFMP4() { logger.debug("Application / web scope: {}", appScope); assertTrue(appScope.getDepth() == 1); } - + String streamId = "stream_name_" + (int) (Math.random() * 10000); hlsMuxer.setHlsParameters("5", "2", "event", null, null, "fmp4"); //init hlsMuxer.init(appScope, streamId, 0, null, 0); - + //add video stream assertEquals(AVMEDIA_TYPE_VIDEO, inputFormatContext.streams(0).codecpar().codec_type()); assertEquals(AV_CODEC_ID_HEVC, inputFormatContext.streams(0).codecpar().codec_id()); boolean addStreamResult = hlsMuxer.addStream(inputFormatContext.streams(0).codecpar(), inputFormatContext.streams(0).time_base(), 0); assertTrue(addStreamResult); - + assertEquals("hevc_mp4toannexb", hlsMuxer.getBitStreamFilter()); - - + + assertEquals(AVMEDIA_TYPE_AUDIO, inputFormatContext.streams(1).codecpar().codec_type()); assertEquals(AV_CODEC_ID_AAC, inputFormatContext.streams(1).codecpar().codec_id()); addStreamResult = hlsMuxer.addStream(inputFormatContext.streams(1).codecpar(), inputFormatContext.streams(1).time_base(), 1); assertTrue(addStreamResult); - + assertTrue(hlsMuxer.getBsfAudioNames().contains("aac_adtstoasc")); assertEquals(1, hlsMuxer.getBsfAudioNames().size()); //prepare io boolean prepareIOresult = hlsMuxer.prepareIO(); assertTrue(prepareIOresult); - - + + AVPacket pkt = new AVPacket(); - + while (av_read_frame(inputFormatContext, pkt) >= 0) { hlsMuxer.writePacket(pkt, inputFormatContext.streams(pkt.stream_index())); av_packet_unref(pkt); } hlsMuxer.writeTrailer(); - + //check the init file and m4s files there assertTrue(hlsMuxer.getFile().exists()); assertTrue(new File(hlsMuxer.getFile().getParentFile()+ "/" + streamId + "_init.mp4").exists()); assertTrue(new File(hlsMuxer.getFile().getParentFile()+ "/" + streamId + "000000003.m4s").exists()); - + assertTrue(MuxingTest.testFile(hlsMuxer.getFile().getAbsolutePath(), 107000)); - + assertEquals(0, hlsMuxer.getAudioNotWrittenCount()); assertEquals(0, hlsMuxer.getVideoNotWrittenCount()); - + avformat_close_input(inputFormatContext); - + //wait and check the files are deleted - + Awaitility.await().atMost(5 * 2 * 1000 + 3000, TimeUnit.MILLISECONDS).pollInterval(1, TimeUnit.SECONDS) .until(() -> { File[] filesTmp = hlsMuxer.getFile().getParentFile().listFiles(new FilenameFilter() { @@ -623,7 +623,7 @@ public boolean accept(File dir, String name) { }); return 0 == filesTmp.length; }); - + } @@ -653,19 +653,19 @@ public void testFFmpegReadPacket() { logger.info("codecpar.bit_rate(): {}\n" + - " codecpar.bits_per_coded_sample(): {} \n" + - " codecpar.bits_per_raw_sample(): {} \n" + - " codecpar.block_align(): {}\n" + - " codecpar.channel_layout(): {}\n" + - " codecpar.channels(): {}\n" + - " codecpar.codec_id(): {}\n" + - " codecpar.codec_tag(): {}\n" + - " codecpar.codec_type(): {} \n" + - " codecpar.format(): {}\n" + - " codecpar.frame_size():{} \n" + - " codecpar.level():{} \n" + - " codecpar.profile():{} \n" + - " codecpar.sample_rate(): {}", + " codecpar.bits_per_coded_sample(): {} \n" + + " codecpar.bits_per_raw_sample(): {} \n" + + " codecpar.block_align(): {}\n" + + " codecpar.channel_layout(): {}\n" + + " codecpar.channels(): {}\n" + + " codecpar.codec_id(): {}\n" + + " codecpar.codec_tag(): {}\n" + + " codecpar.codec_type(): {} \n" + + " codecpar.format(): {}\n" + + " codecpar.frame_size():{} \n" + + " codecpar.level():{} \n" + + " codecpar.profile():{} \n" + + " codecpar.sample_rate(): {}", codecpar.bit_rate(), codecpar.bits_per_coded_sample(), @@ -693,10 +693,10 @@ public void testFFmpegReadPacket() { logger.info(" pkt.duration():{} \n" + - " pkt.flags(): {} \n" + - " pkt.pos(): {}\n" + - " pkt.size(): {}\n" + - " pkt.stream_index():{} ", + " pkt.flags(): {} \n" + + " pkt.pos(): {}\n" + + " pkt.size(): {}\n" + + " pkt.stream_index():{} ", pkt.duration(), pkt.flags(), pkt.pos(), @@ -727,7 +727,7 @@ public void testFFmpegReadPacket() { } catch (Exception e) { e.printStackTrace(); } - + avformat_close_input(inputFormatContext); } @@ -896,7 +896,7 @@ public void testHLSMuxerGetOutputURLAndSegmentFilename() { String subFolder = "subfolder/"; hlsMuxer.setHlsParameters("1", "1", null, null, null, null); - + File[] file = new File[1]; file[0] = Mockito.mock(File.class); Mockito.when(file[0].exists()).thenReturn(true); @@ -923,7 +923,7 @@ public void testHLSMuxerGetOutputURLAndSegmentFilename() { String subFolder = "subfolder"; hlsMuxer.setHlsParameters("1", "1", null, null, null, null); - + File[] file = new File[1]; file[0] = Mockito.mock(File.class); Mockito.when(file[0].exists()).thenReturn(true); @@ -948,7 +948,7 @@ public void testHLSMuxerGetOutputURLAndSegmentFilename() { HLSMuxer hlsMuxer = Mockito.spy(new HLSMuxer(vertx, storageClient, "streams", 0b010, null, false)); String streamId = "streamId"; hlsMuxer.setHlsParameters("1", "1", null, null, null, null); - + File[] file = new File[1]; file[0] = Mockito.mock(File.class); Mockito.when(file[0].exists()).thenReturn(true); @@ -1595,7 +1595,7 @@ public void testMuxAdaptorEnableSettingsPreviewCreatePeriod() { assertEquals(createPreviewPeriod, muxAdaptor.getPreviewCreatePeriod()); } - + @Test public void testClientBroadcastStreamStartPublish() { @@ -1604,25 +1604,25 @@ public void testClientBroadcastStreamStartPublish() { ClientBroadcastStream clientBroadcastStream = Mockito.spy(clientBroadcastStreamReal); StreamCodecInfo info = new StreamCodecInfo(); clientBroadcastStream.setCodecInfo(info); - + IStreamCapableConnection conn = Mockito.mock(IStreamCapableConnection.class); Mockito.doReturn(conn).when(clientBroadcastStream).getConnection(); - - // IContext context = conn.getScope().getContext(); - // ApplicationContext appCtx = context.getApplicationContext(); + + // IContext context = conn.getScope().getContext(); + // ApplicationContext appCtx = context.getApplicationContext(); appScope = (WebScope) applicationContext.getBean("web.scope"); - + Mockito.when(conn.getScope()).thenReturn(appScope); - + assertNull(clientBroadcastStream.getMuxAdaptor()); - + clientBroadcastStream.startPublishing(); - + //because no streamId assertNull(clientBroadcastStream.getMuxAdaptor()); - - + + clientBroadcastStream.setPublishedName("streamId"); clientBroadcastStream.startPublishing(); assertNotNull(clientBroadcastStream.getMuxAdaptor()); @@ -1701,23 +1701,23 @@ public void testMuxingSimultaneously() { } } - + @Test public void testIsEncoderAdaptorShouldBeTried() { - + AppSettings appSettingsLocal = new AppSettings(); appSettingsLocal.setWebRTCEnabled(false); appSettingsLocal.setForceDecoding(false); - + assertFalse(MuxAdaptor.isEncoderAdaptorShouldBeTried(null, appSettingsLocal)); - + appSettingsLocal.setWebRTCEnabled(true); assertTrue(MuxAdaptor.isEncoderAdaptorShouldBeTried(null, appSettingsLocal)); - + appSettingsLocal.setWebRTCEnabled(false); appSettingsLocal.setForceDecoding(true); assertTrue(MuxAdaptor.isEncoderAdaptorShouldBeTried(null, appSettingsLocal)); - + appSettingsLocal.setWebRTCEnabled(true); appSettingsLocal.setForceDecoding(true); assertTrue(MuxAdaptor.isEncoderAdaptorShouldBeTried(null, appSettingsLocal)); @@ -1727,10 +1727,10 @@ public void testIsEncoderAdaptorShouldBeTried() { appSettingsLocal.setEncoderSettings(null); assertFalse(MuxAdaptor.isEncoderAdaptorShouldBeTried(null, appSettingsLocal)); - + appSettingsLocal.setEncoderSettings(Arrays.asList()); assertFalse(MuxAdaptor.isEncoderAdaptorShouldBeTried(null, appSettingsLocal)); - + appSettingsLocal.setEncoderSettings(Arrays.asList(new EncoderSettings(144, 150000, 32000, true))); assertTrue(MuxAdaptor.isEncoderAdaptorShouldBeTried(null, appSettingsLocal)); @@ -1740,8 +1740,8 @@ public void testIsEncoderAdaptorShouldBeTried() { broadcast.setEncoderSettingsList(Arrays.asList()); assertFalse(MuxAdaptor.isEncoderAdaptorShouldBeTried(broadcast, appSettingsLocal)); - - + + broadcast.setEncoderSettingsList(Arrays.asList(new EncoderSettings(144, 150000, 32000, true))); assertTrue(MuxAdaptor.isEncoderAdaptorShouldBeTried(broadcast, appSettingsLocal)); @@ -2016,9 +2016,9 @@ public void testApplicationStreamLimit() { long activeBroadcastCountFinal = activeBroadcastCount; Awaitility.await().atMost(5, TimeUnit.SECONDS).pollInterval(1, TimeUnit.SECONDS) - .until(() -> { - return activeBroadcastCountFinal + 2 == appAdaptor.getDataStore().getActiveBroadcastCount(); - }); + .until(() -> { + return activeBroadcastCountFinal + 2 == appAdaptor.getDataStore().getActiveBroadcastCount(); + }); if (activeBroadcastCount == 1) { Mockito.verify(appAdaptor, timeout(1000)).stopStreaming(Mockito.any()); @@ -2053,15 +2053,15 @@ public void testAbsoluteStartTimeMs() { when(stream.getAbsoluteStartTimeMs()).thenReturn(absoluteTimeMS); Awaitility.await().atMost(5, TimeUnit.SECONDS).pollInterval(1, TimeUnit.SECONDS) - .until(() -> - appAdaptor.getDataStore().get(streamId).getAbsoluteStartTimeMs() == absoluteTimeMS); + .until(() -> + appAdaptor.getDataStore().get(streamId).getAbsoluteStartTimeMs() == absoluteTimeMS); spyAdaptor.stopPublish(stream.getPublishedName()); Awaitility.await().atMost(5, TimeUnit.SECONDS).pollInterval(1, TimeUnit.SECONDS) - .until(() -> - appAdaptor.getDataStore().get(streamId) == null); + .until(() -> + appAdaptor.getDataStore().get(streamId) == null); } @@ -2202,7 +2202,7 @@ public void testMp4MuxingHighProfileDelayedVideo() { int finalDuration = 20000; Awaitility.await().atMost(10, TimeUnit.SECONDS).pollInterval(2, TimeUnit.SECONDS).until(() -> - MuxingTest.testFile(muxAdaptor.getMuxerList().get(0).getFile().getAbsolutePath(), finalDuration)); + MuxingTest.testFile(muxAdaptor.getMuxerList().get(0).getFile().getAbsolutePath(), finalDuration)); assertEquals(1640, MuxingTest.videoStartTimeMs); assertEquals(0, MuxingTest.audioStartTimeMs); @@ -2623,7 +2623,7 @@ public File testMp4Muxing(String name, boolean shortVersion, boolean checkDurati if (checkDuration) { int finalDuration = duration; Awaitility.await().atMost(10, TimeUnit.SECONDS).pollInterval(2, TimeUnit.SECONDS).until(() -> - MuxingTest.testFile(muxAdaptor.getMuxerList().get(0).getFile().getAbsolutePath(), finalDuration)); + MuxingTest.testFile(muxAdaptor.getMuxerList().get(0).getFile().getAbsolutePath(), finalDuration)); } return muxAdaptor.getMuxerList().get(0).getFile(); } catch (Exception e) { @@ -2633,12 +2633,12 @@ public File testMp4Muxing(String name, boolean shortVersion, boolean checkDurati logger.info("leaving testMp4Muxing"); return null; } - + @Test public void testHLSMuxerCodecSupported() { HLSMuxer hlsMuxerTester = new HLSMuxer(vertx, null, "streams", 1, null, false); - + assertFalse(hlsMuxerTester.isCodecSupported(AV_CODEC_ID_VP8)); assertTrue(hlsMuxerTester.isCodecSupported(AV_CODEC_ID_AC3)); assertTrue(hlsMuxerTester.isCodecSupported(AV_CODEC_ID_AAC)); @@ -2705,10 +2705,10 @@ public void updateStreamQualityParameters() { Awaitility.await().pollDelay(MuxAdaptor.STAT_UPDATE_PERIOD_MS + 1000, TimeUnit.MILLISECONDS) - .atMost(MuxAdaptor.STAT_UPDATE_PERIOD_MS * 2, TimeUnit.MILLISECONDS).until(() -> { - muxAdaptor.updateStreamQualityParameters(streamId, null, 1.0123, 12120); - return true; - }); + .atMost(MuxAdaptor.STAT_UPDATE_PERIOD_MS * 2, TimeUnit.MILLISECONDS).until(() -> { + muxAdaptor.updateStreamQualityParameters(streamId, null, 1.0123, 12120); + return true; + }); Awaitility.await().atMost(MuxAdaptor.STAT_UPDATE_PERIOD_MS + 1000, TimeUnit.MILLISECONDS).until(() -> { Broadcast broadcastTmp = muxAdaptor.getDataStore().get(streamId); @@ -2991,10 +2991,10 @@ public void testMp4MuxingWithDirectParams() { mp4Muxer.writeTrailer(); Awaitility.await().atMost(20, TimeUnit.SECONDS) - .pollInterval(1, TimeUnit.SECONDS) - .until(() -> { - return MuxingTest.testFile("webapps/junit/streams/" + streamName + ".mp4", 10000); - }); + .pollInterval(1, TimeUnit.SECONDS) + .until(() -> { + return MuxingTest.testFile("webapps/junit/streams/" + streamName + ".mp4", 10000); + }); } @@ -3019,9 +3019,9 @@ public void testHLSMuxingWithDirectParams() { String streamName = "stream_name_" + (int) (Math.random() * 10000); //init hlsMuxer.init(appScope, streamName, 0, null, 0); - + hlsMuxer.setId3Enabled(true); - + //add stream int width = 640; int height = 480; @@ -3398,15 +3398,15 @@ public boolean accept(File dir, String name) { //wait to let hls muxer delete ts and m3u8 file Awaitility.await().atMost(hlsListSize * hlsTime * 1000 + 3000, TimeUnit.MILLISECONDS).pollInterval(1, TimeUnit.SECONDS) - .until(() -> { - File[] filesTmp = dir.listFiles(new FilenameFilter() { - @Override - public boolean accept(File dir, String name) { - return name.endsWith(".ts") || name.endsWith(".m3u8"); - } - }); - return 0 == filesTmp.length; - }); + .until(() -> { + File[] filesTmp = dir.listFiles(new FilenameFilter() { + @Override + public boolean accept(File dir, String name) { + return name.endsWith(".ts") || name.endsWith(".m3u8"); + } + }); + return 0 == filesTmp.length; + }); assertFalse(hlsFile.exists()); @@ -3465,6 +3465,167 @@ public void feedMuxAdaptor(FLVReader flvReader, List muxAdaptorList, } } + @Test + public void testStreamSpeed() throws IOException { + + int hlsListSize = 3; + int hlsTime = 2; + + getAppSettings().setMp4MuxingEnabled(false); + getAppSettings().setAddDateTimeToMp4FileName(false); + getAppSettings().setHlsMuxingEnabled(true); + getAppSettings().setDeleteHLSFilesOnEnded(true); + getAppSettings().setHlsTime(String.valueOf(hlsTime)); + getAppSettings().setHlsListSize(String.valueOf(hlsListSize)); + getAppSettings().setUploadExtensionsToS3(2); + + + String name = "stream_id_speed"; + + if (appScope == null) { + appScope = (WebScope) applicationContext.getBean("web.scope"); + logger.debug("Application / web scope: {}", appScope); + assertTrue(appScope.getDepth() == 1); + } + + ClientBroadcastStream clientBroadcastStream = new ClientBroadcastStream(); + StreamCodecInfo info = new StreamCodecInfo(); + clientBroadcastStream.setCodecInfo(info); + + MuxAdaptor muxAdaptor = MuxAdaptor.initializeMuxAdaptor(clientBroadcastStream, null, false, appScope); + + File file = null; + + + file = new File("src/test/resources/test.flv"); //ResourceUtils.getFile(this.getClass().getResource("test.flv")); + final FLVReader flvReader = new FLVReader(file); + + logger.info("f path:" + file.getAbsolutePath()); + assertTrue(file.exists()); + Broadcast broadcast = new Broadcast(); + try { + broadcast.setStreamId(name); + } catch (Exception e) { + e.printStackTrace(); + } + + muxAdaptor.setBroadcast(broadcast); + boolean result = muxAdaptor.init(appScope, name, false); + assert (result); + + muxAdaptor.start(); + + + long packetSize = 0; + + boolean firstAudioPacketReceived = false; + boolean firstVideoPacketReceived = false; + while (flvReader.hasMoreTags()) { + ITag readTag = flvReader.readTag(); + StreamPacket streamPacket = new StreamPacket(readTag); + if (!firstAudioPacketReceived && streamPacket.getDataType() == Constants.TYPE_AUDIO_DATA) { + IAudioStreamCodec audioStreamCodec = AudioCodecFactory.getAudioCodec(streamPacket.getData().position(0)); + info.setAudioCodec(audioStreamCodec); + audioStreamCodec.addData(streamPacket.getData().position(0)); + info.setHasAudio(true); + firstAudioPacketReceived = true; + } else if (!firstVideoPacketReceived && streamPacket.getDataType() == Constants.TYPE_VIDEO_DATA) { + IVideoStreamCodec videoStreamCodec = VideoCodecFactory.getVideoCodec(streamPacket.getData().position(0)); + videoStreamCodec.addData(streamPacket.getData().position(0)); + info.setVideoCodec(videoStreamCodec); + info.setHasVideo(true); + firstVideoPacketReceived = true; + + } + + streamPacket = new StreamPacket(readTag); + int bodySize = streamPacket.getData().position(0).limit(); + byte[] data = new byte[bodySize]; + streamPacket.getData().get(data); + + streamPacket.setData(IoBuffer.wrap(data)); + + muxAdaptor.packetReceived(null, streamPacket); + + packetSize++; + if (packetSize > 10000) + { + break; + } + } + + + + Awaitility.await().atMost(200, TimeUnit.SECONDS).until(() -> muxAdaptor.isRecording()); + logger.info("----- 1. speed:{}", muxAdaptor.getLatestSpeed()); + + Awaitility.await().atMost(10, TimeUnit.SECONDS).until(() -> 50 < muxAdaptor.getLatestSpeed()); + + + packetSize = 0; + while (flvReader.hasMoreTags()) { + ITag readTag = flvReader.readTag(); + StreamPacket streamPacket = new StreamPacket(readTag); + if (!firstAudioPacketReceived && streamPacket.getDataType() == Constants.TYPE_AUDIO_DATA) { + IAudioStreamCodec audioStreamCodec = AudioCodecFactory.getAudioCodec(streamPacket.getData().position(0)); + info.setAudioCodec(audioStreamCodec); + audioStreamCodec.addData(streamPacket.getData().position(0)); + info.setHasAudio(true); + firstAudioPacketReceived = true; + } else if (!firstVideoPacketReceived && streamPacket.getDataType() == Constants.TYPE_VIDEO_DATA) { + IVideoStreamCodec videoStreamCodec = VideoCodecFactory.getVideoCodec(streamPacket.getData().position(0)); + videoStreamCodec.addData(streamPacket.getData().position(0)); + info.setVideoCodec(videoStreamCodec); + info.setHasVideo(true); + firstVideoPacketReceived = true; + + } + + streamPacket = new StreamPacket(readTag); + int bodySize = streamPacket.getData().position(0).limit(); + byte[] data = new byte[bodySize]; + streamPacket.getData().get(data); + + streamPacket.setData(IoBuffer.wrap(data)); + + muxAdaptor.packetReceived(null, streamPacket); + + packetSize++; + if (packetSize > 300) + { + break; + } + + try { + //slow down the process to check the speed + Thread.sleep(100); + } catch (InterruptedException e) { + e.printStackTrace(); + } + + if (muxAdaptor.getLatestSpeed() < 0.7) { + //break the loop if the speed is less than 0.7 + break; + } + } + + + logger.info("----- 2. speed:{}", muxAdaptor.getLatestSpeed()); + + Awaitility.await().atMost(200, TimeUnit.SECONDS).until(() -> 0.7 > muxAdaptor.getLatestSpeed()); + + + muxAdaptor.stop(true); + + flvReader.close(); + + Awaitility.await().atMost(10, TimeUnit.SECONDS).until(() -> !muxAdaptor.isRecording()); + + + getAppSettings().setDeleteHLSFilesOnEnded(false); + + } + @Test public void testHLSNaming() { @@ -3592,15 +3753,15 @@ public boolean accept(File dir, String name) { //wait to let hls muxer delete ts and m3u8 file Awaitility.await().atMost(hlsListSize * hlsTime * 1000 + 3000, TimeUnit.MILLISECONDS).pollInterval(1, TimeUnit.SECONDS) - .until(() -> { - File[] filesTmp = dir.listFiles(new FilenameFilter() { - @Override - public boolean accept(File dir, String name) { - return name.endsWith(".ts") || name.endsWith(".m3u8"); - } - }); - return 0 == filesTmp.length; - }); + .until(() -> { + File[] filesTmp = dir.listFiles(new FilenameFilter() { + @Override + public boolean accept(File dir, String name) { + return name.endsWith(".ts") || name.endsWith(".m3u8"); + } + }); + return 0 == filesTmp.length; + }); } catch (Exception e) { @@ -3714,15 +3875,15 @@ public boolean accept(File dir, String name) { //wait to let hls muxer delete ts and m3u8 file Awaitility.await().atMost(hlsListSize * hlsTime * 1000 + 3000, TimeUnit.MILLISECONDS).pollInterval(1, TimeUnit.SECONDS) - .until(() -> { - File[] filesTmp = dir.listFiles(new FilenameFilter() { - @Override - public boolean accept(File dir, String name) { - return name.endsWith(".ts") || name.endsWith(".m3u8"); - } - }); - return 0 == filesTmp.length; - }); + .until(() -> { + File[] filesTmp = dir.listFiles(new FilenameFilter() { + @Override + public boolean accept(File dir, String name) { + return name.endsWith(".ts") || name.endsWith(".m3u8"); + } + }); + return 0 == filesTmp.length; + }); assertFalse(hlsFile.exists()); @@ -4126,10 +4287,10 @@ public void testAnalyzeTime() { muxAdaptor.start(); Awaitility.await().atLeast(getAppSettings().getMaxAnalyzeDurationMS() * 2, TimeUnit.MILLISECONDS) - .atMost(getAppSettings().getMaxAnalyzeDurationMS() * 2 + 1000, TimeUnit.MILLISECONDS) - .until(() -> { - return muxAdaptor.isStopRequestExist(); - }); + .atMost(getAppSettings().getMaxAnalyzeDurationMS() * 2 + 1000, TimeUnit.MILLISECONDS) + .until(() -> { + return muxAdaptor.isStopRequestExist(); + }); Mockito.verify(muxAdaptor, Mockito.timeout(500)).closeRtmpConnection(); @@ -4495,8 +4656,8 @@ public void testID3Timing() { assertEquals(lastPts, pkt.pts()); assertEquals(lastPts, pkt.dts()); - - + + HLSMuxer.logError(-1, "test error message", "stream1"); HLSMuxer.logError(0, "test error message", "stream1"); HLSMuxer.logError(1, "test error message", "stream1"); @@ -4602,40 +4763,40 @@ public void testSetSEIData() { String data = "some data to put frame"; muxAdaptorReal.addSEIData(data); verify(hlsMuxer, times(1)).setSeiData(data); - - + + { hlsMuxer = new HLSMuxer(Mockito.mock(Vertx.class), Mockito.mock(StorageClient.class), "streams", 7, null, false); - - + + String streamId = "stream_name_" + (int) (Math.random() * 10000); hlsMuxer.setHlsParameters("5", "2", "event", null, null, "mpegts"); - + //init hlsMuxer.init(appScope, streamId, 0, null, 0); - + int width = 640; int height = 480; boolean addStreamResult = hlsMuxer.addVideoStream(width, height, null, AV_CODEC_ID_H264, 0, false, null); assertTrue(addStreamResult); - + //prepare io boolean prepareIOresult = hlsMuxer.prepareIO(); assertTrue(prepareIOresult); - + String seiData = "test_data"; hlsMuxer.setSeiData(seiData); - + //it's annexb format, it means there is not mp4toannexb format //it should be 4 bytes for start code, 1 byte for nal type, 1 byte for sei type, 1 byte for payload size, 16 byste for UUID, data length, 1 byte for alignment assertEquals(4 + 1 + 1 + 1 + 16 + seiData.length() + 1, hlsMuxer.getPendingSEIData().limit()); - + assertEquals(0, hlsMuxer.getPendingSEIData().get(0)); assertEquals(0, hlsMuxer.getPendingSEIData().get(1)); assertEquals(0, hlsMuxer.getPendingSEIData().get(2)); assertEquals(1, hlsMuxer.getPendingSEIData().get(3)); - - + + try { FileInputStream fis = new FileInputStream("src/test/resources/frame0"); byte[] byteArray = fis.readAllBytes(); @@ -4647,8 +4808,8 @@ public void testSetSEIData() { AVPacket videoPkt = avcodec.av_packet_alloc(); av_init_packet(videoPkt); - - + + videoPkt.stream_index(0); videoPkt.pts(now); videoPkt.dts(now); @@ -4661,12 +4822,12 @@ public void testSetSEIData() { videoPkt.position(0); videoPkt.duration(5); hlsMuxer.writePacket(videoPkt, new AVCodecContext()); - + assertNull(hlsMuxer.getPendingSEIData()); av_packet_unref(videoPkt); - + } catch (IOException e) { e.printStackTrace(); @@ -4675,122 +4836,122 @@ public void testSetSEIData() { //write trailer hlsMuxer.writeTrailer(); - - + + } - + { hlsMuxer = new HLSMuxer(vertx, Mockito.mock(StorageClient.class), "streams", 7, null, false); - + String streamId = "stream_name_" + (int) (Math.random() * 10000); hlsMuxer.setHlsParameters("5", "2", "event", null, null, "fmp4"); - + //init hlsMuxer.init(appScope, streamId, 0, null, 0); - + int width = 640; int height = 480; boolean addStreamResult = hlsMuxer.addVideoStream(width, height, null, AV_CODEC_ID_H264, 0, false, null); assertTrue(addStreamResult); - + //prepare io boolean prepareIOresult = hlsMuxer.prepareIO(); assertTrue(prepareIOresult); - + String seiData = ""; - + for (int i = 0; i < 300; i++) { seiData += "i"; } - + //size is more than 255, it means data length is 2 bytes hlsMuxer.setSeiData(seiData); - + //it's annexb format, it means there is not mp4toannexb format //it should be 4 bytes for start code, 1 byte for nal type, 1 byte for sei type, 2 byte for payload size, 16 byste for UUID, data length, 1 byte for alignment int totalLength = 4 + 1 + 1 + 2 + 16 + seiData.length() + 1; assertEquals(totalLength, hlsMuxer.getPendingSEIData().limit()); - + //it should totalLength-4 because 4 bytes are length code assertEquals(totalLength-4, hlsMuxer.getPendingSEIData().getInt()); - + } - + { hlsMuxer = new HLSMuxer(vertx, Mockito.mock(StorageClient.class), "streams", 7, null, false); - + String streamId = "stream_name_" + (int) (Math.random() * 10000); hlsMuxer.setHlsParameters("5", "2", "event", null, null, "mpegts"); - + //init hlsMuxer.init(appScope, streamId, 0, null, 0); - + int width = 640; int height = 480; boolean addStreamResult = hlsMuxer.addVideoStream(width, height, null, AV_CODEC_ID_H265, 0, false, null); assertTrue(addStreamResult); - + //prepare io boolean prepareIOresult = hlsMuxer.prepareIO(); assertTrue(prepareIOresult); - + String seiData = ""; - + for (int i = 0; i < 300; i++) { seiData += "i"; } - + //size is more than 255, it means data length is 2 bytes hlsMuxer.setSeiData(seiData); - + //it's annexb format, it means there is not mp4toannexb format //it should be 4 bytes for start code, 2 byte for nal type(Because HEVC), 1 byte for sei type, 2 byte for payload size, 16 byste for UUID, data length, 1 byte for alignment assertEquals(4 + 2 + 1 + 2 + 16 + seiData.length() + 1, hlsMuxer.getPendingSEIData().limit()); - + assertEquals(0, hlsMuxer.getPendingSEIData().get(0)); assertEquals(0, hlsMuxer.getPendingSEIData().get(1)); assertEquals(0, hlsMuxer.getPendingSEIData().get(2)); assertEquals(1, hlsMuxer.getPendingSEIData().get(3)); - - + + } - + { hlsMuxer = new HLSMuxer(vertx, Mockito.mock(StorageClient.class), "streams", 7, null, false); - + String streamId = "stream_name_" + (int) (Math.random() * 10000); hlsMuxer.setHlsParameters("5", "2", "event", null, null, "mpegts"); - + //init hlsMuxer.init(appScope, streamId, 0, null, 0); - + AVChannelLayout channelLayout = new AVChannelLayout(); av_channel_layout_default(channelLayout, 2); boolean addStreamResult = hlsMuxer.addAudioStream(44100, channelLayout, AV_CODEC_ID_AAC, 0); assertTrue(addStreamResult); - + //prepare io boolean prepareIOresult = hlsMuxer.prepareIO(); assertTrue(prepareIOresult); - + String seiData = ""; - + for (int i = 0; i < 300; i++) { seiData += "i"; } - + //size is more than 255, it means data length is 2 bytes hlsMuxer.setSeiData(seiData); - + //it's annexb format, it means there is not mp4toannexb format //it should be 4 bytes for start code, 2 byte for nal type(Because HEVC), 1 byte for sei type, 2 byte for payload size, 16 byste for UUID, data length, 1 byte for alignment assertNull(hlsMuxer.getPendingSEIData()); - + } - - + + } diff --git a/src/test/java/io/antmedia/test/StreamSchedularUnitTest.java b/src/test/java/io/antmedia/test/StreamSchedularUnitTest.java index 6d20d710d..7d445391d 100644 --- a/src/test/java/io/antmedia/test/StreamSchedularUnitTest.java +++ b/src/test/java/io/antmedia/test/StreamSchedularUnitTest.java @@ -1067,140 +1067,11 @@ public void testIPTVStream() { * */ - //TODO: Comment out the test because it is not compatible with latest version //@Test public void testBandwidth() { - - boolean deleteHLSFilesOnExit = getAppSettings().isDeleteHLSFilesOnEnded(); - getAppSettings().setDeleteHLSFilesOnEnded(false); - getAppSettings().setRtspTimeoutDurationMs(15000); - - File f = new File("target/test.db"); - if (f.exists()) { - try { - Files.delete(f.toPath()); - } catch (IOException e) { - e.printStackTrace(); - } - } - DataStore dataStore = app.getDataStore(); //new MapDBStore("target/test.db"); //applicationContext.getBean(IDataStore.BEAN_NAME); - - //assertNotNull(dataStore); - - //DataStoreFactory dsf = Mockito.mock(DataStoreFactory.class); - //Mockito.when(dsf.getDataStore()).thenReturn(dataStore); - //app.setDataStoreFactory(dsf); - - //set mapdb datastore to stream fetcher because in memory datastore just have references and updating broadcst - // object updates the reference in inmemorydatastore - app.getStreamFetcherManager().setDatastore(dataStore); - - - logger.info("running testBandwidth"); - Application.enableSourceHealthUpdate = true; - assertNotNull(dataStore); - - startCameraEmulator(); - - Broadcast newSource = new Broadcast("testBandwidth", "10.2.40.63:8080", "admin", "admin", - "rtsp://127.0.0.1:6554/test.flv", - AntMediaApplicationAdapter.STREAM_SOURCE); - - try { - newSource.setStreamId("zombiSource" + RandomStringUtils.randomAlphanumeric(12)); - } catch (Exception e) { - e.printStackTrace(); - } - //add stream to data store - dataStore.save(newSource); - - Broadcast newZombiSource = new Broadcast("testBandwidth", "10.2.40.63:8080", "admin", "admin", - "rtsp://127.0.0.1:6554/test.flv", - AntMediaApplicationAdapter.STREAM_SOURCE); - try { - newZombiSource.setStreamId("newZombiSource" + RandomStringUtils.randomAlphanumeric(12)); - } catch (Exception e) { - e.printStackTrace(); - } - - newZombiSource.setZombi(true); - //add second stream to datastore - dataStore.save(newZombiSource); - - //let stream fetching start - app.getStreamFetcherManager().testSetStreamCheckerInterval(5000); - //restart becaue sometimes connection drops of the network limitation - app.getStreamFetcherManager().setRestartStreamAutomatically(true); - app.getStreamFetcherManager().startStreaming(newSource); - app.getStreamFetcherManager().startStreaming(newZombiSource); - - - - Awaitility.await().atMost(MuxAdaptor.STAT_UPDATE_PERIOD_MS*2, TimeUnit.MILLISECONDS).until(() -> { - return dataStore.get(newZombiSource.getStreamId()).getSpeed() != 0; - }); - - logger.info("before first control"); - - List broadcastList = dataStore.getBroadcastList(0, 20, null, null, null, null); - - Broadcast fetchedBroadcast = null; - - for (Broadcast broadcast : broadcastList) { - - logger.info("broadcast name: " + broadcast.getName() + " broadcast status :" + broadcast.getStatus() + " broadcast is zombi: " + broadcast.isZombi()); - if(broadcast.isZombi()) { - - fetchedBroadcast=broadcast; - break; - } - } - - assertNotNull(fetchedBroadcast); - assertEquals(fetchedBroadcast.getStreamId(), newZombiSource.getStreamId()); - assertNotNull(fetchedBroadcast.getSpeed()); - - - Awaitility.await().atMost(MuxAdaptor.STAT_UPDATE_PERIOD_MS*2, TimeUnit.MILLISECONDS).pollInterval(1, TimeUnit.SECONDS).until(() -> { - Broadcast stream = dataStore.get(newSource.getStreamId()); - logger.info("speed {} stream id: {}" , stream.getSpeed(), stream.getStreamId()) ; - return stream != null && Math.abs(stream.getSpeed()-1) < 0.2; - }); - - assertEquals(0, limitNetworkInterfaceBandwidth(findActiveInterface())); - - logger.info("Checking quality is again"); - - Awaitility.await().atMost(MuxAdaptor.STAT_UPDATE_PERIOD_MS*6, TimeUnit.MILLISECONDS).pollInterval(1, TimeUnit.SECONDS).until(() -> { - Broadcast streamTmp = dataStore.get(newSource.getStreamId()); - logger.info("speed {}" , streamTmp.getSpeed()) ; - - return streamTmp != null && streamTmp.getSpeed() < 0.8; - // the critical thing is the speed which less that 0.8 - }); - - assertEquals(0, resetNetworkInterface(findActiveInterface())); - - for (Broadcast broadcast: broadcastList) { - app.getStreamFetcherManager().stopStreaming(broadcast.getStreamId()); - } - - Awaitility.await().atMost(MuxAdaptor.STAT_UPDATE_PERIOD_MS*2, TimeUnit.MILLISECONDS).pollInterval(1, TimeUnit.SECONDS).until(() -> { - return app.getStreamFetcherManager().getStreamFetcherList().size() == 0; - }); - - //list size should be zero - //assertEquals(0, app.getStreamFetcherManager().getStreamFetcherList().size()); - logger.info("leaving testBandwidth"); - - Application.enableSourceHealthUpdate = false; - - getAppSettings().setDeleteHLSFilesOnEnded(deleteHLSFilesOnExit); - getAppSettings().setRtspTimeoutDurationMs(5000); - - - stopCameraEmulator(); + //This test is moved to {@link @MuxerUnitTest#testStreamSpeed} because it uses wondershaper and there is some kind of incompatibility with wondershaper and + //new versions } From 2650ec050ff1f0ec084233b921386f0576acf500 Mon Sep 17 00:00:00 2001 From: mekya Date: Mon, 1 Jul 2024 16:38:42 +0300 Subject: [PATCH 10/12] Increase code coverage --- src/main/java/io/antmedia/muxer/HLSMuxer.java | 2 +- src/test/java/io/antmedia/test/MuxerUnitTest.java | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/io/antmedia/muxer/HLSMuxer.java b/src/main/java/io/antmedia/muxer/HLSMuxer.java index 97c01321e..be66c1ddb 100644 --- a/src/main/java/io/antmedia/muxer/HLSMuxer.java +++ b/src/main/java/io/antmedia/muxer/HLSMuxer.java @@ -265,7 +265,7 @@ public synchronized void writePacket(AVPacket pkt, AVRational inputTimebase, AVR - logger.info("side data limit:{} for streamId:{}", pendingSEIData.limit(), streamId); + logger.info("sei data size:{} for streamId:{}", pendingSEIData.limit(), streamId); //inject SEI NAL Unit pendingSEIData.rewind(); diff --git a/src/test/java/io/antmedia/test/MuxerUnitTest.java b/src/test/java/io/antmedia/test/MuxerUnitTest.java index 344437d9e..e090bd597 100644 --- a/src/test/java/io/antmedia/test/MuxerUnitTest.java +++ b/src/test/java/io/antmedia/test/MuxerUnitTest.java @@ -3064,6 +3064,8 @@ public void testHLSMuxingWithDirectParams() { if (i == 0) { videoPkt.flags(videoPkt.flags() | AV_PKT_FLAG_KEY); + String seiData = "test_data"; + hlsMuxer.setSeiData(seiData); } videoPkt.data(new BytePointer(encodedVideoFrame)); videoPkt.size(encodedVideoFrame.limit()); From a66a271e919cb75aa70bfe993f828ea123353ce2 Mon Sep 17 00:00:00 2001 From: mekya Date: Mon, 1 Jul 2024 16:54:06 +0300 Subject: [PATCH 11/12] Remove excluded test from unit tests --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 6a198db42..8bddccf78 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -66,7 +66,7 @@ jobs: run: | export RELEASE_VERSION="$(mvn -q -Dexec.executable="echo" -Dexec.args='${project.version}' --non-recursive exec:exec)" echo $RELEASE_VERSION - mvn clean org.jacoco:jacoco-maven-plugin:prepare-agent package -Dtest=!*/integration/*,!ConsoleRestV2UnitTest,!MuxerUnitTest -Dorg.bytedeco.javacpp.logger.debug=true org.jacoco:jacoco-maven-plugin:report sonar:sonar -Dmaven.javadoc.skip=true --quiet + mvn clean org.jacoco:jacoco-maven-plugin:prepare-agent package -Dtest=!*/integration/* -Dorg.bytedeco.javacpp.logger.debug=true org.jacoco:jacoco-maven-plugin:report sonar:sonar -Dmaven.javadoc.skip=true --quiet - name: Show MongoDB Log, Crash Log and Servis Status on failure if: failure() From b81f672e3fa804e23b977135cbd392455e3c55c2 Mon Sep 17 00:00:00 2001 From: mekya Date: Mon, 1 Jul 2024 18:05:39 +0300 Subject: [PATCH 12/12] Fix test case --- src/test/java/io/antmedia/test/AppSettingsUnitTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test/java/io/antmedia/test/AppSettingsUnitTest.java b/src/test/java/io/antmedia/test/AppSettingsUnitTest.java index 24bd8e9ce..70696ec8d 100644 --- a/src/test/java/io/antmedia/test/AppSettingsUnitTest.java +++ b/src/test/java/io/antmedia/test/AppSettingsUnitTest.java @@ -291,7 +291,7 @@ public void testSettings() { assertEquals("secretpublish", appSettings.getTimeTokenSecretForPublish()); - assertEquals(true, appSettings.isHwScalingEnabled()); + assertEquals(false, appSettings.isHwScalingEnabled()); appSettings.setHwScalingEnabled(false); assertEquals(false, appSettings.isHwScalingEnabled()); @@ -541,7 +541,7 @@ public void testUnsetAppSettings(AppSettings appSettings) { assertNull(appSettings.getTimeTokenSecretForPublish()); assertNull(appSettings.getTimeTokenSecretForPlay()); - assertTrue(appSettings.isHwScalingEnabled()); + assertFalse(appSettings.isHwScalingEnabled()); assertNotNull(appSettings.getSubscriberAuthenticationKey()); assertNull(appSettings.getFirebaseAccountKeyJSON());