diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 00000000..dd84ea78 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,38 @@ +--- +name: Bug report +about: Create a report to help us improve +title: '' +labels: '' +assignees: '' + +--- + +**Describe the bug** +A clear and concise description of what the bug is. + +**To Reproduce** +Steps to reproduce the behavior: +1. Go to '...' +2. Click on '....' +3. Scroll down to '....' +4. See error + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Screenshots** +If applicable, add screenshots to help explain your problem. + +**Desktop (please complete the following information):** + - OS: [e.g. iOS] + - Browser [e.g. chrome, safari] + - Version [e.g. 22] + +**Smartphone (please complete the following information):** + - Device: [e.g. iPhone6] + - OS: [e.g. iOS8.1] + - Browser [e.g. stock browser, safari] + - Version [e.g. 22] + +**Additional context** +Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 00000000..8e8e9472 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,17 @@ +--- +name: Feature request +about: Suggest an idea for this project +title: '' +labels: enhancement +assignees: '' + +--- + +**Feature description** +to be filled by you + +**Relevant specifications and corresponding sections** +to be filled by you + +**Additional context** +to be filled by you diff --git a/.github/workflows/build-gradle.yml b/.github/workflows/build-gradle.yml new file mode 100644 index 00000000..34331840 --- /dev/null +++ b/.github/workflows/build-gradle.yml @@ -0,0 +1,37 @@ +# This workflow uses actions that are not certified by GitHub. +# They are provided by a third-party and are governed by +# separate terms of service, privacy policy, and support +# documentation. +# This workflow will build a Java project with Gradle and cache/restore any dependencies to improve the workflow execution time +# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-java-with-gradle + +name: Java CI with Gradle + +on: + pull_request: + branches: [ "main", "development" ] + +permissions: + contents: read + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + lfs: 'true' + - uses: actions/setup-java@v4 + with: + java-version: '17' + distribution: 'temurin' + - name: Setup Gradle + uses: gradle/actions/setup-gradle@417ae3ccd767c252f5661f1ace9f835f9654f2b5 # v3.1.0 + - name: Make gradlew executable + run: chmod +x ./gradlew + - name: Setup cmake + uses: jwlawson/actions-setup-cmake@v2 + with: + cmake-version: '3.26.1' + - name: Build with Gradle + run: ./gradlew build \ No newline at end of file diff --git a/.gitignore b/.gitignore index 547eb34a..e48a6dde 100644 --- a/.gitignore +++ b/.gitignore @@ -82,3 +82,5 @@ lint/generated/ lint/outputs/ lint/tmp/ # lint/reports/ + +.gradle diff --git a/.idea/compiler.xml b/.idea/compiler.xml index 7d7ec2ea..8fabff5a 100644 --- a/.idea/compiler.xml +++ b/.idea/compiler.xml @@ -1,6 +1,6 @@ - + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml index d1b2b8d9..5539aa86 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,24 +1,24 @@ - - - - - - - - - - + + + + + + + + + + \ No newline at end of file diff --git a/CONTRIBUTION_NOTICE.md b/CONTRIBUTION_NOTICE.md new file mode 100644 index 00000000..07f08ef6 --- /dev/null +++ b/CONTRIBUTION_NOTICE.md @@ -0,0 +1,2 @@ +# Contribution Notice +Original development by Klaus Kuenhammer, Bitstem, Nakolos and ORS. \ No newline at end of file diff --git a/README.md b/README.md index 44eee319..2f349d33 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,95 @@ -

MBMS Middleware for Android

+

MBMS MW Android

- Version Under Development - License + Version + License

## Introduction -Additional information can be found at: https://5g-mag.github.io/Getting-Started/pages/lte-based-5g-broadcast/ +The MBMS Middleware for Android enables the reception of media content via 5G Broadcast and +unicast (OTT streaming). Based on an MBMS Service Announcement the media manifests and segments are +either received directly via 5G Broadcast or fetched via unicast from a CDN. The MBMS Middleware +exposes the received media files via a local webserver to the MediaPlayer. As a result, the device +can dynamically switch between broadcast and unicast consumption without the media player being +aware of how the media files were originally received. + +### About the implementation + +The implementation is basically a port of +the [MBMS Middleware](https://github.com/5G-MAG/rt-mbms-mw) to Android. It uses +the [MbmsGroupCallSession API](https://developer.android.com/reference/android/telephony/MbmsGroupCallSession) +for accessing packets received via 5G Broadcast. + +## Install dependencies + +The MBMS MW Android requires Github Large File Storage to be installed before cloning the +repository. Please check +the [Github documentation](https://docs.github.com/en/repositories/working-with-files/managing-large-files/installing-git-large-file-storage) +for details. + +## Downloading + +Release versions can be downloaded from +the [releases](https://github.com/5G-MAG/rt-mbms-mw-android/releases) page. + +The source can be obtained by cloning the github repository. + +``` +cd ~ +git clone https://github.com/5G-MAG/rt-mbms-mw-android +``` + +## Building + +Call the following command in order to generate the `apk` bundles. + +```` +./gradlew assemble +```` + +The resulting `apk` bundles can be found in `app/build/outputs/apk`. The debug build is located +in `debug` folder the release build in the `release` folder. + +## Install + +To install the `apk` on an Android device follow the following steps: + +1. Connect your Android device to your development machine +2. Call `adb devices` to list the available Android devices. The output should look like the + following: + +```` +List of devices attached +CQ30022U4R device +```` + +3. Install the `apk` on the target + device: `adb -s install -r app/build/outputs/apk/debug/app-debug.apk`. Using `-r` + we reinstall an existing app, keeping its data. + +## Running + +After installing the Media Session Handler application can be started from the Android app selection +screen. + +As an alternative we can also run the app from the command +line: `adb shell am start -n com.fivegmag.a5gmsmediasessionhandler/com.fivegmag.a5gmsmediasessionhandler.MainActivity ` + +## Development + +This project follows +the [Gitflow workflow](https://www.atlassian.com/git/tutorials/comparing-workflows/gitflow-workflow) +. The `development` +branch of this project serves as an integration branch for new features. Consequently, please make +sure to switch to the `development` +branch before starting the implementation of a new feature. + +## Acknowledgement + +The MBMS MW Android was originally developed by Bistem, Nakolos and ORS. + +## Troubleshooting + +* If you get an error `gradlew: Permission Denied` try to set the execution flag on your gradlew + file: `chmod +x gradlew` \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index 5be3fc94..c43109a7 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -11,11 +11,11 @@ android { compileSdk 33 ndkVersion '25.1.8937393' defaultConfig { - applicationId "com.nakolos.ossmw" + applicationId "com.fivegmag.ossmw" minSdk 28 targetSdk 32 versionCode 1 - versionName "0.1.0" + versionName "1.0.0" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" externalNativeBuild { @@ -43,7 +43,7 @@ android { externalNativeBuild { cmake { path file('src/main/cpp/CMakeLists.txt') - version '3.22.1' + version '3.26.1' } } diff --git a/app/src/androidTest/java/com/bitstem/ossmw/ExampleInstrumentedTest.kt b/app/src/androidTest/java/com/bitstem/ossmw/ExampleInstrumentedTest.kt deleted file mode 100644 index fcf998df..00000000 --- a/app/src/androidTest/java/com/bitstem/ossmw/ExampleInstrumentedTest.kt +++ /dev/null @@ -1,24 +0,0 @@ -package com.bitstem.`nakolos-mw` - -import androidx.test.platform.app.InstrumentationRegistry -import androidx.test.ext.junit.runners.AndroidJUnit4 - -import org.junit.Test -import org.junit.runner.RunWith - -import org.junit.Assert.* - -/** - * Instrumented test, which will execute on an Android device. - * - * See [testing documentation](http://d.android.com/tools/testing). - */ -@RunWith(AndroidJUnit4::class) -class ExampleInstrumentedTest { - @Test - fun useAppContext() { - // Context of the app under test. - val appContext = InstrumentationRegistry.getInstrumentation().targetContext - assertEquals("com.bitstem.libflute", appContext.packageName) - } -} \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 10344a5d..998e6339 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,7 +1,7 @@ + package="com.fivegmag.ossmw"> @@ -18,20 +18,20 @@ android:theme="@style/Theme.LibFlute" tools:targetApi="31"> @@ -39,7 +39,7 @@ diff --git a/app/src/main/assets/bootstrap.multipart.hls b/app/src/main/assets/bootstrap.multipart.hls new file mode 100644 index 00000000..b3b60ffd --- /dev/null +++ b/app/src/main/assets/bootstrap.multipart.hls @@ -0,0 +1,158 @@ +--++++++++++++++++++++++++Rohde&Schwarz-BSCC++++++++++++++++++++++++-- +Content-Type: application/mbms-envelope+xml +Content-Transfer-Encoding: 7bit +Content-Location: file:///envelope.xml + + + + + + + + + +--++++++++++++++++++++++++Rohde&Schwarz-BSCC++++++++++++++++++++++++-- +Content-Type: application/sdp +Content-Transfer-Encoding: 7bit +Content-Location: file:///TMGI-0x1009f165.sdp + +v=0 +o=ROHDE-SCHWARZ-BSCC 269087077 1634036383 IN IP4 11.11.11.11 +s=HLS Streaming Session 0x1009f165 +i=File Download Session +t=3843025183 4789105183 +a=mbms-mode:broadcast-mbsfn 269087077 +c=IN IP4 239.11.4.9/127 +b=AS:2000 +m=application 5519 FLUTE/UDP 0 +a=flute-tsi:0 +a=flute-ch:1 +a=3GPP-QoE-Metrics:metrics={Object_Loss};rate=null;resolution=10 +a=3GPP-QoE-Metrics:metrics={Network_Resource};rate=null;resolution=10 + +--++++++++++++++++++++++++Rohde&Schwarz-BSCC++++++++++++++++++++++++-- +Content-Type: application/vnd.apple.mpegurl +Content-Transfer-Encoding: 7bit +Content-Location: file:///TMGI-0x1009f165.m3u8 + +#EXTM3U +#EXT-X-VERSION:3 + +#EXT-X-STREAM-INF:CODECS="avc1.4D401E,mp4a.40.2",BANDWIDTH=1425427,FRAME-RATE=25.000,RESOLUTION=640x360 +watchfolder/hls/stream_0.m3u8 + +--++++++++++++++++++++++++Rohde&Schwarz-BSCC++++++++++++++++++++++++-- +Content-Type: application/vnd.apple.mpegurl +Content-Transfer-Encoding: 7bit +Content-Location: http://192.168.2.2:3333/watchfolder/hls/manifest.m3u8 + +#EXTM3U +#EXT-X-VERSION:3 +#EXT-X-STREAM-INF:BANDWIDTH=3845427,AVERAGE-BANDWIDTH=3845427,RESOLUTION=1280x720,FRAME-RATE=25.000,CODECS="avc1.64001F,mp4a.40.2" +watchfolder/hls/stream_0.m3u8 + +--++++++++++++++++++++++++Rohde&Schwarz-BSCC++++++++++++++++++++++++-- +Content-Type: application/mbms-user-service-description+xml +Content-Transfer-Encoding: 7bit +Content-Location: file:///usdBundle.xml + + + + 1 + + BSCC Service1 + BSCC Dienst1 + EN-GB + DE-DE + + 23 + 27 + + + 0 + + file:///TMGI-0x1009f165.m3u8 + 2 + + + http://192.168.2.2:3333/watchfolder/hls/manifest.m3u8 + + 0 + + + + file:///TMGI-0x1009f165.m3u8 + + + file:///TMGI-0x1009f165.m3u8 + http://192.168.2.2:3333/watchfolder/hls/stream_0.m3u8 + + + + file:///TMGI-0x1009f165schedule.xml + + 0 + + + 2 + + + + +--++++++++++++++++++++++++Rohde&Schwarz-BSCC++++++++++++++++++++++++-- +Content-Type: application/mbms-schedule+xml +Content-Transfer-Encoding: 7bit +Content-Location: file:///TMGI-0x1009f165schedule.xml + + + + 1 + + + 2021-10-12T10:59:43Z + 2051-10-05T10:59:43Z + 0 + 0 + 0 + + + +--++++++++++++++++++++++++Rohde&Schwarz-BSCC++++++++++++++++++++++++-- diff --git a/app/src/main/cpp/HlsMediaPlaylist.cpp b/app/src/main/cpp/HlsMediaPlaylist.cpp index 0048ff14..fdf23758 100644 --- a/app/src/main/cpp/HlsMediaPlaylist.cpp +++ b/app/src/main/cpp/HlsMediaPlaylist.cpp @@ -21,70 +21,82 @@ #include "boost/algorithm/string.hpp" #include -MBMS_RT::HlsMediaPlaylist::HlsMediaPlaylist(const std::string& content) -{ - spdlog::debug("Parsing HLS media playlist: {}", content); +MBMS_RT::HlsMediaPlaylist::HlsMediaPlaylist(const std::string &content) { + spdlog::debug("Parsing HLS media playlist: {}", content); - std::istringstream iss(content); - int idx = 0; - int seq_nr = 0; - double extinf = -1; - for (std::string line; std::getline(iss, line); idx++ ) - { - boost::algorithm::trim(line); + std::istringstream iss(content); + int idx = 0; + int seq_nr = 0; + double extinf = -1; + for (std::string line; std::getline(iss, line); idx++) { + boost::algorithm::trim(line); - if (idx==0) { - if ( line != "#EXTM3U") { - throw("HLS playlist parsing failed: first line is not #EXTM3U"); - } else { - continue; - } - } + if (idx == 0) { + if (line != "#EXTM3U") { + throw ("HLS playlist parsing failed: first line is not #EXTM3U"); + } else { + continue; + } + } - size_t cpos = line.rfind(':'); + size_t cpos = line.rfind(':'); - if (line.rfind("#EXT-X-VERSION", 0) == 0) { - if (_version != -1) { - throw("HLS playlist parsing failed: duplicate #EXT-X-VERSION"); - } - if (cpos != std::string::npos) { - _version = atoi(line.substr(cpos+1).c_str()); - } - } else if (line.rfind("#EXT-X-TARGETDURATION", 0) == 0) { - if (cpos != std::string::npos) { - _targetduration = atoi(line.substr(cpos+1).c_str()); - } - } else if (line.rfind("#EXT-X-MEDIA-SEQUENCE", 0) == 0) { - if (cpos != std::string::npos) { - seq_nr = atoi(line.substr(cpos+1).c_str()); - } - } else if (line.rfind("#EXTINF", 0) == 0) { - if (cpos != std::string::npos) { - extinf = atof(line.substr(cpos+1).c_str()); - } - } else if (line.rfind('#', 0) == 0) { - spdlog::debug("HLS playlist parser ignoring unhandled line {}", line); - } else if (line.size() > 0) { - _segments.push_back({line, seq_nr++, extinf}); + if (line.rfind("#EXT-X-VERSION", 0) == 0) { + if (_version != -1) { + throw ("HLS playlist parsing failed: duplicate #EXT-X-VERSION"); + } + if (cpos != std::string::npos) { + _version = atoi(line.substr(cpos + 1).c_str()); + } + } else if (line.rfind("#EXT-X-TARGETDURATION", 0) == 0) { + if (cpos != std::string::npos) { + _targetduration = atoi(line.substr(cpos + 1).c_str()); + } + } else if (line.rfind("#EXT-X-MEDIA-SEQUENCE", 0) == 0) { + if (cpos != std::string::npos) { + seq_nr = atoi(line.substr(cpos + 1).c_str()); + } + } else if (line.rfind("#EXTINF", 0) == 0) { + if (cpos != std::string::npos) { + extinf = atof(line.substr(cpos + 1).c_str()); + } + } else if (line.rfind('#', 0) == 0) { + spdlog::debug("HLS playlist parser ignoring unhandled line {}", line); + } else if (line.size() > 0) { + _segments.push_back({line, seq_nr++, extinf}); + } } - } } -auto MBMS_RT::HlsMediaPlaylist::to_string() const -> std::string -{ - std::stringstream pl; +auto MBMS_RT::HlsMediaPlaylist::to_string() const -> std::string { + std::stringstream pl; - pl << "#EXTM3U" << std::endl; - pl << "#EXT-X-VERSION:3" << std::endl; + pl << "#EXTM3U" << std::endl; + pl << "#EXT-X-VERSION:3" << std::endl; - if (_segments.size() > 0) { - pl << "#EXT-X-TARGETDURATION:" << _targetduration << std::endl; - pl << "#EXT-X-MEDIA-SEQUENCE:" << _segments[0].seq << std::endl; - } - for (const auto& seg : _segments) { - pl << "#EXTINF:" << seg.extinf << std::endl; - pl << "/" << seg.uri << std::endl; - } - return std::move(pl.str()); + if (_segments.size() > 0) { + pl << "#EXT-X-TARGETDURATION:" << _targetduration << std::endl; + pl << "#EXT-X-MEDIA-SEQUENCE:" << _segments[0].seq << std::endl; + } + for (const auto &seg: _segments) { + pl << "#EXTINF:" << seg.extinf << std::endl; + pl << "" << remove_base_url(seg.uri) << std::endl; + //pl << "/" << seg.uri << std::endl; + } + return std::move(pl.str()); } + +auto MBMS_RT::HlsMediaPlaylist::remove_base_url(std::string uri) const -> std::string { + size_t lastSlashPos = uri.find_last_of('/'); + + // Extract the substring after the last slash + if (lastSlashPos != std::string::npos) { + std::string lastPart = uri.substr(lastSlashPos + 1); + return lastPart; + } else { + return uri; + } + +} + diff --git a/app/src/main/cpp/HlsMediaPlaylist.h b/app/src/main/cpp/HlsMediaPlaylist.h index 158a3de8..9fb642e9 100644 --- a/app/src/main/cpp/HlsMediaPlaylist.h +++ b/app/src/main/cpp/HlsMediaPlaylist.h @@ -36,6 +36,7 @@ namespace MBMS_RT { void add_segment(Segment segment) { _segments.push_back(std::move(segment)); }; std::string to_string() const; + std::string remove_base_url(std::string uri) const; void set_target_duration(int duration) { _targetduration = duration; }; int target_duration() const { return _targetduration; }; diff --git a/app/src/main/cpp/RestHandler.cpp b/app/src/main/cpp/RestHandler.cpp index 08938e23..f188fbc6 100644 --- a/app/src/main/cpp/RestHandler.cpp +++ b/app/src/main/cpp/RestHandler.cpp @@ -228,7 +228,7 @@ void MBMS_RT::RestHandler::get(http_request message) { if (it->second->buffer() != nullptr) { web::http::http_response response(status_codes::OK); if (it->second->item_type() == CacheItem::ItemType::Segment) { - response.headers().add(U("NAKOLOS-File-Origin"), it->second->item_source_as_string()); + response.headers().add(U("FIVEGMAG-File-Origin"), it->second->item_source_as_string()); if (_active_source_cb) _active_source_cb(it->second->item_source_as_string()); } else { if (_active_stream_cb) _active_stream_cb(it->second->content_location()); diff --git a/app/src/main/cpp/android-jni-bindings.cpp b/app/src/main/cpp/android-jni-bindings.cpp index 8d71e2ec..f93c978d 100644 --- a/app/src/main/cpp/android-jni-bindings.cpp +++ b/app/src/main/cpp/android-jni-bindings.cpp @@ -29,13 +29,13 @@ extern "C" jint JNI_OnLoad(JavaVM* vm, void* reserved) } extern "C" JNIEXPORT bool JNICALL -Java_com_nakolos_ossmw_NakolosMwService_startNativeMiddleware(JNIEnv* env, -jobject thiz, jstring device_name, jstring japi_key) { - std::string tag = "nakolos-mw"; +Java_com_fivegmag_ossmw_MwService_startNativeMiddleware(JNIEnv* env, + jobject thiz, jstring device_name, jstring japi_key) { + std::string tag = "fivegmag-mw"; try { - auto android_logger = spdlog::android_logger_mt("nakolos-mw", tag); + auto android_logger = spdlog::android_logger_mt("fivegmag-mw", tag); spdlog::set_default_logger(android_logger); - spdlog::info("Launching NAKOLOS native middleware"); + spdlog::info("Launching 5G-MAG native middleware"); spdlog::set_level(spdlog::level::debug); } catch (...) {} @@ -60,7 +60,7 @@ jobject thiz, jstring device_name, jstring japi_key) { } extern "C" JNIEXPORT bool JNICALL -Java_com_nakolos_ossmw_UdpReceiver_handlePacket(JNIEnv* env, +Java_com_fivegmag_ossmw_UdpReceiver_handlePacket(JNIEnv* env, jobject thiz, jbyteArray buffer, jint size) { @@ -73,9 +73,9 @@ Java_com_nakolos_ossmw_UdpReceiver_handlePacket(JNIEnv* env, return true; } extern "C" JNIEXPORT bool JNICALL -Java_com_nakolos_ossmw_NakolosMwService_setLocalServiceAnnouncement(JNIEnv* env, - jobject thiz, - jstring sa) { +Java_com_fivegmag_ossmw_MwService_setLocalServiceAnnouncement(JNIEnv* env, + jobject thiz, + jstring sa) { const char *csa = env->GetStringUTFChars(sa, NULL); mw->set_local_service_announcement(csa); env->ReleaseStringUTFChars(sa,csa); @@ -83,8 +83,8 @@ Java_com_nakolos_ossmw_NakolosMwService_setLocalServiceAnnouncement(JNIEnv* env, } extern "C" JNIEXPORT bool JNICALL -Java_com_nakolos_ossmw_NakolosMwService_stopNativeMiddleware(JNIEnv* env, - jobject thiz) { +Java_com_fivegmag_ossmw_MwService_stopNativeMiddleware(JNIEnv* env, + jobject thiz) { using namespace std::chrono_literals; spdlog::info("Stopping io_service tasks"); if (mw) { diff --git a/app/src/main/cpp/libflute/Receiver.cpp b/app/src/main/cpp/libflute/Receiver.cpp index 1df6cb47..28a384a8 100644 --- a/app/src/main/cpp/libflute/Receiver.cpp +++ b/app/src/main/cpp/libflute/Receiver.cpp @@ -253,7 +253,7 @@ auto LibFlute::Receiver::remove_expired_files(unsigned max_age) -> void for (auto it = _files.cbegin(); it != _files.cend();) { auto age = time(nullptr) - it->second->received_at(); - if ( it->second->meta().content_location != "bootstrap.multipart" && age > max_age) { + if ( it->second->meta().content_location != "bootstrap.multipart.hls" && age > max_age) { it = _files.erase(it); } else { ++it; diff --git a/app/src/main/cpp/seamless/CdnClient.cpp b/app/src/main/cpp/seamless/CdnClient.cpp index d09dedb2..8ecf1131 100644 --- a/app/src/main/cpp/seamless/CdnClient.cpp +++ b/app/src/main/cpp/seamless/CdnClient.cpp @@ -58,7 +58,7 @@ auto MBMS_RT::CdnClient::get(const std::string& path, std::function req{http::verb::get, _base_path + path, 11}; req.set(http::field::host, _host); - req.set(http::field::user_agent, "nakolos MW/0.9"); + req.set(http::field::user_agent, "fivegmag MW/0.9"); http::write(stream, req); beast::flat_buffer buffer(1024*1024*128); diff --git a/app/src/main/cpp/seamless/SeamlessContentStream.cpp b/app/src/main/cpp/seamless/SeamlessContentStream.cpp index 00a05f3e..d93990a4 100644 --- a/app/src/main/cpp/seamless/SeamlessContentStream.cpp +++ b/app/src/main/cpp/seamless/SeamlessContentStream.cpp @@ -77,8 +77,8 @@ auto MBMS_RT::SeamlessContentStream::set_cdn_endpoint(const std::string& cdn_ept size_t spos = _playlist_path.rfind('/'); _playlist_dir = _playlist_path.substr(0, spos+1); spdlog::debug("ContentStream: playlist dir is {}", _playlist_dir); - //cdn_base.set_path("/"); - cdn_base.set_path("/cdn/"); + cdn_base.set_path("/"); + //cdn_base.set_path("/cdn/"); cdn_base.set_query(""); _cdn_endpoint = cdn_base.to_string(); spdlog::info("ContentStream: setting CDN ept to {}", _cdn_endpoint); @@ -121,7 +121,7 @@ auto MBMS_RT::SeamlessContentStream::handle_playlist( const std::string& content for (const auto& segment : playlist.segments()) { // spdlog::debug("segment: seq {}, extinf {}, uri {}", segment.seq, segment.extinf, segment.uri); if (_segments[plidx].find(segment.seq) == _segments[plidx].end()) { - std::string full_uri = (plidx == 0 ? "ch1/" : "ch2/") + segment.uri; + std::string full_uri = (plidx == 0 ? "watchfolder/hls/" : "ch2/") + segment.uri; auto seg = std::make_shared(full_uri, segment.seq, segment.extinf); if (_cdn_client) { diff --git a/app/src/main/ic_launcher-playstore.png b/app/src/main/ic_launcher-playstore.png new file mode 100644 index 00000000..ffb6406c Binary files /dev/null and b/app/src/main/ic_launcher-playstore.png differ diff --git a/app/src/main/java/com/nakolos/ossmw/Actions.kt b/app/src/main/java/com/fivegmag/ossmw/Actions.kt similarity index 94% rename from app/src/main/java/com/nakolos/ossmw/Actions.kt rename to app/src/main/java/com/fivegmag/ossmw/Actions.kt index a1ba7fa7..a9346212 100644 --- a/app/src/main/java/com/nakolos/ossmw/Actions.kt +++ b/app/src/main/java/com/fivegmag/ossmw/Actions.kt @@ -1,21 +1,21 @@ -// Copyright (C) 2023 Klaus Kühnhammer (Österreichische Rundfunksender GmbH & Co KG) -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . - -package com.nakolos.ossmw - -enum class Actions { - START, - STOP +// Copyright (C) 2023 Klaus Kühnhammer (Österreichische Rundfunksender GmbH & Co KG) +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +package com.fivegmag.ossmw + +enum class Actions { + START, + STOP } \ No newline at end of file diff --git a/app/src/main/java/com/nakolos/ossmw/CppOssNoticesActivity.kt b/app/src/main/java/com/fivegmag/ossmw/CppOssNoticesActivity.kt similarity index 97% rename from app/src/main/java/com/nakolos/ossmw/CppOssNoticesActivity.kt rename to app/src/main/java/com/fivegmag/ossmw/CppOssNoticesActivity.kt index 200a759d..87f5e1eb 100644 --- a/app/src/main/java/com/nakolos/ossmw/CppOssNoticesActivity.kt +++ b/app/src/main/java/com/fivegmag/ossmw/CppOssNoticesActivity.kt @@ -13,7 +13,7 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . -package com.nakolos.ossmw +package com.fivegmag.ossmw import androidx.appcompat.app.AppCompatActivity import android.os.Bundle diff --git a/app/src/main/java/com/nakolos/ossmw/LicenseActivity.kt b/app/src/main/java/com/fivegmag/ossmw/LicenseActivity.kt similarity index 94% rename from app/src/main/java/com/nakolos/ossmw/LicenseActivity.kt rename to app/src/main/java/com/fivegmag/ossmw/LicenseActivity.kt index 1281abd5..92acff96 100644 --- a/app/src/main/java/com/nakolos/ossmw/LicenseActivity.kt +++ b/app/src/main/java/com/fivegmag/ossmw/LicenseActivity.kt @@ -13,12 +13,12 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . -package com.nakolos.ossmw +package com.fivegmag.ossmw import android.os.Bundle import android.widget.TextView import androidx.appcompat.app.AppCompatActivity -import com.nakolos.ossmw.databinding.ActivityLicenseBinding +import com.fivegmag.ossmw.databinding.ActivityLicenseBinding class LicenseActivity : AppCompatActivity() { diff --git a/app/src/main/java/com/nakolos/ossmw/MainActivity.kt b/app/src/main/java/com/fivegmag/ossmw/MainActivity.kt similarity index 95% rename from app/src/main/java/com/nakolos/ossmw/MainActivity.kt rename to app/src/main/java/com/fivegmag/ossmw/MainActivity.kt index c62b6ff7..9c7ea8a3 100644 --- a/app/src/main/java/com/nakolos/ossmw/MainActivity.kt +++ b/app/src/main/java/com/fivegmag/ossmw/MainActivity.kt @@ -13,14 +13,13 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . -package com.nakolos.ossmw +package com.fivegmag.ossmw import android.content.Intent import android.os.Build import android.os.Bundle import android.os.Handler import android.os.Looper -import android.util.Log import android.widget.Button import android.widget.TextView import androidx.appcompat.app.AppCompatActivity @@ -36,8 +35,7 @@ import androidx.media3.exoplayer.source.* import androidx.media3.exoplayer.upstream.DefaultLoadErrorHandlingPolicy import androidx.media3.exoplayer.upstream.LoadErrorHandlingPolicy import androidx.media3.ui.PlayerView -import com.nakolos.ossmw.databinding.ActivityMainBinding -import java.net.InetAddress +import com.fivegmag.ossmw.databinding.ActivityMainBinding class RetryOnErrorPolicy : DefaultLoadErrorHandlingPolicy() { override fun getRetryDelayMsFor(loadErrorInfo: LoadErrorHandlingPolicy.LoadErrorInfo): Long { @@ -92,7 +90,7 @@ class MainActivity : AppCompatActivity() { } private fun actionOnService(action: Actions) { // if (getServiceState(this) == ServiceState.STOPPED && action == Actions.STOP) return - Intent(this, NakolosMwService::class.java).also { + Intent(this, MwService::class.java).also { it.action = action.name if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { // log("Starting the service in >=26 Mode") @@ -120,8 +118,8 @@ class MainActivity : AppCompatActivity() { val cdn_ept = "http://127.0.0.1:" + 3020 + "/" + - "ch1" + "/" + - "stream.m3u8" + "watchfolder/hls" + "/" + + "stream_0.m3u8" val mediaSource: MediaSource = HlsMediaSource.Factory(dataSourceFactory) .setLoadErrorHandlingPolicy(policy) @@ -136,8 +134,8 @@ class MainActivity : AppCompatActivity() { loadEventInfo: LoadEventInfo, mediaLoadData: MediaLoadData ) { - if (loadEventInfo.responseHeaders.contains("NAKOLOS-File-Origin")) { - for (header in loadEventInfo.responseHeaders.get("NAKOLOS-File-Origin")!!) { + if (loadEventInfo.responseHeaders.contains("FIVEGMAG-File-Origin")) { + for (header in loadEventInfo.responseHeaders.get("FIVEGMAG-File-Origin")!!) { sourceInfoText.text = header; if (lastSource != header) { lastSource = header diff --git a/app/src/main/java/com/nakolos/ossmw/NakolosMwService.kt b/app/src/main/java/com/fivegmag/ossmw/MwService.kt similarity index 94% rename from app/src/main/java/com/nakolos/ossmw/NakolosMwService.kt rename to app/src/main/java/com/fivegmag/ossmw/MwService.kt index df355ed5..1103aabb 100644 --- a/app/src/main/java/com/nakolos/ossmw/NakolosMwService.kt +++ b/app/src/main/java/com/fivegmag/ossmw/MwService.kt @@ -13,7 +13,7 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . -package com.nakolos.ossmw +package com.fivegmag.ossmw import android.annotation.SuppressLint import android.app.* @@ -39,9 +39,9 @@ import java.util.concurrent.ExecutorService import java.util.concurrent.Executors -private const val TAG = "NakolosMW" +private const val TAG = "5G-MAG MW" -class NakolosMwService : Service() { +class MwService : Service() { private var groupCallSession: MbmsGroupCallSession? = null private lateinit var groupCallSessionCallback: MbmsGroupCallSessionCallback @@ -56,7 +56,7 @@ class NakolosMwService : Service() { private var isServiceStarted = false fun log(msg: String) { - Log.d("NAKOLOS MW Service", msg) + Log.d("5G-MAG MW Service", msg) } @@ -199,8 +199,8 @@ class NakolosMwService : Service() { @RequiresApi(Build.VERSION_CODES.Q) private fun startService() { if (isServiceStarted) return - log("Starting the NAKOLOS MW foreground service task") - Toast.makeText(this, "NAKOLOS MW started", Toast.LENGTH_SHORT).show() + log("Starting the FIVEGMAG MW foreground service task") + Toast.makeText(this, "5G-MAG MW started", Toast.LENGTH_SHORT).show() isServiceStarted = true wakeLock = @@ -227,7 +227,7 @@ class NakolosMwService : Service() { startNativeMiddleware(userDeviceName, api_key!!) - var service_announcement = readAssetFile("bootstrap.multipart") + var service_announcement = readAssetFile("bootstrap.multipart.hls") //var service_announcement = readAssetFile("bootstrap.multipart.debug") if (service_announcement != null) { setLocalServiceAnnouncement(service_announcement) @@ -252,13 +252,13 @@ class NakolosMwService : Service() { udpReceiver.running = false stopNativeMiddleware() disconnectFromMiddleware() - Toast.makeText(this, "NAKOLOS MW stopped", Toast.LENGTH_SHORT).show() + Toast.makeText(this, "5G-MAG MW stopped", Toast.LENGTH_SHORT).show() } // override fun onBind(intent: Intent): IBinder? { // return null // } private fun createNotification(): Notification { - val notificationChannelId = "NAKOLOS MW" + val notificationChannelId = "FIVEGMAG MW" // depending on the Android API that we're dealing with we will have // to use a specific method to create the notification @@ -267,10 +267,10 @@ class NakolosMwService : Service() { val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager; val channel = NotificationChannel( notificationChannelId, - "NAKOLOS middleware notifications channel", + "5G-MAG middleware notifications channel", NotificationManager.IMPORTANCE_HIGH ).let { - it.description = "NAKOLOS middleware notifications channel" + it.description = "5G-MAG middleware notifications channel" it.enableLights(true) it.lightColor = Color.RED it.enableVibration(false) @@ -290,8 +290,8 @@ class NakolosMwService : Service() { ) else Notification.Builder(this) return builder - .setContentTitle("NAKOLOS") - .setContentText("NAKOLOS middleware is running") + .setContentTitle("5G-MAG") + .setContentText("5G-MAG middleware is running") .setContentIntent(pendingIntent) .setSmallIcon(R.drawable.ic_stat_name) .setTicker("Ticker text") @@ -326,8 +326,8 @@ class UdpReceiver(applicationContext: Context, interfaceName: String) : Runnable var running = true public override fun run() { - address = InetAddress.getByName("239.11.4.10") - socketAddress = InetSocketAddress(address, 5501) + address = InetAddress.getByName("239.11.4.50") + socketAddress = InetSocketAddress(address, 9988) multicastSocket = MulticastSocket(socketAddress) multicastSocket!!.networkInterface = NetworkInterface.getByName(interfaceName) diff --git a/app/src/main/java/com/nakolos/ossmw/SettingsActivity.kt b/app/src/main/java/com/fivegmag/ossmw/SettingsActivity.kt similarity index 98% rename from app/src/main/java/com/nakolos/ossmw/SettingsActivity.kt rename to app/src/main/java/com/fivegmag/ossmw/SettingsActivity.kt index e1305b07..c47323ba 100644 --- a/app/src/main/java/com/nakolos/ossmw/SettingsActivity.kt +++ b/app/src/main/java/com/fivegmag/ossmw/SettingsActivity.kt @@ -13,7 +13,7 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . -package com.nakolos.ossmw +package com.fivegmag.ossmw import android.content.Intent import android.os.Bundle diff --git a/app/src/main/res/drawable/ic_launcher_background.xml b/app/src/main/res/drawable/ic_launcher_background.xml deleted file mode 100644 index 07d5da9c..00000000 --- a/app/src/main/res/drawable/ic_launcher_background.xml +++ /dev/null @@ -1,170 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index c302fb24..cc7e3e34 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -10,18 +10,18 @@ + android:paddingRight="20dp"> + android:paddingTop="50dp" + android:paddingBottom="30dp" + android:src="@drawable/fiveg_mag"> + + +