diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index a8335efcf..a86521eda 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -6,7 +6,7 @@ on: [push, pull_request] jobs: analyze: name: actions-codeql-analyze - runs-on: ubuntu-latest + runs-on: ubuntu-20.04 strategy: fail-fast: false diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 9351c1ace..9c8c3b96b 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -3,6 +3,22 @@ name: "Test" # @see https://docs.github.com/en/actions/reference/workflow-syntax-for-github-actions#onpushpull_requestbranchestags on: [push, pull_request] +# The dependency graph: +# multiple-arch-armv7(13m) +# multiple-arch-aarch64(7m) +# cygwin64-cache(1m) +# cygwin64(6m) - Must depends on cygwin64-cache. +# fast(0s) - To limit all fastly run jobs after slow jobs. +# build-centos7(3m) +# build-ubuntu16(3m) +# build-ubuntu18(2m) +# build-ubuntu20(2m) +# build-cross-arm(3m) +# build-cross-aarch64(3m) +# multiple-arch-amd64(2m) +# utest(3m) +# coverage(3m) - Must depends on utest. + jobs: cygwin64-cache: name: cygwin64-cache @@ -66,7 +82,7 @@ jobs: name: build-centos7 runs-on: ubuntu-20.04 needs: - - utest + - fast steps: - name: Checkout repository uses: actions/checkout@v2 @@ -88,7 +104,7 @@ jobs: name: build-ubuntu16 runs-on: ubuntu-20.04 needs: - - utest + - fast steps: - name: Checkout repository uses: actions/checkout@v2 @@ -104,7 +120,7 @@ jobs: name: build-ubuntu18 runs-on: ubuntu-20.04 needs: - - utest + - fast steps: - name: Checkout repository uses: actions/checkout@v2 @@ -120,7 +136,7 @@ jobs: name: build-ubuntu20 runs-on: ubuntu-20.04 needs: - - utest + - fast steps: - name: Checkout repository uses: actions/checkout@v2 @@ -135,6 +151,8 @@ jobs: build-cross-arm: name: build-cross-arm runs-on: ubuntu-20.04 + needs: + - fast steps: - name: Checkout repository uses: actions/checkout@v2 @@ -148,6 +166,8 @@ jobs: build-cross-aarch64: name: build-cross-aarch64 runs-on: ubuntu-20.04 + needs: + - fast steps: - name: Checkout repository uses: actions/checkout@v2 @@ -158,21 +178,6 @@ jobs: outputs: SRS_BUILD_CROSS_AARCH64_DONE: ok - build: - name: build - needs: - - build-centos7 - - build-ubuntu16 - - build-ubuntu18 - - build-ubuntu20 - - build-cross-arm - - build-cross-aarch64 - runs-on: ubuntu-20.04 - steps: - - run: echo 'Build done' - outputs: - SRS_BUILD_DONE: ok - utest: name: utest runs-on: ubuntu-20.04 @@ -229,8 +234,8 @@ jobs: outputs: SRS_COVERAGE_DONE: ok - multile-arch-armv7: - name: multile-arch-armv7 + multiple-arch-armv7: + name: multiple-arch-armv7 runs-on: ubuntu-20.04 steps: - name: Checkout repository @@ -251,8 +256,8 @@ jobs: outputs: SRS_MULTIPLE_ARCH_ARMV7_DONE: ok - multile-arch-aarch64: - name: multile-arch-aarch64 + multiple-arch-aarch64: + name: multiple-arch-aarch64 runs-on: ubuntu-20.04 steps: - name: Checkout repository @@ -273,11 +278,11 @@ jobs: outputs: SRS_MULTIPLE_ARCH_AARCH64_DONE: ok - multile-arch-amd64: - name: multile-arch-amd64 + multiple-arch-amd64: + name: multiple-arch-amd64 runs-on: ubuntu-20.04 needs: - - utest + - fast steps: - name: Checkout repository uses: actions/checkout@v2 @@ -297,15 +302,30 @@ jobs: outputs: SRS_MULTIPLE_ARCH_AMD64_DONE: ok + fast: + name: fast + needs: + - cygwin64-cache + runs-on: ubuntu-20.04 + steps: + - run: echo 'Start fast jobs' + outputs: + SRS_FAST_DONE: ok + done: name: done needs: - cygwin64 - - build - coverage - - multile-arch-armv7 - - multile-arch-aarch64 - - multile-arch-amd64 + - build-centos7 + - build-ubuntu16 + - build-ubuntu18 + - build-ubuntu20 + - build-cross-arm + - build-cross-aarch64 + - multiple-arch-armv7 + - multiple-arch-aarch64 + - multiple-arch-amd64 runs-on: ubuntu-20.04 steps: - run: echo 'All done' diff --git a/trunk/conf/mp3.conf b/trunk/conf/mp3.conf new file mode 100644 index 000000000..d3e0f01ad --- /dev/null +++ b/trunk/conf/mp3.conf @@ -0,0 +1,19 @@ +listen 1935; +max_connections 1000; +daemon off; +srs_log_tank console; +http_server { + enabled on; + listen 8080; + dir ./objs/nginx/html; +} +vhost __defaultVhost__ { + http_remux { + enabled on; + mount [vhost]/[app]/[stream].flv; + } + hls { + enabled on; + hls_acodec mp3; + } +} diff --git a/trunk/conf/mp3.http.conf b/trunk/conf/mp3.http.conf new file mode 100644 index 000000000..7b198230f --- /dev/null +++ b/trunk/conf/mp3.http.conf @@ -0,0 +1,15 @@ +listen 1935; +max_connections 1000; +daemon off; +srs_log_tank console; +http_server { + enabled on; + listen 8080; + dir ./objs/nginx/html; +} +vhost __defaultVhost__ { + http_remux { + enabled on; + mount [vhost]/[app]/[stream].mp3; + } +} diff --git a/trunk/conf/mp3.ts.conf b/trunk/conf/mp3.ts.conf new file mode 100644 index 000000000..c7b7ddd40 --- /dev/null +++ b/trunk/conf/mp3.ts.conf @@ -0,0 +1,15 @@ +listen 1935; +max_connections 1000; +daemon off; +srs_log_tank console; +http_server { + enabled on; + listen 8080; + dir ./objs/nginx/html; +} +vhost __defaultVhost__ { + http_remux { + enabled on; + mount [vhost]/[app]/[stream].ts; + } +} diff --git a/trunk/doc/CHANGELOG.md b/trunk/doc/CHANGELOG.md index 50da61ca8..6a326341e 100644 --- a/trunk/doc/CHANGELOG.md +++ b/trunk/doc/CHANGELOG.md @@ -20,6 +20,7 @@ The changelog for SRS. ## SRS 5.0 Changelog +* v5.0, 2022-12-25, For [#296](https://github.com/ossrs/srs/issues/296): MP3: Support mp3 for RTMP/HLS/HTTP-FLV/HTTP-TS/HLS etc. v5.0.116 * v5.0, 2022-12-24, Fix [#3328](https://github.com/ossrs/srs/issues/3328): Docker: Avoiding duplicated copy files. v5.0.115 * v5.0, 2022-12-20, Merge [#3321](https://github.com/ossrs/srs/pull/3321): GB: Refine lazy object GC. v5.0.114 * v5.0, 2022-12-18, Merge [#3324](https://github.com/ossrs/srs/pull/3324): Asan: Support parse asan symbol backtrace log. v5.0.113 @@ -130,6 +131,7 @@ The changelog for SRS. ## SRS 4.0 Changelog +* v4.0, 2022-12-24, For [#296](https://github.com/ossrs/srs/issues/296): MP3: Fix bug for TS or HLS with mp3 codec. v4.0.269 * v4.0, 2022-11-22, Pick [#3079](https://github.com/ossrs/srs/issues/3079): WebRTC: Fix no audio and video issue for Firefox. v4.0.268 * v4.0, 2022-10-10, For [#2901](https://github.com/ossrs/srs/issues/2901): Edge: Fast disconnect and reconnect. v4.0.267 * v4.0, 2022-09-27, For [#3167](https://github.com/ossrs/srs/issues/3167): WebRTC: Refine sequence jitter algorithm. v4.0.266 diff --git a/trunk/src/app/srs_app_config.cpp b/trunk/src/app/srs_app_config.cpp index 539b0ee64..0f70ce1ae 100644 --- a/trunk/src/app/srs_app_config.cpp +++ b/trunk/src/app/srs_app_config.cpp @@ -1851,11 +1851,11 @@ srs_error_t SrsConfig::parse_options(int argc, char** argv) } if (show_version) { - fprintf(stderr, "%s\n", RTMP_SIG_SRS_VERSION); + fprintf(stdout, "%s\n", RTMP_SIG_SRS_VERSION); exit(0); } if (show_signature) { - fprintf(stderr, "%s\n", RTMP_SIG_SRS_SERVER); + fprintf(stdout, "%s\n", RTMP_SIG_SRS_SERVER); exit(0); } diff --git a/trunk/src/app/srs_app_hls.cpp b/trunk/src/app/srs_app_hls.cpp index a1a544631..cb0eb1308 100644 --- a/trunk/src/app/srs_app_hls.cpp +++ b/trunk/src/app/srs_app_hls.cpp @@ -202,6 +202,7 @@ SrsHlsMuxer::SrsHlsMuxer() async = new SrsAsyncCallWorker(); context = new SrsTsContext(); segments = new SrsFragmentWindow(); + latest_acodec_ = SrsAudioCodecIdForbidden; memset(key, 0, 16); memset(iv, 0, 16); @@ -263,6 +264,24 @@ int SrsHlsMuxer::deviation() return deviation_ts; } +SrsAudioCodecId SrsHlsMuxer::latest_acodec() +{ + // If current context writer exists, we query from it. + if (current && current->tscw) return current->tscw->acodec(); + + // Get the configured or updated config. + return latest_acodec_; +} + +void SrsHlsMuxer::set_latest_acodec(SrsAudioCodecId v) +{ + // Refresh the codec in context writer for current segment. + if (current && current->tscw) current->tscw->set_acodec(v); + + // Refresh the codec for future segments. + latest_acodec_ = v; +} + srs_error_t SrsHlsMuxer::initialize() { return srs_success; @@ -371,6 +390,8 @@ srs_error_t SrsHlsMuxer::segment_open() srs_warn("hls: use aac for other codec=%s", default_acodec_str.c_str()); } } + // Now that we know the latest audio codec in stream, use it. + if (latest_acodec_ != SrsAudioCodecIdForbidden) default_acodec = latest_acodec_; // load the default vcodec from config. SrsVideoCodecId default_vcodec = SrsVideoCodecIdAVC; @@ -969,6 +990,13 @@ srs_error_t SrsHlsController::on_sequence_header() srs_error_t SrsHlsController::write_audio(SrsAudioFrame* frame, int64_t pts) { srs_error_t err = srs_success; + + // Refresh the codec ASAP. + if (muxer->latest_acodec() != frame->acodec()->id) { + srs_trace("HLS: Switch audio codec %d(%s) to %d(%s)", muxer->latest_acodec(), srs_audio_codec_id2str(muxer->latest_acodec()).c_str(), + frame->acodec()->id, srs_audio_codec_id2str(frame->acodec()->id).c_str()); + muxer->set_latest_acodec(frame->acodec()->id); + } // write audio to cache. if ((err = tsmc->cache_audio(frame, pts)) != srs_success) { diff --git a/trunk/src/app/srs_app_hls.hpp b/trunk/src/app/srs_app_hls.hpp index b5dd8a82a..aa69ffa61 100644 --- a/trunk/src/app/srs_app_hls.hpp +++ b/trunk/src/app/srs_app_hls.hpp @@ -156,6 +156,9 @@ private: SrsHlsSegment* current; // The ts context, to keep cc continous between ts. SrsTsContext* context; +private: + // Latest audio codec, parsed from stream. + SrsAudioCodecId latest_acodec_; public: SrsHlsMuxer(); virtual ~SrsHlsMuxer(); @@ -166,6 +169,9 @@ public: virtual std::string ts_url(); virtual srs_utime_t duration(); virtual int deviation(); +public: + SrsAudioCodecId latest_acodec(); + void set_latest_acodec(SrsAudioCodecId v); public: // Initialize the hls muxer. virtual srs_error_t initialize(); diff --git a/trunk/src/app/srs_app_http_stream.cpp b/trunk/src/app/srs_app_http_stream.cpp index 1c920ae67..8f2816531 100755 --- a/trunk/src/app/srs_app_http_stream.cpp +++ b/trunk/src/app/srs_app_http_stream.cpp @@ -829,7 +829,9 @@ void SrsLiveStream::http_hooks_on_stop(ISrsHttpMessage* r) srs_error_t SrsLiveStream::streaming_send_messages(ISrsBufferEncoder* enc, SrsSharedPtrMessage** msgs, int nb_msgs) { srs_error_t err = srs_success; - + + // TODO: In gop cache, we know both the audio and video codec, so we should notice the encoder, which might depends + // on setting the correct codec information, for example, HTTP-TS or HLS will write PMT. for (int i = 0; i < nb_msgs; i++) { SrsSharedPtrMessage* msg = msgs[i]; diff --git a/trunk/src/core/srs_core_version4.hpp b/trunk/src/core/srs_core_version4.hpp index 3617bb387..b86713df9 100644 --- a/trunk/src/core/srs_core_version4.hpp +++ b/trunk/src/core/srs_core_version4.hpp @@ -9,6 +9,6 @@ #define VERSION_MAJOR 4 #define VERSION_MINOR 0 -#define VERSION_REVISION 268 +#define VERSION_REVISION 269 #endif diff --git a/trunk/src/core/srs_core_version5.hpp b/trunk/src/core/srs_core_version5.hpp index 1d83c4320..da08ee970 100644 --- a/trunk/src/core/srs_core_version5.hpp +++ b/trunk/src/core/srs_core_version5.hpp @@ -9,6 +9,6 @@ #define VERSION_MAJOR 5 #define VERSION_MINOR 0 -#define VERSION_REVISION 115 +#define VERSION_REVISION 116 #endif diff --git a/trunk/src/kernel/srs_kernel_codec.cpp b/trunk/src/kernel/srs_kernel_codec.cpp index b650b1bea..2ec4397e1 100644 --- a/trunk/src/kernel/srs_kernel_codec.cpp +++ b/trunk/src/kernel/srs_kernel_codec.cpp @@ -595,6 +595,9 @@ srs_error_t SrsFrame::initialize(SrsCodecConfig* c) srs_error_t SrsFrame::add_sample(char* bytes, int size) { srs_error_t err = srs_success; + + // Ignore empty sample. + if (!bytes || size <= 0) return err; if (nb_samples >= SrsMaxNbSamples) { return srs_error_new(ERROR_HLS_DECODE_ERROR, "Frame samples overflow"); @@ -2063,20 +2066,13 @@ srs_error_t SrsFormat::audio_mp3_demux(SrsBuffer* stream, int64_t timestamp) // we always decode aac then mp3. srs_assert(acodec->id == SrsAudioCodecIdMP3); - // Update the RAW MP3 data. + // Update the RAW MP3 data. Note the start is 12 bits syncword 0xFFF, so we should not skip any bytes, for detail + // please see ISO_IEC_11172-3-MP3-1993.pdf page 20 and 26. raw = stream->data() + stream->pos(); nb_raw = stream->size() - stream->pos(); - stream->skip(1); - if (stream->empty()) { - return err; - } - - char* data = stream->data() + stream->pos(); - int size = stream->size() - stream->pos(); - // mp3 payload. - if ((err = audio->add_sample(data, size)) != srs_success) { + if ((err = audio->add_sample(raw, nb_raw)) != srs_success) { return srs_error_wrap(err, "add audio frame"); } diff --git a/trunk/src/kernel/srs_kernel_ts.cpp b/trunk/src/kernel/srs_kernel_ts.cpp index 59fca4ae0..cba4e8bda 100644 --- a/trunk/src/kernel/srs_kernel_ts.cpp +++ b/trunk/src/kernel/srs_kernel_ts.cpp @@ -2676,8 +2676,8 @@ SrsTsContextWriter::SrsTsContextWriter(ISrsStreamWriter* w, SrsTsContext* c, Srs { writer = w; context = c; - - acodec = ac; + + acodec_ = ac; vcodec = vc; } @@ -2692,7 +2692,7 @@ srs_error_t SrsTsContextWriter::write_audio(SrsTsMessage* audio) srs_info("hls: write audio pts=%" PRId64 ", dts=%" PRId64 ", size=%d", audio->pts, audio->dts, audio->PES_packet_length); - if ((err = context->encode(writer, audio, vcodec, acodec)) != srs_success) { + if ((err = context->encode(writer, audio, vcodec, acodec_)) != srs_success) { return srs_error_wrap(err, "ts: write audio"); } srs_info("hls encode audio ok"); @@ -2707,7 +2707,7 @@ srs_error_t SrsTsContextWriter::write_video(SrsTsMessage* video) srs_info("hls: write video pts=%" PRId64 ", dts=%" PRId64 ", size=%d", video->pts, video->dts, video->PES_packet_length); - if ((err = context->encode(writer, video, vcodec, acodec)) != srs_success) { + if ((err = context->encode(writer, video, vcodec, acodec_)) != srs_success) { return srs_error_wrap(err, "ts: write video"); } srs_info("hls encode video ok"); @@ -2725,6 +2725,16 @@ void SrsTsContextWriter::update_video_codec(SrsVideoCodecId v) vcodec = v; } +SrsAudioCodecId SrsTsContextWriter::acodec() +{ + return acodec_; +} + +void SrsTsContextWriter::set_acodec(SrsAudioCodecId v) +{ + acodec_ = v; +} + SrsEncFileWriter::SrsEncFileWriter() { memset(iv,0,16); @@ -3217,6 +3227,13 @@ srs_error_t SrsTsTransmuxer::write_audio(int64_t timestamp, char* data, int size if (format->acodec->id == SrsAudioCodecIdAAC && format->audio->aac_packet_type == SrsAudioAacFrameTraitSequenceHeader) { return err; } + + // Switch audio codec if not AAC. + if (tscw->acodec() != format->acodec->id) { + srs_trace("TS: Switch audio codec %d(%s) to %d(%s)", tscw->acodec(), srs_audio_codec_id2str(tscw->acodec()).c_str(), + format->acodec->id, srs_audio_codec_id2str(format->acodec->id).c_str()); + tscw->set_acodec(format->acodec->id); + } // the dts calc from rtmp/flv header. // @remark for http ts stream, the timestamp is always monotonically increase, diff --git a/trunk/src/kernel/srs_kernel_ts.hpp b/trunk/src/kernel/srs_kernel_ts.hpp index f00853a8c..92c691c83 100644 --- a/trunk/src/kernel/srs_kernel_ts.hpp +++ b/trunk/src/kernel/srs_kernel_ts.hpp @@ -107,8 +107,8 @@ enum SrsTsStream // ISO/IEC 11172 Video // ITU-T Rec. H.262 | ISO/IEC 13818-2 Video or ISO/IEC 11172-2 constrained parameter video stream // ISO/IEC 11172 Audio + SrsTsStreamAudioMp3 = 0x03, // ISO/IEC 13818-3 Audio - SrsTsStreamAudioMp3 = 0x04, // ITU-T Rec. H.222.0 | ISO/IEC 13818-1 private_sections // ITU-T Rec. H.222.0 | ISO/IEC 13818-1 PES packets containing private data // ISO/IEC 13522 MHEG @@ -1260,7 +1260,7 @@ private: // User must config the codec in right way. // @see https://github.com/ossrs/srs/issues/301 SrsVideoCodecId vcodec; - SrsAudioCodecId acodec; + SrsAudioCodecId acodec_; private: SrsTsContext* context; ISrsStreamWriter* writer; @@ -1277,6 +1277,10 @@ public: // Get or update the video codec of ts muxer. virtual SrsVideoCodecId video_codec(); virtual void update_video_codec(SrsVideoCodecId v); +public: + // Get and set the audio codec. + SrsAudioCodecId acodec(); + void set_acodec(SrsAudioCodecId v); }; // Used for HLS Encryption diff --git a/trunk/src/utest/srs_utest_kernel.cpp b/trunk/src/utest/srs_utest_kernel.cpp index b76cc9986..21675a553 100644 --- a/trunk/src/utest/srs_utest_kernel.cpp +++ b/trunk/src/utest/srs_utest_kernel.cpp @@ -3471,11 +3471,23 @@ VOID TEST(KernelCodecTest, AVFrame) EXPECT_TRUE(20 == f.samples[1].size); EXPECT_TRUE(2 == f.nb_samples); } + + + if (true) { + SrsAudioFrame f; + EXPECT_TRUE(0 == f.nb_samples); + + HELPER_EXPECT_SUCCESS(f.add_sample((char*)1, 0)); + EXPECT_TRUE(0 == f.nb_samples); + + HELPER_EXPECT_SUCCESS(f.add_sample(NULL, 1)); + EXPECT_TRUE(0 == f.nb_samples); + } if (true) { SrsAudioFrame f; for (int i = 0; i < SrsMaxNbSamples; i++) { - HELPER_EXPECT_SUCCESS(f.add_sample((char*)(int64_t)i, i*10)); + HELPER_EXPECT_SUCCESS(f.add_sample((char*)(int64_t)(i + 1), i*10 + 1)); } srs_error_t err = f.add_sample((char*)1, 1); @@ -3601,18 +3613,39 @@ VOID TEST(KernelCodecTest, AudioFormat) HELPER_EXPECT_SUCCESS(f.on_audio(0, (char*)"\x00", 0)); HELPER_EXPECT_SUCCESS(f.on_audio(0, (char*)"\x00", 1)); } - + + // For MP3 if (true) { SrsFormat f; HELPER_EXPECT_SUCCESS(f.initialize()); + HELPER_EXPECT_SUCCESS(f.on_audio(0, (char*)"\x20", 1)); + EXPECT_TRUE(0 == f.nb_raw); + EXPECT_TRUE(0 == f.audio->nb_samples); + HELPER_EXPECT_SUCCESS(f.on_audio(0, (char*)"\x20\x00", 2)); EXPECT_TRUE(1 == f.nb_raw); - EXPECT_TRUE(0 == f.audio->nb_samples); + EXPECT_TRUE(1 == f.audio->nb_samples); HELPER_EXPECT_SUCCESS(f.on_audio(0, (char*)"\x20\x00\x00", 3)); EXPECT_TRUE(2 == f.nb_raw); EXPECT_TRUE(1 == f.audio->nb_samples); } + + // For AAC + if (true) { + SrsFormat f; + HELPER_EXPECT_SUCCESS(f.initialize()); + HELPER_EXPECT_FAILED(f.on_audio(0, (char*)"\xa0", 1)); + + HELPER_EXPECT_SUCCESS(f.on_audio(0, (char*)"\xaf\x00\x12\x10", 4)); + HELPER_EXPECT_SUCCESS(f.on_audio(0, (char*)"\xa0\x01", 2)); + EXPECT_TRUE(0 == f.nb_raw); + EXPECT_TRUE(0 == f.audio->nb_samples); + + HELPER_EXPECT_SUCCESS(f.on_audio(0, (char*)"\xa0\x01\x00", 3)); + EXPECT_TRUE(1 == f.nb_raw); + EXPECT_TRUE(1 == f.audio->nb_samples); + } if (true) { SrsFormat f;