From 642b68d886fd8c0112e04d42eb2572958259a4db Mon Sep 17 00:00:00 2001 From: "Jordi J. Gimenez" <87380947+jordijoangimenez@users.noreply.github.com> Date: Tue, 30 Apr 2024 11:08:00 +0200 Subject: [PATCH] Big documentation update (#63) * Create MBMS-Service-Announcement-Files.md * Update quick-start-guide.md * Rename pages/lte-based-5g-broadcast/MBMS-Service-Announcement-Files.md to pages/lte-based-5g-broadcast/rt-common-share/MBMS-service-announcement-files.md * Update quick-start-guide.md * Rename MBMS-service-announcement-files.md to MBMS-service-announcement-files.md * Update quick-start-guide.md * Create GPS-time-synchronization.md * Update GPS-time-synchronization.md * Update GPS-time-synchronization.md * Create development-and-testing.md * Create installation-system-service.md * Create testing-local-user.md * Create configuration.md * Create compatibility.md * Create features.md * Create testing-postman.md * Create testing-m1-v120 * Create testing-m1-v130.md * Rename testing-m1-v120 to testing-m1-v120.md * Create testing-m1-v141.md * Create testing-m3-v110.md * Create testing-m3-v120-md * Create testing-m5-v100.md * Create testing-m5-v120.md * Create testing-m5-v130.md * Update repositories.md * Update features.md * Create development.md * Delete pages/5g-media-streaming/features.md * Update testing-m1-v120.md * Update testing-m1-v130.md * Update testing-m1-v141.md * Update testing-m1-v141.md * Update testing-m3-v110.md * Update and rename testing-m3-v120-md to testing-m3-v120.md * Update testing-m5-v100.md * Update testing-m5-v120.md * Update testing-m5-v130.md * Update repositories.md * Create testing.md * Update testing-postman.md * Update testing-local-user.md * Update testing-m1-v120.md * Update testing-m1-v130.md * Update testing-m1-v141.md * Update testing-m3-v110.md * Update testing-m3-v120.md * Update testing-m5-v100.md * Update testing-m5-v120.md * Update testing-m5-v130.md * Rename compatibility.md to compatibility.md * Update compatibility.md * Update compatibility.md * Update and rename configuration.md to configuration5GMSAF.md * Update repositories.md * Update and rename features.md to features.md * Rename features.md to features5GMSAF.md * Rename testing-local-user.md to testing-local-user.md * Update and rename configuration5GMSAF.md to configuration-5GMSAF.md * Update and rename installation-system-service.md to installation-system-service-5GMSAF.md * Update and rename testing-local-user.md to installation-local-user-5GMSAF.md * Update installation-system-service-5GMSAF.md * Update installation-local-user-5GMSAF.md * Update installation-system-service-5GMSAF.md * Update testing.md * Update and rename testing-postman.md to testing-postman.md * Update and rename testing-m1-v120.md to testing-m1-v120.md * Update testing-m1-v120.md * Update _config.yml * Create custom.scss * Update custom.scss * Update custom.scss * Update _config.yml * Update _config.yml * Update and rename testing-m1-v130.md to testing-m1-v130.md * Update and rename testing-m1-v141.md to testing-m1-v141.md * Update and rename testing-m3-v110.md to testing-m3-v110.md * Update and rename testing-m3-v120.md to testing-m3-v120.md * Update and rename testing-m5-v100.md to testing-m5-v100.md * Update and rename testing-m5-v120.md to testing-m5-v120.md * Update testing-m5-v100.md * Update and rename testing-m5-v130.md to testing-m5-v130.md * Update and rename development-and-testing.md to testing-AS.md * Update testing-postman.md * Update testing-m1-v120.md * Update testing-m1-v130.md * Update testing-m1-v141.md * Update testing-m3-v110.md * Update testing-m3-v120.md * Update testing-m5-v100.md * Update testing-m5-v120.md * Update testing-m5-v130.md * Update custom.scss * Update _config.yml * Update custom.scss * Add files via upload * Add files via upload * Update index.md * Add files via upload * Update index.md * Add files via upload * Update index.md * Update index.md * Update specifications.md * Update specifications.md * Update specifications.md * Update specifications.md * Update specifications.md * Update specifications.md * Update specifications.md * Rename development.md to development-AS.md * Update development-AS.md * Update testing-postman.md * Update testing-m1-v120.md * Update testing-m1-v130.md * Update testing-m1-v141.md * Update testing-m3-v110.md * Update testing-m3-v120.md * Update testing-m5-v100.md * Update testing-m5-v120.md * Update testing-m5-v130.md * Update end-to-end.md * Update and rename pages/xr-media-integration-in-5g/features.md to pages/xr-media-integration-in-5g/repositories/featuresXRplayer.md * Update xr-player-overview.md * Update xr-player-overview.md * Update and rename pages/xr-media-integration-in-5g/xr-player-overview.md to pages/xr-media-integration-in-5g/tutorials/xr-player-overview.md * Update repositories.md * Update xr-player-win11-openXR.md * Update xr-player-overview.md * Update featuresXRplayer.md * Update xr-player-win11-openXR.md * Create README.md * Update repositories.md * Update repositories.md * Update repositories.md * Update repositories.md --- README.md | 5 + _config.yml | 3 +- _sass/custom/custom.scss | 3 + assets/images/5g-mag-logo-with-text.png | Bin 31084 -> 31037 bytes assets/images/5g-mag-reference-tools.png | Bin 95121 -> 108925 bytes index.md | 2 +- pages/5g-media-streaming/repositories.md | 9 +- .../features5GMSAF.md} | 267 ++++-- pages/5g-media-streaming/specifications.md | 9 +- pages/5g-media-streaming/testing.md | 54 ++ .../testing/compatibility.md | 24 + .../testing/development-AS.md | 275 ++++++ .../testing/installation-local-user-5GMSAF.md | 161 ++++ .../installation-system-service-5GMSAF.md | 239 +++++ .../5g-media-streaming/testing/testing-AS.md | 231 +++++ .../testing/testing-m1-v120.md | 642 +++++++++++++ .../testing/testing-m1-v130.md | 859 ++++++++++++++++++ .../testing/testing-m1-v141.md | 859 ++++++++++++++++++ .../testing/testing-m3-v110.md | 216 +++++ .../testing/testing-m3-v120.md | 231 +++++ .../testing/testing-m5-v100.md | 306 +++++++ .../testing/testing-m5-v120.md | 334 +++++++ .../testing/testing-m5-v130.md | 368 ++++++++ .../testing/testing-postman.md | 74 ++ .../tutorials/configuration-5GMSAF.md | 437 +++++++++ .../tutorials/end-to-end.md | 2 +- .../specifications.md | 12 +- pages/emergency-alerts/repositories.md | 3 + .../quick-start-guide.md | 2 +- pages/lte-based-5g-broadcast/repositories.md | 4 + .../MBMS-service-announcement-files.md | 535 +++++++++++ .../rt-mbms-modem/GPS-time-synchronization.md | 63 ++ .../lte-based-5g-broadcast/specifications.md | 10 +- .../repositories.md | 10 +- .../featuresXRplayer.md} | 7 +- .../specifications.md | 9 +- .../{ => tutorials}/xr-player-overview.md | 18 +- .../tutorials/xr-player-win11-openXR.md | 11 +- 38 files changed, 6179 insertions(+), 115 deletions(-) create mode 100644 README.md create mode 100644 _sass/custom/custom.scss rename pages/5g-media-streaming/{features.md => repositories/features5GMSAF.md} (57%) create mode 100644 pages/5g-media-streaming/testing.md create mode 100644 pages/5g-media-streaming/testing/compatibility.md create mode 100644 pages/5g-media-streaming/testing/development-AS.md create mode 100644 pages/5g-media-streaming/testing/installation-local-user-5GMSAF.md create mode 100644 pages/5g-media-streaming/testing/installation-system-service-5GMSAF.md create mode 100644 pages/5g-media-streaming/testing/testing-AS.md create mode 100644 pages/5g-media-streaming/testing/testing-m1-v120.md create mode 100644 pages/5g-media-streaming/testing/testing-m1-v130.md create mode 100644 pages/5g-media-streaming/testing/testing-m1-v141.md create mode 100644 pages/5g-media-streaming/testing/testing-m3-v110.md create mode 100644 pages/5g-media-streaming/testing/testing-m3-v120.md create mode 100644 pages/5g-media-streaming/testing/testing-m5-v100.md create mode 100644 pages/5g-media-streaming/testing/testing-m5-v120.md create mode 100644 pages/5g-media-streaming/testing/testing-m5-v130.md create mode 100644 pages/5g-media-streaming/testing/testing-postman.md create mode 100644 pages/5g-media-streaming/tutorials/configuration-5GMSAF.md create mode 100644 pages/lte-based-5g-broadcast/rt-common-shared/MBMS-service-announcement-files.md create mode 100644 pages/lte-based-5g-broadcast/rt-mbms-modem/GPS-time-synchronization.md rename pages/xr-media-integration-in-5g/{features.md => repositories/featuresXRplayer.md} (99%) rename pages/xr-media-integration-in-5g/{ => tutorials}/xr-player-overview.md (90%) diff --git a/README.md b/README.md new file mode 100644 index 00000000..f51e5d04 --- /dev/null +++ b/README.md @@ -0,0 +1,5 @@ +# Welcome to the 5G-MAG Reference Tools Repositories + +This repository is meant as an entry point to all the repositories related to the 5G-MAG Reference Tools. It serves as an umbrella for documentation, practical guidelines and examples of applications that can be built with the software packages and also in combination with other platforms. + +All the relevant information is available in: https://5g-mag.github.io/Getting-Started/ diff --git a/_config.yml b/_config.yml index 874c1388..348d6fae 100644 --- a/_config.yml +++ b/_config.yml @@ -56,7 +56,6 @@ callouts: title: Warning color: red - # Footer content # appears at the bottom of every page's main content @@ -64,7 +63,7 @@ callouts: back_to_top: true back_to_top_text: "Back to top" -footer_content: "Copyright © 5G-MAG" +footer_content: "Copyright © 5G-MAG MEDIA ACTION GROUP" # Footer last edited timestamp last_edit_timestamp: true # show or hide edit time - page must have `last_modified_date` defined in the frontmatter diff --git a/_sass/custom/custom.scss b/_sass/custom/custom.scss new file mode 100644 index 00000000..be8551a8 --- /dev/null +++ b/_sass/custom/custom.scss @@ -0,0 +1,3 @@ +.side-bar { + width: 30%; +} diff --git a/assets/images/5g-mag-logo-with-text.png b/assets/images/5g-mag-logo-with-text.png index d7ffaf362aaf647f753580006cd9b73b23cca56b..017da0051e25c2f7361a0163089ed0ea61c2477d 100644 GIT binary patch literal 31037 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 OqbSI#%2XlDkpB;KYu8=? literal 31084 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 diff --git a/assets/images/5g-mag-reference-tools.png b/assets/images/5g-mag-reference-tools.png index ed6257fef0dc8fc86ab53e1747cc1f8c0256f904..9646e61984f51fb9c0c4341cc6c82c90bc2eb4db 100644 GIT binary patch literal 108925 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≻AO_=^*A< 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

sO-05UK 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 diff --git a/index.md b/index.md index 839b760d..f763f371 100644 --- a/index.md +++ b/index.md @@ -5,7 +5,7 @@ nav_order: 0 has_children: true --- -5g-mag +5g-mag # Overview diff --git a/pages/5g-media-streaming/repositories.md b/pages/5g-media-streaming/repositories.md index 11ae8bf9..cb517172 100644 --- a/pages/5g-media-streaming/repositories.md +++ b/pages/5g-media-streaming/repositories.md @@ -2,12 +2,17 @@ layout: default title: Repositories parent: 5G Downlink Media Streaming -has_children: false +has_children: true nav_order: 3 --- # ⭐ Related repositories -Please note that 5G Media Streaming makes use of other generic [5G Core Network components](https://jordijoangimenez.github.io/Getting-Started/pages/5g-core-network-components/) +Please note that 5G Media Streaming makes use of other generic [5G Core Network components](https://5g-mag.github.io/Getting-Started/pages/5g-core-network-components/) + +## 5GMS Application Provider: [rt-5gms-application-provider](https://github.com/5G-MAG/rt-5gms-application-provider) +* [Information and how to download, build, install and run](https://github.com/5G-MAG/rt-5gms-application-provider#readme) +* [Releases](https://github.com/5G-MAG/rt-5gms-application-provider/releases) +* [Projects](https://github.com/5G-MAG/rt-5gms-application-provider/projects?query=is%3Aopen) ## 5GMSd Application Function: [rt-5gms-application-function](https://github.com/5G-MAG/rt-5gms-application-function) * [Information and how to download, build, install and run](https://github.com/5G-MAG/rt-5gms-application-function#readme) diff --git a/pages/5g-media-streaming/features.md b/pages/5g-media-streaming/repositories/features5GMSAF.md similarity index 57% rename from pages/5g-media-streaming/features.md rename to pages/5g-media-streaming/repositories/features5GMSAF.md index 2b26f6f7..022f9e4a 100644 --- a/pages/5g-media-streaming/features.md +++ b/pages/5g-media-streaming/repositories/features5GMSAF.md @@ -1,83 +1,144 @@ --- layout: default -title: Features -parent: 5G Downlink Media Streaming +title: Features 5GMS AF +parent: Repositories +grand_parent: 5G Downlink Media Streaming has_children: false -nav_order: 1 +nav_order: 0 --- -# List of features under implementation +# 5GMSd Application Function Supported Features -The following describes the features of the Application Function that have been implemented, due to be implemented or not planned -yet. - -## Implemented in latest release (v1.2.0) - -- Reference point M1 (AP <=> AF) - - Provisioning Sessions (TS 26.512 v17.3.0) - - Content Protocols Discovery (TS 26.512 v17.3.0) - - Server Certificates Provisioning (TS 26.512 v17.3.0) - - Content Hosting Provisioning (TS 26.512 v17.3.0) -- Reference point M3 (AS <=> AF) - - Server Certificates Provisioning - - Content Hosting Provisioning -- Reference point M5 (UE <=> AF) - - Service Access Information (TS 26.512 v17.3.0) - -## Being worked on for pending releases - -This is a list of features being worked on for upcoming releases (and who is primarily working on that feature): - -- Reference point M1 (AP <=> AF) - - Uplift to TS 26.512 V17.4.0 (BBC) - - Policy Templates Provisioning (BBC) - - Uplift to TS 26.512 V17.5.0 (BBC) - - QoE Metrics Reporting Provisioning (Fraunhofer Fokus) -- Reference point M5 (UE <=> AF) - - Network Assistance (BBC) - - Dynamic Policies (BBC) - - QoE Metrics Reporting (Fraunhofer Fokus) - - Consumption Reporting (Qualcomm) -- 5GMBS seamless switching () - -## Unimplemented - -The following is a list of possible areas of the project to work on, noting which reference point(s) each is for or where a component -resides: - -- Data Collection AF -- Direct Data Collection Client (UE) -- Reference point M1 (AP <=> AF) - - Edge Computing integration - - Content Preparation Template Provisioning - - Consumption Reporting Provisioning - - Event Data Processing Provisioning -- Reference point M3 (AS <=> AF) - - Life-cycle management (heartbeat/registration/deregistration) -- Reference point M5 (UE <=> AF) -- Reference point R4 (AS <=> AF) - - Media Streaming Access Reporting -- Reference point R6 (AP <=> AF) - - Exposure of Dynamic Policy invocation events - - Exposure of Network Assistance invocation events - - Exposure of QoE Metrics Reporting events - - Exposure of Consumption Reporting events - - Exposure of Media Streaming Access Report events - -# Supported Features in the Software Releases - -## 5GMSd Application Function - -The release versions of the 5GMSd Application Function support differing sets of reference points, as described by the different +The release versions of the 5GMSd Application Function support differing sets of interfaces, as described by the different versions of the 3GPP specifications, and differing levels of feature support for those interfaces. The page attempts to capture the feature sets and specification versions for each release, starting with the most recent release or upcoming releases. -### Key +## Key -Where a feature of the specifications is supported the entry will be marked with ☑ and where it is unimplemented in that +Where a feature of the specifications is supported the entry will be marked with ☑, where it is being worked on and slated for the next release the feature will be marked with ✎ and where it is unimplemented in that version the feature will be marked with ☐. -### Upcoming Release v1.3.0 +## Upcoming Release v1.4.0 (development branch) + + + + + + + + + + + + + + + + + +
Interface reference pointSpecifications & VersionsProtocolsFeatures
M1 (server)
    +
  • TS 26.501 v17.6.0
  • +
  • TS 26.512 v17.6.0
  • +
    +
  • ☑ HTTP/1.1
  • +
  • ☐ HTTP/2.0
  • +
  • ☑ HTTP/1.1 over SSL/TLS
  • +
  • ☐ HTTP/2.0 over SSL/TLS
  • +
    +
  • ☑ Content Hosting Provisioning
  • +
  • ☑ Content Protocols Discovery
  • +
  • ☑ Provisioning Sessions
  • +
  • ☑ Server Certificates Provisioning
  • +
  • ☑ Consumption Reporting Provisioning
  • +
  • ☐ Content Preparation Templates Provisioning
  • +
  • ☐ Edge Resources Provisioning
  • +
  • ☐ Event Data Processing Provisioning
  • +
  • ✎ Metrics Reporting Provisioning
  • +
  • ☑ Policy Templates Provisioning
  • +
M3 (client)
    +
  • 5G-MAG prototype
  • +
    +
  • ☑ HTTP/1.1
  • +
  • ☑ HTTP/2.0
  • +
  • ☑ HTTP/1.1 over SSL/TLS
  • +
  • ☑ HTTP/2.0 over SSL/TLS
  • +
    +
  • ☑ Content Hosting Provisioning
  • +
  • ☑ Server Certificates Provisioning
  • +
M5 (server)
    +
  • TS 26.501 v17.6.0
  • +
  • TS 26.512 v17.6.0
  • +
    +
  • ☑ HTTP/1.1
  • +
  • ☐ HTTP/2.0
  • +
  • ☑ HTTP/1.1 over SSL/TLS
  • +
  • ☐ HTTP/2.0 over SSL/TLS
  • +
    +
  • ☑ Service Access Information
  • +
  • ☑ Consumption Reporting
  • +
  • ☑ Dynamic Policies
    +   Service Data Flow Description Methods:
      +
    • ☐ 2 Tuple
    • +
    • ☑ 5 Tuple
    • +
    • ☐ ToS
    • +
    • ☐ Flow Label
    • +
    • ☐ Domain Name
    • +
  • +
  • ✎ Metrics Reporting
  • +
  • ☑ Network Assistance
      +
    • ☐ Throughput Estimation
    • +
    • ☑ Delivery Boost
    • +
  • +
N5 (Npcf client/server)
    +
  • TS 29.514 v17.8.0
  • +
    +
  • ☑ HTTP/2.0
  • +
  • ☑ HTTP/2.0 over SSL/TLS
  • +
    +
  • ☑ Policy Authorization
  • +
  • ☑ Policy Authorization Notifications
  • +
N33 (client)
    +
  • TS 29.591 v17.9.0
  • +
    +
  • ☐ HTTP/2.0
  • +
  • ☐ HTTP/2.0 over SSL/TLS
  • +
    +
  • ☐ Event Exposure
  • +
R4 (server)
    +
  • TS 26.512 v17.6.0
  • +
    +
  • ☐ HTTP/1.1
  • +
  • ☐ HTTP/2.0
  • +
  • ☐ HTTP/1.1 over SSL/TLS
  • +
  • ☐ HTTP/2.0 over SSL/TLS
  • +
    +
  • ☐ Media Streaming Access
  • +
R5/R6 (client/server)
    +
  • TS 26.512 v17.6.0
  • +
  • TS 29.517 v17.9.0
  • +
    +
  • ☐ HTTP/1.1
  • +
  • ☐ HTTP/2.0
  • +
  • ☐ HTTP/1.1 over SSL/TLS
  • +
  • ☐ HTTP/2.0 over SSL/TLS
  • +
    +
  • ☐ Media Streaming QoE Event
  • +
  • ☐ Media Streaming Consumption Event
  • +
  • ☐ Media Streaming Network Assistance Invocation Event
  • +
  • ☐ Media Streaming Dynamic Policy Invocation Event
  • +
  • ☐ Media Streaming Access Event
  • +
  • ☐ Event Subscription
  • +
Nbsf (client)
    +
  • TS 29.513 v17.10.0
  • +
  • TS 29.521 v17.8.0
  • +
    +
  • ☑ HTTP/2.0
  • +
  • ☑ HTTP/2.0 over SSL/TLS
  • +
    +
  • ☑ Binding Information Retrieval
  • +
+ +## Release v1.3.0 @@ -158,7 +219,7 @@ version the feature will be marked with ☐.
Interface reference pointSpecifications & VersionsProtocolsFeatures
-### Release v1.2.0 +# Release v1.2.0 @@ -239,7 +300,7 @@ version the feature will be marked with ☐.
Interface reference pointSpecifications & VersionsProtocolsFeatures
-### Release v1.1.0 +# Release v1.1.0 @@ -320,3 +381,69 @@ version the feature will be marked with ☐.
Interface reference pointSpecifications & VersionsProtocolsFeatures
+# Features + +The following describes the features of the Application Function that have been implemented, due to be implemented or not planned +yet. + +## Implemented in the upcoming release (v1.4.1) + +- Everything from v1.4.0 plus... +- Interface at M1 (AP <=> AF) + - Metrics Reporting Provisioning (TS 26.512 v17.7.0 + fixes) + - Python command line tools moved to [rt-5gms-application-provider](https://github.com/5G-MAG/rt-5gms-application-provider) repository +- Interface at M5 (UE <=> AF) + - Metrics Reporting (TS 26.512 v17.7.0 + fixes) + +## Implemented in the latest release (v1.4.0) + +- Interface at M1 (AP <=> AF) + - Provisioning Sessions (TS 26.512 v17.4.0) + - Content Protocols Discovery (TS 26.512 v17.4.0) + - Server Certificates Provisioning (TS 26.512 v17.4.0) + - Content Hosting Provisioning (TS 26.512 v17.4.0) + - Consumption Reporting Provisioning (TS 26.512 v17.7.0) + - Dynamic Policy Templates Provisioning (TS 26.512 v17.7.0) +- Interface at M3 (AS <=> AF) + - Server Certificates Provisioning + - Content Hosting Provisioning +- Interface at M5 (UE <=> AF) + - Service Access Information (TS 26.512 v17.7.0) + - Consumption Reporting (TS 26.512 v17.7.0) + - Network Assistance (TS 26.512 v17.7.0) + - Delivery Boost only + - Dynamic Policies (TS 26.512 v17.7.0) + - 5 Tuple Service Flow Description method only. +- Uplift of other interfaces to TS 26.512 v17.7.0 + +## Being worked on for pending releases + +This is a list of features being worked on for upcoming releases of the 5GMS Application Function (and who is primarily working on that feature): + +- Data Collection AF (BBC) + - Internal AF interface (originating at M5 (UE <=> AF)) + - Collection of Metrics Reports + - Collection of Consumption Reports + - Interface at R4 (AS <=> AF) + - Media Streaming Access Reporting + - Interface at R5/R6 (AP <=> NWDAF/AF) + - Metrics Report Events + - Consumption Report Events + - Network Assistance Invocation Events + - Dynamic Policy Invocation Events + - Media Streaming Access Report Events +- 5GMBS seamless switching () + +## Unimplemented + +The following is a list of possible areas of the project to work on noting which interfaces each is for or where a component +resides: + +- Direct Data Collection Client (UE) +- Interface at M1 (AP <=> AF) + - Edge Computing integration + - Content Preparation Template Provisioning + - Event Data Processing Provisioning +- Interface at M3 (AS <=> AF) + - Lifecycle management (heartbeat/registration/deregistration) + - Hosting activation/deactivation diff --git a/pages/5g-media-streaming/specifications.md b/pages/5g-media-streaming/specifications.md index 787d2172..b843f4cc 100644 --- a/pages/5g-media-streaming/specifications.md +++ b/pages/5g-media-streaming/specifications.md @@ -6,6 +6,9 @@ has_children: false nav_order: 0 --- -# 📑 Specifications and relevant references -* Information about relevant specifications can be found at the [Standards Wiki](https://github.com/5G-MAG/Standards/wiki/5G-Downlink-Media-Streaming-Architecture-(5GMSd):-Relevant-Specifications) -* A list of relevant 3GPP Work Items can be found at [Standards Wiki](https://github.com/5G-MAG/Standards/wiki/5G-Downlink-Media-Streaming-Architecture-(5GMSd):-Relevant-Work-Items) +# 5G Media Streaming +## 📑 Specifications and relevant references +* Information about relevant specifications can be found in this [page](https://5g-mag.github.io/Standards/pages/5g-media-streaming/5g-media-streaming-specifications.html) + +## 📑 Relevant Work Items +* A list of relevant 3GPP Work Items can be found in this [page](https://5g-mag.github.io/Standards/pages/5g-media-streaming/5g-media-streaming-workitems.html) diff --git a/pages/5g-media-streaming/testing.md b/pages/5g-media-streaming/testing.md new file mode 100644 index 00000000..4afbed55 --- /dev/null +++ b/pages/5g-media-streaming/testing.md @@ -0,0 +1,54 @@ +--- +layout: default +title: Testing +parent: 5G Downlink Media Streaming +has_children: true +nav_order: 3 +--- +# 🚧 Testing + +## Installation of the 5GMS AF as a Local User +Follow the instructions in this [page](testing/installation-local-user-5GMSAF.html) for setting up a test environment without requiring full +system installation. + +## Installation of the 5GMS AF as a System Service +Follow the instructions in this [page](testing/installation-system-service-5GMSAF.html) for setting up a full system installation. + +## Testing APIs + +### Testing: M1 Interface + +The details of these tests change with different versions of the 5GMSd Application Function. + +If you are testing the v1.2.x versions then please visit the [Testing the M1 Interface on v1.2.0](testing/testing-m1-v120.html) +page. + +If you are testing the M1 interface on 5GMSd Application Function v1.3.0 to v1.4.0 then please visit the +[Testing the M1 Interface on v1.3.0](testing/testing-m1-v130.html) page. + +For testing the M1 interface on 5GMSd Application Function v1.4.1 or later, then please visit the +[Testing the M1 Interface on v1.4.1](testing/testing-m1-v141.html) page. + +### Testing: M3 Interface + +Depending on which version of the 5GMSd Application Function you wish to test, the commands to test the interface at reference point M3 change. + +If you wish to test 5GMSd Application Function v1.1.x then please see the [Testing the M3 Interface on v1.1.0](testing/testing-m3-v110.html) page. + +For versions after v1.1.x (i.e. v1.2.0 and above) please use the [Testing the M3 Interface on v1.2.0](testing/testing-m3-v120.html) page. + +### Testing: M5 Interface + +The details of these tests change with different versions of the 5GMSd Application Function. + +If you are testing versions up to v1.1.x then please visit the [Testing: M5 Interface on v1.0.0](testing/testing-m5-v100.html) page. + +If you are testing the M5 interface on 5GMSd Application Function v1.2.x please visit the +[Testing the M5 Interface on v1.2.0](testing/testing-m5-v120.html) page. + +If you are testing the M5 interface on 5GMSd Application Function v1.3.0 or later please visit the +[Testing the M5 Interface on v1.3.0](testing/testing-m5-v130.html) page. + +### Testing with Postman + +Postman is a popular API development and testing tool that allows users to create, send, and manage HTTP requests. Postman comes in very handy when testing and working with the M1 and M5 interfaces of the Application Function. Plese visit the [Testing with Postman](testing/testing-postman.html) diff --git a/pages/5g-media-streaming/testing/compatibility.md b/pages/5g-media-streaming/testing/compatibility.md new file mode 100644 index 00000000..5be9b902 --- /dev/null +++ b/pages/5g-media-streaming/testing/compatibility.md @@ -0,0 +1,24 @@ +--- +layout: default +title: Compability 5GMS components +parent: Testing +grand_parent: 5G Downlink Media Streaming +has_children: false +nav_order: 0 +--- + +# Compatibility between 5GMSd components + +Some versions of the 5GMSd Application Function will only work with particular versions of the 5GMSd Application Server and 5GMSd Media Session Handler. + +This page describes which versions will work with each other. + +| 5GMSd AF release | 5GMSd AS release | 5GMSd Media Session Handler | +| --- | --- | --- | +| [v1.0.0](https://github.com/5G-MAG/rt-5gms-application-function/releases/tag/rt-5gms-application-function-v1.0.0) | [v1.0.0](https://github.com/5G-MAG/rt-5gms-application-server/releases/tag/rt-5gms-application-server-1.0.0) and [v1.0.1](https://github.com/5G-MAG/rt-5gms-application-server/releases/tag/rt-5gms-application-server-1.0.1) | - | +| [v1.0.1](https://github.com/5G-MAG/rt-5gms-application-function/releases/tag/rt-5gms-application-function-v1.0.1) | [v1.0.0](https://github.com/5G-MAG/rt-5gms-application-server/releases/tag/rt-5gms-application-server-1.0.0) and [v1.0.1](https://github.com/5G-MAG/rt-5gms-application-server/releases/tag/rt-5gms-application-server-1.0.1) | - | +| [v1.1.0](https://github.com/5G-MAG/rt-5gms-application-function/releases/tag/rt-5gms-application-function-v1.1.0) | [v1.1.1](https://github.com/5G-MAG/rt-5gms-application-server/releases/tag/rt-5gms-application-server-1.1.1) | - | +| [v1.2.0](https://github.com/5G-MAG/rt-5gms-application-function/releases/tag/rt-5gms-application-function-v1.2.0) | [v1.1.1](https://github.com/5G-MAG/rt-5gms-application-server/releases/tag/rt-5gms-application-server-1.1.1) | - | +| [v1.2.1](https://github.com/5G-MAG/rt-5gms-application-function/releases/tag/rt-5gms-application-function-v1.2.1) | [v1.1.1](https://github.com/5G-MAG/rt-5gms-application-server/releases/tag/rt-5gms-application-server-1.1.1) | - | +| [v1.3.0](https://github.com/5G-MAG/rt-5gms-application-function/releases/tag/rt-5gms-application-function-v1.3.0) | [v1.1.2](https://github.com/5G-MAG/rt-5gms-application-server/releases/tag/rt-5gms-application-server-1.1.2), [v1.2.0](https://github.com/5G-MAG/rt-5gms-application-server/releases/tag/rt-5gms-application-server-1.2.0) and [v1.2.1](https://github.com/5G-MAG/rt-5gms-application-server/releases/tag/rt-5gms-application-server-1.2.1) | - | +| [v1.4.0](https://github.com/5G-MAG/rt-5gms-application-function/releases/tag/rt-5gms-application-function-v1.4.0) | [v1.3.0](https://github.com/5G-MAG/rt-5gms-application-server/releases/tag/rt-5gms-application-server-1.3.0) | - | diff --git a/pages/5g-media-streaming/testing/development-AS.md b/pages/5g-media-streaming/testing/development-AS.md new file mode 100644 index 00000000..14e02424 --- /dev/null +++ b/pages/5g-media-streaming/testing/development-AS.md @@ -0,0 +1,275 @@ +--- +layout: default +title: Developing the 5GMS AS +parent: Testing +grand_parent: 5G Downlink Media Streaming +has_children: false +nav_order: 4 +--- + +CONTENT TO BE CHECKED + +In this directory you will find files to assist with development and testing of +the 5G-MAG Reference Tools 5GMS Application Server (AS). + +Files in this repository: +- ATTRIBUTION_NOTICE - List of 3rd party software used when running the 5GMS application server. +- LICENSE - The software license for this project. +- README.md - Project README file. +- pyproject.toml - The Python project description for building and installing the application. +- build_scripts/ - Scripts used when building the python project. + - api.mustache - openapi-generator template file to pass operations onto methods in the class defined in src/rt_5gms_as/server.py. + - backend.py - build backend wrapper to trigger extra build actions. + - generate_5gms_as_openapi - Will generate the OpenAPI python modules if not already present. + - M3_merged.yaml - OpenAPI YAML wrapper file to merge the M3 interface files into one for API bindings generation. + - openapi-generator-config.yaml.in - openapi-generator configuration file template. +- docs/ - Development documentation and examples. + - README.md - This document. + - example-application-server.conf - An application configuration which documents the defaults and meaning for each application configuration option. +- external/ - Directory containing submodule mount points. + - rt-common-shared/ - The common shared examples and scripts. +- src/ - The application source modules. + - rt_5gms_as/ - The main Python module for this application + - app.py - Application entry point. + - exceptions.py - Application specific Exception class definitions. + - context.py - Module for the application Context class. + - openapi_5g/ - Python bindings generated by openapi-generator-cli from the 5G APIs. Note: This directory is not present in the tree until `build_scripts/generate_openapi` is run. + - proxies/ - Contains the web server/proxy detection and configuration classes and any data files they need. + - proxy_factory.py - Factory module to pick a suitable web server/proxy. + - server.py - M3 Server implementation. + - utils.py - Common utility functions for the web server/proxy classes. +- tests/ - Regression and build acceptance tests and other testing tools. + - examples/ - Example configurations to go along with the tests. + +Running the example without building +------------------------------------ +Make sure that git, java, wget and nginx are installed on the local system and +can be found on the current command path (`$PATH`). +``` +sudo apt install git wget nginx default-jdk python3-regex +``` + +Generate the OpenAPI python modules (these are not part of the source +distribution). Read documentation below on "Regenerating the 5G API bindings": +``` +cd ~/rt-5gms-application-server +build_scripts/generate_5gms_as_openapi +``` + +Create a configuration to run the application server as a local, unprivileged, +user. +``` +mkdir ~/.rt_5gms +cat > ~/.rt_5gms/application-server.conf < + m3_client_cli.py -c | --certificate (add|update) + m3_client_cli.py -c | --certificate delete + m3_client_cli.py -H | --content-hosting-configuration + m3_client_cli.py -H | --content-hosting-configuration (add|update) + m3_client_cli.py -H | --content-hosting-configuration delete + m3_client_cli.py -H | --content-hosting-configuration purge [] + +Parameters: + connect Hostname:Port of the server providing M3. + provisioning-session-id Provisioning Session Identifier. + certificate-id Certificate Identifier. + pem-file Server PEM format X.509 public certificate, private key and intermediate CA certificates. + content-hosting-configuration-json-file + Filename of a ContentHostingConfiguration in JSON format. + pattern Regular expression to match the cache entry URL paths to delete. + +Options: + -h --help Display the command help + -v --version Display command version + -c --certificate List known certificates or perform a certificate operation. + -H --content-hosting-configuration + List known ContentHostingConfigurations or perform an operation on ContentHostingConfigurations. +``` + +This can be used instead of the AF to configure a running AS. + +### Prerequisite packages + +These testing scripts require a few more Python 3 modules to be installed, beyond what is brought in as requirements when the when the application server is installed. + +The extra modules are: `docopt`, `aiofiles` and `httpx[http2]`. + +These can be installed on ubuntu using: +```bash +apt install python3-docopt python3-aiofiles python3-httpx python3-h2 +``` +...or on most distributions by using the python `pip` module: +``` +python3 -m pip install docopt aiofiles 'httpx[http2]' +``` + +### Running the Application Server for testing with `m3_client_cli.py` + +When running any of the following tests the Application Server must be running first. The exact command will depend on how you installed the Application Server or whether you are [running directly without building](#running-the-example-without-building). + +#### Running from a virtual Python environment installation + +If the AS you are testing has been installed in a virtual Python environment, as described in the [Development and Testing](https://github.com/5G-MAG/rt-5gms-application-server/wiki/Development-and-Testing) wiki page, then you would simply run the AS from the virtual environment using your local configuration file. For example: + +```bash +cd ~/rt-5gms-application-server +venv/bin/5gms-application-server -c local-dev.conf +``` + +#### Running a system wide AS installation + +If the Application Server under test has been installed as a system process, using a command like `sudo python3 -m pip install .` or `sudo python3 -m pip install rt-5gms-application-server-1.X.X.tar.gz`, then you can run the AS as root. For example: + +```bash +sudo 5gms-application-server +``` + +### To configure a simple HTTP Application Server + +Make sure the AS is running first (see ["Running the Application Server for testing with `m3_client_cli.py`"](#running-the-application-server-for-testing-with-m3_client_clipy) above). + +```bash +cd ~/rt-5gms-application-server +tests/m3_client_cli.py -H localhost:7777 add ps1 tests/examples/ContentHostingConfiguration_Big-Buck-Bunny_pull-ingest.json +``` +This should respond with a "Success!" message, and NGINX should now be running on port 8080 using the example Big Buck Bunny configuration. You can check the NGINX configuration in `/tmp/rt_5gms_as.conf`. + +### To configure an HTTPS Application Server + +Make sure the AS is running first (see ["Running the Application Server for testing with `m3_client_cli.py`"](#running-the-application-server-for-testing-with-m3_client_clipy) above). + +This requires that the server certificate is pushed to the Application Server before the content hosting configuration is. + +To generate server certificates, ensure that `openssl` is installed (e.g. `apt -y install openssl`), and then: +```bash +cd ~/rt-5gms-application-server +external/rt-common-shared/5gms/scripts/make_self_signed_certs.py tests/examples/ContentHostingConfiguration_Big-Buck-Bunny_pull-ingest_https.json tests/examples/Certificates.json +``` + +The 5GMS Application Server stores the certificates it has been configured with in a certificates cache. This cache is reloaded when the Application Server starts up, so it will remember certificates from previous runs. + +The 5GMS Application Server can be checked for what certificates it already has by using the command: +```bash +cd ~/rt-5gms-application-server +tests/m3_client_cli.py -c localhost:7777 +``` + +To push a new certificate (with id "testcert1" using the generated certificate file): +```bash +cd ~/rt-5gms-application-server +tests/m3_client_cli.py -c localhost:7777 add testcert1 tests/examples/certificate-1.pem +``` + +...or to update an existing certificate: +```bash +cd ~/rt-5gms-application-server +tests/m3_client_cli.py -c localhost:7777 update testcert1 tests/examples/certificate-1.pem +``` + +Now the Content Hosting Configuration can be pushed: +```bash +cd ~/rt-5gms-application-server +tests/m3_client_cli.py -H localhost:7777 add ps1 tests/examples/ContentHostingConfiguration_Big-Buck-Bunny_pull-ingest_https.json +``` +This should result in "Success!" and NGINX will now be listening on "https://localhost:8443/...". + +To start both HTTPS and HTTP reverse proxies for the Big Buck Bunny content, substitute the ContentHostingConfiguration above for the `tests/examples/ContentHostingConfiguration_Big-Buck-Bunny_pull-ingest_http_and_https.json` file, or update the configuration using: +```bash +cd ~/rt-5gms-application-server +tests/m3_client_cli.py -H localhost:7777 update ps1 tests/examples/ContentHostingConfiguration_Big-Buck-Bunny_pull-ingest_http_and_https.json +``` + +Note: Following these instructions will create a self-signed certificate for localhost in `~/rt-5gms-application-server/tests/examples/certificate-1.pem`, this certificate will not pass normal CA verification so to access the URL you need to turn off SSL validation or accept the self-signed certificate in your browser or media player application. + +Regenerating the 5G API bindings +-------------------------------- +The `build_scripts/generate_5gms_as_openapi` script will use wget, git and java to download the openapi-generator tool, the 5G OpenAPI YAML and generate the `rt_5gms_as.openapi_5g` Python module package. The script will only do this if the `src/rt_5gms_as/openapi_5g` directory does not already exist. + +Therefore to regenerate the API bindings you first need to remove the old bindings: +``` +cd ~/rt-5gms-application-server +rm -rf src/rt_5gms_as/openapi_5g +``` + +Then run the generator script: +``` +~/rt-5gms-application-server/build_scripts/generate_5gms_as_openapi +``` + +For reference (or if it is desirable to recreate the steps manually) the `generate_5gms_as_openapi` script performs the following actions: +- Uses `wget` to fetch version 6.0.1 of the [openapi-generator-cli](https://github.com/OpenAPITools/openapi-generator-cli). + - e.g. `wget https://repo1.maven.org/maven2/org/openapitools/openapi-generator-cli/6.0.1/openapi-generator-cli-6.0.1.jar -O openapi-generator-cli.jar` +- Uses `git` to clone the [5G OpenAPI repository](https://forge.3gpp.org/rep/all/5G_APIs.git). + - e.g. `git clone -b REL-17 https://forge.3gpp.org/rep/all/5G_APIs.git` +- Copies in the API override files + - e.g. `cp -f ~/rt-5gms-application-server/external/rt-common-shared/5gms/5G_APIs-overrides/*.yaml ~/rt-5gms-application-server/build_scripts/M3_merged.yaml 5G_APIs/` +- Uses the openapi-generator-cli, downloaded in the first step, to generate the API bindings. + - e.g. `mkdir 5g-api-python; java -jar openapi-generator-cli.jar generate -i 5G_APIs/TS26512_M1_ContentHostingProvisioning.yaml -g python --additional-properties packageName=rt_5gms_as.openapi_5g,projectName=openapi-5g -o 5g-api-python; java -jar openapi-generator-cli.jar generate -t ~/rt-5gms-application-server/build_scripts -i 5G_APIs/M3_merged.yaml -g python --additional-properties packageName=rt_5gms_as.openapi_5g,projectName=openapi-5g -o 5g-api-python` +- Copies the API Python package to the `src/rt_5gms_as/openapi_5g` directory. + - e.g. `cp -r 5g-api-python/rt_5gms_as/openapi_5g ~/rt-5gms-application-server/src/rt_5gms_as/` + diff --git a/pages/5g-media-streaming/testing/installation-local-user-5GMSAF.md b/pages/5g-media-streaming/testing/installation-local-user-5GMSAF.md new file mode 100644 index 00000000..639db522 --- /dev/null +++ b/pages/5g-media-streaming/testing/installation-local-user-5GMSAF.md @@ -0,0 +1,161 @@ +--- +layout: default +title: Installation 5GMS AF as Local User +parent: Testing +grand_parent: 5G Downlink Media Streaming +has_children: false +nav_order: 1 +--- + +# Testing the 5GMSd Application Function as a Local User + +**TODO: Intro** + +## Build Dependencies + +Before building the 5GMSd Application Function, there are a few build dependencies that are needed. + +The following are needed: +- **Commands** + - bison + - C compiler + - curl + - flex + - git + - java + - meson (version 0.63.0 or higher)[\[1\]](#footnote-1) + - ninja + - pip + - python3 + - wget +- **Development libraries** + - sctp + - gnutls + - yaml + - nghttp2 + - talloc + - microhttpd + - curl + - mongo DB + - openssl + - gcrypt + - tins + - idn +- **Python modules** + - venv + - yaml + +Notes: + - [1]: `meson` must be at least version 0.63.0 to patch open5gs properly, therefore it is suggested that this be installed using the python pip module to pull the latest version rather than relying on the packaged version from your operating system. + +These can be installed using your system package manager, for example: + +**Ubuntu and derivatives** +```bash +sudo apt install bison build-essential curl flex git default-jdk ninja-build wget python3-pip python3-venv python3-setuptools python3-wheel python3-yaml libsctp-dev libgnutls28-dev libgcrypt-dev libssl-dev libidn11-dev libmongoc-dev libbson-dev libyaml-dev libnghttp2-dev libmicrohttpd-dev libcurl4-gnutls-dev libnghttp2-dev libtins-dev libtalloc-dev +sudo python3 -m pip install build meson +``` + +## Retrieving the source + +When developing always use the `development` branch of the 5GMSd Application Function. + +If you are intending to commit code back to the project then we advise creating your own "Fork" of the repository at , and working on your own fork. + +To clone the source use: + +```bash +cd +git clone -b development --recurse-submodules git@github.com:${github_username}/rt-5gms-application-function.git +``` + +Where `${github_username}` is your GitHub user name. + +If you are not intending committing changes back to the repository (only testing the latest development version) then you can obtain a clone using: + +```bash +cd +git clone -b development --recurse-submodules https://github.com/5G-MAG/rt-5gms-application-function.git +``` + +If you wish to commit back changes then these should be pushed to a branch on your fork and PR raised to submit the changes back to the development branch of the 5G-MAG repository. + +## Building + +```bash +cd ~/rt-5gms-application-function +meson setup --prefix=`pwd`/install build +ninja -C build +``` + +## Installing + +If you are upgrading from one release to the next then it is advisable to delete the old installed `msaf.yaml` configuration and +replace it with a new one. To delete the old installed configuration use: + +```bash +cd ~/rt-5gms-application-function +rm -f install/etc/open5gs/msaf.yaml +``` + +To install the Application Function, its default configuration and supporting scripts: + +```bash +cd ~/rt-5gms-application-function +meson install -C build --no-rebuild +``` + +It is advisable to review the configuration file in `~/rt-5gms-application-function/install/etc/open5gs/msaf.yaml` before running +the 5GMSd Application Function for the first time after installation. + +## Configuring + +The configuration can be found in `~/rt-5gms-application-function/install/etc/open5gs/msaf.yaml`. Edit this YAML file to change the operating configuration of the 5GMSd Application Function. + +See the [Configuring the Application Function](Configuring-the-Application-Function) page for more details on the settings. + +## Running + +Once the configuration has been set, execute the 5GMSd Application Function using: + +```bash +~/rt-5gms-application-function/install/bin/open5gs-msafd +``` + +For v1.2.0 to v1.4.0: + + To run the `m1-session` tool with a local user installation the python path for the user installed module needs to be included in + the `PYTHONPATH` environment variable. + + ```bash + PYTHONPATH=`find ~/rt-5gms-application-function/install -type d '(' -name 'site-packages' -o -name 'dist-packages' ')' -print` export PYTHONPATH + ``` + + The default configuration for the `m1-session` tool will try to write the persistent store to `/var/cache/rt-5gms/m1-client`. This may not be accessible as a local user and therefore it is a good idea to change the `data_store` configuration setting to a place where the local user can write to: + + ```bash + ~/rt-5gms-application-function/install/bin/m1-session configure set data_store ~/m1-client-data-store + ``` + +For v1.4.1 and above: + + The `m1-session` tool has moved to the [rt-5gms-application-provider](https://github.com/5G-MAG/rt-5gms-application-provider) + repository. Please follow the instructions in that repository for manual installation of the M1 python tools and classes to + install the `msaf-configuration`, `m1-session` and `m1-client` tools either to your system or to a virtual python environment. + + If you have installed the tools in the python virtual environment then activate the environment before proceeding, for example: + + ```bash + source venv/bin/activate + ``` + + Then you can change the data store directory, for example: + + ```bash + m1-session configure set data_store ~/m1-client-data-store + ``` + +## Stopping + +Press CTRL-C on the terminal from which the 5GMSd Application Function is running. + diff --git a/pages/5g-media-streaming/testing/installation-system-service-5GMSAF.md b/pages/5g-media-streaming/testing/installation-system-service-5GMSAF.md new file mode 100644 index 00000000..70689a20 --- /dev/null +++ b/pages/5g-media-streaming/testing/installation-system-service-5GMSAF.md @@ -0,0 +1,239 @@ +--- +layout: default +title: Installation 5GMS AF as System Service +parent: Testing +grand_parent: 5G Downlink Media Streaming +has_children: false +nav_order: 2 +--- +# Installing the 5GMSd Application Function as a System Service + +## Install dependencies + +```bash +sudo apt install git python3-pip python3-venv python3-setuptools python3-wheel ninja-build build-essential flex bison git libsctp-dev libgnutls28-dev libgcrypt-dev libssl-dev libidn11-dev libmongoc-dev libbson-dev libyaml-dev libnghttp2-dev libmicrohttpd-dev libcurl4-gnutls-dev libnghttp2-dev libtins-dev libtalloc-dev curl wget default-jdk +sudo python3 -m pip install build meson +``` + +## Downloading + +Release tar files can be downloaded from . + +The source can be obtained by cloning the github repository. + +For example to download the latest release you can use: + +```bash +cd ~ +git clone --recurse-submodules https://github.com/5G-MAG/rt-5gms-application-function.git +cd rt-5gms-application-function +git submodule update +``` + +## Build the 5GMSd Application Function + +The build process requires a working Internet connection as the API files are retrieved at build time. + +To build the 5GMSd Application Function from the source: + +```bash +cd ~/rt-5gms-application-function +meson build +ninja -C build +``` + +**Note:** Errors during the `meson build` command are often caused by missing dependancies or a network issue while trying to retrieve the API files and `openapi-generator` JAR file. See the `~/rt-5gms-application-function/build/meson-logs/meson-log.txt` log file for the errors in greater detail. Search for `generator-5gmsaf` to find the start of the API fetch sequence. + +## Installing + +To install the built Application Function as a system process: + +```bash +cd ~/rt-5gms-application-function/build +sudo meson install --no-rebuild +``` + +## Configuration of streams (v1.3.0 and above) + +To assist with runtime configuration of the Application Function when it is started as a Systemd service, there is a utility at `/usr/local/bin/msaf-configuration`. This utility uses two configuration files at `/etc/rt-5gms/af-sync.conf` and `/etc/rt-5gms/streams.json`. The utility will also write out the `m8.json` file to be distributed via the interface at reference point M8 to the 5GMSd-Aware Application running on the UE. + +### `/etc/rt-5gms/af-sync.conf` - `msaf-configuration` configuration file + +This file holds some configuration parameters for the `msaf-configuration` utility. + +```ini +[af-sync] +m5_authority = af.example.com:7777 +docroot = /var/cache/rt-5gms/as/docroots +default_docroot = /usr/share/nginx/html +``` + +**m5_authority**: This should be set to the M5 address to advertise to the UE via the M8 application data object (default: 127.0.0.23:7777). +**docroot**: This is the directory path to where the Application Server will configure the document roots for each domainNameAlias configured (default: /var/cache/rt-5gms/as/docroots). +**default_docroot**: This is the directory path to the document root for the fallback virtual host offered by the Application Server (default: /usr/share/nginx/html). + +The defaults for *docroot* and *default_docroot* should not need to be set if the Application Server is running on the same host. These may need to be changed to network filesystem shares, shared with the 5GMSd Application Server, if the AS runs on a different host. + +The *m5_authority* should be set the the external hostname and port that the Application Function M5 interface can be found at. + +### `/etc/rt-5gms/streams.json` - Configuration of media streams to synchronise with the Application Function + +This is a JSON file which contains the information required to configure the Provisioning Sessions and Content Hosting Configurations on the 5GMSd Application Function. + +An example of this file would be: +```json +{ + "aspId": "The ASP ID to use, can be omitted", + "appId": "The external application id to use for all provisioning sessions", + "streams": { + "stream-id-1": { + "name": "Abc123 media", + "ingestURL": "https://media.example.com/media_assets/abc123/", + "distributionConfigurations": [ + { + "domainNameAlias": "as.exmaple.com", + "entryPoint": { + "relativePath": "manifest.mpd", + "contentType": "application/dash+xml", + "profiles": ["urn:mpeg:dash:profile:isoff-on-demand:2011"] + } + }, + { + "domainNameAlias": "as.exmaple.com", + "entryPoint": { + "relativePath": "manifest.m3u8", + "contentType": "application/vnd.apple.mpegurl" + } + } + ], + "consumptionReporting": { + "reportingInterval": 30, + "samplePercentage": 50.00, + "locationReporting": true, + "accessReporting": true + }, + "policies": [ + { + "externalReference": "pol-ext-1", + "applicationSessionContext": { + "sliceInfo": { "sst": 1, "sd": "000001" }, + "dnn": "internet" + }, + "qoSSpecification": { + "qosReference": "qosRef1", + "maxAuthBtrUl": "10 Mbps", + "maxAuthBtrDl": "1 Gbps", + "defPacketLossRateUl": 0, + "defPacketLossRateDl": 5 + }, + "chargingSpecification": { + "sponId": "sponsorIdentity1", + "sponStatus": "SPONSOR_ENABLED", + "gpsi": [ + "msimsi-447000123456", + "msimsi-447000654321" + ] + } + }, + { + "externalReference": "pol-ext-2", + "qoSSpecification": { + "maxAuthBtrUl": "100 Mbps", + "maxAuthBtrDl": "2 Gbps" + } + } + ] + }, + "stream-id-2": { + "name": "VOD service", + "ingestURL": "https://media.example.com/media_assets/", + "distributionConfigurations": [ + { + "domainNameAlias": "as.exmaple.com" + }, + { + "domainNameAlias": "as.exmaple.com", + "certificateId": "cert1" + } + ] + } + }, + "vodMedia": [ + { + "name": "Def456 VOD Media", + "stream": "stream_id-2", + "entryPoints": [ + { + "relativePath": "def456/manifest.mpd", + "contentType": "application/dash+xml", + "profiles": ["urn:mpeg:dash:profile:isoff-live:2011"] + }, + { + "relativePath": "def456/manifest.m3u8", + "contentType": "application/vnd.apple.mpegurl" + } + ] + }, + { + "name": "Ghi789 VOD Media", + "stream": "stream_id-2", + "entryPoints": [ + { + "relativePath": "ghi789/manifest.mpd", + "contentType": "application/dash+xml", + "profiles": ["urn:mpeg:dash:profile:isoff-live:2011"] + }, + { + "relativePath": "ghi789/manifest.m3u8", + "contentType": "application/vnd.apple.mpegurl" + } + ] + } + ] +} +``` + +The format is a JSON object which has the following fields: +- **aspID** - The Application Service Provider ID to provide when creating Provisioning Sessions. +- **appID** - The external application ID to use when creating Provisioning Sessions. +- **streams** - A description of the streams to configure, each in its own Provisioning Session. + - Each stream is identified in the configuration file by a unique identifier so that it may be referenced in the *vodMedia* field. + - The value for each stream contains the following fields: + - **name** - The name of the stream that will be configured in the ContentHostingConfiguration + - **ingestURL** - The base URL for media ingest from the media origin. + - **distributionConfigurations** - The array of distribution configurations that can appear in the ContentHostingConfiguration. + - See TS 26.512 for details of the format for this field. + - Any **certificateId** fields given in a distribution configuration will cause a certificate to be generated and the real certificate id will be substituted. Where the same certificate id is used for multiple entries in the *distributionConfigurations*, the same real certificate id will be substitued and that certificate only generated once. + - If an **entryPoint** field appears in any distribution configuration, then the M8 data file will contain an entry for this media and will use the *name* field for the stream as the media name and will only the include the Provisioning Session ID. The UE will be expected to consult the M5 interface for the entry point via the Service Access Information. + - If there are no *entryPoint*s then it is expected that the stream will be referenced from the *vodMedia* array in this configuration file. + - **consumptionReporting** - The optional consumption reporting configuration for the provisioning session. + - See TS 26.512 for details of this configuration. + - If this item is present then consumption reporting will be configured for the provisioning session. + - If this is an empty structure then consumption reporting is enabled for all clients using a single report at the end of the media playback. + - If the **reportingInterval** is given then the client will send a consumption report every **reportingInterval** seconds. + - If the **samplePercentage** is given this represents the percentage probability that a client will send consumption reports. At the start of the session the client determines a random number between 0 and 100 and if the value is less than or equal to this field value then the client will send reports for the session. If this field is not given then all clients will send reports. + - If the **locationReporting** field is present and is `true` then consumption reports will include UE location information. + - If the **accessReporting** field is present and is `true` then consumption reports will contain access information too. + - **policies** - The optional list of dynamic policy templates available for this stream. + - See TS 26.512 for details of PolicyTemplate configurations. + - If this item is present and has at least one entry then dynamic policies will be advertised to the clients for the provisioning session. + - Each object in the list must have an `externalReference` property, all other properties are optional. +- **vodMedia** - Array of media asset objects to advertise directly in the `m8.json` M8 interface data file. + - Each entry will create a media entry in the M8 data file which will have 1 or more entry points associated with it. + - Each media asset in the array contains the following fields: + - **name** - The name to use for the media name in the M8 data file media entry. + - **stream** - A reference to a stream defined in the *streams* map in this configuration file. + - This is used to find the provisioning session id and distribution points for the stream. + - **entryPoints** - An array of relative entry points for the media assets. + - Each entry takes the same format as the *streams.distributionConfigurations.entryPoint*. + - Entry points for the media asset written to the M8 data file are absolute URLs and are derived from the *entryPoints* fields combined with each entry in *stream.distributionConfigurations*. + +In the example above there are two Provisioning Sessions which will be configured in the running Application Function. + +The first, *stream-id-1*, is for a single media asset. This will result in the the M8 data file containing an entry for the "Abc123 media" media asset which has no entry points, just a provisioning session Id. The M5 Service Access Information for this provisioning session will contain the two entry points defined, one for the DASH stream and one for the HLS stream both available using the HTTP protocol (no "certificateId" present). This stream will also request that half the clients submit consumption reports every 30 seconds which contain both location and access information. There will also be two dynamic policies advertised as available for use with this stream. + +The second, *stream-id-2*, will create a Provisioning Session which acts as a VOD entry point for multiple streams advertised via M8. There are two distribution points for this provisioning session, one HTTP and the other HTTPS. The M5 Service Access Information will have no entryPoints listed. The M8 data file entries using this provisioning session id will be derived from the *vodMedia* entries which reference stream *stream-id-2*. + +The *vodMedia* array contains two media assets which will be listed in the M8 data file using the *name* from the *vodMedia* entry and the Provisioning session Id of the provisioning session created for *stream-id-2* in the *streams* section above. Each entry will list the entryPoints for each of the *entryPoints* defined for the media asset combined with each of the distribution points from the provisioning session for *stream-id-2*. Therefore, in the M8 data file, there will be 4 entry points for "Def456 VOD Media": DASH over HTTP, DASH over HTTPS, HLS over HTTP and HLS over HTTPS; and a similar 4 entry points for the "Ghi789 VOD Media". + +The M8 data file generated from the example above will have 3 media assets, the first for "Abc123" will just have a provisioning session id and the other two, "Def456" and "Ghi789", will both contain the same provisioning session id but will have 4 different entry points each for the combinations of DASH/HLS and HTTP/HTTPS. diff --git a/pages/5g-media-streaming/testing/testing-AS.md b/pages/5g-media-streaming/testing/testing-AS.md new file mode 100644 index 00000000..d3529d3b --- /dev/null +++ b/pages/5g-media-streaming/testing/testing-AS.md @@ -0,0 +1,231 @@ +--- +layout: default +title: Testing the 5GMS AS +parent: Testing +grand_parent: 5G Downlink Media Streaming +has_children: false +nav_order: 3 +--- + +# Developing and Testing the 5GMSd Application Server + +While the instructions in the main project [README](https://github.com/5G-MAG/rt-5gms-application-server#readme) tell you how to install the 5GMSd Application Server as a system-wide application, during development it is usually more appropriate to have one or more local clones of the repository that are being used for development and testing. This page provides details of one suggested way to arrange your development environment to ensure separation from the main system during development and testing. + +# Prerequisites + +There are some packages that will need to be installed system wide that the build and install system relies on. These prerequisite packages are: +- **Commands** + - Git + - Java + - Python 3 + - Wget +- **Python 3 modules** + - pip + - venv + +These can usually be installed as system packages, for example: +**Debian/Ubuntu Linux and derivatives** +```bash +sudo apt -y install git default-jdk python3 wget python3-pip python3-venv +``` + +**RHEL/CentOS/Fedora/Rocky** +```bash +sudo dnf -y install git java-latest-openjdk python3 wget python3-pip python3-venv +``` + +# Checking out the project code + +Since this will be used for development and testing, the instructions here will show you how to check out the latest development branch. + +## Checkout for development + +1. **Create a fork** + 1. Login to GitHub ([create a GitHub account](https://github.com/join) and [set an SSH key](https://docs.github.com/en/authentication/connecting-to-github-with-ssh/adding-a-new-ssh-key-to-your-github-account) if you haven't already done so). + 1. On the main [GitHub project page](https://github.com/5G-MAG/rt-5gms-application-server) click on the "Fork" label in the top right of the page, then untick "Copy the `main` branch only" on the page that appears and select the "Create fork" button. + 1. On your new fork page select the `Settings` option just below the main repository title. Then select "Branches" under the "Code and automation" topic from the sections on the left, and edit the "Default branch" to change it to `development`. + 1. Clone the repository + ```bash + cd + git clone --recurse-submodules git@github.com:/rt-5gms-application-server.git + ``` + Where `` is the username of your GitHub login. + +## Checkout for testing only + +1. **Clone the 5G-MAG repository** + ```bash + cd + git clone -b development --recurse-submodules https://github.com/5G-MAG/rt-5gms-application-server.git + ``` + +# Creating the virtual Python environment + +By using a Python virtual environment you can use upgraded versions of existing system Python modules and automatically install project module dependencies without having to install or upgrade modules system wide. + +1. Create the virtual Python environment + + ```bash + cd ~/rt-5gms-application-server + python3 -m venv venv + ``` + +1. Update base modules and test script dependencies + + ```bash + cd ~/rt-5gms-application-server + venv/bin/python3 -m pip install --upgrade pip build setuptools docopt PyYAML + ``` + +# Build and install the 5GMSd Application Server + +```bash +cd ~/rt-5gms-application-server +venv/bin/python3 -m pip install . +``` + +# Create a local-user friendly configuration + +Save these configuration file contents as `~/rt-5gms-application-server/local-dev.conf`: +```ini +### Defaults for the 5G-MAG Reference Tools: 5GMSd applications +[DEFAULT] +log_dir = /tmp/rt-5gms-as/logs +run_dir = /tmp/rt-5gms-as + +### 5GMSd Application Server specific configurations +[5gms_as] +log_level = debug +cache_dir = /tmp/rt-5gms-as/cache +certificates_cache = /tmp/rt-5gms-as/certificates +http_port = 8080 +https_port = 8443 +#m3_listen = localhost +#m3_port = 7777 +#access_log = %(log_dir)s/application-server-access.log +#error_log = %(log_dir)s/application-server-error.log +#pid_path = %(run_dir)s/application-server.pid + +### 5GMSd Application Server nginx specific configuration +[5gms_as.nginx] +root_temp = /tmp/rt-5gms-as +#client_body_temp = %(root_temp)s/client-body-tmp +#proxy_temp = %(root_temp)s/proxy-tmp +#fastcgi_temp = %(root_temp)s/fastcgi-tmp +#uwsgi_temp = %(root_temp)s/uwsgi-tmp +#scgi_temp = %(root_temp)s/scgi-tmp +#pid_path = %(root_temp)s/5gms-as-nginx.pid +``` + +Using this configuration will: +- Place all 5GMSd Application Server cache directories and other temporary files and logs under the `/tmp/rt-5gms-as` directory for testing. +- Change the default ports used for the 5GMSd distribution service at reference point M4d. Note that this means that all URLs output by the 5GMSd Application Function with respect to M4d will need to be manually modified to insert the new port numbers before use, e.g. if the AF publishes a URL starting `http://your.hostname/...` you will need to change that to `http://your.hostname:8080/...` in order to use the URL. This is necessary as the default ports of 80 and 443 are not available to normal unprivileged users. A normal user has to use ports with a number greater than 1024. +- Turn on `debug` level output from the 5GMSd Application Server in order to better see what it happening. +- Some settings above are commented out but may be useful while testing and so have been left in with their default values and prefixed with `#` to comment them out. If you wish to change one then remove the `#` and change the value to your desired setting. + +# Running the installed 5GMSd Application server + +To run the version of the 5GMSd Application Server installed in the virtual environment with the `local-dev.conf` configuration (above), use: + +```bash +cd ~/rt-5gms-application-server +PATH="/usr/local/openresty/nginx/sbin:$PATH" venv/bin/5gms-application-server -c local-dev.conf +``` + +# Runtime configuration + +The 5GMSd Application Server is configured at run-time for distribution of media via the interface at reference point M3. This is usually done by the [5GMSd Application Function](https://github.com/5G-MAG/rt-5gms-application-function), but this project also contains a simple M3 client that can be used to push run-time configuration for testing. + +## M3 test client + +This client script provides a simple command line interface to issue M3 API calls and see the result from the response. + +The script can be run as: +```bash +cd ~/rt-5gms-application-server +tests/m3_client_cli.py -h|{ [...]} +``` + +If the default values for `m3_listen` and `m3_port` are used in the configuration file the `` will be `localhost:7777`. + +To see the command line help use: +```bash +cd ~/rt-5gms-application-server +tests/m3_client_cli.py -h +``` + +## M3 Certificates API + +### List known certificates + +```bash +cd ~/rt-5gms-application-server +tests/m3_client_cli.py -c localhost:7777 +``` + +### Add a new certificate + +```bash +cd ~/rt-5gms-application-server +tests/m3_client_cli.py -c localhost:7777 add testcert1 tests/examples/certificate-1.pem +``` + +### Update an existing certificate + +```bash +cd ~/rt-5gms-application-server +tests/m3_client_cli.py -c localhost:7777 update testcert1 tests/examples/certificate-1.pem +``` + +### Delete a certificate + +```bash +cd ~/rt-5gms-application-server +tests/m3_client_cli.py -c localhost:7777 delete testcert1 +``` + +## M3 ContentHostingConfiguration API + +### List known ContentHostingConfigurations + +```bash +cd ~/rt-5gms-application-server +tests/m3_client_cli.py -H localhost:7777 +``` + +### Add a new ContentHostingConfiguration + +```bash +cd ~/rt-5gms-application-server +tests/m3_client_cli.py -H localhost:7777 add prov-sess-1 tests/examples/ContentHostingConfiguration_Big-Buck-Bunny_pull-ingest_http_and_https.json +``` + +### Update an existing ContentHostingConfiguration + +```bash +cd ~/rt-5gms-application-server +tests/m3_client_cli.py -H localhost:7777 update prov-sess-1 tests/examples/ContentHostingConfiguration_Big-Buck-Bunny_pull-ingest_https.json +``` + +### Delete a ContentHostingConfiguration + +```bash +cd ~/rt-5gms-application-server +tests/m3_client_cli.py -H localhost:7777 delete prov-sess-1 +``` + +### Purge all cached objects for a ContentHostingConfiguration + +```bash +cd ~/rt-5gms-application-server +tests/m3_client_cli.py -H localhost:7777 purge prov-sess-1 +``` + +### Purge cached objects using a path regex for a ContentHostingConfiguration + +For example to purge all DASH manifests: +```bash +cd ~/rt-5gms-application-server +tests/m3_client_cli.py -H localhost:7777 purge prov-sess-1 '\.mpd$' +``` + diff --git a/pages/5g-media-streaming/testing/testing-m1-v120.md b/pages/5g-media-streaming/testing/testing-m1-v120.md new file mode 100644 index 00000000..1133934c --- /dev/null +++ b/pages/5g-media-streaming/testing/testing-m1-v120.md @@ -0,0 +1,642 @@ +--- +layout: default +title: Testing M1 AF v1.2.x +parent: Testing +grand_parent: 5G Downlink Media Streaming +has_children: false +nav_order: 6 +--- + +# Testing: M1 Interface (5GMSd Application Function v1.2.x) + +To prepare, follow the instructions for [local user building and installation](Testing-as-a-Local-User). + +These tests require a [5GMSd Application Server](https://github.com/5G-MAG/rt-5gms-application-server) to be running. Please follow +the instructions to [build, install and run the 5GMSd Application Server](https://github.com/5G-MAG/rt-5gms-application-server#readme) as a system service or the [instructions to run the AS as a local user](https://github.com/5G-MAG/rt-5gms-application-server/wiki/Development-and-Testing) for a temporary installation for testing. + +## Test Provisioning Sessions + +This will test the ability of the Application Function to allocate and retrieve information about provisioning session upon request by an Application Provider. + +### Create provisioning sessions + +1. Stop the Application Function if it is already running. + +1. Remove previous configurations: + + ```bash + rm -rf ~/rt-5gms-application-function/install/var/cache/rt-5gms/af/certificates + ``` + +1. Start the Application Function: + + ```bash + ~/rt-5gms-application-function/install/bin/open5gs-msafd + ``` + +1. Create a single Provisioning Session: + + ```bash + ~/rt-5gms-application-function/install/bin/m1-session new-provisioning-session -e MyAppId -a MyASPId + ``` + +1. Check the Provisioning Session: + + ```bash + ~/rt-5gms-application-function/install/bin/m1-session list + ``` + + This should list a single provisioning session. + +1. Create a second Provisioning Session: + + ```bash + ~/rt-5gms-application-function/install/bin/m1-session new-provisioning-session -e MyAppId -a MyASPId + ``` + +1. Check the Provisioning Sessions: + + ```bash + ~/rt-5gms-application-function/install/bin/m1-session list + ``` + + This should list the two provisioning sessions. + +### Get details for a provisioning session + +1. Stop the Application Function if it is already running. + +1. Remove previous configurations: + + ```bash + rm -rf ~/rt-5gms-application-function/install/var/cache/rt-5gms/af/certificates + ``` + +1. Start the Application Function: + + ```bash + ~/rt-5gms-application-function/install/bin/open5gs-msafd + ``` + +1. Create a single Provisioning Session with Content Hosting Configuration + + ```bash + ~/rt-5gms-application-function/install/bin/m1-session new-stream -e MyAppId -a MyASPId -n 'Big Buck Bunny' 'https://ftp.itec.aau.at/datasets/DASHDataset2014/BigBuckBunny/4sec/' 'BigBuckBunny_4s_onDemand_2014_05_09.mpd' + ``` + +1. Check the Provisioning Session details: + + ```bash + ~/rt-5gms-application-function/install/bin/m1-session list -v + ``` + + This will list the provisioning session showing the Content Hosting Configuration attached to it. + + For example: + + ``` + 1c961622-c803-41ed-83c5-e304b44dbd7e: + Certificates: + ContentHostingConfiguration: + Name: Big Buck Bunny + Entry Point Path: BigBuckBunny_4s_onDemand_2014_05_09.mpd + Ingest: + Type: urn:3gpp:5gms:content-protocol:http-pull-ingest + URL: https://ftp.itec.aau.at/datasets/DASHDataset2014/BigBuckBunny/4sec/ + Distributions: + - URL: http://localhost/m4d/provisioning-session-1c961622-c803-41ed-83c5-e304b44dbd7e/ + Canonical Domain Name: localhost + ``` + +### Delete a provisioning session + +1. Stop the Application Function if it is already running. + +1. Remove previous configurations: + + ```bash + rm -rf ~/rt-5gms-application-function/install/var/cache/rt-5gms/af/certificates + ``` + +1. Start the Application Function: + + ```bash + ~/rt-5gms-application-function/install/bin/open5gs-msafd + ``` + +1. Create a single Provisioning Session: + + ```bash + ~/rt-5gms-application-function/install/bin/m1-session new-provisioning-session -e MyAppId -a MyASPId + ``` + +1. Check the Provisioning Session: + + ```bash + ~/rt-5gms-application-function/install/bin/m1-session list + ``` + + This should list a single provisioning session. + +1. Delete the Provisioning Session by Id: + + ```bash + ~/rt-5gms-application-function/install/bin/m1-session del-stream -p ${provisioning_session_id} + ``` + + Where `${provisioning_session_id}` is the provisioning session id of the session that was created in the earlier step. + + For example: + + ```bash + ~/rt-5gms-application-function/install/bin/m1-session del-stream -p 1c961622-c803-41ed-83c5-e304b44dbd7e + ``` + +1. Check the Provisioning Session is deleted: + + ```bash + ~/rt-5gms-application-function/install/bin/m1-session list + ``` + + There should be no provisioning sessions listed. + +1. Create a single Provisioning Session with a stream identifier: + + ```bash + ~/rt-5gms-application-function/install/bin/m1-session new-stream -e MyAppId -a MyASPId -n 'Test Stream' 'https://ftp.itec.aau.at/datasets/DASHDataset2014/BigBuckBunny/4sec/' 'BigBuckBunny_4s_onDemand_2014_05_09.mpd' + ``` + +1. Check the Provisioning Session: + + ```bash + ~/rt-5gms-application-function/install/bin/m1-session list -v + ``` + +1. Delete the Provisioning Session by ingest URL and entry point path: + + ```bash + ~/rt-5gms-application-function/install/bin/m1-session del-stream 'https://ftp.itec.aau.at/datasets/DASHDataset2014/BigBuckBunny/4sec/' 'BigBuckBunny_4s_onDemand_2014_05_09.mpd' + ``` + +1. Check the Provisioning Session is deleted: + + ```bash + ~/rt-5gms-application-function/install/bin/m1-session list + ``` + + There should be no provisioning sessions listed. + +## Server Certificates + +### Create Server Certificates + +1. Stop the Application Function if it is already running. + +1. Remove previous configurations: + + ```bash + rm -rf ~/rt-5gms-application-function/install/var/cache/rt-5gms/af/certificates + ``` + +1. Start the Application Function: + + ```bash + ~/rt-5gms-application-function/install/bin/open5gs-msafd + ``` + +1. Create a single Provisioning Session: + + ```bash + ~/rt-5gms-application-function/install/bin/m1-session new-provisioning-session -e MyAppId -a MyASPId + ``` + +1. Create a certificate: + + ```bash + ~/rt-5gms-application-function/install/bin/m1-session new-certificate -p ${provisioning_session_id} + ``` + + Where `${provisioning_session_id}` is the provisioning session id of the session that was created in the previous step. + +1. Check the Provisioning Session: + + ```bash + ~/rt-5gms-application-function/install/bin/m1-session list -v + ``` + + The output should show a provisioning session with a single certificate where the subject and issuer of the certificate are + identical. + + For example: + + ``` + 40b75340-c8a3-41ed-9d6e-cbf27240da7a: + Certificates: + 4fff7e04-c8a3-41ed-9d6e-cbf27240da7a: + Serial = 723264618754945153424478507276304617300583059881 + Not before = 2023-03-22 11:18:46+00:00 + Not after = 2023-06-20 11:18:46+00:00 + Subject = C=GB,L=London,CN=localhost + key=C2:56:2C:A6:D7:B3:AE:C7:3A:2C:18:9D:5B:2A:EB:62:C0:7E:35:05 + Issuer = C=GB,L=London,CN=localhost + key=C2:56:2C:A6:D7:B3:AE:C7:3A:2C:18:9D:5B:2A:EB:62:C0:7E:35:05 + Subject Alternative Names: + DNS:localhost + ContentHostingConfiguration: + Not defined + ``` + + This shows that for provisioning session 40b75340-c8a3-41ed-9d6e-cbf27240da7a there is a certificate with id + 4fff7e04-c8a3-41ed-9d6e-cbf27240da7a lasting for 90 days from 22nd Mar 2023. The "Subject" and "Issuer" both have the same + designated name ("C=GB,L=London,CN=localhost") and key hash, showing that this is a self signed certificate. The + "Subject Alternative Names" contains one "DNS" entry for the canonical name of the 5GMSd Application Server. + +1. Create a certificate with a domain name: + + ```bash + ~/rt-5gms-application-function/install/bin/m1-session new-certificate -p ${provisioning_session_id} -d as.example.com + ``` + + Since a domain name was requested, the `m1-session` tool will request a CSR from the 5GMSd Application Function and sign it + itself. + +1. Check the Provisioning Session: + + ```bash + ~/rt-5gms-application-function/install/bin/m1-session list -v + ``` + + The output should now show an extra certificate on the provisioning session. + + For example: + + ``` + 40b75340-c8a3-41ed-9d6e-cbf27240da7a: + Certificates: + 4fff7e04-c8a3-41ed-9d6e-cbf27240da7a: + Serial = 723264618754945153424478507276304617300583059881 + Not before = 2023-03-22 11:18:46+00:00 + Not after = 2023-06-20 11:18:46+00:00 + Subject = C=GB,L=London,CN=localhost + key=C2:56:2C:A6:D7:B3:AE:C7:3A:2C:18:9D:5B:2A:EB:62:C0:7E:35:05 + Issuer = C=GB,L=London,CN=localhost + key=C2:56:2C:A6:D7:B3:AE:C7:3A:2C:18:9D:5B:2A:EB:62:C0:7E:35:05 + Subject Alternative Names: + DNS:localhost + 8aa9e5ac-c8a9-41ed-9d6e-cbf27240da7a: + Serial = 1 + Not before = 2023-03-22 12:03:22+00:00 + Not after = 2023-04-21 12:03:22+00:00 + Subject = CN=as.example.com,O=5G-MAG + key=37:62:38:E1:D2:18:23:90:A9:12:2A:C7:EF:5F:7E:F8:91:3A:89:8F + Issuer = O=5G-MAG,CN=5G-MAG Reference Tools Local CA + key=B4:2F:13:EE:02:D0:34:75:C0:7B:9D:C7:67:6D:90:76:F5:A8:CC:EF + Subject Alternative Names: + DNS:as.example.com + DNS:localhost + ContentHostingConfiguration: + Not defined + ``` + + This shows that there is now a second certificate (8aa9e5ac-c8a9-41ed-9d6e-cbf27240da7a) issued by + "5G-MAG Reference Tools Local CA" and the Subject Common Name is the domain name alias used with the `-d` command line option + when the certificate was created. The canonical domain name of the 5GMSd Application Server is the second Subject Alternative + Name. These certificates last for 30 days by default. + +1. Reserve a certificate: + + ```bash + ~/rt-5gms-application-function/install/bin/m1-session new-certificate -p ${provisioning_session_id} --csr + ``` + + The output includes the new certificate id and a CSR in PEM format. + +1. Check the Provisioning Session: + + ```bash + ~/rt-5gms-application-function/install/bin/m1-session list -v + ``` + + The output should now show a third certificate id but the certificate detail says "Certificate not yet uploaded". + + For example: + + ``` + 40b75340-c8a3-41ed-9d6e-cbf27240da7a: + Certificates: + 4fff7e04-c8a3-41ed-9d6e-cbf27240da7a: + Serial = 723264618754945153424478507276304617300583059881 + Not before = 2023-03-22 11:18:46+00:00 + Not after = 2023-06-20 11:18:46+00:00 + Subject = C=GB,L=London,CN=localhost + key=C2:56:2C:A6:D7:B3:AE:C7:3A:2C:18:9D:5B:2A:EB:62:C0:7E:35:05 + Issuer = C=GB,L=London,CN=localhost + key=C2:56:2C:A6:D7:B3:AE:C7:3A:2C:18:9D:5B:2A:EB:62:C0:7E:35:05 + Subject Alternative Names: + DNS:localhost + 8aa9e5ac-c8a9-41ed-9d6e-cbf27240da7a: + Serial = 1 + Not before = 2023-03-22 12:03:22+00:00 + Not after = 2023-04-21 12:03:22+00:00 + Subject = CN=as.example.com,O=5G-MAG + key=37:62:38:E1:D2:18:23:90:A9:12:2A:C7:EF:5F:7E:F8:91:3A:89:8F + Issuer = O=5G-MAG,CN=5G-MAG Reference Tools Local CA + key=B4:2F:13:EE:02:D0:34:75:C0:7B:9D:C7:67:6D:90:76:F5:A8:CC:EF + Subject Alternative Names: + DNS:as.example.com + DNS:localhost + 2a118b8e-c8a7-41ed-9d6e-cbf27240da7a: + Certificate not yet uploaded + ContentHostingConfiguration: + Not defined + ``` + + This shows that the 2a118b8e-c8a7-41ed-9d6e-cbf27240da7a certificate is waiting for a signed certificate to be uploaded. + +### Output certificate details + +1. Stop the Application Function if it is already running. + +1. Remove previous configurations: + + ```bash + rm -rf ~/rt-5gms-application-function/install/var/cache/rt-5gms/af/certificates + ``` + +1. Start the Application Function: + + ```bash + ~/rt-5gms-application-function/install/bin/open5gs-msafd + ``` + +1. Create a single Provisioning Session: + + ```bash + ~/rt-5gms-application-function/install/bin/m1-session new-provisioning-session -e MyAppId -a MyASPId + ``` + +1. Create a certificate + + ```bash + ~/rt-5gms-application-function/install/bin/m1-session new-certificate -p ${provisioning_session_id} + ``` + + Where `${provisioning_session_id}` is the provisioning session id of the session that was created in the previous step. + +1. Display the details of the certificate + + ```bash + ~/rt-5gms-application-function/install/bin/m1-session show-certificate -p ${provisioning_session_id} -c ${certificate_id} + ``` + + Where `${provisioning_session_id}` is the provisioning session id of the session that was created in step 4 and + `${certificate_id}` is the certificate id of the certificate created in the previous step. + + This will display the certificate details. + + For example: + + ``` + Certificate details for d921a6e2-c977-41ed-ae8f-4f7bb018a30b: + Serial = 570812267048735513617861647966053937458169779179 + Not before = 2023-03-23 12:40:10+00:00 + Not after = 2023-06-21 12:40:10+00:00 + Subject = C=GB,L=London,CN=localhost + key=E9:61:FD:5A:31:0C:ED:C0:B0:CC:29:0D:29:89:AE:EE:F9:25:89:CA + Issuer = C=GB,L=London,CN=localhost + key=E9:61:FD:5A:31:0C:ED:C0:B0:CC:29:0D:29:89:AE:EE:F9:25:89:CA + Subject Alternative Names: + DNS:localhost + ``` + +1. Display the public certificate PEM data + + ```bash + ~/rt-5gms-application-function/install/bin/m1-session show-certificate -p ${provisioning_session_id} -c ${certificate_id} -r + ``` + + The `-r` flag causes the command to display the "raw" output which is the PEM data for the certificate. + + For example: + + ``` + -----BEGIN CERTIFICATE----- + MIIDWzCCAkOgAwIBAgIUY/wbeD1YUiEeBPLpxf8ldkkEE+swDQYJKoZIhvcNAQEL + BQAwMjELMAkGA1UEBhMCR0IxDzANBgNVBAcMBkxvbmRvbjESMBAGA1UEAwwJbG9j + YWxob3N0MB4XDTIzMDMyMzEyNDAxMFoXDTIzMDYyMTEyNDAxMFowMjELMAkGA1UE + BhMCR0IxDzANBgNVBAcMBkxvbmRvbjESMBAGA1UEAwwJbG9jYWxob3N0MIIBIjAN + BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0bftLTaaXXpW1qIDHYTTmeIGvupy + G/dQpK5Ko9mW9IEcb+wfn2fX8SMHGA7O8TvpqUGEmyoBXIuIFmmeR+w5xQRcKkyi + NGhIhnUIfOqaevh4MCX/8Ius9NOjF0bz+aWtwOCmWKkNvknRMzClAeVRJm6g+U6m + IH30TE5H3ItiYxk63MNvuYeqIEK2rEKu69jWvTFkV1Wzd+5rH3ZC9qrt4uryUqAZ + X6XPx5AkQzbnBRQooDJzhqKgW+7YFWFtwi6WX8poGwx4RuruxYRLEBDfxRCE/1k6 + ALP3IktsVp7rFqwOt0LRNH1+MLly/MIOEA+NjnaIReG3/6Kt7oAqiDCBpQIDAQAB + o2kwZzAdBgNVHQ4EFgQU6WH9WjEM7cCwzCkNKYmu7vklicowHwYDVR0jBBgwFoAU + 6WH9WjEM7cCwzCkNKYmu7vklicowDwYDVR0TAQH/BAUwAwEB/zAUBgNVHREEDTAL + gglsb2NhbGhvc3QwDQYJKoZIhvcNAQELBQADggEBAIAELtWEMwzoXnaWRn74JngW + 3DF5IUtdFOiEKPWzdju+RleUsQHm5hsbPxpAz/MDVKIQQBGrJNMNpMtJ1VNlAsJ1 + 7gndSkSFf0zw7+DxgKYiwNj7tbBU8yTW+qVyUJh6XUxOHlaLjXzct4jw/NgjrjRZ + YuwXKCebRf+DUtQGt87rSib+GVpI//XBweyd8D0vFnGPRU9yyAvuqUdfu7enGFMr + qyBBqTqrTgX9o852lrMnWbc+g+of90Ym9HkVpifmc12jZSJlfS4cykvwnRqwPC9f + YnNABBMDJwJOM8g69OIRw+67O01ZulRzCvSaSbBEG6xC08XAD7BJn9CPIMypHdw= + -----END CERTIFICATE----- + ``` + +### Upload a public certificate + +**TODO:** Test where we reserve a certificate and the fetch CSR for the new certificate, sign it, and upload the result. + +## Content Protocol Discovery + +### List the Content Protocols available + +1. Stop the Application Function if it is already running. + +1. Remove previous configurations: + + ```bash + rm -rf ~/rt-5gms-application-function/install/var/cache/rt-5gms/af/certificates + ``` + +1. Start the Application Function: + + ```bash + ~/rt-5gms-application-function/install/bin/open5gs-msafd + ``` + +1. Create a single Provisioning Session: + + ```bash + ~/rt-5gms-application-function/install/bin/m1-session new-provisioning-session -e MyAppId -a MyASPId + ``` + +1. List the Content Protocols for the Provisioning Session + + ```bash + ~/rt-5gms-application-function/install/bin/m1-session protocols -p ${provisioning_session_id} + ``` + + Where `${provisioning_session_id}` is the provisioning session id of the session that was created in the previous step. + + The available protocols will be listed. + + For example: + + ``` + Protocols for 40b75340-c8a3-41ed-9d6e-cbf27240da7a: + Downlink: + urn:3gpp:5gms:content-protocol:http-pull-ingest + No uplink capability + No geo-fencing capability + ``` + +## Content Hosting Provisioning + +### Add a Content Hosting Configuration without certificates + +1. Stop the Application Function if it is already running. + +1. Remove previous configurations: + + ```bash + rm -rf ~/rt-5gms-application-function/install/var/cache/rt-5gms/af/certificates + ``` + +1. Start the Application Function: + + ```bash + ~/rt-5gms-application-function/install/bin/open5gs-msafd + ``` + +1. Create a single Provisioning Session: + + ```bash + ~/rt-5gms-application-function/install/bin/m1-session new-provisioning-session -e MyAppId -a MyASPId + ``` + +1. Create the hosting configuration: + + ```bash + ~/rt-5gms-application-function/install/bin/m1-session set-stream -p ${provisioning_session_id} ~/rt-5gms-application-function/examples/ContentHostingConfiguration_Big-Buck-Bunny_pull-ingest.json + ``` + + Where `${provisioning_session_id}` is the provisioning session id of the session that was created in the previous step. + +1. Check the provisioning session configuration: + + ```bash + ~/rt-5gms-application-function/install/bin/m1-session list -v + ``` + + This will display the provisioning session created, showing no certificates and the details from the example + ContentHostingConfiguration. + + For example: + + ``` + 2ef78712-c9a0-41ed-ac37-f9964ab0d12a: + Certificates: + ContentHostingConfiguration: + Name: Big Buck Bunny + Entry Point Path: BigBuckBunny_4s_onDemand_2014_05_09.mpd + Ingest: + Type: urn:3gpp:5gms:content-protocol:http-pull-ingest + URL: https://ftp.itec.aau.at/datasets/DASHDataset2014/BigBuckBunny/4sec/ + Distributions: + - URL: http://localhost/m4d/provisioning-session-2ef78712-c9a0-41ed-ac37-f9964ab0d12a/ + Canonical Domain Name: localhost + ``` + +**Note:** The `m1-session new-stream` command is a convience command that will create a provisioning session, generate the +ContentHostingConfiguration and set it in the newly created provisioning session. The above can also be done using: +``` +~/rt-5gms-application-function/install/bin/m1-session new-stream -e MyAppId -a MyASPId -n 'Big Buck Bunny' 'https://ftp.itec.aau.at/datasets/DASHDataset2014/BigBuckBunny/4sec/' 'BigBuckBunny_4s_onDemand_2014_05_09.mpd' +``` + +### Add a Content Hosting Configuration which uses an existing certificate + +1. Stop the Application Function if it is already running. + +1. Remove previous configurations: + + ```bash + rm -rf ~/rt-5gms-application-function/install/var/cache/rt-5gms/af/certificates + ``` + +1. Start the Application Function: + + ```bash + ~/rt-5gms-application-function/install/bin/open5gs-msafd + ``` + +1. Create a single Provisioning Session: + + ```bash + ~/rt-5gms-application-function/install/bin/m1-session new-provisioning-session -e MyAppId -a MyASPId + ``` + +1. Create a certificate: + + ```bash + ~/rt-5gms-application-function/install/bin/m1-session new-certificate -p ${provisioning_session_id} + ``` + + Where `${provisioning_session_id}` is the provisioning session id of the session that was created in the previous step. + +1. Generate a ContentHostingConfiguration using the certificate: + + ```bash + sed "s/@certificate-id@/${certificate_id}/g" ~/rt-5gms-application-function/examples/ContentHostingConfiguration_Big-Buck-Bunny_pull-ingest_https.json.tmpl > chc.json + ``` + + Where `${certificate_id}` is the certificate id of the certificate created in the previous step. + +1. Create the hosting configuration using the generated ContentHostingConfiguration: + + ```bash + ~/rt-5gms-application-function/install/bin/m1-session set-stream -p ${provisioning_session_id} chc.json + ``` + + Where `${provisioning_session_id}` is the provisioning session id of the session that was created in step 4. + +1. Check the provisioning session configuration: + + ```bash + ~/rt-5gms-application-function/install/bin/m1-session list -v + ``` + + This will display the provisioning session created, showing no certificates and the details from the example + ContentHostingConfiguration. + + For example: + + ``` + ed5079d6-c9a4-41ed-b2ad-41232d457177: + Certificates: + fbd05ddc-c9a4-41ed-b2ad-41232d457177: + Serial = 186484472102456711697672477872825927418601662068 + Not before = 2023-03-23 18:03:15+00:00 + Not after = 2023-06-21 18:03:15+00:00 + Subject = C=GB,L=London,CN=localhost + key=8A:C2:9A:48:15:6A:15:BB:EE:B7:A0:9E:07:7C:CB:A4:CF:7C:51:F5 + Issuer = C=GB,L=London,CN=localhost + key=8A:C2:9A:48:15:6A:15:BB:EE:B7:A0:9E:07:7C:CB:A4:CF:7C:51:F5 + Subject Alternative Names: + DNS:localhost + ContentHostingConfiguration: + Name: Big Buck Bunny + Entry Point Path: BigBuckBunny_4s_onDemand_2014_05_09.mpd + Ingest: + Type: urn:3gpp:5gms:content-protocol:http-pull-ingest + URL: https://ftp.itec.aau.at/datasets/DASHDataset2014/BigBuckBunny/4sec/ + Distributions: + - URL: https://localhost/m4d/provisioning-session-ed5079d6-c9a4-41ed-b2ad-41232d457177/ + Canonical Domain Name: localhost + Certificate: fbd05ddc-c9a4-41ed-b2ad-41232d457177 + ``` + +**Note:** The `m1-session new-stream` command is a convience command that will create a provisioning session, generate the +ContentHostingConfiguration and set it in the newly created provisioning session. The above configuration with the `m1-session` tool can also be done using this single command instead: +``` +~/rt-5gms-application-function/install/bin/m1-session new-stream -e MyAppId -a MyASPId -n 'Big Buck Bunny' --ssl-only 'https://ftp.itec.aau.at/datasets/DASHDataset2014/BigBuckBunny/4sec/' 'BigBuckBunny_4s_onDemand_2014_05_09.mpd' +``` diff --git a/pages/5g-media-streaming/testing/testing-m1-v130.md b/pages/5g-media-streaming/testing/testing-m1-v130.md new file mode 100644 index 00000000..38f9a333 --- /dev/null +++ b/pages/5g-media-streaming/testing/testing-m1-v130.md @@ -0,0 +1,859 @@ +--- +layout: default +title: Testing M1 AF v1.3.x +parent: Testing +grand_parent: 5G Downlink Media Streaming +has_children: false +nav_order: 7 +--- + +# Testing: M1 Interface (5GMSd Application Function v1.3.0 to v1.4.0) + +To prepare, follow the instructions for [local user building and installation](Testing-as-a-Local-User). + +# Testing + +These tests require a [5GMSd Application Server](https://github.com/5G-MAG/rt-5gms-application-server) to be running. Please follow +the instructions to [build, install and run the 5GMSd Application Server](https://github.com/5G-MAG/rt-5gms-application-server#readme) as a system service or the [instructions to run the AS as a local user](https://github.com/5G-MAG/rt-5gms-application-server/wiki/Development-and-Testing) for a temporary installation for testing. + +## Test Provisioning Sessions + +This will test the ability of the Application Function to allocate and retrieve information about provisioning session upon request by an Application Provider. + +### Create provisioning sessions + +1. Stop the Application Function if it is already running. + +1. Remove previous configurations: + + ```bash + rm -rf ~/rt-5gms-application-function/install/var/cache/rt-5gms/af/certificates + ``` + +1. Start the Application Function: + + ```bash + ~/rt-5gms-application-function/install/bin/open5gs-msafd + ``` + +1. Create a single Provisioning Session: + + ```bash + ~/rt-5gms-application-function/install/bin/m1-session new-provisioning-session -e MyAppId -a MyASPId + ``` + +1. Check the Provisioning Session: + + ```bash + ~/rt-5gms-application-function/install/bin/m1-session list + ``` + + This should list a single provisioning session. + +1. Create a second Provisioning Session: + + ```bash + ~/rt-5gms-application-function/install/bin/m1-session new-provisioning-session -e MyAppId -a MyASPId + ``` + +1. Check the Provisioning Sessions: + + ```bash + ~/rt-5gms-application-function/install/bin/m1-session list + ``` + + This should list the two provisioning sessions. + +### Get details for a provisioning session + +1. Stop the Application Function if it is already running. + +1. Remove previous configurations: + + ```bash + rm -rf ~/rt-5gms-application-function/install/var/cache/rt-5gms/af/certificates + ``` + +1. Start the Application Function: + + ```bash + ~/rt-5gms-application-function/install/bin/open5gs-msafd + ``` + +1. Create a single Provisioning Session with Content Hosting Configuration + + ```bash + ~/rt-5gms-application-function/install/bin/m1-session new-stream -e MyAppId -a MyASPId -n 'Big Buck Bunny' 'https://ftp.itec.aau.at/datasets/DASHDataset2014/BigBuckBunny/4sec/' 'BigBuckBunny_4s_onDemand_2014_05_09.mpd' + ``` + +1. Check the Provisioning Session details: + + ```bash + ~/rt-5gms-application-function/install/bin/m1-session list -v + ``` + + This will list the provisioning session showing the Content Hosting Configuration attached to it. + + For example: + + ``` + 39f4f698-daa0-41ed-862b-c1f4c44bccf3: + Certificates: + ContentHostingConfiguration: + Name: Big Buck Bunny + Ingest: + Type: urn:3gpp:5gms:content-protocol:http-pull-ingest + Pull Ingest?: True + URL: https://ftp.itec.aau.at/datasets/DASHDataset2014/BigBuckBunny/4sec/ + Distributions: + - URL: http://localhost/m4d/provisioning-session-39f4f698-daa0-41ed-862b-c1f4c44bccf3/ + Canonical Domain Name: localhost + Entry point: + Relative Path: BigBuckBunny_4s_onDemand_2014_05_09.mpd + Content Type: application/dash+xml + ``` + +### Delete a provisioning session + +1. Stop the Application Function if it is already running. + +1. Remove previous configurations: + + ```bash + rm -rf ~/rt-5gms-application-function/install/var/cache/rt-5gms/af/certificates + ``` + +1. Start the Application Function: + + ```bash + ~/rt-5gms-application-function/install/bin/open5gs-msafd + ``` + +1. Create a single Provisioning Session: + + ```bash + ~/rt-5gms-application-function/install/bin/m1-session new-provisioning-session -e MyAppId -a MyASPId + ``` + + **Hint:** Set the shell variable `provisioning_session_id` to the returned provisioning session id for use in later commands. + +1. Check the Provisioning Session: + + ```bash + ~/rt-5gms-application-function/install/bin/m1-session list + ``` + + This should list a single provisioning session. + +1. Delete the Provisioning Session by Id: + + ```bash + ~/rt-5gms-application-function/install/bin/m1-session del-stream -p ${provisioning_session_id} + ``` + + Where `${provisioning_session_id}` is the provisioning session id of the session that was created in the earlier step. + + For example: + + ```bash + ~/rt-5gms-application-function/install/bin/m1-session del-stream -p 1c961622-c803-41ed-83c5-e304b44dbd7e + ``` + +1. Check the Provisioning Session is deleted: + + ```bash + ~/rt-5gms-application-function/install/bin/m1-session list + ``` + + There should be no provisioning sessions listed. + +1. Create a single Provisioning Session with a stream identifier: + + ```bash + ~/rt-5gms-application-function/install/bin/m1-session new-stream -e MyAppId -a MyASPId -n 'Test Stream' 'https://ftp.itec.aau.at/datasets/DASHDataset2014/BigBuckBunny/4sec/' 'BigBuckBunny_4s_onDemand_2014_05_09.mpd' + ``` + +1. Check the Provisioning Session: + + ```bash + ~/rt-5gms-application-function/install/bin/m1-session list -v + ``` + +1. Delete the Provisioning Session by ingest URL and entry point path: + + ```bash + ~/rt-5gms-application-function/install/bin/m1-session del-stream 'https://ftp.itec.aau.at/datasets/DASHDataset2014/BigBuckBunny/4sec/' 'BigBuckBunny_4s_onDemand_2014_05_09.mpd' + ``` + + **Note:** The entry point (last command line parameter) can be any one of the entry point relative paths present in a + distribution configuration for the content hosting configuration of a provisioning session. + +1. Check the Provisioning Session is deleted: + + ```bash + ~/rt-5gms-application-function/install/bin/m1-session list + ``` + + There should be no provisioning sessions listed. + +### Create a hosting configuration with multiple entry points + +1. Stop the Application Function if it is already running. + +1. Remove previous configurations: + + ```bash + rm -rf ~/rt-5gms-application-function/install/var/cache/rt-5gms/af/certificates + ``` + +1. Start the Application Function: + + ```bash + ~/rt-5gms-application-function/install/bin/open5gs-msafd + ``` + +1. Create a single Provisioning Session with Content Hosting Configuration containing multiple entry points + + ```bash + ~/rt-5gms-application-function/install/bin/m1-session new-stream -e MyAppId -a MyASPId -n 'Big Buck Bunny' 'http://amssamples.streaming.mediaservices.windows.net/622b189f-ec39-43f2-93a2-201ac4e31ce1/BigBuckBunny.ism/' 'manifest(format=mpd-time-csf)' 'manifest(format=m3u8-aapl-v3)' + ``` + +1. Check the Provisioning Session details: + + ```bash + ~/rt-5gms-application-function/install/bin/m1-session list -v + ``` + + This will list the provisioning session showing the Content Hosting Configuration attached to it. + + For example: + + ``` + 74f4c492-dacf-41ed-87fe-93ba2b9b790a: + Certificates: + ContentHostingConfiguration: + Name: Big Buck Bunny + Ingest: + Type: urn:3gpp:5gms:content-protocol:http-pull-ingest + Pull Ingest?: True + URL: http://amssamples.streaming.mediaservices.windows.net/622b189f-ec39-43f2-93a2-201ac4e31ce1/BigBuckBunny.ism/ + Distributions: + - URL: http://localhost/m4d/provisioning-session-74f4c492-dacf-41ed-87fe-93ba2b9b790a/ + Canonical Domain Name: localhost + Entry point: + Relative Path: manifest(format=mpd-time-csf) + Content Type: application/dash+xml + - URL: http://localhost/m4d/provisioning-session-74f4c492-dacf-41ed-87fe-93ba2b9b790a/ + Canonical Domain Name: localhost + Entry point: + Relative Path: manifest(format=m3u8-aapl-v3) + Content Type: application/vnd.apple.mpegurl + ``` + + The output shows that the ContentHostingConfiguration for Provisioning Session 74f4c492-dacf-41ed-87fe-93ba2b9b790a has two + distribution configurations. There is one distribution configuration for DASH and one for HLS. + +### Create a hosting configuration from a JSON file + +1. Stop the Application Function if it is already running. + +1. Remove previous configurations: + + ```bash + rm -rf ~/rt-5gms-application-function/install/var/cache/rt-5gms/af/certificates + ``` + +1. Start the Application Function: + + ```bash + ~/rt-5gms-application-function/install/bin/open5gs-msafd + ``` + +1. Create a single Provisioning Session: + + ```bash + ~/rt-5gms-application-function/install/bin/m1-session new-provisioning-session -e MyAppId -a MyASPId + ``` + + **Hint:** Set the shell variable `provisioning_session_id` to the returned provisioning session id for use in later commands. + +1. Upload the ContentHostingConfiguration JSON file: + + ```bash + ~/rt-5gms-application-function/install/bin/m1-session set-stream -p ${provisioning_session_id} ~/rt-5gms-application-function/examples/ContentHostingConfiguration_Big-Buck-Bunny_pull-ingest.json + ``` + + Where `${provisioning_session_id}` is the Provisioning Session Id reported by the previous step. + + For this step one of the example configuration files from the `~/rt-5gms-application-function/examples` directory was used, but any valid ContentHostingConfiguration JSON file can be used. + +1. Check the Provisioning Session: + + ```bash + ~/rt-5gms-application-function/install/bin/m1-session list -v + ``` + + The output should show a provisioning session with the contents of the JSON file used as the ContentHostingConfiguration. + + For example: + + ``` + 74f4c492-dacf-41ed-87fe-93ba2b9b790a: + Certificates: + ContentHostingConfiguration: + Name: AMP Demo Stream: Big Buck Bunny + Ingest: + Type: urn:3gpp:5gms:content-protocol:http-pull-ingest + Pull Ingest?: True + URL: http://amssamples.streaming.mediaservices.windows.net/622b189f-ec39-43f2-93a2-201ac4e31ce1/BigBuckBunny.ism/ + Distributions: + - URL: http://localhost/m4d/provisioning-session-74f4c492-dacf-41ed-87fe-93ba2b9b790a/ + Canonical Domain Name: localhost + Entry point: + Relative Path: manifest(format=mpd-time-csf) + Content Type: application/dash+xml + Profiles: + - urn:mpeg:dash:profile:isoff-live:2011 + - URL: http://localhost/m4d/provisioning-session-74f4c492-dacf-41ed-87fe-93ba2b9b790a/ + Canonical Domain Name: localhost + Entry point: + Relative Path: manifest(format=m3u8-aapl-v3) + Content Type: application/vnd.apple.mpegurl + ``` + + This also tests the use of profile lists in the distribution entry points. + +## Server Certificates + +### Create Server Certificates + +1. Stop the Application Function if it is already running. + +1. Remove previous configurations: + + ```bash + rm -rf ~/rt-5gms-application-function/install/var/cache/rt-5gms/af/certificates + ``` + +1. Start the Application Function: + + ```bash + ~/rt-5gms-application-function/install/bin/open5gs-msafd + ``` + +1. Create a single Provisioning Session: + + ```bash + ~/rt-5gms-application-function/install/bin/m1-session new-provisioning-session -e MyAppId -a MyASPId + ``` + + **Hint:** Set the shell variable `provisioning_session_id` to the returned provisioning session id for use in later commands. + +1. Create a certificate: + + ```bash + ~/rt-5gms-application-function/install/bin/m1-session new-certificate -p ${provisioning_session_id} + ``` + + Where `${provisioning_session_id}` is the provisioning session id of the session that was created in the previous step. + +1. Check the Provisioning Session: + + ```bash + ~/rt-5gms-application-function/install/bin/m1-session list -v + ``` + + The output should show a provisioning session with a single certificate where the subject and issuer of the certificate are + identical. + + For example: + + ``` + 40b75340-c8a3-41ed-9d6e-cbf27240da7a: + Certificates: + 4fff7e04-c8a3-41ed-9d6e-cbf27240da7a: + Serial = 723264618754945153424478507276304617300583059881 + Not before = 2023-03-22 11:18:46+00:00 + Not after = 2023-06-20 11:18:46+00:00 + Subject = C=GB,L=London,CN=localhost + key=C2:56:2C:A6:D7:B3:AE:C7:3A:2C:18:9D:5B:2A:EB:62:C0:7E:35:05 + Issuer = C=GB,L=London,CN=localhost + key=C2:56:2C:A6:D7:B3:AE:C7:3A:2C:18:9D:5B:2A:EB:62:C0:7E:35:05 + Subject Alternative Names: + DNS:localhost + ContentHostingConfiguration: + Not defined + ``` + + This shows that for provisioning session 40b75340-c8a3-41ed-9d6e-cbf27240da7a there is a certificate with id + 4fff7e04-c8a3-41ed-9d6e-cbf27240da7a lasting for 90 days from 22nd Mar 2023. The "Subject" and "Issuer" both have the same + designated name ("C=GB,L=London,CN=localhost") and key hash, showing that this is a self signed certificate. The + "Subject Alternative Names" contains one "DNS" entry for the canonical name of the 5GMSd Application Server. + +1. Create a certificate with a domain name: + + ```bash + ~/rt-5gms-application-function/install/bin/m1-session new-certificate -p ${provisioning_session_id} -d as.example.com + ``` + + Since a domain name was requested, the `m1-session` tool will request a CSR from the 5GMSd Application Function and sign it + itself. + +1. Check the Provisioning Session: + + ```bash + ~/rt-5gms-application-function/install/bin/m1-session list -v + ``` + + The output should now show an extra certificate on the provisioning session. + + For example: + + ``` + 40b75340-c8a3-41ed-9d6e-cbf27240da7a: + Certificates: + 4fff7e04-c8a3-41ed-9d6e-cbf27240da7a: + Serial = 723264618754945153424478507276304617300583059881 + Not before = 2023-03-22 11:18:46+00:00 + Not after = 2023-06-20 11:18:46+00:00 + Subject = C=GB,L=London,CN=localhost + key=C2:56:2C:A6:D7:B3:AE:C7:3A:2C:18:9D:5B:2A:EB:62:C0:7E:35:05 + Issuer = C=GB,L=London,CN=localhost + key=C2:56:2C:A6:D7:B3:AE:C7:3A:2C:18:9D:5B:2A:EB:62:C0:7E:35:05 + Subject Alternative Names: + DNS:localhost + 8aa9e5ac-c8a9-41ed-9d6e-cbf27240da7a: + Serial = 1 + Not before = 2023-03-22 12:03:22+00:00 + Not after = 2023-04-21 12:03:22+00:00 + Subject = CN=as.example.com,O=5G-MAG + key=37:62:38:E1:D2:18:23:90:A9:12:2A:C7:EF:5F:7E:F8:91:3A:89:8F + Issuer = O=5G-MAG,CN=5G-MAG Reference Tools Local CA + key=B4:2F:13:EE:02:D0:34:75:C0:7B:9D:C7:67:6D:90:76:F5:A8:CC:EF + Subject Alternative Names: + DNS:as.example.com + DNS:localhost + ContentHostingConfiguration: + Not defined + ``` + + This shows that there is now a second certificate (8aa9e5ac-c8a9-41ed-9d6e-cbf27240da7a) issued by + "5G-MAG Reference Tools Local CA" and the Subject Common Name is the domain name alias used with the `-d` command line option + when the certificate was created. The canonical domain name of the 5GMSd Application Server is the second Subject Alternative + Name. These certificates last for 30 days by default. + +1. Reserve a certificate: + + ```bash + ~/rt-5gms-application-function/install/bin/m1-session new-certificate -p ${provisioning_session_id} --csr + ``` + + The output includes the new certificate id and a CSR in PEM format. + +1. Check the Provisioning Session: + + ```bash + ~/rt-5gms-application-function/install/bin/m1-session list -v + ``` + + The output should now show a third certificate id but the certificate detail says "Certificate not yet uploaded". + + For example: + + ``` + 40b75340-c8a3-41ed-9d6e-cbf27240da7a: + Certificates: + 4fff7e04-c8a3-41ed-9d6e-cbf27240da7a: + Serial = 723264618754945153424478507276304617300583059881 + Not before = 2023-03-22 11:18:46+00:00 + Not after = 2023-06-20 11:18:46+00:00 + Subject = C=GB,L=London,CN=localhost + key=C2:56:2C:A6:D7:B3:AE:C7:3A:2C:18:9D:5B:2A:EB:62:C0:7E:35:05 + Issuer = C=GB,L=London,CN=localhost + key=C2:56:2C:A6:D7:B3:AE:C7:3A:2C:18:9D:5B:2A:EB:62:C0:7E:35:05 + Subject Alternative Names: + DNS:localhost + 8aa9e5ac-c8a9-41ed-9d6e-cbf27240da7a: + Serial = 1 + Not before = 2023-03-22 12:03:22+00:00 + Not after = 2023-04-21 12:03:22+00:00 + Subject = CN=as.example.com,O=5G-MAG + key=37:62:38:E1:D2:18:23:90:A9:12:2A:C7:EF:5F:7E:F8:91:3A:89:8F + Issuer = O=5G-MAG,CN=5G-MAG Reference Tools Local CA + key=B4:2F:13:EE:02:D0:34:75:C0:7B:9D:C7:67:6D:90:76:F5:A8:CC:EF + Subject Alternative Names: + DNS:as.example.com + DNS:localhost + 2a118b8e-c8a7-41ed-9d6e-cbf27240da7a: + Certificate not yet uploaded + ContentHostingConfiguration: + Not defined + ``` + + This shows that the 2a118b8e-c8a7-41ed-9d6e-cbf27240da7a certificate is waiting for a signed certificate to be uploaded. + +### Output certificate details + +1. Stop the Application Function if it is already running. + +1. Remove previous configurations: + + ```bash + rm -rf ~/rt-5gms-application-function/install/var/cache/rt-5gms/af/certificates + ``` + +1. Start the Application Function: + + ```bash + ~/rt-5gms-application-function/install/bin/open5gs-msafd + ``` + +1. Create a single Provisioning Session: + + ```bash + ~/rt-5gms-application-function/install/bin/m1-session new-provisioning-session -e MyAppId -a MyASPId + ``` + + **Hint:** Set the shell variable `provisioning_session_id` to the returned provisioning session id for use in later commands. + +1. Create a certificate + + ```bash + ~/rt-5gms-application-function/install/bin/m1-session new-certificate -p ${provisioning_session_id} + ``` + + Where `${provisioning_session_id}` is the provisioning session id of the session that was created in the previous step. + + **Hint:** Set the shell variable `certificate_id` to the returned certificate id for use in later commands. + +1. Display the details of the certificate + + ```bash + ~/rt-5gms-application-function/install/bin/m1-session show-certificate -p ${provisioning_session_id} -c ${certificate_id} + ``` + + Where `${provisioning_session_id}` is the provisioning session id of the session that was created in step 4 and + `${certificate_id}` is the certificate id of the certificate created in the previous step. + + This will display the certificate details. + + For example: + + ``` + Certificate details for d921a6e2-c977-41ed-ae8f-4f7bb018a30b: + Serial = 570812267048735513617861647966053937458169779179 + Not before = 2023-03-23 12:40:10+00:00 + Not after = 2023-06-21 12:40:10+00:00 + Subject = C=GB,L=London,CN=localhost + key=E9:61:FD:5A:31:0C:ED:C0:B0:CC:29:0D:29:89:AE:EE:F9:25:89:CA + Issuer = C=GB,L=London,CN=localhost + key=E9:61:FD:5A:31:0C:ED:C0:B0:CC:29:0D:29:89:AE:EE:F9:25:89:CA + Subject Alternative Names: + DNS:localhost + ``` + +1. Display the public certificate PEM data + + ```bash + ~/rt-5gms-application-function/install/bin/m1-session show-certificate -p ${provisioning_session_id} -c ${certificate_id} -r + ``` + + The `-r` flag causes the command to display the "raw" output which is the PEM data for the certificate. + + For example: + + ``` + -----BEGIN CERTIFICATE----- + MIIDWzCCAkOgAwIBAgIUY/wbeD1YUiEeBPLpxf8ldkkEE+swDQYJKoZIhvcNAQEL + BQAwMjELMAkGA1UEBhMCR0IxDzANBgNVBAcMBkxvbmRvbjESMBAGA1UEAwwJbG9j + YWxob3N0MB4XDTIzMDMyMzEyNDAxMFoXDTIzMDYyMTEyNDAxMFowMjELMAkGA1UE + BhMCR0IxDzANBgNVBAcMBkxvbmRvbjESMBAGA1UEAwwJbG9jYWxob3N0MIIBIjAN + BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0bftLTaaXXpW1qIDHYTTmeIGvupy + G/dQpK5Ko9mW9IEcb+wfn2fX8SMHGA7O8TvpqUGEmyoBXIuIFmmeR+w5xQRcKkyi + NGhIhnUIfOqaevh4MCX/8Ius9NOjF0bz+aWtwOCmWKkNvknRMzClAeVRJm6g+U6m + IH30TE5H3ItiYxk63MNvuYeqIEK2rEKu69jWvTFkV1Wzd+5rH3ZC9qrt4uryUqAZ + X6XPx5AkQzbnBRQooDJzhqKgW+7YFWFtwi6WX8poGwx4RuruxYRLEBDfxRCE/1k6 + ALP3IktsVp7rFqwOt0LRNH1+MLly/MIOEA+NjnaIReG3/6Kt7oAqiDCBpQIDAQAB + o2kwZzAdBgNVHQ4EFgQU6WH9WjEM7cCwzCkNKYmu7vklicowHwYDVR0jBBgwFoAU + 6WH9WjEM7cCwzCkNKYmu7vklicowDwYDVR0TAQH/BAUwAwEB/zAUBgNVHREEDTAL + gglsb2NhbGhvc3QwDQYJKoZIhvcNAQELBQADggEBAIAELtWEMwzoXnaWRn74JngW + 3DF5IUtdFOiEKPWzdju+RleUsQHm5hsbPxpAz/MDVKIQQBGrJNMNpMtJ1VNlAsJ1 + 7gndSkSFf0zw7+DxgKYiwNj7tbBU8yTW+qVyUJh6XUxOHlaLjXzct4jw/NgjrjRZ + YuwXKCebRf+DUtQGt87rSib+GVpI//XBweyd8D0vFnGPRU9yyAvuqUdfu7enGFMr + qyBBqTqrTgX9o852lrMnWbc+g+of90Ym9HkVpifmc12jZSJlfS4cykvwnRqwPC9f + YnNABBMDJwJOM8g69OIRw+67O01ZulRzCvSaSbBEG6xC08XAD7BJn9CPIMypHdw= + -----END CERTIFICATE----- + ``` + +### Upload a public certificate + +**TODO:** Test where we reserve a certificate and the fetch CSR for the new certificate, sign it, and upload the result. + +## Content Protocol Discovery + +### List the Content Protocols available + +1. Stop the Application Function if it is already running. + +1. Remove previous configurations: + + ```bash + rm -rf ~/rt-5gms-application-function/install/var/cache/rt-5gms/af/certificates + ``` + +1. Start the Application Function: + + ```bash + ~/rt-5gms-application-function/install/bin/open5gs-msafd + ``` + +1. Create a single Provisioning Session: + + ```bash + ~/rt-5gms-application-function/install/bin/m1-session new-provisioning-session -e MyAppId -a MyASPId + ``` + + **Hint:** Set the shell variable `provisioning_session_id` to the returned provisioning session id for use in later commands. + +1. List the Content Protocols for the Provisioning Session + + ```bash + ~/rt-5gms-application-function/install/bin/m1-session protocols -p ${provisioning_session_id} + ``` + + Where `${provisioning_session_id}` is the provisioning session id of the session that was created in the previous step. + + The available protocols will be listed. + + For example: + + ``` + Protocols for 40b75340-c8a3-41ed-9d6e-cbf27240da7a: + Downlink: + urn:3gpp:5gms:content-protocol:http-pull-ingest + No uplink capability + No geo-fencing capability + ``` + +## Content Hosting Provisioning + +### Add a Content Hosting Configuration without certificates + +1. Stop the Application Function if it is already running. + +1. Remove previous configurations: + + ```bash + rm -rf ~/rt-5gms-application-function/install/var/cache/rt-5gms/af/certificates + ``` + +1. Start the Application Function: + + ```bash + ~/rt-5gms-application-function/install/bin/open5gs-msafd + ``` + +1. Create a single Provisioning Session: + + ```bash + ~/rt-5gms-application-function/install/bin/m1-session new-provisioning-session -e MyAppId -a MyASPId + ``` + + **Hint:** Set the shell variable `provisioning_session_id` to the returned provisioning session id for use in later commands. + +1. Create the hosting configuration: + + ```bash + ~/rt-5gms-application-function/install/bin/m1-session set-stream -p ${provisioning_session_id} ~/rt-5gms-application-function/examples/ContentHostingConfiguration_Big-Buck-Bunny_pull-ingest.json + ``` + + Where `${provisioning_session_id}` is the provisioning session id of the session that was created in the previous step. + +1. Check the provisioning session configuration: + + ```bash + ~/rt-5gms-application-function/install/bin/m1-session list -v + ``` + + This will display the provisioning session created, showing no certificates and the details from the example + ContentHostingConfiguration. + + For example: + + ``` + 2ef78712-c9a0-41ed-ac37-f9964ab0d12a: + Certificates: + ContentHostingConfiguration: + Name: Big Buck Bunny + Entry Point Path: BigBuckBunny_4s_onDemand_2014_05_09.mpd + Ingest: + Type: urn:3gpp:5gms:content-protocol:http-pull-ingest + URL: https://ftp.itec.aau.at/datasets/DASHDataset2014/BigBuckBunny/4sec/ + Distributions: + - URL: http://localhost/m4d/provisioning-session-2ef78712-c9a0-41ed-ac37-f9964ab0d12a/ + Canonical Domain Name: localhost + ``` + +**Note:** The `m1-session new-stream` command is a convience command that will create a provisioning session, generate the +ContentHostingConfiguration and set it in the newly created provisioning session. The above can also be done using: +``` +~/rt-5gms-application-function/install/bin/m1-session new-stream -e MyAppId -a MyASPId -n 'Big Buck Bunny' 'https://ftp.itec.aau.at/datasets/DASHDataset2014/BigBuckBunny/4sec/' 'BigBuckBunny_4s_onDemand_2014_05_09.mpd' +``` + +### Add a Content Hosting Configuration which uses an existing certificate + +1. Stop the Application Function if it is already running. + +1. Remove previous configurations: + + ```bash + rm -rf ~/rt-5gms-application-function/install/var/cache/rt-5gms/af/certificates + ``` + +1. Start the Application Function: + + ```bash + ~/rt-5gms-application-function/install/bin/open5gs-msafd + ``` + +1. Create a single Provisioning Session: + + ```bash + ~/rt-5gms-application-function/install/bin/m1-session new-provisioning-session -e MyAppId -a MyASPId + ``` + + **Hint:** Set the shell variable `provisioning_session_id` to the returned provisioning session id for use in later commands. + +1. Create a certificate: + + ```bash + ~/rt-5gms-application-function/install/bin/m1-session new-certificate -p ${provisioning_session_id} + ``` + + Where `${provisioning_session_id}` is the provisioning session id of the session that was created in the previous step. + +1. Generate a ContentHostingConfiguration using the certificate: + + ```bash + sed "s/@certificate-id@/${certificate_id}/g" ~/rt-5gms-application-function/examples/ContentHostingConfiguration_Big-Buck-Bunny_pull-ingest_https.json.tmpl > chc.json + ``` + + Where `${certificate_id}` is the certificate id of the certificate created in the previous step. + +1. Create the hosting configuration using the generated ContentHostingConfiguration: + + ```bash + ~/rt-5gms-application-function/install/bin/m1-session set-stream -p ${provisioning_session_id} chc.json + ``` + + Where `${provisioning_session_id}` is the provisioning session id of the session that was created in step 4. + +1. Check the provisioning session configuration: + + ```bash + ~/rt-5gms-application-function/install/bin/m1-session list -v + ``` + + This will display the provisioning session created, showing no certificates and the details from the example + ContentHostingConfiguration. + + For example: + + ``` + ed5079d6-c9a4-41ed-b2ad-41232d457177: + Certificates: + fbd05ddc-c9a4-41ed-b2ad-41232d457177: + Serial = 186484472102456711697672477872825927418601662068 + Not before = 2023-03-23 18:03:15+00:00 + Not after = 2023-06-21 18:03:15+00:00 + Subject = C=GB,L=London,CN=localhost + key=8A:C2:9A:48:15:6A:15:BB:EE:B7:A0:9E:07:7C:CB:A4:CF:7C:51:F5 + Issuer = C=GB,L=London,CN=localhost + key=8A:C2:9A:48:15:6A:15:BB:EE:B7:A0:9E:07:7C:CB:A4:CF:7C:51:F5 + Subject Alternative Names: + DNS:localhost + ContentHostingConfiguration: + Name: Big Buck Bunny + Entry Point Path: BigBuckBunny_4s_onDemand_2014_05_09.mpd + Ingest: + Type: urn:3gpp:5gms:content-protocol:http-pull-ingest + URL: https://ftp.itec.aau.at/datasets/DASHDataset2014/BigBuckBunny/4sec/ + Distributions: + - URL: https://localhost/m4d/provisioning-session-ed5079d6-c9a4-41ed-b2ad-41232d457177/ + Canonical Domain Name: localhost + Certificate: fbd05ddc-c9a4-41ed-b2ad-41232d457177 + ``` + +**Note:** The `m1-session new-stream` command is a convience command that will create a provisioning session, generate the +ContentHostingConfiguration and set it in the newly created provisioning session. The above configuration with the `m1-session` tool can also be done using this single command instead: +``` +~/rt-5gms-application-function/install/bin/m1-session new-stream -e MyAppId -a MyASPId -n 'Big Buck Bunny' --ssl-only 'https://ftp.itec.aau.at/datasets/DASHDataset2014/BigBuckBunny/4sec/' 'BigBuckBunny_4s_onDemand_2014_05_09.mpd' +``` + +## Consumption Reporting (v1.4.0 and later) + +### Add a Consumption Reporting Configuration + +1. Stop the Application Function if it is already running. + +1. Remove previous configurations: + + ```bash + rm -rf ~/rt-5gms-application-function/install/var/cache/rt-5gms/af/certificates + ``` + +1. Start the Application Function: + + ```bash + ~/rt-5gms-application-function/install/bin/open5gs-msafd + ``` + +1. Create a single Provisioning Session: + + ```bash + ~/rt-5gms-application-function/install/bin/m1-session new-provisioning-session -e MyAppId -a MyASPId + ``` + + **Hint:** Set the shell variable `provisioning_session_id` to the returned provisioning session id for use in later commands. + +1. Create a Consumption Reporting Configuration for the Provisioning Session + ```bash + ~/rt-5gms-application-function/install/bin/m1-session set-consumption-reporting -p ${provisioning_session_id} --interval 15 --sample-percentage 66.66 --location-reporting --access-reporting + ``` + + Where `${provisioning_session_id}` is the provisioning session id of the session that was created in step 4. + + This will set consumption reporting to every 15 seconds for 66.66% of clients and reports should include Location and Access reporting. + + All Consumption Reporting parameters are optional so doing the following: + ```bash + ~/rt-5gms-application-function/install/bin/m1-session set-consumption-reporting -p ${provisioning_session_id} + ``` + ...will request all clients send a single Consumption Report at the end of the media without Location or Access reports (the defaults for consumption reporting). + +### Show current Consumption Reporting Configuration + +1. Display the current Consumption Reporting Configuration for a Provisioning Session + ```bash + ~/rt-5gms-application-function/install/bin/m1-session show-consumption-reporting -p ${provisioning_session_id} + ``` + + Where `${provisioning_session_id}` is the provisioning session id of the session you wish to view. + +### Remove the Consumption Reporting Configuration + +1. Remove Consumption Reporting from a Provisioning Session + ```bash + ~/rt-5gms-application-function/install/bin/m1-session del-consumption-reporting -p ${provisioning_session_id} + ``` + + Where `${provisioning_session_id}` is the provisioning session id of the session you wish to remove consumption reporting from. + +1. Display the current Consumption Reporting Configuration for the Provisioning Session to check it has gone + ```bash + ~/rt-5gms-application-function/install/bin/m1-session show-consumption-reporting -p ${provisioning_session_id} + ``` + + Where `${provisioning_session_id}` is the provisioning session id of the session you wish to view. + + This will report no Consumption Reporting Configuration is present. diff --git a/pages/5g-media-streaming/testing/testing-m1-v141.md b/pages/5g-media-streaming/testing/testing-m1-v141.md new file mode 100644 index 00000000..2942fd37 --- /dev/null +++ b/pages/5g-media-streaming/testing/testing-m1-v141.md @@ -0,0 +1,859 @@ +--- +layout: default +title: Testing M1 AF v1.4.x +parent: Testing +grand_parent: 5G Downlink Media Streaming +has_children: false +nav_order: 8 +--- + +# Testing: M1 Interface (5GMSd Application Function v1.4.1 and later) + +To prepare, follow the instructions for [local user building and installation](Testing-as-a-Local-User). + +# Testing + +These tests require a [5GMSd Application Server](https://github.com/5G-MAG/rt-5gms-application-server) to be running. Please follow +the instructions to [build, install and run the 5GMSd Application Server](https://github.com/5G-MAG/rt-5gms-application-server#readme) as a system service or the [instructions to run the AS as a local user](https://github.com/5G-MAG/rt-5gms-application-server/wiki/Development-and-Testing) for a temporary installation for testing. + +## Test Provisioning Sessions + +This will test the ability of the Application Function to allocate and retrieve information about provisioning session upon request by an Application Provider. + +### Create provisioning sessions + +1. Stop the Application Function if it is already running. + +1. Remove previous configurations: + + ```bash + rm -rf ~/rt-5gms-application-function/install/var/cache/rt-5gms/af/certificates + ``` + +1. Start the Application Function: + + ```bash + ~/rt-5gms-application-function/install/bin/open5gs-msafd + ``` + +1. Create a single Provisioning Session: + + ```bash + m1-session new-provisioning-session -e MyAppId -a MyASPId + ``` + +1. Check the Provisioning Session: + + ```bash + m1-session list + ``` + + This should list a single provisioning session. + +1. Create a second Provisioning Session: + + ```bash + m1-session new-provisioning-session -e MyAppId -a MyASPId + ``` + +1. Check the Provisioning Sessions: + + ```bash + m1-session list + ``` + + This should list the two provisioning sessions. + +### Get details for a provisioning session + +1. Stop the Application Function if it is already running. + +1. Remove previous configurations: + + ```bash + rm -rf ~/rt-5gms-application-function/install/var/cache/rt-5gms/af/certificates + ``` + +1. Start the Application Function: + + ```bash + ~/rt-5gms-application-function/install/bin/open5gs-msafd + ``` + +1. Create a single Provisioning Session with Content Hosting Configuration + + ```bash + m1-session new-stream -e MyAppId -a MyASPId -n 'Big Buck Bunny' 'https://ftp.itec.aau.at/datasets/DASHDataset2014/BigBuckBunny/4sec/' 'BigBuckBunny_4s_onDemand_2014_05_09.mpd' + ``` + +1. Check the Provisioning Session details: + + ```bash + m1-session list -v + ``` + + This will list the provisioning session showing the Content Hosting Configuration attached to it. + + For example: + + ``` + 39f4f698-daa0-41ed-862b-c1f4c44bccf3: + Certificates: + ContentHostingConfiguration: + Name: Big Buck Bunny + Ingest: + Type: urn:3gpp:5gms:content-protocol:http-pull-ingest + Pull Ingest?: True + URL: https://ftp.itec.aau.at/datasets/DASHDataset2014/BigBuckBunny/4sec/ + Distributions: + - URL: http://localhost/m4d/provisioning-session-39f4f698-daa0-41ed-862b-c1f4c44bccf3/ + Canonical Domain Name: localhost + Entry point: + Relative Path: BigBuckBunny_4s_onDemand_2014_05_09.mpd + Content Type: application/dash+xml + ``` + +### Delete a provisioning session + +1. Stop the Application Function if it is already running. + +1. Remove previous configurations: + + ```bash + rm -rf ~/rt-5gms-application-function/install/var/cache/rt-5gms/af/certificates + ``` + +1. Start the Application Function: + + ```bash + ~/rt-5gms-application-function/install/bin/open5gs-msafd + ``` + +1. Create a single Provisioning Session: + + ```bash + m1-session new-provisioning-session -e MyAppId -a MyASPId + ``` + + **Hint:** Set the shell variable `provisioning_session_id` to the returned provisioning session id for use in later commands. + +1. Check the Provisioning Session: + + ```bash + m1-session list + ``` + + This should list a single provisioning session. + +1. Delete the Provisioning Session by Id: + + ```bash + m1-session del-stream -p ${provisioning_session_id} + ``` + + Where `${provisioning_session_id}` is the provisioning session id of the session that was created in the earlier step. + + For example: + + ```bash + m1-session del-stream -p 1c961622-c803-41ed-83c5-e304b44dbd7e + ``` + +1. Check the Provisioning Session is deleted: + + ```bash + m1-session list + ``` + + There should be no provisioning sessions listed. + +1. Create a single Provisioning Session with a stream identifier: + + ```bash + m1-session new-stream -e MyAppId -a MyASPId -n 'Test Stream' 'https://ftp.itec.aau.at/datasets/DASHDataset2014/BigBuckBunny/4sec/' 'BigBuckBunny_4s_onDemand_2014_05_09.mpd' + ``` + +1. Check the Provisioning Session: + + ```bash + m1-session list -v + ``` + +1. Delete the Provisioning Session by ingest URL and entry point path: + + ```bash + m1-session del-stream 'https://ftp.itec.aau.at/datasets/DASHDataset2014/BigBuckBunny/4sec/' 'BigBuckBunny_4s_onDemand_2014_05_09.mpd' + ``` + + **Note:** The entry point (last command line parameter) can be any one of the entry point relative paths present in a + distribution configuration for the content hosting configuration of a provisioning session. + +1. Check the Provisioning Session is deleted: + + ```bash + m1-session list + ``` + + There should be no provisioning sessions listed. + +### Create a hosting configuration with multiple entry points + +1. Stop the Application Function if it is already running. + +1. Remove previous configurations: + + ```bash + rm -rf ~/rt-5gms-application-function/install/var/cache/rt-5gms/af/certificates + ``` + +1. Start the Application Function: + + ```bash + ~/rt-5gms-application-function/install/bin/open5gs-msafd + ``` + +1. Create a single Provisioning Session with Content Hosting Configuration containing multiple entry points + + ```bash + m1-session new-stream -e MyAppId -a MyASPId -n 'Big Buck Bunny' 'http://amssamples.streaming.mediaservices.windows.net/622b189f-ec39-43f2-93a2-201ac4e31ce1/BigBuckBunny.ism/' 'manifest(format=mpd-time-csf)' 'manifest(format=m3u8-aapl-v3)' + ``` + +1. Check the Provisioning Session details: + + ```bash + m1-session list -v + ``` + + This will list the provisioning session showing the Content Hosting Configuration attached to it. + + For example: + + ``` + 74f4c492-dacf-41ed-87fe-93ba2b9b790a: + Certificates: + ContentHostingConfiguration: + Name: Big Buck Bunny + Ingest: + Type: urn:3gpp:5gms:content-protocol:http-pull-ingest + Pull Ingest?: True + URL: http://amssamples.streaming.mediaservices.windows.net/622b189f-ec39-43f2-93a2-201ac4e31ce1/BigBuckBunny.ism/ + Distributions: + - URL: http://localhost/m4d/provisioning-session-74f4c492-dacf-41ed-87fe-93ba2b9b790a/ + Canonical Domain Name: localhost + Entry point: + Relative Path: manifest(format=mpd-time-csf) + Content Type: application/dash+xml + - URL: http://localhost/m4d/provisioning-session-74f4c492-dacf-41ed-87fe-93ba2b9b790a/ + Canonical Domain Name: localhost + Entry point: + Relative Path: manifest(format=m3u8-aapl-v3) + Content Type: application/vnd.apple.mpegurl + ``` + + The output shows that the ContentHostingConfiguration for Provisioning Session 74f4c492-dacf-41ed-87fe-93ba2b9b790a has two + distribution configurations. There is one distribution configuration for DASH and one for HLS. + +### Create a hosting configuration from a JSON file + +1. Stop the Application Function if it is already running. + +1. Remove previous configurations: + + ```bash + rm -rf ~/rt-5gms-application-function/install/var/cache/rt-5gms/af/certificates + ``` + +1. Start the Application Function: + + ```bash + ~/rt-5gms-application-function/install/bin/open5gs-msafd + ``` + +1. Create a single Provisioning Session: + + ```bash + m1-session new-provisioning-session -e MyAppId -a MyASPId + ``` + + **Hint:** Set the shell variable `provisioning_session_id` to the returned provisioning session id for use in later commands. + +1. Upload the ContentHostingConfiguration JSON file: + + ```bash + m1-session set-stream -p ${provisioning_session_id} ~/rt-5gms-application-function/examples/ContentHostingConfiguration_Big-Buck-Bunny_pull-ingest.json + ``` + + Where `${provisioning_session_id}` is the Provisioning Session Id reported by the previous step. + + For this step one of the example configuration files from the `~/rt-5gms-application-function/examples` directory was used, but any valid ContentHostingConfiguration JSON file can be used. + +1. Check the Provisioning Session: + + ```bash + m1-session list -v + ``` + + The output should show a provisioning session with the contents of the JSON file used as the ContentHostingConfiguration. + + For example: + + ``` + 74f4c492-dacf-41ed-87fe-93ba2b9b790a: + Certificates: + ContentHostingConfiguration: + Name: AMP Demo Stream: Big Buck Bunny + Ingest: + Type: urn:3gpp:5gms:content-protocol:http-pull-ingest + Pull Ingest?: True + URL: http://amssamples.streaming.mediaservices.windows.net/622b189f-ec39-43f2-93a2-201ac4e31ce1/BigBuckBunny.ism/ + Distributions: + - URL: http://localhost/m4d/provisioning-session-74f4c492-dacf-41ed-87fe-93ba2b9b790a/ + Canonical Domain Name: localhost + Entry point: + Relative Path: manifest(format=mpd-time-csf) + Content Type: application/dash+xml + Profiles: + - urn:mpeg:dash:profile:isoff-live:2011 + - URL: http://localhost/m4d/provisioning-session-74f4c492-dacf-41ed-87fe-93ba2b9b790a/ + Canonical Domain Name: localhost + Entry point: + Relative Path: manifest(format=m3u8-aapl-v3) + Content Type: application/vnd.apple.mpegurl + ``` + + This also tests the use of profile lists in the distribution entry points. + +## Server Certificates + +### Create Server Certificates + +1. Stop the Application Function if it is already running. + +1. Remove previous configurations: + + ```bash + rm -rf ~/rt-5gms-application-function/install/var/cache/rt-5gms/af/certificates + ``` + +1. Start the Application Function: + + ```bash + ~/rt-5gms-application-function/install/bin/open5gs-msafd + ``` + +1. Create a single Provisioning Session: + + ```bash + m1-session new-provisioning-session -e MyAppId -a MyASPId + ``` + + **Hint:** Set the shell variable `provisioning_session_id` to the returned provisioning session id for use in later commands. + +1. Create a certificate: + + ```bash + m1-session new-certificate -p ${provisioning_session_id} + ``` + + Where `${provisioning_session_id}` is the provisioning session id of the session that was created in the previous step. + +1. Check the Provisioning Session: + + ```bash + m1-session list -v + ``` + + The output should show a provisioning session with a single certificate where the subject and issuer of the certificate are + identical. + + For example: + + ``` + 40b75340-c8a3-41ed-9d6e-cbf27240da7a: + Certificates: + 4fff7e04-c8a3-41ed-9d6e-cbf27240da7a: + Serial = 723264618754945153424478507276304617300583059881 + Not before = 2023-03-22 11:18:46+00:00 + Not after = 2023-06-20 11:18:46+00:00 + Subject = C=GB,L=London,CN=localhost + key=C2:56:2C:A6:D7:B3:AE:C7:3A:2C:18:9D:5B:2A:EB:62:C0:7E:35:05 + Issuer = C=GB,L=London,CN=localhost + key=C2:56:2C:A6:D7:B3:AE:C7:3A:2C:18:9D:5B:2A:EB:62:C0:7E:35:05 + Subject Alternative Names: + DNS:localhost + ContentHostingConfiguration: + Not defined + ``` + + This shows that for provisioning session 40b75340-c8a3-41ed-9d6e-cbf27240da7a there is a certificate with id + 4fff7e04-c8a3-41ed-9d6e-cbf27240da7a lasting for 90 days from 22nd Mar 2023. The "Subject" and "Issuer" both have the same + designated name ("C=GB,L=London,CN=localhost") and key hash, showing that this is a self signed certificate. The + "Subject Alternative Names" contains one "DNS" entry for the canonical name of the 5GMSd Application Server. + +1. Create a certificate with a domain name: + + ```bash + m1-session new-certificate -p ${provisioning_session_id} -d as.example.com + ``` + + Since a domain name was requested, the `m1-session` tool will request a CSR from the 5GMSd Application Function and sign it + itself. + +1. Check the Provisioning Session: + + ```bash + m1-session list -v + ``` + + The output should now show an extra certificate on the provisioning session. + + For example: + + ``` + 40b75340-c8a3-41ed-9d6e-cbf27240da7a: + Certificates: + 4fff7e04-c8a3-41ed-9d6e-cbf27240da7a: + Serial = 723264618754945153424478507276304617300583059881 + Not before = 2023-03-22 11:18:46+00:00 + Not after = 2023-06-20 11:18:46+00:00 + Subject = C=GB,L=London,CN=localhost + key=C2:56:2C:A6:D7:B3:AE:C7:3A:2C:18:9D:5B:2A:EB:62:C0:7E:35:05 + Issuer = C=GB,L=London,CN=localhost + key=C2:56:2C:A6:D7:B3:AE:C7:3A:2C:18:9D:5B:2A:EB:62:C0:7E:35:05 + Subject Alternative Names: + DNS:localhost + 8aa9e5ac-c8a9-41ed-9d6e-cbf27240da7a: + Serial = 1 + Not before = 2023-03-22 12:03:22+00:00 + Not after = 2023-04-21 12:03:22+00:00 + Subject = CN=as.example.com,O=5G-MAG + key=37:62:38:E1:D2:18:23:90:A9:12:2A:C7:EF:5F:7E:F8:91:3A:89:8F + Issuer = O=5G-MAG,CN=5G-MAG Reference Tools Local CA + key=B4:2F:13:EE:02:D0:34:75:C0:7B:9D:C7:67:6D:90:76:F5:A8:CC:EF + Subject Alternative Names: + DNS:as.example.com + DNS:localhost + ContentHostingConfiguration: + Not defined + ``` + + This shows that there is now a second certificate (8aa9e5ac-c8a9-41ed-9d6e-cbf27240da7a) issued by + "5G-MAG Reference Tools Local CA" and the Subject Common Name is the domain name alias used with the `-d` command line option + when the certificate was created. The canonical domain name of the 5GMSd Application Server is the second Subject Alternative + Name. These certificates last for 30 days by default. + +1. Reserve a certificate: + + ```bash + m1-session new-certificate -p ${provisioning_session_id} --csr + ``` + + The output includes the new certificate id and a CSR in PEM format. + +1. Check the Provisioning Session: + + ```bash + m1-session list -v + ``` + + The output should now show a third certificate id but the certificate detail says "Certificate not yet uploaded". + + For example: + + ``` + 40b75340-c8a3-41ed-9d6e-cbf27240da7a: + Certificates: + 4fff7e04-c8a3-41ed-9d6e-cbf27240da7a: + Serial = 723264618754945153424478507276304617300583059881 + Not before = 2023-03-22 11:18:46+00:00 + Not after = 2023-06-20 11:18:46+00:00 + Subject = C=GB,L=London,CN=localhost + key=C2:56:2C:A6:D7:B3:AE:C7:3A:2C:18:9D:5B:2A:EB:62:C0:7E:35:05 + Issuer = C=GB,L=London,CN=localhost + key=C2:56:2C:A6:D7:B3:AE:C7:3A:2C:18:9D:5B:2A:EB:62:C0:7E:35:05 + Subject Alternative Names: + DNS:localhost + 8aa9e5ac-c8a9-41ed-9d6e-cbf27240da7a: + Serial = 1 + Not before = 2023-03-22 12:03:22+00:00 + Not after = 2023-04-21 12:03:22+00:00 + Subject = CN=as.example.com,O=5G-MAG + key=37:62:38:E1:D2:18:23:90:A9:12:2A:C7:EF:5F:7E:F8:91:3A:89:8F + Issuer = O=5G-MAG,CN=5G-MAG Reference Tools Local CA + key=B4:2F:13:EE:02:D0:34:75:C0:7B:9D:C7:67:6D:90:76:F5:A8:CC:EF + Subject Alternative Names: + DNS:as.example.com + DNS:localhost + 2a118b8e-c8a7-41ed-9d6e-cbf27240da7a: + Certificate not yet uploaded + ContentHostingConfiguration: + Not defined + ``` + + This shows that the 2a118b8e-c8a7-41ed-9d6e-cbf27240da7a certificate is waiting for a signed certificate to be uploaded. + +### Output certificate details + +1. Stop the Application Function if it is already running. + +1. Remove previous configurations: + + ```bash + rm -rf ~/rt-5gms-application-function/install/var/cache/rt-5gms/af/certificates + ``` + +1. Start the Application Function: + + ```bash + ~/rt-5gms-application-function/install/bin/open5gs-msafd + ``` + +1. Create a single Provisioning Session: + + ```bash + m1-session new-provisioning-session -e MyAppId -a MyASPId + ``` + + **Hint:** Set the shell variable `provisioning_session_id` to the returned provisioning session id for use in later commands. + +1. Create a certificate + + ```bash + m1-session new-certificate -p ${provisioning_session_id} + ``` + + Where `${provisioning_session_id}` is the provisioning session id of the session that was created in the previous step. + + **Hint:** Set the shell variable `certificate_id` to the returned certificate id for use in later commands. + +1. Display the details of the certificate + + ```bash + m1-session show-certificate -p ${provisioning_session_id} -c ${certificate_id} + ``` + + Where `${provisioning_session_id}` is the provisioning session id of the session that was created in step 4 and + `${certificate_id}` is the certificate id of the certificate created in the previous step. + + This will display the certificate details. + + For example: + + ``` + Certificate details for d921a6e2-c977-41ed-ae8f-4f7bb018a30b: + Serial = 570812267048735513617861647966053937458169779179 + Not before = 2023-03-23 12:40:10+00:00 + Not after = 2023-06-21 12:40:10+00:00 + Subject = C=GB,L=London,CN=localhost + key=E9:61:FD:5A:31:0C:ED:C0:B0:CC:29:0D:29:89:AE:EE:F9:25:89:CA + Issuer = C=GB,L=London,CN=localhost + key=E9:61:FD:5A:31:0C:ED:C0:B0:CC:29:0D:29:89:AE:EE:F9:25:89:CA + Subject Alternative Names: + DNS:localhost + ``` + +1. Display the public certificate PEM data + + ```bash + m1-session show-certificate -p ${provisioning_session_id} -c ${certificate_id} -r + ``` + + The `-r` flag causes the command to display the "raw" output which is the PEM data for the certificate. + + For example: + + ``` + -----BEGIN CERTIFICATE----- + MIIDWzCCAkOgAwIBAgIUY/wbeD1YUiEeBPLpxf8ldkkEE+swDQYJKoZIhvcNAQEL + BQAwMjELMAkGA1UEBhMCR0IxDzANBgNVBAcMBkxvbmRvbjESMBAGA1UEAwwJbG9j + YWxob3N0MB4XDTIzMDMyMzEyNDAxMFoXDTIzMDYyMTEyNDAxMFowMjELMAkGA1UE + BhMCR0IxDzANBgNVBAcMBkxvbmRvbjESMBAGA1UEAwwJbG9jYWxob3N0MIIBIjAN + BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0bftLTaaXXpW1qIDHYTTmeIGvupy + G/dQpK5Ko9mW9IEcb+wfn2fX8SMHGA7O8TvpqUGEmyoBXIuIFmmeR+w5xQRcKkyi + NGhIhnUIfOqaevh4MCX/8Ius9NOjF0bz+aWtwOCmWKkNvknRMzClAeVRJm6g+U6m + IH30TE5H3ItiYxk63MNvuYeqIEK2rEKu69jWvTFkV1Wzd+5rH3ZC9qrt4uryUqAZ + X6XPx5AkQzbnBRQooDJzhqKgW+7YFWFtwi6WX8poGwx4RuruxYRLEBDfxRCE/1k6 + ALP3IktsVp7rFqwOt0LRNH1+MLly/MIOEA+NjnaIReG3/6Kt7oAqiDCBpQIDAQAB + o2kwZzAdBgNVHQ4EFgQU6WH9WjEM7cCwzCkNKYmu7vklicowHwYDVR0jBBgwFoAU + 6WH9WjEM7cCwzCkNKYmu7vklicowDwYDVR0TAQH/BAUwAwEB/zAUBgNVHREEDTAL + gglsb2NhbGhvc3QwDQYJKoZIhvcNAQELBQADggEBAIAELtWEMwzoXnaWRn74JngW + 3DF5IUtdFOiEKPWzdju+RleUsQHm5hsbPxpAz/MDVKIQQBGrJNMNpMtJ1VNlAsJ1 + 7gndSkSFf0zw7+DxgKYiwNj7tbBU8yTW+qVyUJh6XUxOHlaLjXzct4jw/NgjrjRZ + YuwXKCebRf+DUtQGt87rSib+GVpI//XBweyd8D0vFnGPRU9yyAvuqUdfu7enGFMr + qyBBqTqrTgX9o852lrMnWbc+g+of90Ym9HkVpifmc12jZSJlfS4cykvwnRqwPC9f + YnNABBMDJwJOM8g69OIRw+67O01ZulRzCvSaSbBEG6xC08XAD7BJn9CPIMypHdw= + -----END CERTIFICATE----- + ``` + +### Upload a public certificate + +**TODO:** Test where we reserve a certificate and the fetch CSR for the new certificate, sign it, and upload the result. + +## Content Protocol Discovery + +### List the Content Protocols available + +1. Stop the Application Function if it is already running. + +1. Remove previous configurations: + + ```bash + rm -rf ~/rt-5gms-application-function/install/var/cache/rt-5gms/af/certificates + ``` + +1. Start the Application Function: + + ```bash + ~/rt-5gms-application-function/install/bin/open5gs-msafd + ``` + +1. Create a single Provisioning Session: + + ```bash + m1-session new-provisioning-session -e MyAppId -a MyASPId + ``` + + **Hint:** Set the shell variable `provisioning_session_id` to the returned provisioning session id for use in later commands. + +1. List the Content Protocols for the Provisioning Session + + ```bash + m1-session protocols -p ${provisioning_session_id} + ``` + + Where `${provisioning_session_id}` is the provisioning session id of the session that was created in the previous step. + + The available protocols will be listed. + + For example: + + ``` + Protocols for 40b75340-c8a3-41ed-9d6e-cbf27240da7a: + Downlink: + urn:3gpp:5gms:content-protocol:http-pull-ingest + No uplink capability + No geo-fencing capability + ``` + +## Content Hosting Provisioning + +### Add a Content Hosting Configuration without certificates + +1. Stop the Application Function if it is already running. + +1. Remove previous configurations: + + ```bash + rm -rf ~/rt-5gms-application-function/install/var/cache/rt-5gms/af/certificates + ``` + +1. Start the Application Function: + + ```bash + ~/rt-5gms-application-function/install/bin/open5gs-msafd + ``` + +1. Create a single Provisioning Session: + + ```bash + m1-session new-provisioning-session -e MyAppId -a MyASPId + ``` + + **Hint:** Set the shell variable `provisioning_session_id` to the returned provisioning session id for use in later commands. + +1. Create the hosting configuration: + + ```bash + m1-session set-stream -p ${provisioning_session_id} ~/rt-5gms-application-function/examples/ContentHostingConfiguration_Big-Buck-Bunny_pull-ingest.json + ``` + + Where `${provisioning_session_id}` is the provisioning session id of the session that was created in the previous step. + +1. Check the provisioning session configuration: + + ```bash + m1-session list -v + ``` + + This will display the provisioning session created, showing no certificates and the details from the example + ContentHostingConfiguration. + + For example: + + ``` + 2ef78712-c9a0-41ed-ac37-f9964ab0d12a: + Certificates: + ContentHostingConfiguration: + Name: Big Buck Bunny + Entry Point Path: BigBuckBunny_4s_onDemand_2014_05_09.mpd + Ingest: + Type: urn:3gpp:5gms:content-protocol:http-pull-ingest + URL: https://ftp.itec.aau.at/datasets/DASHDataset2014/BigBuckBunny/4sec/ + Distributions: + - URL: http://localhost/m4d/provisioning-session-2ef78712-c9a0-41ed-ac37-f9964ab0d12a/ + Canonical Domain Name: localhost + ``` + +**Note:** The `m1-session new-stream` command is a convience command that will create a provisioning session, generate the +ContentHostingConfiguration and set it in the newly created provisioning session. The above can also be done using: +``` +m1-session new-stream -e MyAppId -a MyASPId -n 'Big Buck Bunny' 'https://ftp.itec.aau.at/datasets/DASHDataset2014/BigBuckBunny/4sec/' 'BigBuckBunny_4s_onDemand_2014_05_09.mpd' +``` + +### Add a Content Hosting Configuration which uses an existing certificate + +1. Stop the Application Function if it is already running. + +1. Remove previous configurations: + + ```bash + rm -rf ~/rt-5gms-application-function/install/var/cache/rt-5gms/af/certificates + ``` + +1. Start the Application Function: + + ```bash + ~/rt-5gms-application-function/install/bin/open5gs-msafd + ``` + +1. Create a single Provisioning Session: + + ```bash + m1-session new-provisioning-session -e MyAppId -a MyASPId + ``` + + **Hint:** Set the shell variable `provisioning_session_id` to the returned provisioning session id for use in later commands. + +1. Create a certificate: + + ```bash + m1-session new-certificate -p ${provisioning_session_id} + ``` + + Where `${provisioning_session_id}` is the provisioning session id of the session that was created in the previous step. + +1. Generate a ContentHostingConfiguration using the certificate: + + ```bash + sed "s/@certificate-id@/${certificate_id}/g" ~/rt-5gms-application-function/examples/ContentHostingConfiguration_Big-Buck-Bunny_pull-ingest_https.json.tmpl > chc.json + ``` + + Where `${certificate_id}` is the certificate id of the certificate created in the previous step. + +1. Create the hosting configuration using the generated ContentHostingConfiguration: + + ```bash + m1-session set-stream -p ${provisioning_session_id} chc.json + ``` + + Where `${provisioning_session_id}` is the provisioning session id of the session that was created in step 4. + +1. Check the provisioning session configuration: + + ```bash + m1-session list -v + ``` + + This will display the provisioning session created, showing no certificates and the details from the example + ContentHostingConfiguration. + + For example: + + ``` + ed5079d6-c9a4-41ed-b2ad-41232d457177: + Certificates: + fbd05ddc-c9a4-41ed-b2ad-41232d457177: + Serial = 186484472102456711697672477872825927418601662068 + Not before = 2023-03-23 18:03:15+00:00 + Not after = 2023-06-21 18:03:15+00:00 + Subject = C=GB,L=London,CN=localhost + key=8A:C2:9A:48:15:6A:15:BB:EE:B7:A0:9E:07:7C:CB:A4:CF:7C:51:F5 + Issuer = C=GB,L=London,CN=localhost + key=8A:C2:9A:48:15:6A:15:BB:EE:B7:A0:9E:07:7C:CB:A4:CF:7C:51:F5 + Subject Alternative Names: + DNS:localhost + ContentHostingConfiguration: + Name: Big Buck Bunny + Entry Point Path: BigBuckBunny_4s_onDemand_2014_05_09.mpd + Ingest: + Type: urn:3gpp:5gms:content-protocol:http-pull-ingest + URL: https://ftp.itec.aau.at/datasets/DASHDataset2014/BigBuckBunny/4sec/ + Distributions: + - URL: https://localhost/m4d/provisioning-session-ed5079d6-c9a4-41ed-b2ad-41232d457177/ + Canonical Domain Name: localhost + Certificate: fbd05ddc-c9a4-41ed-b2ad-41232d457177 + ``` + +**Note:** The `m1-session new-stream` command is a convience command that will create a provisioning session, generate the +ContentHostingConfiguration and set it in the newly created provisioning session. The above configuration with the `m1-session` tool can also be done using this single command instead: +``` +m1-session new-stream -e MyAppId -a MyASPId -n 'Big Buck Bunny' --ssl-only 'https://ftp.itec.aau.at/datasets/DASHDataset2014/BigBuckBunny/4sec/' 'BigBuckBunny_4s_onDemand_2014_05_09.mpd' +``` + +## Consumption Reporting (v1.4.0 and later) + +### Add a Consumption Reporting Configuration + +1. Stop the Application Function if it is already running. + +1. Remove previous configurations: + + ```bash + rm -rf ~/rt-5gms-application-function/install/var/cache/rt-5gms/af/certificates + ``` + +1. Start the Application Function: + + ```bash + ~/rt-5gms-application-function/install/bin/open5gs-msafd + ``` + +1. Create a single Provisioning Session: + + ```bash + m1-session new-provisioning-session -e MyAppId -a MyASPId + ``` + + **Hint:** Set the shell variable `provisioning_session_id` to the returned provisioning session id for use in later commands. + +1. Create a Consumption Reporting Configuration for the Provisioning Session + ```bash + m1-session set-consumption-reporting -p ${provisioning_session_id} --interval 15 --sample-percentage 66.66 --location-reporting --access-reporting + ``` + + Where `${provisioning_session_id}` is the provisioning session id of the session that was created in step 4. + + This will set consumption reporting to every 15 seconds for 66.66% of clients and reports should include Location and Access reporting. + + All Consumption Reporting parameters are optional so doing the following: + ```bash + m1-session set-consumption-reporting -p ${provisioning_session_id} + ``` + ...will request all clients send a single Consumption Report at the end of the media without Location or Access reports (the defaults for consumption reporting). + +### Show current Consumption Reporting Configuration + +1. Display the current Consumption Reporting Configuration for a Provisioning Session + ```bash + m1-session show-consumption-reporting -p ${provisioning_session_id} + ``` + + Where `${provisioning_session_id}` is the provisioning session id of the session you wish to view. + +### Remove the Consumption Reporting Configuration + +1. Remove Consumption Reporting from a Provisioning Session + ```bash + m1-session del-consumption-reporting -p ${provisioning_session_id} + ``` + + Where `${provisioning_session_id}` is the provisioning session id of the session you wish to remove consumption reporting from. + +1. Display the current Consumption Reporting Configuration for the Provisioning Session to check it has gone + ```bash + m1-session show-consumption-reporting -p ${provisioning_session_id} + ``` + + Where `${provisioning_session_id}` is the provisioning session id of the session you wish to view. + + This will report no Consumption Reporting Configuration is present. diff --git a/pages/5g-media-streaming/testing/testing-m3-v110.md b/pages/5g-media-streaming/testing/testing-m3-v110.md new file mode 100644 index 00000000..9a26da25 --- /dev/null +++ b/pages/5g-media-streaming/testing/testing-m3-v110.md @@ -0,0 +1,216 @@ +--- +layout: default +title: Testing M3 AF v1.1.x +parent: Testing +grand_parent: 5G Downlink Media Streaming +has_children: false +nav_order: 9 +--- + +# Testing: M3 Interface (5GMSd Application Function v1.1.x) + +To prepare, follow the instructions for [local user building and installation](Testing-as-a-Local-User). + +# Configuration + +The Application Function is an M3 Client and most of the communication is only logged at the `debug` level. To properly see the M3 Interface interactions the configuration will need to set the minimum logging level to `debug` for the `msaf` domain. + +The example configurations all set the logging level for the `msaf` logging domain to `debug` and these configurations will be used throughout these tests. + +The `msaf.yaml` file in use by the Application Function is one of (in preference order): +- The file passed on the command line using the `-c` parameter. +- `${prefix}/etc/open5gs/msaf.yaml` + +These tests will use the `-c` command line parameter to override the location of the configuration file to use specific example +configurations for each test. + +Some tests also require an SSL public certificate and key to be generated. Appropriate instructions to generate self-signed certificastes are included in the test instructions where appropriate. + +For more information on configuring the Application Function (and generating self-signed certificates), see [Configuring the Application Function](Configuring-the-Application-Function). + +# Testing + +These tests require a [5GMSd Application Server](https://github.com/5G-MAG/rt-5gms-application-server) to be running. Please follow +the instructions to [build, install and run the 5GMSd Application Server](https://github.com/5G-MAG/rt-5gms-application-server#readme) as a system service or the [instructions to run the AS as a local user](https://github.com/5G-MAG/rt-5gms-application-server/wiki/Development-and-Testing) for a temporary installation for testing. + +## Test Simple HTTP Configuration + +This will test the ability of the Application Function to configure an Application Server via the interface at M3 with a simple +HTTP (unencrypted) distribution. For this test a ContentHostingConfiguration is used which has no `certificateId` fields set in any `distributionConfiguration`. + +1. Stop the Application Function if it is already running. + +1. Start the Application Function with the `Test_http_canonical-msaf.yaml` example configuration, e.g.: + + ```bash + ~/rt-5gms-application-function/install/bin/open5gs-msafd -c ~/rt-5gms-application-function/examples/Test_http_canonical-msaf.yaml + ``` + +1. The log output should indicate that the Application function: + 1. Requests a list of Server Certificates known by the Application Server (this is only done upon first communicating with an Application Server or when the Application Function needs to resynchronise the state from the Application Server) + + ``` + 02/10 10:39:26.792: [msaf] DEBUG: M3 client: Sending GET method to Application Server [localhost] to request the list of known certificates (../src/5gmsaf/application-server-context.c:151) + ``` + + 1. Receives the response + + ``` + 02/10 10:39:26.834: [msaf] DEBUG: [certificates] Method [GET] with Response [200] received (../src/5gmsaf/msaf-sm.c:1111) + ``` + + There may be log output following this with a list of certificates known to the Application Server. + + 1. Requests the list of known provisioning session ids on the Application Server for ContentHostingConfigurations + + ``` + 02/10 10:39:26.834: [msaf] DEBUG: M3 client: Sending GET method to Application Server [localhost] to request the list of known content-hosting-configurations (../src/5gmsaf/application-server-context.c:154) + ``` + + 1. Receives the response + + ``` + 02/10 10:39:26.837: [msaf] DEBUG: [content-hosting-configurations] Method [GET] with Response [200] for Content Hosting Configuration operation [(null)] (../src/5gmsaf/msaf-sm.c:888) + ``` + + There may be log output following this indicating a list of ContentHostingConfigurations known to the Application Server. + + 1. Pushes a new ContentHostingConfiguration to the Application Server + + ``` + 02/10 10:39:26.881: [msaf] DEBUG: M3 client: Sending POST method to Application Server [localhost] for Content Hosting Configuration: [30ef86aa-a92f-41ed-870f-4501bf315b24] (../src/5gmsaf/application-server-context.c:235) + ``` + + 1. Receives a success (201) response back, removes the ContentHostingConfiguration from the upload queue and marks it as current configuration for the Application Server. + + ``` + 02/10 10:39:26.966: [msaf] DEBUG: [content-hosting-configurations] Method [POST] with Response [201] recieved for Content Hosting Configuration [30ef86aa-a92f-41ed-870f-4501bf315b24] (../src/5gmsaf/msaf-sm.c:736) + 02/10 10:39:26.966: [msaf] DEBUG: Removing 30ef86aa-a92f-41ed-870f-4501bf315b24 from upload_content_hosting_configurations (../src/5gmsaf/msaf-sm.c:745) + 02/10 10:39:26.966: [msaf] DEBUG: Adding 30ef86aa-a92f-41ed-870f-4501bf315b24 to current_content_hosting_configurations (../src/5gmsaf/msaf-sm.c:747) + ``` + +## Test HTTPS configuration and certificate sending + +This will test the ability of the Application Function to configure an Application Server via the interface at M3 with a simple +HTTPS (encrypted) distribution, SSL/TLS private key and SSL/TLS public certificate. For this test a ContentHostingConfiguration is used which has `certificateId` fields set in the `distributionConfigurations`. + +1. Stop the Application Function if it is already running. + +1. Create the self-signed certificates. + + ```bash + cd ~/rt-5gms-application-function + subprojects/rt-common-shared/5gms/scripts/make_self_signed_certs.py --af-conf=examples/Test_https_canonical-msaf.yaml + ``` + + (see the information about [generating test certificates](Configuring-the-Application-Function#generating-test-certificates) + for more details) + +1. Start the Application Function with the `Test_https_canonical-msaf.yaml` example configuration, e.g.: + + ```bash + ~/rt-5gms-application-function/install/bin/open5gs-msafd -c ~/rt-5gms-application-function/examples/Test_https_canonical-msaf.yaml + ``` + +1. The log output should indicate that the Application function: + 1. Requests a list of Server Certificates known by the Application Server (this is only done upon first communicating with an Application Server or when the Application Function needs to resynchronise the state from the Application Server) + + ``` + 02/10 10:39:26.792: [msaf] DEBUG: M3 client: Sending GET method to Application Server [localhost] to request the list of known certificates (../src/5gmsaf/application-server-context.c:151) + ``` + + 1. Receives the response + + ``` + 02/10 10:39:26.834: [msaf] DEBUG: [certificates] Method [GET] with Response [200] received (../src/5gmsaf/msaf-sm.c:1111) + ``` + + There may be log output following this with a list of certificates known to the Application Server. + + 1. Requests the list of known provisioning session ids on the Application Server for ContentHostingConfigurations + + ``` + 02/10 10:39:26.834: [msaf] DEBUG: M3 client: Sending GET method to Application Server [localhost] to request the list of known content-hosting-configurations (../src/5gmsaf/application-server-context.c:154) + ``` + + 1. Receives the response + + ``` + 02/10 10:39:26.837: [msaf] DEBUG: [content-hosting-configurations] Method [GET] with Response [200] for Content Hosting Configuration operation [(null)] (../src/5gmsaf/msaf-sm.c:888) + ``` + + There may be log output following this indicating a list of ContentHostingConfigurations known to the Application Server. + + 1. Pushes the private key and public certificate + + ``` + 02/10 10:39:26.838: [msaf] DEBUG: M3 client: Sending POST method to Application Server [localhost]for Certificate: [30ef86aa-a92f-41ed-870f-4501bf315b24:testcert1] (../src/5gmsaf/application-server-context.c:187 + ``` + + 1. Receives a success (201) response back, removes the Server Certificate from the upload queue and marks it as a current Server + Certificate for the Application Server + + ``` + 02/10 10:39:26.881: [msaf] DEBUG: [certificates] Method [POST] with Response [201] recieved for certificate [30ef86aa-a92f-41ed-870f-4501bf315b24:testcert1] (../src/5gmsaf/msaf-sm.c:947) + 02/10 10:39:26.881: [msaf] DEBUG: Removing certificate [30ef86aa-a92f-41ed-870f-4501bf315b24:testcert1] from upload_certificates (../src/5gmsaf/msaf-sm.c:958) + 02/10 10:39:26.881: [msaf] DEBUG: Adding certificate [30ef86aa-a92f-41ed-870f-4501bf315b24:testcert1] to current_certificates (../src/5gmsaf/msaf-sm.c:962) + ``` + + 1. Pushes a new ContentHostingConfiguration to the Application Server + + ``` + 02/10 10:39:26.881: [msaf] DEBUG: M3 client: Sending POST method to Application Server [localhost] for Content Hosting Configuration: [30ef86aa-a92f-41ed-870f-4501bf315b24] (../src/5gmsaf/application-server-context.c:235) + ``` + + 1. Receives a success (201) response back, removes the ContentHostingConfiguration from the upload queue and marks it as current configuration for the Application Server + + ``` + 02/10 10:39:26.966: [msaf] DEBUG: [content-hosting-configurations] Method [POST] with Response [201] recieved for Content Hosting Configuration [30ef86aa-a92f-41ed-870f-4501bf315b24] (../src/5gmsaf/msaf-sm.c:736) + 02/10 10:39:26.966: [msaf] DEBUG: Removing 30ef86aa-a92f-41ed-870f-4501bf315b24 from upload_content_hosting_configurations (../src/5gmsaf/msaf-sm.c:745) + 02/10 10:39:26.966: [msaf] DEBUG: Adding 30ef86aa-a92f-41ed-870f-4501bf315b24 to current_content_hosting_configurations (../src/5gmsaf/msaf-sm.c:747) + ``` + + 1. Using the ProvisioningSessionId from the log output, check the configuration works via the Application Server using the generated certificate: + + ```bash + curl --cacert ~/rt-5gms-application-function/examples/certificate-1.pem -v https://localhost/m4d/provisioning-session-30ef86aa-a92f-41ed-870f-4501bf315b24/BigBuckBunny_4s_onDemand_2014_05_09.mpd + ``` + + ...or if you followed the "instructions to run the AS as a local user" then the port number used in the request URL will need to change to 8443, e.g + + ```bash + curl --cacert ~/rt-5gms-application-function/examples/certificate-1.pem -v https://localhost:8443/m4d/provisioning-session-30ef86aa-a92f-41ed-870f-4501bf315b24/BigBuckBunny_4s_onDemand_2014_05_09.mpd + ``` + + This should succeed and fetch the DASH MPD file for Big Buck Bunny. + +## Test state synchronisation + +If the Application Function is restarted without also restarting the Application Server, then the Application Server will retain +configurations (certificates and ContentHostingConfigurations) from older runs. This allows us to observe the state synchronisation +between the Application Function and Application Server so that the Application Function knows whether it needs to create or update entries. + +Perform this test by repeating the [Test HTTPS configuration and certificate sending](#test-https-configuration-and-certificate-sending) test a few times (the create self-signed certificate step can be skipped after the first time). After each restart you will observe the list of certificates and ContentHostingConfigurations known by the Application Server increase. For example: + +``` +02/10 11:51:20.871: [msaf] DEBUG: M3 client: Sending GET method to Application Server [localhost] to request the list of known certificates (../src/5gmsaf/application-server-context.c:151) +... +02/10 11:51:20.913: [msaf] DEBUG: [certificates] Method [GET] with Response [200] received (../src/5gmsaf/msaf-sm.c:1111) +02/10 11:51:20.913: [msaf] DEBUG: Adding certificate [30ef86aa-a92f-41ed-870f-4501bf315b24:testcert1] to Current certificates (../src/5gmsaf/msaf-sm.c:1139) +02/10 11:51:20.913: [msaf] DEBUG: Adding certificate [b3e6c736-a938-41ed-b967-ff8e23a4f49f:testcert1] to Current certificates (../src/5gmsaf/msaf-sm.c:1139) +... +02/10 11:51:20.913: [msaf] DEBUG: M3 client: Sending GET method to Application Server [localhost] to request the list of known content-hosting-configurations (../src/5gmsaf/application-server-context.c:154) +... +02/10 11:51:20.916: [msaf] DEBUG: [content-hosting-configurations] Method [GET] with Response [200] for Content Hosting Configuration operation [(null)] (../src/5gmsaf/msaf-sm.c:888) +02/10 11:51:20.916: [msaf] DEBUG: Adding [30ef86aa-a92f-41ed-870f-4501bf315b24] to the current Content Hosting Configuration list (../src/5gmsaf/msaf-sm.c:913) +02/10 11:51:20.916: [msaf] DEBUG: Adding [b3e6c736-a938-41ed-b967-ff8e23a4f49f] to the current Content Hosting Configuration list (../src/5gmsaf/msaf-sm.c:913) +``` + +This shows the Application server already knows about: +- Certificates + - `30ef86aa-a92f-41ed-870f-4501bf315b24:testcert1` + - `b3e6c736-a938-41ed-b967-ff8e23a4f49f:testcert1` +- ContentHostingConfigurations + - `30ef86aa-a92f-41ed-870f-4501bf315b24` + - `b3e6c736-a938-41ed-b967-ff8e23a4f49f` + diff --git a/pages/5g-media-streaming/testing/testing-m3-v120.md b/pages/5g-media-streaming/testing/testing-m3-v120.md new file mode 100644 index 00000000..f3c62bbb --- /dev/null +++ b/pages/5g-media-streaming/testing/testing-m3-v120.md @@ -0,0 +1,231 @@ +--- +layout: default +title: Testing M3 AF v1.2.x +parent: Testing +grand_parent: 5G Downlink Media Streaming +has_children: false +nav_order: 10 +--- + +# Testing: M3 Interface (5GMSd Application Function v1.2.0 and above) + +To prepare, follow the instructions for [local user building and installation](Testing-as-a-Local-User). + +# Configuration + +The Application Function is an M3 Client and most of the communication is only logged at the `debug` level. To properly see the M3 Interface interactions the configuration will need to set the minimum logging level to `debug` for the `msaf` domain. + +To do this edit the installed `msaf.yaml` file and set the `logger` section to the following: + +```yaml +logger: + level: debug + domain: msaf +``` + +The `msaf.yaml` file in use by the Application Function is one of (in preference order): +- The file passed on the command line using the `-c` parameter. +- `${prefix}/etc/open5gs/msaf.yaml` + +The command for the tests will use the installed configuration file at `${prefix}/etc/open5gs/msaf.yaml`, if the instructions for [local user building and installation](Testing-as-a-Local-User) have been followed then this will be `~/rt-5gms-application-function/install/etc/open5gs/msaf.yaml`. You can optionally take a copy of this file and make the changes to a copy and then add the `-c` command line parameter and the path to your modified copy of the configuration file to the `open5gs-msafd` commands below. + +# Testing + +These tests require a [5GMSd Application Server](https://github.com/5G-MAG/rt-5gms-application-server) to be running. Please follow +the instructions to [build, install and run the 5GMSd Application Server](https://github.com/5G-MAG/rt-5gms-application-server#readme) as a system service or the [instructions to run the AS as a local user](https://github.com/5G-MAG/rt-5gms-application-server/wiki/Development-and-Testing) for a temporary installation for testing. + +## Test Simple HTTP Configuration + +This will test the ability of the Application Function to configure an Application Server via the interface at M3 with a simple +HTTP (unencrypted) distribution. For this test a ContentHostingConfiguration is used which has no `certificateId` fields set in any `distributionConfiguration`. + +1. Stop the Application Function if it is already running. + +1. Remove previous configurations: + + ```bash + rm -rf ~/rt-5gms-application-function/install/var/cache/rt-5gms/af/certificates + ``` + +1. Start the Application Function: + + ```bash + ~/rt-5gms-application-function/install/bin/open5gs-msafd + ``` + +1. Configure a simple HTTP only stream hosting session: + + ```bash + ~/rt-5gms-application-function/install/bin/m1-session new-stream -e MyAppId -a MyASPId -n 'Big Buck Bunny' 'https://ftp.itec.aau.at/datasets/DASHDataset2014/BigBuckBunny/4sec/' 'BigBuckBunny_4s_onDemand_2014_05_09.mpd' + ``` + +1. The log output should indicate that the Application function: + 1. Requests a list of Server Certificates known by the Application Server (this is only done upon first communicating with an Application Server or when the Application Function needs to resynchronise the state from the Application Server) + + ``` + 02/10 10:39:26.792: [msaf] DEBUG: M3 client: Sending GET method to Application Server [localhost] to request the list of known certificates (../src/5gmsaf/application-server-context.c:151) + ``` + + 1. Receives the response + + ``` + 02/10 10:39:26.834: [msaf] DEBUG: [certificates] Method [GET] with Response [200] received (../src/5gmsaf/msaf-sm.c:1111) + ``` + + There may be log output following this with a list of certificates known to the Application Server. + + 1. Requests the list of known provisioning session ids on the Application Server for ContentHostingConfigurations + + ``` + 02/10 10:39:26.834: [msaf] DEBUG: M3 client: Sending GET method to Application Server [localhost] to request the list of known content-hosting-configurations (../src/5gmsaf/application-server-context.c:154) + ``` + + 1. Receives the response + + ``` + 02/10 10:39:26.837: [msaf] DEBUG: [content-hosting-configurations] Method [GET] with Response [200] for Content Hosting Configuration operation [(null)] (../src/5gmsaf/msaf-sm.c:888) + ``` + + There may be log output following this indicating a list of ContentHostingConfigurations known to the Application Server. + + 1. Pushes a new ContentHostingConfiguration to the Application Server + + ``` + 02/10 10:39:26.881: [msaf] DEBUG: M3 client: Sending POST method to Application Server [localhost] for Content Hosting Configuration: [30ef86aa-a92f-41ed-870f-4501bf315b24] (../src/5gmsaf/application-server-context.c:235) + ``` + + 1. Receives a success (201) response back, removes the ContentHostingConfiguration from the upload queue and marks it as current configuration for the Application Server. + + ``` + 02/10 10:39:26.966: [msaf] DEBUG: [content-hosting-configurations] Method [POST] with Response [201] recieved for Content Hosting Configuration [30ef86aa-a92f-41ed-870f-4501bf315b24] (../src/5gmsaf/msaf-sm.c:736) + 02/10 10:39:26.966: [msaf] DEBUG: Removing 30ef86aa-a92f-41ed-870f-4501bf315b24 from upload_content_hosting_configurations (../src/5gmsaf/msaf-sm.c:745) + 02/10 10:39:26.966: [msaf] DEBUG: Adding 30ef86aa-a92f-41ed-870f-4501bf315b24 to current_content_hosting_configurations (../src/5gmsaf/msaf-sm.c:747) + ``` + +## Test HTTPS configuration and certificate sending + +This will test the ability of the Application Function to configure an Application Server via the interface at M3 with a simple +HTTPS (encrypted) distribution, SSL/TLS private key and SSL/TLS public certificate. For this test a ContentHostingConfiguration is used which has `certificateId` fields set in the `distributionConfigurations`. + +1. Stop the Application Function if it is already running. + +1. Remove previous configurations: + + ```bash + rm -rf ~/rt-5gms-application-function/install/var/cache/rt-5gms/af/certificates + ``` + +1. Start the Application Function: + + ```bash + ~/rt-5gms-application-function/install/bin/open5gs-msafd + ``` + +1. Configure a simple HTTPS only stream hosting session with self signed certificate: + + ```bash + ~/rt-5gms-application-function/install/bin/m1-session new-stream -e MyAppId -a MyASPId -n 'Big Buck Bunny' --ssl-only 'https://ftp.itec.aau.at/datasets/DASHDataset2014/BigBuckBunny/4sec/' 'BigBuckBunny_4s_onDemand_2014_05_09.mpd' + ``` + +1. The log output should indicate that the Application function: + 1. Requests a list of Server Certificates known by the Application Server (this is only done upon first communicating with an Application Server or when the Application Function needs to resynchronise the state from the Application Server) + + ``` + 02/10 10:39:26.792: [msaf] DEBUG: M3 client: Sending GET method to Application Server [localhost] to request the list of known certificates (../src/5gmsaf/application-server-context.c:151) + ``` + + 1. Receives the response + + ``` + 02/10 10:39:26.834: [msaf] DEBUG: [certificates] Method [GET] with Response [200] received (../src/5gmsaf/msaf-sm.c:1111) + ``` + + There may be log output following this with a list of certificates known to the Application Server. + + 1. Requests the list of known provisioning session ids on the Application Server for ContentHostingConfigurations + + ``` + 02/10 10:39:26.834: [msaf] DEBUG: M3 client: Sending GET method to Application Server [localhost] to request the list of known content-hosting-configurations (../src/5gmsaf/application-server-context.c:154) + ``` + + 1. Receives the response + + ``` + 02/10 10:39:26.837: [msaf] DEBUG: [content-hosting-configurations] Method [GET] with Response [200] for Content Hosting Configuration operation [(null)] (../src/5gmsaf/msaf-sm.c:888) + ``` + + There may be log output following this indicating a list of ContentHostingConfigurations known to the Application Server. + + 1. Pushes the private key and public certificate + + ``` + 02/10 10:39:26.838: [msaf] DEBUG: M3 client: Sending POST method to Application Server [localhost]for Certificate: [30ef86aa-a92f-41ed-870f-4501bf315b24:ea94917c-3726-45c5-9706-b6bb0be77e29] (../src/5gmsaf/application-server-context.c:187 + ``` + + 1. Receives a success (201) response back, removes the Server Certificate from the upload queue and marks it as a current Server + Certificate for the Application Server + + ``` + 02/10 10:39:26.881: [msaf] DEBUG: [certificates] Method [POST] with Response [201] recieved for certificate [30ef86aa-a92f-41ed-870f-4501bf315b24:ea94917c-3726-45c5-9706-b6bb0be77e29] (../src/5gmsaf/msaf-sm.c:947) + 02/10 10:39:26.881: [msaf] DEBUG: Removing certificate [30ef86aa-a92f-41ed-870f-4501bf315b24:ea94917c-3726-45c5-9706-b6bb0be77e29] from upload_certificates (../src/5gmsaf/msaf-sm.c:958) + 02/10 10:39:26.881: [msaf] DEBUG: Adding certificate [30ef86aa-a92f-41ed-870f-4501bf315b24:ea94917c-3726-45c5-9706-b6bb0be77e29] to current_certificates (../src/5gmsaf/msaf-sm.c:962) + ``` + + 1. Pushes a new ContentHostingConfiguration to the Application Server + + ``` + 02/10 10:39:26.881: [msaf] DEBUG: M3 client: Sending POST method to Application Server [localhost] for Content Hosting Configuration: [30ef86aa-a92f-41ed-870f-4501bf315b24] (../src/5gmsaf/application-server-context.c:235) + ``` + + 1. Receives a success (201) response back, removes the ContentHostingConfiguration from the upload queue and marks it as current configuration for the Application Server + + ``` + 02/10 10:39:26.966: [msaf] DEBUG: [content-hosting-configurations] Method [POST] with Response [201] recieved for Content Hosting Configuration [30ef86aa-a92f-41ed-870f-4501bf315b24] (../src/5gmsaf/msaf-sm.c:736) + 02/10 10:39:26.966: [msaf] DEBUG: Removing 30ef86aa-a92f-41ed-870f-4501bf315b24 from upload_content_hosting_configurations (../src/5gmsaf/msaf-sm.c:745) + 02/10 10:39:26.966: [msaf] DEBUG: Adding 30ef86aa-a92f-41ed-870f-4501bf315b24 to current_content_hosting_configurations (../src/5gmsaf/msaf-sm.c:747) + ``` + + 1. Using the ProvisioningSessionId from the log output, check the configuration works via the Application Server using the generated certificate: + + ```bash + curl -k -v https://localhost/m4d/provisioning-session-30ef86aa-a92f-41ed-870f-4501bf315b24/BigBuckBunny_4s_onDemand_2014_05_09.mpd + ``` + + ...or if you followed the "instructions to run the AS as a local user" then the port number used in the request URL will need to change to 8443, e.g + + ```bash + curl -k -v https://localhost:8443/m4d/provisioning-session-30ef86aa-a92f-41ed-870f-4501bf315b24/BigBuckBunny_4s_onDemand_2014_05_09.mpd + ``` + + This should succeed and fetch the DASH MPD file for Big Buck Bunny. + +## Test state synchronisation + +If the Application Function is restarted without also restarting the Application Server, then the Application Server will retain +configurations (certificates and ContentHostingConfigurations) from older runs. This allows us to observe the state synchronisation +between the Application Function and Application Server so that the Application Function knows whether it needs to create or update entries. + +Perform this test by performing the [Test HTTPS configuration and certificate sending](#test-https-configuration-and-certificate-sending) test and repeating step 4 (`m1-session` command) a few times, then perform the test once more from the beginning. You will observe the list of certificates and ContentHostingConfigurations known by the Application Server include the identifiers generated in the first run by the several `m1-session` commands. For example: + +``` +02/10 11:51:20.871: [msaf] DEBUG: M3 client: Sending GET method to Application Server [localhost] to request the list of known certificates (../src/5gmsaf/application-server-context.c:151) +... +02/10 11:51:20.913: [msaf] DEBUG: [certificates] Method [GET] with Response [200] received (../src/5gmsaf/msaf-sm.c:1111) +02/10 11:51:20.913: [msaf] DEBUG: Adding certificate [30ef86aa-a92f-41ed-870f-4501bf315b24:ea94917c-3726-45c5-9706-b6bb0be77e29] to Current certificates (../src/5gmsaf/msaf-sm.c:1139) +02/10 11:51:20.913: [msaf] DEBUG: Adding certificate [b3e6c736-a938-41ed-b967-ff8e23a4f49f:04844ed2-b8f5-4e22-b948-2b939fc2e21c] to Current certificates (../src/5gmsaf/msaf-sm.c:1139) +... +02/10 11:51:20.913: [msaf] DEBUG: M3 client: Sending GET method to Application Server [localhost] to request the list of known content-hosting-configurations (../src/5gmsaf/application-server-context.c:154) +... +02/10 11:51:20.916: [msaf] DEBUG: [content-hosting-configurations] Method [GET] with Response [200] for Content Hosting Configuration operation [(null)] (../src/5gmsaf/msaf-sm.c:888) +02/10 11:51:20.916: [msaf] DEBUG: Adding [30ef86aa-a92f-41ed-870f-4501bf315b24] to the current Content Hosting Configuration list (../src/5gmsaf/msaf-sm.c:913) +02/10 11:51:20.916: [msaf] DEBUG: Adding [b3e6c736-a938-41ed-b967-ff8e23a4f49f] to the current Content Hosting Configuration list (../src/5gmsaf/msaf-sm.c:913) +``` + +This example shows the Application server already knows about: +- Certificates + - `30ef86aa-a92f-41ed-870f-4501bf315b24:ea94917c-3726-45c5-9706-b6bb0be77e29` + - `b3e6c736-a938-41ed-b967-ff8e23a4f49f:04844ed2-b8f5-4e22-b948-2b939fc2e21c` +- ContentHostingConfigurations + - `30ef86aa-a92f-41ed-870f-4501bf315b24` + - `b3e6c736-a938-41ed-b967-ff8e23a4f49f` + diff --git a/pages/5g-media-streaming/testing/testing-m5-v100.md b/pages/5g-media-streaming/testing/testing-m5-v100.md new file mode 100644 index 00000000..77a5f74a --- /dev/null +++ b/pages/5g-media-streaming/testing/testing-m5-v100.md @@ -0,0 +1,306 @@ +--- +layout: default +title: Testing M5 AF v1.0.x +parent: Testing +grand_parent: 5G Downlink Media Streaming +has_children: false +nav_order: 11 +--- + +# Testing: M5 Interface (5GMSd Application Function v1.0.0 to v1.1.x) + +To prepare, follow the instructions for [local user building and installation](Testing-as-a-Local-User). + +# Configuration + +These tests use test configurations from the `examples` directory. Some of these example configuration will need self-signed public certificates generating, and appropriate instructions are included to run the generation script with the correct configuration where appropriate. + +The `msaf.yaml` file in use by the Application Function is one of (in preference order): +- The file passed on the command line using the `-c` parameter. +- `${prefix}/etc/open5gs/msaf.yaml` + +These tests use the `-c` command line parameter to override the default configuration location. + +See [Configuring the Application Function](Configuring-the-Application-Function) for more details on Application Function +configuration. + +# Testing + +This section will describe common tests and the expected outcomes for the M5 interface. + +**Note: At this time the only implemented part of the M5 interface is the ServiceAccessInformation API** + +## M5 API prefix + +The current implementation of the interface at M5 uses the URL prefix of `http://${msaf.sbi.addr}:${msaf.sbi.port}/3gpp-m5/v2/`, +no other versions (i.e. "v2" only) are implemented and attempts to use another version will result a 400 Bad Request error response. + +### Unsupported M5 API version + +Test URL: `http://${msaf.sbi.addr}:${msaf.sbi.port}/3gpp-m5/v1/service-access-information` +Example command: `curl -v 'http://127.0.0.1:7778/3gpp-m5/v1/service-access-information'` + +Expected result: +``` +> GET /3gpp-m5/v1/service-access-information/id HTTP/1.1 +> Host: 127.0.0.1:7778 +> User-Agent: curl/7.85.0 +> Accept: */* +> +< HTTP/1.1 400 Bad Request +< Date: Tue, 07 Feb 2023 11:46:59 GMT +< Connection: close +< Content-Type: application/problem+json +< Content-Length: 124 +< +{ + "type": "/3gpp-m5/v1", + "title": "Not supported version", + "status": 400, + "instance": "/service-access-information" +} +``` +**Note:** There may also be additional HTTP library information lines output by curl starting with a `*` character interspersed with the output. + +AF log output: +``` +02/07 11:46:59.263: [msaf] ERROR: Not supported version [v1] (../src/5gmsaf/msaf-sm.c:494) +``` + +The HTTP response is a 400 status code with a ProblemDetail JSON object in the body. The problem detail shows that the error +happened because the request contained an unsupported version. The application itself produces an `ERROR` log message indicating +that a client requested an unsupported version. + +## ServiceAccessInformation + +These tests describe the available actions and possible responses for the M5 ServiceAccessInformation APIs. + +Interface URL: `http://${msaf.sbi.addr}:${msaf.sbi.port}/3gpp-m5/v2/service-access-information/${provisioningSessionId}` + +Where `${provisioningSessionId}` is the provisioning session identifier for the provisioning session. In a full 5GMSd Application +Function this would come from the `Location` in the response on the interface at M1 to a createProvisioningSession API action. +However, in the current version of the 5GMSd Application Function there is only one provisioning session created at start up. The +currently valid provisioning session id can be found in the log output from the `open5gs-msafd` command in the line containing +"INFO: Provisioning session = ". + +### Testing Success + +#### Testing the unencrypted media entry point on the canonical hostname + +If the ContentHostingConfiguration does not contain any `distributionConfigurations.certificateId` or +`distributionConfigurations.domainNameAlias` properties then the `mediaPlayerEntry` in the ServiceAccessInformation will use the +"http" protocol and the canonical hostname of the 5GMSd Application Server as the authority part. + +1. Stop the AF if it is running. + +1. Start the AF with the `Test_http_canonical-msaf.yaml` example configuration, e.g.: + + ```bash + ~/rt-5gms-application-function/install/bin/open5gs-msafd -c ~/rt-5gms-application-function/examples/Test_http_canonical-msaf.yaml + ``` + +1. Using the provisioning session id from the AF log output perform a GET request on the interface URL. + + Example command if the provisioning session id is 0c5cc9e6-a6dd-41ed-ae90-e781c44d0f0c: + + ```bash + curl -v 'http://127.0.0.1:7778/3gpp-m5/v2/service-access-information/0c5cc9e6-a6dd-41ed-ae90-e781c44d0f0c' + ``` + + Expected result: + + ``` + > GET /3gpp-m5/v2/service-access-information/0c5cc9e6-a6dd-41ed-ae90-e781c44d0f0c HTTP/1.1 + > Host: 127.0.0.1:7778 + > User-Agent: curl/7.85.0 + > Accept: */* + > + < HTTP/1.1 200 OK + < Date: Tue, 07 Feb 2023 12:11:56 GMT + < Connection: close + < Content-Type: application/json + < Content-Length: 278 + < + { + "provisioningSessionId": "0c5cc9e6-a6dd-41ed-ae90-e781c44d0f0c", + "provisioningSessionType": "DOWNLINK", + "streamingAccess": { + "mediaPlayerEntry": "http://localhost/m4d/provisioning-session-0c5cc9e6-a6dd-41ed-ae90-e781c44d0f0c/BigBuckBunny_4s_onDemand_2014_05_09.mpd" + } + } + ``` + **Note:** There may also be additional HTTP library information lines output by curl starting with a `*` character interspersed with the output. + + The HTTP response is a 200 status code with the body containing a ServiceAccessInformation JSON object. The + `provisioningSessionId` in the ServiceAccessInformation object should match the id presented in the request URL. The + `provisioningSessionType` will be "DOWNLINK". The `mediaPlayerEntry` will be a "http://" URL for the canonical name set in the + `msaf.yaml` file in the `msaf.applicationServers.canonicalHostname` property, and a path starting with the value of the + `msaf.applicationServers.urlPathPrefixFormat` and ending in "`/BigBuckBunny_4s_onDemand_2014_05_09.mpd`" (which comes from the + `entryPointPath` in the ContentHostingConfiguration). + +#### Testing the encrypted media entry point on the canonical hostname + +If the ContentHostingConfiguration contains `distributionConfigurations.certificateId` properties then an "https://" will be used +for the `mediaPlayerEntry` in the ServiceAccessInformation. + +1. Stop the AF if it is running. + +1. Create the self signed certificate: + + ```bash + cd ~/rt-5gms-application-function + subprojects/rt-common-shared/5gms/scripts/make_self_signed_certs.py --af-conf=examples/Test_https_canonical-msaf.yaml + ``` + + (see the information about [generating test certificates](Configuring-the-Application-Function#generating-test-certificates) + for more details) + +1. Start the AF with the `Test_https_canonical-msaf.yaml` configuration, e.g.: + + ```bash + ~/rt-5gms-application-function/install/bin/open5gs-msafd -c ~/rt-5gms-application-function/examples/Test_https_canonical-msaf.yaml + ``` + +1. Using the provisioning session id from the AF log output perform a GET request on the interface URL. + + Example command if the provisioning session id is 0c5cc9e6-a6dd-41ed-ae90-e781c44d0f0c: + + ```bash + curl -v 'http://127.0.0.1:7778/3gpp-m5/v2/service-access-information/0c5cc9e6-a6dd-41ed-ae90-e781c44d0f0c' + ``` + + Expected result: + + ``` + > GET /3gpp-m5/v2/service-access-information/0c5cc9e6-a6dd-41ed-ae90-e781c44d0f0c HTTP/1.1 + > Host: 127.0.0.1:7778 + > User-Agent: curl/7.85.0 + > Accept: */* + > + < HTTP/1.1 200 OK + < Date: Tue, 07 Feb 2023 12:11:56 GMT + < Connection: close + < Content-Type: application/json + < Content-Length: 278 + < + { + "provisioningSessionId": "0c5cc9e6-a6dd-41ed-ae90-e781c44d0f0c", + "provisioningSessionType": "DOWNLINK", + "streamingAccess": { + "mediaPlayerEntry": "https://localhost/m4d/provisioning-session-0c5cc9e6-a6dd-41ed-ae90-e781c44d0f0c/BigBuckBunny_4s_onDemand_2014_05_09.mpd" + } + } + ``` + **Note:** There may also be additional HTTP library information lines output by curl starting with a `*` character interspersed with the output. + + The HTTP response is a 200 status code with the body containing a ServiceAccessInformation JSON object. The + `provisioningSessionId` in the ServiceAccessInformation object should match the id presented in the request URL. The + `provisioningSessionType` will be "DOWNLINK". The `mediaPlayerEntry` will be a "https://" URL (to show the AF is giving + preference to the HTTPS access point) for the canonical name `localhost` (set in the `Test_https_canonical-msaf.yaml` file in the + `msaf.applicationServers.canonicalHostname` property), and a path starting with the value of the + `msaf.applicationServers.urlPathPrefixFormat` and ending in "`/BigBuckBunny_4s_onDemand_2014_05_09.mpd`" (which comes from the + `entryPointPath` in the ContentHostingConfiguration). + +#### Testing the `mediaPlayerEntry` when providing a `domainNameAlias` + +If the ContentHostingConfiguration given in the `msaf.yaml` configuration file includes a +`distributionConfigurations.domainNameAlias` then this hostname will be used as the authority for the `mediaPlayerEntry` in the +ServiceAccessInformation in preference to the canonical hostname of the AS. + +To test this: +1. Stop the AF process. + +1. Start the AF with the `Test_http_domain_alias-msaf.yaml` configuration, e.g.: + + ```bash + ~/rt-5gms-application-function/install/bin/open5gs-msafd -c ~/rt-5gms-application-function/examples/Test_http_domain_alias-msaf.yaml + ``` + +1. Using the provisioning session id from the AF log output perform a GET request on the interface URL. + + Example command if the provisioning session id is 0c5cc9e6-a6dd-41ed-ae90-e781c44d0f0c: + + ```bash + curl -v 'http://127.0.0.1:7778/3gpp-m5/v2/service-access-information/0c5cc9e6-a6dd-41ed-ae90-e781c44d0f0c' + ``` + + Expected result: + + ``` + > GET /3gpp-m5/v2/service-access-information/0c5cc9e6-a6dd-41ed-ae90-e781c44d0f0c HTTP/1.1 + > Host: 127.0.0.1:7778 + > User-Agent: curl/7.85.0 + > Accept: */* + > + < HTTP/1.1 200 OK + < Date: Tue, 07 Feb 2023 12:11:56 GMT + < Connection: close + < Content-Type: application/json + < Content-Length: 278 + < + { + "provisioningSessionId": "0c5cc9e6-a6dd-41ed-ae90-e781c44d0f0c", + "provisioningSessionType": "DOWNLINK", + "streamingAccess": { + "mediaPlayerEntry": "https://media.example.com/m4d/provisioning-session-0c5cc9e6-a6dd-41ed-ae90-e781c44d0f0c/BigBuckBunny_4s_onDemand_2014_05_09.mpd" + } + } + ``` + **Note:** There may also be additional HTTP library information lines output by curl starting with a `*` character interspersed with the output. + + The HTTP response is a 200 status code with the body containing a ServiceAccessInformation JSON object. The + `provisioningSessionId` in the ServiceAccessInformation object should match the id presented in the request URL. The + `provisioningSessionType` will be "DOWNLINK". The `mediaPlayerEntry` will be a "http://" URL followed by the + `distributionConfigurations.domainNameAlias` property from the example + `ContentHostingConfiguration_Big-Buck-Bunny_domain-name_http.json` file, and a path starting with the value of the + `msaf.applicationServers.urlPathPrefixFormat` from the `Test_http_domain_alias-msaf.yaml` configuration file and ending in + "`/BigBuckBunny_4s_onDemand_2014_05_09.mpd`" (which comes from the `entryPointPath` in the ContentHostingConfiguration). + + +### Testing No Such Provisioning Session + +If the `${provisioningSessionId}` used in the request URL does not exist as an active provisioning session in the AF then the +HTTP response will be a 404 Not Found status code. + +To test this +1. Start the AF if it is not already running, e.g.: + + ```bash + ~/rt-5gms-application-function/install/bin/open5gs-msafd + ``` + +1. Using a bogus provisioning session id, perform a GET request on the interface URL. + + For example, if the provisioning session id is 0c5cc9e6-a6dd-41ed-ae90-e781c44d0f0c, then for this test we use a provisioning session id string that does not match that id. + + Example command using `does-not-exist` as a provisioning session id: + + ```bash + curl -v 'http://127.0.0.1:7778/3gpp-m5/v2/service-access-information/does-not-exist' + ``` + + Expected result: + + ``` + > GET /3gpp-m5/v2/service-access-information/does-not-exist HTTP/1.1 + > Host: 127.0.0.1:7778 + > User-Agent: curl/7.85.0 + > Accept: */* + > + < HTTP/1.1 404 Not Found + < Date: Tue, 07 Feb 2023 14:47:26 GMT + < Connection: close + < Content-Type: application/problem+json + < Content-Length: 208 + < + { + "type": "/3gpp-m5/v2", + "title": "Provisioning Session not found", + "status": 404, + "detail": "Provisioning Session [does-not-exist] not found.", + "instance": "/service-access-information/does-not-exist" + } + ``` + **Note:** There may also be additional HTTP library information lines output by curl starting with a `*` character interspersed with the output. + + The HTTP response is a 404 status code with the body containing a ProblemDetail JSON object. diff --git a/pages/5g-media-streaming/testing/testing-m5-v120.md b/pages/5g-media-streaming/testing/testing-m5-v120.md new file mode 100644 index 00000000..c34257ce --- /dev/null +++ b/pages/5g-media-streaming/testing/testing-m5-v120.md @@ -0,0 +1,334 @@ +--- +layout: default +title: Testing M5 AF v1.2.x +parent: Testing +grand_parent: 5G Downlink Media Streaming +has_children: false +nav_order: 12 +--- + +# Testing: M5 Interface (5GMSd Application Function v1.2.x) + +To prepare, follow the instructions for [local user building and installation](Testing-as-a-Local-User). + +# Configuration + +These tests use test configurations from the `examples` directory. Some of these example configuration will need self-signed public certificates generating, and appropriate instructions are included to run the generation script with the correct configuration where appropriate. + +The `msaf.yaml` file in use by the Application Function is one of (in preference order): +- The file passed on the command line using the `-c` parameter. +- `${prefix}/etc/open5gs/msaf.yaml` + +These tests use the `-c` command line parameter to override the default configuration location. + +See [Configuring the Application Function](Configuring-the-Application-Function) for more details on Application Function +configuration. + +# Testing + +This section will describe common tests and the expected outcomes for the M5 interface. + +**Note: At this time the only implemented part of the M5 interface is the ServiceAccessInformation API** + +## M5 API prefix + +The current implementation of the interface at M5 uses the URL prefix of `http://${msaf.m5.addr}:${msaf.m5.port}/3gpp-m5/v2/`, +no other versions (i.e. "v2" only) are implemented and attempts to use another version will result a 400 Bad Request error response. + +### Unsupported M5 API version + +Test URL: `http://${msaf.m5.addr}:${msaf.m5.port}/3gpp-m5/v1/service-access-information` +Example command: `curl -v 'http://127.0.0.24:7777/3gpp-m5/v1/service-access-information'` + +Expected result: +``` +> GET /3gpp-m5/v1/service-access-information HTTP/1.1 +> Host: 127.0.0.24:7777 +> User-Agent: curl/7.85.0 +> Accept: */* +> +< HTTP/1.1 400 Bad Request +< Date: Tue, 07 Feb 2023 11:46:59 GMT +< Connection: close +< Content-Type: application/problem+json +< Content-Length: 124 +< +{ + "type": "/3gpp-m5/v1", + "title": "Not supported version", + "status": 400, + "instance": "/service-access-information" +} +``` +**Note:** There may also be additional HTTP library information lines output by curl starting with a `*` character interspersed with the output. + +AF log output: +``` +02/07 11:46:59.263: [msaf] ERROR: Not supported version [v1] (../src/5gmsaf/msaf-sm.c:494) +``` + +The HTTP response is a 400 status code with a ProblemDetail JSON object in the body. The problem detail shows that the error +happened because the request contained an unsupported version. The application itself produces an `ERROR` log message indicating +that a client requested an unsupported version. + +## ServiceAccessInformation + +These tests describe the available actions and possible responses for the M5 ServiceAccessInformation APIs. + +Interface URL: `http://${msaf.m5.addr}:${msaf.m5.port}/3gpp-m5/v2/service-access-information/${provisioningSessionId}` + +Where `${provisioningSessionId}` is the provisioning session identifier for the provisioning session. This URL comes from the `Location` in the response on the interface at M1 to a createProvisioningSession API action, and is displayed by the `m1-session` tool when a `new-provisioning-session` or `new-stream` command is successful. + +### Testing Success + +#### Testing the unencrypted media entry point on the canonical hostname + +If the ContentHostingConfiguration does not contain any `distributionConfigurations.certificateId` or +`distributionConfigurations.domainNameAlias` properties then the `mediaPlayerEntry` in the ServiceAccessInformation will use the +"http" protocol and the canonical hostname of the 5GMSd Application Server as the authority part. + +1. Stop the AF if it is running. + +1. Clean up old persistent data: + + ```bash + rm -rf ~/rt-5gms-application-function/install/var/cache/rt-5gms/af/certificates + ``` + +1. Start the AF: + + ```bash + ~/rt-5gms-application-function/install/bin/open5gs-msafd + ``` + +1. Configure a simple stream: + + ```bash + ~/rt-5gms-application-function/install/bin/m1-session new-stream -e AppId -n 'Simple Stream' 'https://ftp.itec.aau.at/datasets/DASHDataset2014/BigBuckBunny/4sec/' 'BigBuckBunny_4s_onDemand_2014_05_09.mpd' + ``` + +1. Using the provisioning session id, returned from the `m1-session` command, perform a GET request on the interface URL. + + Example command if the provisioning session id is 0c5cc9e6-a6dd-41ed-ae90-e781c44d0f0c: + + ```bash + curl -v 'http://127.0.0.24:7777/3gpp-m5/v2/service-access-information/0c5cc9e6-a6dd-41ed-ae90-e781c44d0f0c' + ``` + + Expected result: + + ``` + > GET /3gpp-m5/v2/service-access-information/0c5cc9e6-a6dd-41ed-ae90-e781c44d0f0c HTTP/1.1 + > Host: 127.0.0.24:7777 + > User-Agent: curl/7.85.0 + > Accept: */* + > + < HTTP/1.1 200 OK + < Date: Tue, 07 Feb 2023 12:11:56 GMT + < Connection: close + < Content-Type: application/json + < Content-Length: 278 + < + { + "provisioningSessionId": "0c5cc9e6-a6dd-41ed-ae90-e781c44d0f0c", + "provisioningSessionType": "DOWNLINK", + "streamingAccess": { + "mediaPlayerEntry": "http://localhost/m4d/provisioning-session-0c5cc9e6-a6dd-41ed-ae90-e781c44d0f0c/BigBuckBunny_4s_onDemand_2014_05_09.mpd" + } + } + ``` + **Note:** There may also be additional HTTP library information lines output by curl starting with a `*` character interspersed with the output. + + The HTTP response is a 200 status code with the body containing a ServiceAccessInformation JSON object. The + `provisioningSessionId` in the ServiceAccessInformation object should match the id presented in the request URL. The + `provisioningSessionType` will be "DOWNLINK". The `mediaPlayerEntry` will be a "http://" URL for the canonical name set in the + `msaf.yaml` file in the `msaf.applicationServers.canonicalHostname` property, and a path starting with the value of the + `msaf.applicationServers.urlPathPrefixFormat` and ending in "`/BigBuckBunny_4s_onDemand_2014_05_09.mpd`" (which comes from the + `entryPointPath` in the `m1-session` command). + +#### Testing the encrypted media entry point on the canonical hostname + +If the ContentHostingConfiguration contains `distributionConfigurations.certificateId` properties then an "https://" will be used +for the `mediaPlayerEntry` in the ServiceAccessInformation. + +1. Stop the AF if it is running. + +1. Clean up old persistent data: + + ```bash + rm -rf ~/rt-5gms-application-function/install/var/cache/rt-5gms/af/certificates + ``` + +1. Start the AF: + + ```bash + ~/rt-5gms-application-function/install/bin/open5gs-msafd + ``` + +1. Configure a simple stream with self-signed HTTPS distribution: + + ```bash + ~/rt-5gms-application-function/install/bin/m1-session new-stream -e AppId -n 'Simple HTTPS Stream' --with-ssl 'https://ftp.itec.aau.at/datasets/DASHDataset2014/BigBuckBunny/4sec/' 'BigBuckBunny_4s_onDemand_2014_05_09.mpd' + ``` + +1. Using the provisioning session id, returned from the `m1-session` command, perform a GET request on the interface URL + + Example command if the provisioning session id is 0c5cc9e6-a6dd-41ed-ae90-e781c44d0f0c: + + ```bash + curl -v 'http://127.0.0.24:7777/3gpp-m5/v2/service-access-information/0c5cc9e6-a6dd-41ed-ae90-e781c44d0f0c' + ``` + + Expected result: + + ``` + > GET /3gpp-m5/v2/service-access-information/0c5cc9e6-a6dd-41ed-ae90-e781c44d0f0c HTTP/1.1 + > Host: 127.0.0.24:7777 + > User-Agent: curl/7.85.0 + > Accept: */* + > + < HTTP/1.1 200 OK + < Date: Tue, 07 Feb 2023 12:11:56 GMT + < Connection: close + < Content-Type: application/json + < Content-Length: 278 + < + { + "provisioningSessionId": "0c5cc9e6-a6dd-41ed-ae90-e781c44d0f0c", + "provisioningSessionType": "DOWNLINK", + "streamingAccess": { + "mediaPlayerEntry": "https://localhost/m4d/provisioning-session-0c5cc9e6-a6dd-41ed-ae90-e781c44d0f0c/BigBuckBunny_4s_onDemand_2014_05_09.mpd" + } + } + ``` + **Note:** There may also be additional HTTP library information lines output by curl starting with a `*` character interspersed with the output. + + The HTTP response is a 200 status code with the body containing a ServiceAccessInformation JSON object. The + `provisioningSessionId` in the ServiceAccessInformation object should match the id presented in the request URL. The + `provisioningSessionType` will be "DOWNLINK". The `mediaPlayerEntry` will be a "https://" URL (to show the AF is giving + preference to the HTTPS access point) for the canonical name `localhost` (set in the `msaf.yaml` file in the + `msaf.applicationServers.canonicalHostname` property), and a path starting with the value of the + `msaf.applicationServers.urlPathPrefixFormat` and ending in "`/BigBuckBunny_4s_onDemand_2014_05_09.mpd`" (which comes from the + `entryPointPath` in the `m1-session` command). + +#### Testing the `mediaPlayerEntry` when providing a `domainNameAlias` + +If the ContentHostingConfiguration given in the `msaf.yaml` configuration file includes a +`distributionConfigurations.domainNameAlias` then this hostname will be used as the authority for the `mediaPlayerEntry` in the +ServiceAccessInformation in preference to the canonical hostname of the AS. + +To test this: +1. Stop the AF process. + +1. Clean up old persistent data: + + ```bash + rm -rf ~/rt-5gms-application-function/install/var/cache/rt-5gms/af/certificates + ``` + +1. Start the AF: + + ```bash + ~/rt-5gms-application-function/install/bin/open5gs-msafd + ``` + +1. Configure a simple stream with self-signed HTTPS distribution: + + ```bash + ~/rt-5gms-application-function/install/bin/m1-session new-stream -e AppId -n 'Simple HTTPS Stream' -d 'media.example.com' 'https://ftp.itec.aau.at/datasets/DASHDataset2014/BigBuckBunny/4sec/' 'BigBuckBunny_4s_onDemand_2014_05_09.mpd' + ``` + +1. Using the provisioning session id, returned from the `m1-session` command, perform a GET request on the interface URL + + Example command if the provisioning session id is 0c5cc9e6-a6dd-41ed-ae90-e781c44d0f0c: + + ```bash + curl -v 'http://127.0.0.24:7777/3gpp-m5/v2/service-access-information/0c5cc9e6-a6dd-41ed-ae90-e781c44d0f0c' + ``` + + Expected result: + + ``` + > GET /3gpp-m5/v2/service-access-information/0c5cc9e6-a6dd-41ed-ae90-e781c44d0f0c HTTP/1.1 + > Host: 127.0.0.24:7777 + > User-Agent: curl/7.85.0 + > Accept: */* + > + < HTTP/1.1 200 OK + < Date: Tue, 07 Feb 2023 12:11:56 GMT + < Connection: close + < Content-Type: application/json + < Content-Length: 278 + < + { + "provisioningSessionId": "0c5cc9e6-a6dd-41ed-ae90-e781c44d0f0c", + "provisioningSessionType": "DOWNLINK", + "streamingAccess": { + "mediaPlayerEntry": "https://media.example.com/m4d/provisioning-session-0c5cc9e6-a6dd-41ed-ae90-e781c44d0f0c/BigBuckBunny_4s_onDemand_2014_05_09.mpd" + } + } + ``` + **Note:** There may also be additional HTTP library information lines output by curl starting with a `*` character interspersed with the output. + + The HTTP response is a 200 status code with the body containing a ServiceAccessInformation JSON object. The + `provisioningSessionId` in the ServiceAccessInformation object should match the id presented in the request URL. The + `provisioningSessionType` will be "DOWNLINK". The `mediaPlayerEntry` will be a "http://" URL followed by the + `media.example.com` FQDN given with the `-d` command line argument to the `m1-session` command, and a path starting with the + value of the `msaf.applicationServers.urlPathPrefixFormat` from the `msaf.yaml` configuration file and ending in + "`/BigBuckBunny_4s_onDemand_2014_05_09.mpd`" (which comes from the `entryPointPath` given on the `m1-session` command line). + + +### Testing No Such Provisioning Session + +If the `${provisioningSessionId}` used in the request URL does not exist as an active provisioning session in the AF then the +HTTP response will be a 404 Not Found status code. + +To test this +1. Start the AF if it is not already running, e.g.: + + ```bash + ~/rt-5gms-application-function/install/bin/open5gs-msafd + ``` + +1. Configure a simple stream: + + ```bash + ~/rt-5gms-application-function/install/bin/m1-session new-stream -e AppId -n 'Simple Stream' 'https://ftp.itec.aau.at/datasets/DASHDataset2014/BigBuckBunny/4sec/' 'BigBuckBunny_4s_onDemand_2014_05_09.mpd' + ``` + +1. Using a bogus provisioning session id, i.e. anything except the provisioning session id returned by the `m1-session` command, + perform a GET request on the interface URL. + + For example, if the provisioning session id is 0c5cc9e6-a6dd-41ed-ae90-e781c44d0f0c, then for this test we use a provisioning session id string that does not match that id. + + Example command using `does-not-exist` as a provisioning session id: + + ```bash + curl -v 'http://127.0.0.24:7777/3gpp-m5/v2/service-access-information/does-not-exist' + ``` + + Expected result: + + ``` + > GET /3gpp-m5/v2/service-access-information/does-not-exist HTTP/1.1 + > Host: 127.0.0.24:7777 + > User-Agent: curl/7.85.0 + > Accept: */* + > + < HTTP/1.1 404 Not Found + < Date: Tue, 07 Feb 2023 14:47:26 GMT + < Connection: close + < Content-Type: application/problem+json + < Content-Length: 208 + < + { + "type": "/3gpp-m5/v2", + "title": "Provisioning Session not found", + "status": 404, + "detail": "Provisioning Session [does-not-exist] not found.", + "instance": "/service-access-information/does-not-exist" + } + ``` + **Note:** There may also be additional HTTP library information lines output by curl starting with a `*` character interspersed with the output. + + The HTTP response is a 404 status code with the body containing a ProblemDetail JSON object. diff --git a/pages/5g-media-streaming/testing/testing-m5-v130.md b/pages/5g-media-streaming/testing/testing-m5-v130.md new file mode 100644 index 00000000..8ae6ab70 --- /dev/null +++ b/pages/5g-media-streaming/testing/testing-m5-v130.md @@ -0,0 +1,368 @@ +--- +layout: default +title: Testing M5 AF v1.3.x +parent: Testing +grand_parent: 5G Downlink Media Streaming +has_children: false +nav_order: 13 +--- + +# Testing: M5 Interface (5GMSd Application Function v1.3.0 and above) + +To prepare, follow the instructions for [local user building and installation](Testing-as-a-Local-User). + +# Configuration + +These tests use test configurations from the `examples` directory. Some of these example configuration will need self-signed public certificates generating, and appropriate instructions are included to run the generation script with the correct configuration where appropriate. + +The `msaf.yaml` file in use by the Application Function is one of (in preference order): +- The file passed on the command line using the `-c` parameter. +- `${prefix}/etc/open5gs/msaf.yaml` + +These tests use the `-c` command line parameter to override the default configuration location. + +See [Configuring the Application Function](Configuring-the-Application-Function) for more details on Application Function +configuration. + +# Testing + +This section will describe common tests and the expected outcomes for the M5 interface. + +**Note: At this time the only implemented part of the M5 interface is the ServiceAccessInformation API** + +## M5 API prefix + +The current implementation of the interface at M5 uses the URL prefix of `http://${msaf.m5.addr}:${msaf.m5.port}/3gpp-m5/v2/`, +no other versions (i.e. "v2" only) are implemented and attempts to use another version will result a 400 Bad Request error response. + +### Unsupported M5 API version + +Test URL: `http://${msaf.m5.addr}:${msaf.m5.port}/3gpp-m5/v1/service-access-information` +Example command: `curl -v 'http://127.0.0.24:7777/3gpp-m5/v1/service-access-information'` + +Expected result: +``` +> GET /3gpp-m5/v1/service-access-information HTTP/1.1 +> Host: 127.0.0.24:7777 +> User-Agent: curl/7.85.0 +> Accept: */* +> +< HTTP/1.1 400 Bad Request +< Date: Tue, 07 Feb 2023 11:46:59 GMT +< Connection: close +< Content-Type: application/problem+json +< Content-Length: 124 +< +{ + "type": "/3gpp-m5/v1", + "title": "Not supported version", + "status": 400, + "instance": "/service-access-information" +} +``` +**Note:** There may also be additional HTTP library information lines output by curl starting with a `*` character interspersed with the output. + +AF log output: +``` +02/07 11:46:59.263: [msaf] ERROR: Not supported version [v1] (../src/5gmsaf/msaf-sm.c:494) +``` + +The HTTP response is a 400 status code with a ProblemDetail JSON object in the body. The problem detail shows that the error +happened because the request contained an unsupported version. The application itself produces an `ERROR` log message indicating +that a client requested an unsupported version. + +## ServiceAccessInformation + +These tests describe the available actions and possible responses for the M5 ServiceAccessInformation APIs. + +Interface URL: `http://${msaf.m5.addr}:${msaf.m5.port}/3gpp-m5/v2/service-access-information/${provisioningSessionId}` + +Where `${provisioningSessionId}` is the provisioning session identifier for the provisioning session. This URL comes from the `Location` in the response on the interface at M1 to a createProvisioningSession API action, and is displayed by the `m1-session` tool when a `new-provisioning-session` or `new-stream` command is successful. + +### Testing Success + +#### Testing the unencrypted media entry point on the canonical hostname + +If the ContentHostingConfiguration does not contain any `distributionConfigurations.certificateId` or +`distributionConfigurations.domainNameAlias` properties then the `mediaPlayerEntry` in the ServiceAccessInformation will use the +"http" protocol and the canonical hostname of the 5GMSd Application Server as the authority part. + +1. Stop the AF if it is running. + +1. Clean up old persistent data: + + ```bash + rm -rf ~/rt-5gms-application-function/install/var/cache/rt-5gms/af/certificates + ``` + +1. Start the AF: + + ```bash + ~/rt-5gms-application-function/install/bin/open5gs-msafd + ``` + +1. Configure a simple stream: + + ```bash + ~/rt-5gms-application-function/install/bin/m1-session new-stream -e AppId -n 'Simple Stream' 'https://ftp.itec.aau.at/datasets/DASHDataset2014/BigBuckBunny/4sec/' 'BigBuckBunny_4s_onDemand_2014_05_09.mpd' + ``` + +1. Using the provisioning session id, returned from the `m1-session` command, perform a GET request on the interface URL. + + Example command if the provisioning session id is 0c5cc9e6-a6dd-41ed-ae90-e781c44d0f0c: + + ```bash + curl -v 'http://127.0.0.24:7777/3gpp-m5/v2/service-access-information/0c5cc9e6-a6dd-41ed-ae90-e781c44d0f0c' + ``` + + Expected result: + + ``` + > GET /3gpp-m5/v2/service-access-information/f863831c-daa8-41ed-862b-c1f4c44bccf3 HTTP/1.1 + > Host: 127.0.0.24:7777 + > User-Agent: curl/7.85.0 + > Accept: */* + > + < HTTP/1.1 200 OK + < Date: Fri, 14 Apr 2023 09:44:47 GMT + < Connection: close + < Content-Type: application/json + < ETag: 38548172d8eca143db6b4b11af8df6dea66d62acb45f751c9348f257c9682165 + < Last-Modified: Fri, 14 Apr 2023 09:44:37 GMT + < Cache-Control: max-age=60 + < Server: 5GMSdAF-localhost/17 (info.title=M5_ServiceAccessInformation; info.version=2.2.0) rt-5gms-application-function/1.3.0 + < Content-Length: 339 + { + "provisioningSessionId": "f863831c-daa8-41ed-862b-c1f4c44bccf3", + "provisioningSessionType": "DOWNLINK", + "streamingAccess": { + "entryPoints": [{ + "locator": "http://localhost/m4d/provisioning-session-f863831c-daa8-41ed-862b-c1f4c44bccf3/BigBuckBunny_4s_onDemand_2014_05_09.mpd", + "contentType": "application/dash+xml" + }] + } + } + ``` + **Note:** There may also be additional HTTP library information lines output by curl starting with a `*` character interspersed with the output. + + The HTTP response is a 200 status code with the body containing a ServiceAccessInformation JSON object. The + `provisioningSessionId` in the ServiceAccessInformation object should match the id presented in the request URL. The + `provisioningSessionType` will be "DOWNLINK". The `mediaPlayerEntry` will be a "http://" URL for the canonical name set in the + `msaf.yaml` file in the `msaf.applicationServers.canonicalHostname` property, and a path starting with the value of the + `msaf.applicationServers.urlPathPrefixFormat` and ending in "`/BigBuckBunny_4s_onDemand_2014_05_09.mpd`" (which comes from the + `entryPointPath` in the `m1-session` command). + +#### Testing the encrypted media entry point on the canonical hostname + +If the ContentHostingConfiguration contains `distributionConfigurations.certificateId` properties then an "https://" will be used +for the `mediaPlayerEntry` in the ServiceAccessInformation. + +1. Stop the AF if it is running. + +1. Clean up old persistent data: + + ```bash + rm -rf ~/rt-5gms-application-function/install/var/cache/rt-5gms/af/certificates + ``` + +1. Start the AF: + + ```bash + ~/rt-5gms-application-function/install/bin/open5gs-msafd + ``` + +1. Configure a simple stream with self-signed HTTPS distribution: + + ```bash + ~/rt-5gms-application-function/install/bin/m1-session new-stream -e AppId -n 'Simple HTTPS Stream' --with-ssl 'https://ftp.itec.aau.at/datasets/DASHDataset2014/BigBuckBunny/4sec/' 'BigBuckBunny_4s_onDemand_2014_05_09.mpd' + ``` + +1. Using the provisioning session id, returned from the `m1-session` command, perform a GET request on the interface URL + + Example command if the provisioning session id is 30c20cea-daab-41ed-87fe-93ba2b9b790a: + + ```bash + curl -v 'http://127.0.0.24:7777/3gpp-m5/v2/service-access-information/30c20cea-daab-41ed-87fe-93ba2b9b790a' + ``` + + Expected result should look like: + + ``` + > GET /3gpp-m5/v2/service-access-information/30c20cea-daab-41ed-87fe-93ba2b9b790a HTTP/1.1 + > Host: 127.0.0.24:7777 + > User-Agent: curl/7.85.0 + > Accept: */* + > + < HTTP/1.1 200 OK + < Date: Fri, 14 Apr 2023 10:00:40 GMT + < Connection: close + < ETag: 8c12228d39cd5e015fdab5e7972084a6bbd12747b4c482e0cdc1ce82bbac597e + < Last-Modified: Fri, 14 Apr 2023 10:00:31 GMT + < Cache-Control: max-age=60 + < Server: 5GMSdAF-localhost/17 (info.title=M5_ServiceAccessInformation; info.version=2.2.0) rt-5gms-application-function/1.3.0 + < Content-Type: application/json + < Content-Length: 340 + < + { + "provisioningSessionId": "30c20cea-daab-41ed-87fe-93ba2b9b790a", + "provisioningSessionType": "DOWNLINK", + "streamingAccess": { + "entryPoints": [{ + "locator": "https://localhost/m4d/provisioning-session-30c20cea-daab-41ed-87fe-93ba2b9b790a/BigBuckBunny_4s_onDemand_2014_05_09.mpd", + "contentType": "application/dash+xml" + }] + } + } + ``` + **Note:** There may also be additional HTTP library information lines output by curl starting with a `*` character interspersed with the output. + + The HTTP response is a 200 status code with the body containing a ServiceAccessInformation JSON object. The + `provisioningSessionId` in the ServiceAccessInformation object should match the id presented in the request URL. The + `provisioningSessionType` will be "DOWNLINK". The `mediaPlayerEntry` will be a "https://" URL (to show the AF is giving + preference to the HTTPS access point) for the canonical name `localhost` (set in the `msaf.yaml` file in the + `msaf.applicationServers.canonicalHostname` property), and a path starting with the value of the + `msaf.applicationServers.urlPathPrefixFormat` and ending in "`/BigBuckBunny_4s_onDemand_2014_05_09.mpd`" (which comes from the + `entryPointPath` in the `m1-session` command). + +#### Testing the `mediaPlayerEntry` when providing a `domainNameAlias` + +If the ContentHostingConfiguration given in the `msaf.yaml` configuration file includes a +`distributionConfigurations.domainNameAlias` then this hostname will be used as the authority for the `mediaPlayerEntry` in the +ServiceAccessInformation in preference to the canonical hostname of the AS. + +To test this: +1. Stop the AF process. + +1. Clean up old persistent data: + + ```bash + rm -rf ~/rt-5gms-application-function/install/var/cache/rt-5gms/af/certificates + ``` + +1. Start the AF: + + ```bash + ~/rt-5gms-application-function/install/bin/open5gs-msafd + ``` + +1. Configure a simple stream with self-signed HTTPS distribution: + + ```bash + ~/rt-5gms-application-function/install/bin/m1-session new-stream -e AppId -n 'Simple HTTPS Stream' -d 'media.example.com' 'https://ftp.itec.aau.at/datasets/DASHDataset2014/BigBuckBunny/4sec/' 'BigBuckBunny_4s_onDemand_2014_05_09.mpd' + ``` + +1. Using the provisioning session id, returned from the `m1-session` command, perform a GET request on the interface URL + + Example command if the provisioning session id is efbff530-daab-41ed-87fe-93ba2b9b790a: + + ```bash + curl -v 'http://127.0.0.24:7777/3gpp-m5/v2/service-access-information/efbff530-daab-41ed-87fe-93ba2b9b790a' + ``` + + Expected result: + + ``` + > GET /3gpp-m5/v2/service-access-information/efbff530-daab-41ed-87fe-93ba2b9b790a HTTP/1.1 + > Host: 127.0.0.24:7777 + > User-Agent: curl/7.85.0 + > Accept: */* + > + < HTTP/1.1 200 OK + < Date: Fri, 14 Apr 2023 10:06:01 GMT + < Connection: close + < ETag: f5dd70a781efeefed1c034bbe640119019e1a46a55dcf50cfcccae8a323139ad + < Last-Modified: Fri, 14 Apr 2023 10:05:51 GMT + < Cache-Control: max-age=60 + < Server: 5GMSdAF-localhost/17 (info.title=M5_ServiceAccessInformation; info.version=2.2.0) rt-5gms-application-function/1.3.0 + < Content-Type: application/json + < Content-Length: 347 + < + { + "provisioningSessionId": "efbff530-daab-41ed-87fe-93ba2b9b790a", + "provisioningSessionType": "DOWNLINK", + "streamingAccess": { + "entryPoints": [{ + "locator": "http://media.example.com/m4d/provisioning-session-efbff530-daab-41ed-87fe-93ba2b9b790a/BigBuckBunny_4s_onDemand_2014_05_09.mpd", + "contentType": "application/dash+xml" + }] + } + } + ``` + **Note:** There may also be additional HTTP library information lines output by curl starting with a `*` character interspersed with the output. + + The HTTP response is a 200 status code with the body containing a ServiceAccessInformation JSON object. The + `provisioningSessionId` in the ServiceAccessInformation object should match the id presented in the request URL. The + `provisioningSessionType` will be "DOWNLINK". The `mediaPlayerEntry` will be a "http://" URL followed by the + `media.example.com` FQDN given with the `-d` command line argument to the `m1-session` command, and a path starting with the + value of the `msaf.applicationServers.urlPathPrefixFormat` from the `msaf.yaml` configuration file and ending in + "`/BigBuckBunny_4s_onDemand_2014_05_09.mpd`" (which comes from the `entryPointPath` given on the `m1-session` command line). + + +### Testing No Such Provisioning Session + +If the `${provisioningSessionId}` used in the request URL does not exist as an active provisioning session in the AF then the +HTTP response will be a 404 Not Found status code. + +To test this +1. Start the AF if it is not already running, e.g.: + + ```bash + ~/rt-5gms-application-function/install/bin/open5gs-msafd + ``` + +1. Configure a simple stream: + + ```bash + ~/rt-5gms-application-function/install/bin/m1-session new-stream -e AppId -n 'Simple Stream' 'https://ftp.itec.aau.at/datasets/DASHDataset2014/BigBuckBunny/4sec/' 'BigBuckBunny_4s_onDemand_2014_05_09.mpd' + ``` + +1. Using a bogus provisioning session id, i.e. anything except the provisioning session id returned by the `m1-session` command, + perform a GET request on the interface URL. + + For example, if the provisioning session id is 0c5cc9e6-a6dd-41ed-ae90-e781c44d0f0c, then for this test we use a provisioning session id string that does not match that id. + + Example command using `does-not-exist` as a provisioning session id: + + ```bash + curl -v 'http://127.0.0.24:7777/3gpp-m5/v2/service-access-information/does-not-exist' + ``` + + Expected result: + + ``` + > GET /3gpp-m5/v2/service-access-information/does-not-exist HTTP/1.1 + > Host: 127.0.0.24:7777 + > User-Agent: curl/7.85.0 + > Accept: */* + > + < HTTP/1.1 404 Not Found + < Date: Tue, 07 Feb 2023 14:47:26 GMT + < Connection: close + < Server: 5GMSdAF-localhost/17 (info.title=M5_ServiceAccessInformation; info.version=2.2.0) rt-5gms-application-function/1.3.0 + < Content-Type: application/problem+json + < Content-Length: 208 + < + { + "type": "/3gpp-m5/v2", + "title": "Provisioning Session not found", + "status": 404, + "detail": "Provisioning Session [does-not-exist] not found.", + "instance": "/service-access-information/does-not-exist" + } + ``` + **Note:** There may also be additional HTTP library information lines output by curl starting with a `*` character interspersed with the output. + + The HTTP response is a 404 status code with the body containing a ProblemDetail JSON object. + +## Consumption Reporting + +**TODO!** + +## Network Assistance + +**TODO!** + +## Dynamic Policies + +**TODO!** + diff --git a/pages/5g-media-streaming/testing/testing-postman.md b/pages/5g-media-streaming/testing/testing-postman.md new file mode 100644 index 00000000..016b9411 --- /dev/null +++ b/pages/5g-media-streaming/testing/testing-postman.md @@ -0,0 +1,74 @@ +--- +layout: default +title: Testing with Postman +parent: Testing +grand_parent: 5G Downlink Media Streaming +has_children: false +nav_order: 5 +--- + +# Testing with Postman +[Postman](https://www.postman.com/) is a popular API development and testing tool that allows users to create, send, and manage HTTP requests. It provides a user-friendly interface for building and testing API endpoints, making it easier for developers to collaborate and streamline the API development process. Postman comes in very handy when testing and working with the `M1` and `M5` interfaces of the Application Function. + +# Importing the 5G-MAG Postman Collection +Postman provides an easy way to export and import a collection of REST calls. To facilitate getting started, you can download our pre-defined Postman collections and environment here: + +* [5G-MAG M1.postman_collection.json](https://github.com/5G-MAG/rt-5gms-application-function/files/14064359/5G-MAG.M1.postman_collection.json) +* [5G-MAG M5.postman_collection.json](https://github.com/5G-MAG/rt-5gms-application-function/files/14062693/5G-MAG.M5.postman_collection.json) +* [5G-MAG.postman_environment.json](https://github.com/5G-MAG/rt-5gms-application-function/files/14062716/5G-MAG.postman_environment.json) + + +After the download, open Postman and select `File->Import`. After a successful import, you should see two collections like this: + +Bildschirmfoto 2024-01-26 um 09 07 00 + +# Postman Configuration +A very useful feature of Postman is the possibility to define variables. These variables can afterward be used in any of our routes or payloads. The first variables we need to define are the `m1_url` and `m5_url` variables. For that reason, select `Environments` on the left side and then select the `5G-MAG` environment. Now you should see a list of variables similar to this: + +Bildschirmfoto 2024-01-26 um 09 15 05 + +Add the URL to the `M1` and the `M5` endpoint of your Application Function here. In the example above, we are running the Application Function in a local network. Note that both URLs also need to contain the port. The configuration options for the Application Function are documented [here](https://github.com/5G-MAG/rt-5gms-application-function/wiki/Configuring-the-Application-Function). + +# Using M1 +Now that we have defined the `M1` endpoint, we can start using it: + +## Creating a Provisioning Session +Navigate to the `Provisioning Session` folder in the Postman Collection and select `Create Provisioning Session`. Check the URL on the top that we are sending the request to: `{{m1_url}}/3gpp-m1/v2/provisioning-sessions`. Here you can see that we are using the `m1_url` variable that we defined earlier. If the endpoint changes at some point we only need to change our variable instead of all the M1 calls in our Postman collection. + +Click on `Send` on the top right. You should see a successful response (status code `201`) and the payload of the response on the bottom: + +Bildschirmfoto 2024-01-26 um 09 28 04 + + +The response body contains the `provisioningSessionId` of our created provisioning session. The `provisioningSessionId` is an important identifier and used in many of the `M1` and `M5` endpoints. Consequently, it makes sense to assign the `provisioningSessionId` to a variable as well. Our collection contains a small script that automatically assigns the `provisioningSessionId` value of the response body to the `provisioning_session_id` variable: + +````js +var responseBody = pm.response.json(); +var provisioningSessionId = responseBody.provisioningSessionId; +pm.environment.set("provisioning_session_id", provisioningSessionId); +```` + +## Retrieving a Provisioning Session +Now that we have created a provisioning session and assigned its `provisioningSessionId` to our variable, we can directly call the `GET Provisioning Session` endpoint. As expected, our route contains the `m1_url` and the `provisioning_session_id` variable: `{{m1_url}}/3gpp-m1/v2/provisioning-sessions/{{provisioning_session_id}}`. + +Sending this request should result in a response similar to this: +Bildschirmfoto 2024-01-26 um 09 36 19 + +## Deleting a Provisioning Session +Deleting a provisioning session is straight forward as well. As we already defined the required variables, you can simply execute the call and should receive a `202 Accepted` response. + +## Consumption Reporting +To manage consumption reporting configurations, proceed similarly to managing provisioning sessions. Note that the `create` and `update` functions contain a `JSON` structure in the payload to define the required parameters. + +## Policy Templates +To manage policy templates, proceed similarly to managing provisioning sessions. Note that the `create` and `update` functions contain a `JSON` structure in the payload to define the required parameters. Moreover, `open5gsIntegration` must be enabled in the configuration of the Application Function. For more details, refer to the [Configuration Guide](https://github.com/5G-MAG/rt-5gms-application-function/wiki/Configuring-the-Application-Function). + +After successfully creating a policy template, the new `policyTemplateId` contained in the `Location` header is automatically assigned to the `policy_template_id` variable: + +````js +var policyTemplateId = pm.response.headers.get("Location").split("/").pop() +pm.environment.set("policy_template_id", policyTemplateId); +```` + +# Using M5 +The `M5` routes to query the Service Access Information, send a Consumption Report, create a Dynamic Policy resource and provision a new Network Assistance Session can be found in the `5G-MAG M5` folder. There is no additional configuration required, the routes for `M5` use the variables that we defined earlier as part of the configuration and the execution of `M1` endpoints. diff --git a/pages/5g-media-streaming/tutorials/configuration-5GMSAF.md b/pages/5g-media-streaming/tutorials/configuration-5GMSAF.md new file mode 100644 index 00000000..e8703261 --- /dev/null +++ b/pages/5g-media-streaming/tutorials/configuration-5GMSAF.md @@ -0,0 +1,437 @@ +--- +layout: default +title: Configuration 5GMS AF +parent: Tutorials +grand_parent: 5G Downlink Media Streaming +has_children: false +nav_order: 1 +--- + +# Configuration of the 5GMSd Application Function + +The configuration file for the 5GMSd Application function contains several settings + +## File Locations + +The configuration file for the 5GMSd Application Function can be found in `${prefix}/etc/open5gs/msaf.yaml`, where `${prefix}` is +the prefix used in the `meson` command (or `/usr/local` by default). By default this means that the configuration file will be +found in `/usr/local/etc/open5gs/msaf.yaml`. + +The location of the configuration file can be overridden at run-time by using the `-c` command line parameter with the +`open5gs-msafd` command, for example: + +```bash +/usr/local/bin/open5gs-msafd -c local-5gmsaf-config.yaml +``` + +## Configuration File Format + +The configuration file is written in [YAML](https://yaml.org/) 1.1 file format. + +Various settings in the file control things like logging output, listening and connection addresses, details of associated 5GMSd +Application Servers and other presets. + +### Notation + +Throughout this documentation we refer to the location of these configuration properties using a '`.`' separated path. Where a property name contains a `.` character it will be quoted using double quotes. For eaxmple, take the following snippet of YAML: + +```yaml +YAML: + files contain: + - version 1.1: textual value +``` + +We would refer to the field holding the value '`textual value`' as `YAML.files contain."version 1.1"`. + +### Configuration Structure + +The following are the properties in the 5GMSd Application Function configuration file and typical/default values: + +```yaml +logging: + level: info + domain: msaf + +sbi: + server: + no_tls: true + client: + no_tls: true + +msaf: + open5gsIntegration: false + sbi: + - addr: 0.0.0.0 + port: 7778 + m1: # Added in v1.2.0 + - addr: 0.0.0.0 # Added in v1.2.0 + port: 7778 # Added in v1.2.0 + m5: # Added in v1.2.0 + - addr: 0.0.0.0 # Added in v1.2.0 + port: 7778 # Added in v1.2.0 + maf: # Added in v1.2.0 + - addr: 127.0.0.25 # Added in v1.2.0 + port: 7777 # Added in v1.2.0 + applicationServers: + - canonicalHostname: localhost + urlPathPrefixFormat: /m4d/provisioning-session-{provisioningSessionId}/ + m3Host: localhost # Added in v1.4.0 + m3Port: 7777 # Added in v1.1.0 + certificate: examples/CertificatesIndex.json # Removed in v1.2.0 + contentHostingConfiguration: examples/ContentHostingConfiguration.json # Removed in v1.2.0 + provisioningSessionId: 12345678-9abc-def0-123456789abc # Removed in v1.1.0 + certificateManager: /usr/local/libexec/rt-5gms/af/self-signed-certmgr # Added in v1.2.0 + serverResponseCacheControl: # Added in v1.2.0 + - maxAge: 60 # Added in v1.2.0 + m1ProvisioningSessions: 60 # Added in v1.2.0 + m1ContentHostingConfigurations: 60 # Added in v1.2.0 + m1ServerCertificates: 60 # Added in v1.2.0 + m1ContentProtocols: 86400 # Added in v1.2.0 + m5ServiceAccessInformation: 60 # Added in v1.2.0 + dataCollectionDir: /usr/local/var/log/open5gs/reports # Added in v1.4.0 + offerNetworkAssistance: false # Added in v1.4.0 + networkAssistance: # Added in v1.4.0 + deliveryBoost: # Added in v1.4.0 + minDlBitRate: 1 Mbps # Added in v1.4.0 + boostPeriod: 30 # Added in v1.4.0 + +nrf: + sbi: + - addr: + - 127.0.0.10 + - ::1 + port: 7777 + +bsf: + notificationListener: # Added in v1.4.0 + - addr: + - 127.0.0.99 + port: 7779 + +parameter: + no_ipv4: false + no_ipv6: false + prefer_ipv4: false + +time: + nf_instance: + heartbeat: 0 + message: + duration: 10000 + +``` + +### Referencing other files + +The configuration file may also reference other files. If such a reference is made then the reference is either an absolute path or a relative path to file. If it is a relative path then it is relative to the location of the configuration file. + +**Warning: Be careful when moving a `msaf.yaml` configuration file to a different directory in the filesystem as any relative +pathed properties will no longer point to the correct location and will either need to be changed or the referenced files will also need to be moved too to maintain their relative paths.** + +The following properties may optionally contain relative paths: +- `msaf.certificates` - This is the JSON certificates index, see [Certificates Index](#certificates-index) for more details. +- `msaf.contentHostingConfiguration` - This is a ContentHostingConfiguration JSON object used to configure the 5GMSd Application + Server, see [ContentHostingConfiguration file](#contenthostingconfiguration-file) for more details. + +## Configuration Properties + +This section lists the properties in the configuration file, their use and valid values. + +### Logging + +**Location(s):** `logging.level` and `logging.domain` + +#### Logging Level + +The `logging.level` indicates the minimum level of logging messages that will be output. The default is `info`. + +Allowable values are: `trace`, `debug`, `info`, `warn`, `error`, `fatal` and `none` + +When `logging.domain` is also set, then this setting only affects the minimum logging level for the listed domains. + +#### Logging Domain + +The `logging.domain` property limits the affect of the `logging.level` property. This contains a comma separated list of domains. + +Domains relevant to the 5GMSd Application Function are: +- `msaf` - 5GMSd Application Function specific logging +- `sbi` - Open5GS SBI library functions for Client/Server operations. +- `app` - Open5GS App library functions for generic Open5GS application routines. +- `core` - Open5GS Core functions (e.g. low level socket handling, textual parsing and formatting, and memory management routines). + +#### Suggested Logging Configurations + +To reduce logging to warnings, errors and fatal messages only: + +```yaml +logging: + level: warn +``` + +To stop all logging: + +```yaml +logging: + level: none +``` + +To turn debugging output on for the 5GMSd Application Function: + +```yaml +logging: + level: debug + domain: msaf +``` + +### SBI interface security + +**Location(s):** `sbi.server.no_tls`, `sbi.server.cacert`, `sbi.server.key`, `sbi.server.cert`, `sbi.client.no_tls`, `sbi.client.cacert`, `sbi.client.key` and `sbi.client.cert` + +The `sbi.server.no_tls` indicates whether TLS should be configured for SBI server listening sockets. If `false` then no TLS is configured and the `sbi.server.cacert`, `sbi.server.key` and `sbi.server.cert` properties are ignored. If the `sbi.server.no_tls` property is `true` then the server will use: +- `sbi.server.cacert` is the filename of a file containing a CA bundle, in PEM format, to use to authenticate client public certificates. +- `sbi.server.key` is the filename of the private key, in PEM format, which is used for TLS connections for server sockets. +- `sbi.server.cert` is the filename of the public certificate, in PEM format, that will be presented as server authentication for TLS connections for server sockets. + +The `sbi.client.no_tls` indicates whether TLS should be configured for outgoing client requests to other NFs. If `false` then no TLS is configured and the `sbi.client.cacert`, `sbi.client.key` and `sbi.client.cert` properties are ignored. If the `sbi.client.no_tls` property is `true` then outgoing client requests will use: +- `sbi.client.cacert` is the filename of a file containing a CA bundle, in PEM format, to use to authenticate the server certificate presented by the remote NF. +- `sbi.client.key` is the filename of a private key, in PEM format, to use for encrypting the client communications. +- `sbi.client.cert` is the filename of the public certificate, in PEM format, to use for authenticating the client with the NF. + +### Open5GS integration + +**Location(s):** `msaf.open5gsIntegration` + +This is a boolean value (`true` or `false`) which indicates whether or not the 5GMSd Application Function will attempt to register +with a 5G Core, specifically the NRF. + +If this value is `true` then the `nrf` properties will describe the connection information for the NRF to register with. See +[NRF Connection](#nrf-connection) for more details. + +This is required to be `true` if you wish to use the Network Assistance or Dynamic Policies features. + +### SBI and default Interface Listening Address + +**Location(s):** `msaf.sbi.addr` and `msaf.sbi.port` + +The listening IP address is set in `msaf.sbi.addr` and the TCP port number is set in `msaf.sbi.port`. + +This address is used for SBI communications with other 5G Core Network Functions. It is also the default address for M1, M5 and +Management interfaces if they are not explicitly set in the configuration. + +### M1 Interface Listening Address + +**Location(s):** `msaf.m1.addr` and `msaf.m1.port` +**Version:** v1.2.0 and above + +The listening IP address is set in `msaf.m1.addr` and the TCP port number is set in `msaf.m1.port`. + +This is the communication address for external Application Service Providers to configure their services via the interface at +reference point M1. + +### M5 Interface Listening Address + +**Location(s):** `msaf.m5.addr` and `msaf.m5.port` +**Version:** v1.2.0 and above + +The listening IP address is set in `msaf.m5.addr` and the TCP port number is set in `msaf.m5.port`. + +This is the communication address for the 5GMSd Aware Application on the User Equipment implementing the interface at reference +point M5. + +### 5GMS AF Management Interface Listening Address + +**Location(s):** `msaf.maf.addr` and `msaf.maf.port` +**Version:** v1.2.0 and above + +The listening IP address is set in `msaf.maf.addr` and the TCP port number is set in `msaf.maf.port`. + +This is a communication interface for local 5GMSd Application Function management. This is not specified in TS 26.512 and is an +extra interface for this implementation. This should always be listening on a secure network, e.g. localhost only. + +### Application Servers + +**Location(s):** `msaf.applicationServers` + +This property is a list of associated 5GMSd Application Servers. Each entry in the list must contain `canonicalHostname`, +`urlPathPrefixFormat` and `m3Port` (since v1.1.0) properties. + +The `canonicalHostname` is the hostname or IP address of the Application Server. This is used to generate `mediaPlayerEntry` URLs for the ServiceAccessInformation objects available at interface M5 if no `domainNameAlias` is given in the ContentHostingConfiguration. From v1.1.0 of the Application Function, this is also the address that the Application Function will use for interface M3 communication with the Application Server. + +The `urlPathPrefixFormat` is a template string that is used to set the first part of the URL path for distributions from the Application Server at interface M4. Where the template contains `{provisioningSessionId}` will be replaced by the provisioning session Id that is associated with the ContentHostingConfiguration. + +The host name which is used for contacting the Application Server on the interface at M3 will be the `canonicalHostname` unless an `m3Host` property is defined for the application server, in which case the value of `m3Host` will be used as the host name to contact the Application Server at. + +The TCP port at which the Application Server interface at M3 is listening is defined by the `m3Port` property. The combination of `canonicalHostname` or `m3Host` and `m3Port` identifies the connection address that the Application Function will use. This property is only available from v1.1.0 onwards. + +Example: +```yaml +msaf: + applicationServers: + - canonicalHostname: ext-as.example.com + urlPathPrefixFormat: /{provisioningSessionId}/ + m3Host: localhost + m3Port: 7777 +``` + +### Data Collection (Consumption Reporting) + +**Location(s):** `msaf.dataCollectionDir` +**Versions:** v1.4.0 and above + +The Consumption Reporting feature uses a data collection directory to store sent reports in. The path for the data collection root can be set in `msaf.dataCollectionDir`. If not set then consumption reports are discarded after being received. There is no house-keeping for this directory, so an external house-keeping process must be used to free up disk space. + +### Network Assistance + +**Location(s):** `msaf.open5gsIntegration`, `msaf.offerNetworkAssistance`, `msaf.networkAssistance`, `nrf.sbi` and `bsf.notificationListener` +**Versions:** v1.4.0 and above + +The Network Assistance feature requires both `msaf.open5gsIntegration` and `msaf.offerNetworkAssistance` to be `true` (or `yes`) to activate. The `nrf` settings must also point to a valid NRF NF, and will be used to find a BSF and/or the PCF which is handling the policies for the UE. The `bsf.notificationListener` sets the address and optional port that will be used to listen for BSF notifications. + +When active, this feature will be advertised as available via the Service Access Information objects returned from requests at interface M5. + +The Delivery Boost Network Assistance feature will request a guaranteed minimum bit rate for a period of time on behalf of the client. The minimum bit rate used and the period of time are both configurable by setting the values for `msaf.networkAssistance.deliveryBoost.minDlBitRate` and `msaf.networkAssistance.deliveryBoost.boostPeriod` respectively. The `msaf.networkAssistance.deliveryBoost.minDlBitRate` must be expressed as a BitRate string according to TS 29.571, this is a decimal number followed by the bit rate units (`bps`, `Kbps`, `Mbps`, `Gbps` or `Tbps`), e.g. "0.5 Mbps" or "60 Kbps". The `msaf.networkAssistance.deliveryBoost.boostPeriod` is the integer number of seconds the boost will be activated for. These default to 1 Mbps for 30 seconds. + +Example of active Network Assistance: +```yaml +msaf: + open5gsIntegration: yes + offerNetworkAssistance: yes + networkAssistance: + deliveryBoost: + minDlBitRate: 1 Mbps + boostPeriod: 30 + +nrf: + sbi: + - addr: 127.0.0.10 + port: 7777 + +bsf: + notificationListener: + - addr: 127.0.0.99 +``` + +### Dynamic Policies + +**Location(s):** `msaf.open5gsIntegration`, `nrf.sbi` and `bsf.notificationListener` +**Versions:** v1.4.0 and above + +This feature requires the `msaf.open5gsIntegration` to be `true` (or `yes`) and the `nrf` settings to point to a valid NRF NF. The `bsf.notificationListener` sets the address and optional port that will be used to listen for BSF notifications. + +Example of active Network Assistance: +```yaml +msaf: + open5gsIntegration: yes + +nrf: + sbi: + - addr: 127.0.0.10 + port: 7777 + +bsf: + notificationListener: + - addr: 127.0.0.99 + port: 9000 +``` + +### ContentHostingConfiguration file + +**Note:** This setting is removed from v1.2.0 onwards + +**Location(s):** `msaf.contentHostingConfiguration` +**Version:** Up to and including v1.1, removed in v1.2.0 onwards + +The hosting configuration to use comes from a ContentHostingConfiguration JSON object held in an external file. The file-path to this JSON file is stored in the `msaf.contentHostingConfiguration` property using an absolute or relative (to the `msaf.yaml` configuration file) file-path. + +The ContentHostingConfiguration determines the configuration of the 5GMSd Application Server including which certificates and keys to send to the Application Server. + +### Certificates Index + +**Note:** This setting will be removed from v1.2.0 onwards + +**Location(s):** `msaf.certificates` +**Version:** Up to and including v1.1, removed in v1.2.0 onwards + +To test the distribution of media via secure HTTPS, the 5GMSd Application Function (and at runtime the 5GMSd Application Server which +will provide the hosting of the media) needs to be configured with one or more private key and public certificate pairs that can be +referenced from the ContentHostingConfiguration. + +To provide this configuration the 5GMSd Application Function, an index file is used to map certificateId values to a file containing the private key, public certificate and optionally any intermediate CA public certificates, all in PEM format. The index file itself contains a JSON object where the keys are the certificateIds as found in the COntentHostingConfiguration files and the values are the relative (to the index file) or absolute path to the PEM file. + +The filename of this JSON index file is stored in the `msaf.certificates` property of the `msaf.yaml` configuration file as a relative (to the configuration file) or absolute path. + +For a utility to quickly generate self-signed test certificates for a configuration please see the section below on [Generating Test Certificates](#generating-test-certificates). + +### Provisioning Session Id + +**Note:** This setting is removed from v1.1.0 onwards + +**Location(s):** `msaf.provisioningSessionId` +**Version:** Up to and including v1.0, removed in v1.1.0 onwards + +This setting provides the ProvisioningSessionId to use for MVP#2. This is here so that the Application Function and Application +Server can be synchronised with the same identifier in their respective configurations. + +This is the only ProvisioningSessionId the MVP#2 5GMSd Application Function will use. + +### Certificate Manager Program + +**Location(s):** `msaf.certificateManager` +**Version:** From version v1.2.0 onwards + +This is the path of an external program that can manage certificates and request signing of certificates. The example certificate +manager program will produce simple self signed certificates and will be the default certificate manager program set in the +installed configuration. + +The Certificate Manager program follows the "External Certificate Management" specification from the ["Implement M1 Server Certificates Provisionign API" issue on github](https://github.com/5G-MAG/rt-5gms-application-function/issues/17). + +### Default caching ages + +**Location(s):** `msaf.serverResponseCacheControl.maxAge`, `msaf.serverResponseCacheControl.m1ProvisioningSessions`, + `msaf.serverResponseCacheControl.m1ContentHostingConfigurations`, + `msaf.serverResponseCacheControl.m1ServerCertificates`, `msaf.serverResponseCacheControl.m1ContentProtocols`, + `msaf.serverResponseCacheControl.m5ServiceAccessInformation` +**Version:** From version v1.2.0 onwards + +These configuration values give the caching time in seconds signalled with responses in the different interfaces. + +| Parameter | Purpose | +| --- | --- | +| `maxAge` | The default `max-age` caching value. | +| `m1ProvisioningSessions` | The `max-age` for responses for the M1 ProvisioningSessions API. | +| `m1ContentHostingConfigurations` | The `max-age` for responses for the M1 ContentHostingProvisioning API. | +| `m1ServerCertificates` | The `max-age` for responses for the M1 ServerCertificatesProvisioning API. | +| `m1ContentProtocols` | The `max-age` for responses for the M1 ContentProtocolsDiscovery API. | +| `m5ServiceAccessInformation` | The `max-age` for responses for the M5 ServiceAccessInformation API. | + +## Generating Test Certificates + +**Note:** These instructions are not needed from v1.2.0 onwards as certificates are dynamically generated +and signed. + +To make the generation of certificates easier for testing, a script is available in the `~/rt-5gms-application-function/subprojects/rt-common-shared/5gms/scripts/make_self_signed_certs.py` which will generate an appropriate set of self-signed test certificates. This script requires the `python3` and `openssl` commands to be present, and the Python 3 YAML module. The script executes in the `python3` environment and uses the `openssl` command to generate the PEM certificate and private key file. + +If `openssl`, `python3` or Python3 yaml module are not already installed then they can be installed from your system package manager, for example: + +**Ubuntu/Debian and deviratives** +```bash +sudo apt -y install openssl python3 python3-yaml +``` + +**RedHat/CentOS/Fedora/Rocky and derivatives** +```bash +sudo dnf -y install openssl python3 python3-pyyaml +``` + +The `make_self_signed_certs.py` script takes either an Application Function configuration file path or two command line parameters, the first is the file-path for a ContentHostingConfiguration JSON file representing the output of the Application Function and the +second is the file-path of a certificates index JSON file. When using this script with the Application Function it is easier to use +the former command syntax. + +The script will examine the Application Function configuration file and extract the ContentHostingConfiguration. This is then examined for the certificateIds that are referenced and will create the private key and public certificate in PEM format at the file-path referenced from the certificates index JSON file. The generated certificates are self-signed, valid for 30 days and will include the `canonicalHostame` from the AF configuration and any `domainNameAlias` values from the ContentHostingConfiguration as Subject Alternative Names for the generated certificates. + +For example: + +```bash +~/rt-5gms-application-function/subprojects/rt-common-shared/5gms/scripts/make_self_signed_certs.py \ + --af-conf=/usr/local/etc/open5gs/msaf.yaml +``` diff --git a/pages/5g-media-streaming/tutorials/end-to-end.md b/pages/5g-media-streaming/tutorials/end-to-end.md index b07a66aa..23b7b85d 100644 --- a/pages/5g-media-streaming/tutorials/end-to-end.md +++ b/pages/5g-media-streaming/tutorials/end-to-end.md @@ -69,7 +69,7 @@ msaf: As we installed the AF as a local user, we start it with the following command: ```` -~/usr/local/bin/open5gs-msafd +/usr/local/bin/open5gs-msafd ```` #### Creating a content hosting configuration diff --git a/pages/5g-multicast-broadcast-services/specifications.md b/pages/5g-multicast-broadcast-services/specifications.md index 306c816c..787ed445 100644 --- a/pages/5g-multicast-broadcast-services/specifications.md +++ b/pages/5g-multicast-broadcast-services/specifications.md @@ -5,6 +5,12 @@ parent: 5G Multicast Broadcast Services has_children: false nav_order: 0 --- -# 📑 Specifications and relevant references -* Information about relevant specifications can be found at the [Standards Wiki](https://github.com/5G-MAG/Standards/wiki/5G-Multicast-Broadcast-Services-(5MBS):-Relevant-Specifications) -* A list of relevant 3GPP Work Items can be found at [Standards Wiki](https://github.com/5G-MAG/Standards/wiki/5G-Multicast-Broadcast-Services-(5MBS):-Relevant-Work-Items) +# 5G Multicast Broadcast Services +## 📑 Specifications and relevant references +* Information about relevant specifications can be found in this [page](https://5g-mag.github.io/Standards/pages/5g-multicast-broadcast-services/5g-multicast-broadcast-services-specifications.html) + +## 📑 Relevant Work Items +* A list of relevant 3GPP Work Items can be found in this [page](https://5g-mag.github.io/Standards/pages/5g-multicast-broadcast-services/5g-multicast-broadcast-services-workitems.html) + +## 📑 Guidelines and Profiles +* A quick guide on MBS Broadcast RAN Procedure can be found in this [page](https://5g-mag.github.io/Standards/pages/5g-multicast-broadcast-services/mbs-broadcast-RAN.html) diff --git a/pages/emergency-alerts/repositories.md b/pages/emergency-alerts/repositories.md index 4f622e8e..85084ed6 100644 --- a/pages/emergency-alerts/repositories.md +++ b/pages/emergency-alerts/repositories.md @@ -7,3 +7,6 @@ nav_order: 3 --- # ⭐ Related repositories + +## 5G Broadcast Transmitter for QRD and CRD (with support for Emergency Alerts): [rt-mbms-tx-for-qrd-and-crd](https://github.com/5G-MAG/rt-mbms-tx-for-qrd-and-crd/tree/emergency-alerts) +* [Information and how to download, build, install and run](https://github.com/5G-MAG/rt-mbms-tx-for-qrd-and-crd/tree/emergency-alerts/README.md) diff --git a/pages/lte-based-5g-broadcast/quick-start-guide.md b/pages/lte-based-5g-broadcast/quick-start-guide.md index ccf08c0a..34dbebd1 100644 --- a/pages/lte-based-5g-broadcast/quick-start-guide.md +++ b/pages/lte-based-5g-broadcast/quick-start-guide.md @@ -11,7 +11,7 @@ nav_order: 0 ## Setup Resources * [Hardware, OS & SDR Requirements](hardware-requirements.md) * [Sample Files](sample-files.md) -* [Service Announcement Formats](https://github.com/5G-MAG/rt-common-shared/wiki/MBMS-Service-Announcement-Files) +* [Service Announcement Formats](rt-common-shared/MBMS-service-announcement-files.md) ## Preparation After each reboot of your machine run: diff --git a/pages/lte-based-5g-broadcast/repositories.md b/pages/lte-based-5g-broadcast/repositories.md index 302f23b3..5fb6c477 100644 --- a/pages/lte-based-5g-broadcast/repositories.md +++ b/pages/lte-based-5g-broadcast/repositories.md @@ -24,6 +24,10 @@ nav_order: 3 * [Releases](https://github.com/5G-MAG/rt-mbms-mw/releases) * [Docker](https://github.com/5G-MAG/rt-mbms-mw/tree/development/middleware) +## MBMS Middleware for Android: [rt-mbms-mw-android](https://github.com/5G-MAG/rt-mbms-mw-android) +* [Information and how to download, build, install and run](https://github.com/5G-MAG/rt-mbms-mw-android#readme) +* [Releases](https://github.com/5G-MAG/rt-mbms-mw-android/releases) + ## Tools common to various projects: [rt-common-shared](https://github.com/5G-MAG/rt-common-shared) * [Information and how to download, build, install and run](https://github.com/5G-MAG/rt-common-shared#readme) diff --git a/pages/lte-based-5g-broadcast/rt-common-shared/MBMS-service-announcement-files.md b/pages/lte-based-5g-broadcast/rt-common-shared/MBMS-service-announcement-files.md new file mode 100644 index 00000000..c9f6adab --- /dev/null +++ b/pages/lte-based-5g-broadcast/rt-common-shared/MBMS-service-announcement-files.md @@ -0,0 +1,535 @@ +--- +layout: default +title: MBMS Service Announcement Files +parent: Tutorials +grand_parent: MBMS and LTE-based 5G Broadcast +has_children: false +nav_order: 0 +--- + +# MBMS Service Announcement Files + +The `ServiceAnnouncement(SA)` file also referred to as `bootstrap.multipart` in the context of 5G-MAG Reference Tools contains important information about the available broadcast and unicast streams. The 5G-MAG Reference Tools support three main formats. The target format needs to be configured before starting the `rt-mbms-mw` process as an automated format detection at runtime is currently not supported. + +Examples of the different SA formats can be found in the [rt-common-shared project](https://github.com/5G-MAG/rt-common-shared/tree/feature/mbms/mbms/bootstrap_examples). + +## Configuration of the format +The target format can directly be set in `/etc/5gmag-rt.conf` file: + +```` +mw: { + bootstrap_format: "" +} +```` + +Possible values are: +* `default`: Format used for the original implementation of seamless switching +* `5gmag_bc_uc`: Format agreed on by 5G-MAG while reviewing the `default` format +* `5gmag_legacy`: Format used for the sample recordings + +## Default format +The default format is the one that was used for the original seamless switching implementation implemented in close coordination between ORS and Rohde&Schwarz: + +```` +MIME-Version: 1.0 +Content-Type: multipart/related; boundary="++++++++++++++++++++++++Rohde&Schwarz-BSCC++++++++++++++++++++++++--"; type="application/mbms-envelope+xml" +Content-Description: LTE MBMS Service Announcement + +--++++++++++++++++++++++++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 238.1.1.111/127 +b=AS:2000 +m=application 40101 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:6 +#EXT-X-STREAM-INF:BANDWIDTH=2305600,RESOLUTION=1280x720,FRAME-RATE=25.000,CODECS="avc1.64001f,mp4a.40.2" +stream_0.m3u8 + +--++++++++++++++++++++++++Rohde&Schwarz-BSCC++++++++++++++++++++++++-- +Content-Type: application/vnd.apple.mpegurl +Content-Transfer-Encoding: 7bit +Content-Location: http://localhost:3333/watchfolder/hls/manifest.m3u8 + +#EXTM3U +#EXT-X-VERSION:6 +#EXT-X-STREAM-INF:BANDWIDTH=2305600,RESOLUTION=1280x720,FRAME-RATE=25.000,CODECS="avc1.64001f,mp4a.40.2" +stream_0.m3u8 +#EXT-X-STREAM-INF:BANDWIDTH=1205600,RESOLUTION=960x540,FRAME-RATE=25.000,CODECS="avc1.64001f,mp4a.40.2" +stream_1.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://localhost:3333/watchfolder/hls/stream_0.m3u8 + + 0 + + + + file:///TMGI-0x1009f165.m3u8 + http://localhost:3333/watchfolder/hls/stream_1.m3u8 + + + file:///TMGI-0x1009f165.m3u8 + http://localhost: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++++++++++++++++++++++++-- +```` + + +## 5G-MAG BC_UC format +This format represents a revised version of the default format: + +```` +MIME-Version: 1.0 +Content-Type: multipart/related; boundary="++++++++++++++++++++++++Rohde&Schwarz-BSCC++++++++++++++++++++++++--"; type="application/mbms-envelope+xml" +Content-Description: LTE MBMS Service Announcement + +--++++++++++++++++++++++++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 238.1.1.111/127 +b=AS:2000 +m=application 40101 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:6 +#EXT-X-STREAM-INF:BANDWIDTH=2305600,RESOLUTION=1280x720,FRAME-RATE=25.000,CODECS="avc1.64001f,mp4a.40.2" +stream_0.m3u8 + +--++++++++++++++++++++++++Rohde&Schwarz-BSCC++++++++++++++++++++++++-- +Content-Type: application/vnd.apple.mpegurl +Content-Transfer-Encoding: 7bit +Content-Location: http://localhost:3333/watchfolder/hls/manifest.m3u8 + +#EXTM3U +#EXT-X-VERSION:6 +#EXT-X-STREAM-INF:BANDWIDTH=2305600,RESOLUTION=1280x720,FRAME-RATE=25.000,CODECS="avc1.64001f,mp4a.40.2" +stream_0.m3u8 +#EXT-X-STREAM-INF:BANDWIDTH=1205600,RESOLUTION=960x540,FRAME-RATE=25.000,CODECS="avc1.64001f,mp4a.40.2" +stream_1.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 + + stream_0.m3u8 + 2 + + + http://localhost:3333/watchfolder/hls/stream_0.m3u8 + http://localhost:3333/watchfolder/hls/stream_1.m3u8 + + 0 + + + + stream_0.m3u8 + http://localhost:3333/watchfolder/hls/stream_1.m3u8 + + + stream_0.m3u8 + http://localhost: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++++++++++++++++++++++++-- +```` + +## 5G-MAG Legacy format +5G-MAG provides multiple [sample recordings](https://github.com/5G-MAG/Documentation-and-Architecture/wiki/Sample-Files) captured and hosted by ORS. These files were recorded at the start of the project and require a specific format of the ServiceAnnouncement file: + +```` +MIME-Version: 1.0 +Content-Type: multipart/related; boundary="xxx.yyy.zzz.--Rohde&Schwarz-BSCC--zzz.yyy.xxx--"; type="application/mbms-envelope+xml" +Content-Description: LTE MBMS Service Announcement + +--xxx.yyy.zzz.--Rohde&Schwarz-BSCC--zzz.yyy.xxx-- +Content-Type: application/mbms-envelope+xml +Content-Transfer-Encoding: 7bit +Content-Location: file:///envelope.xml + + + + + + + + + +--xxx.yyy.zzz.--Rohde&Schwarz-BSCC--zzz.yyy.xxx-- +Content-Type: application/sdp +Content-Transfer-Encoding: 7bit +Content-Location: file:///TMGI-0x1009f165.sdp + +v=0 +o=ROHDE-SCHWARZ-BSCC 269087077 1630568733 IN IP4 11.11.11.11 +s=HLS Streaming Session 0x1009f165 +i=File Download Session +t=3839557533 4785637533 +a=mbms-mode:broadcast-mbsfn 269087077 +c=IN IP4 238.1.1.111/127 +b=AS:1699 +m=application 40101 FLUTE/UDP 0 +a=flute-tsi:16 +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 + +--xxx.yyy.zzz.--Rohde&Schwarz-BSCC--zzz.yyy.xxx-- +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=1427133,FRAME-RATE=25.000,RESOLUTION=640x360 +out/u/bbb/qxa/manifest_3.m3u8?m=1614073235 + +--xxx.yyy.zzz.--Rohde&Schwarz-BSCC--zzz.yyy.xxx-- +Content-Type: application/vnd.apple.mpegurl +Content-Transfer-Encoding: 7bit +Content-Location: http://10.160.82.131/out/u/bbb/qxa/manifest.m3u8 + +#EXTM3U +#EXT-X-VERSION:3 +#EXT-X-STREAM-INF:BANDWIDTH=3847133,AVERAGE-BANDWIDTH=3847133,RESOLUTION=1280x720,FRAME-RATE=25.000,CODECS="avc1.4D401F,mp4a.40.2" +manifest_1.m3u8?m=1614073235 +#EXT-X-STREAM-INF:BANDWIDTH=2857098,AVERAGE-BANDWIDTH=2857098,RESOLUTION=854x480,FRAME-RATE=25.000,CODECS="avc1.4D401E,mp4a.40.2" +manifest_2.m3u8?m=1614073235 +#EXT-X-STREAM-INF:BANDWIDTH=1427133,AVERAGE-BANDWIDTH=1427133,RESOLUTION=640x360,FRAME-RATE=25.000,CODECS="avc1.4D401E,mp4a.40.2" +manifest_3.m3u8?m=1614073235 + +--xxx.yyy.zzz.--Rohde&Schwarz-BSCC--zzz.yyy.xxx-- +Content-Type: application/mbms-user-service-description+xml +Content-Transfer-Encoding: 7bit +Content-Location: file:///usdBundle.xml + + + + 1 + + Test Service TMGI-0x1009f165 + EN: Test Service TMGI-0x1009f165 + DE: Test Service TMGI-0x1009f165 + EN + DE + + 23 + 27 + + + 0 + + out/u/bbb/qxa/manifest_3.m3u8?m=1614073235 + file:///TMGI-0x1009f165.m3u8 + 2 + + 0 + + + + file:///TMGI-0x1009f165schedule.xml + + 0 + + + 2 + + + + +--xxx.yyy.zzz.--Rohde&Schwarz-BSCC--zzz.yyy.xxx-- +Content-Type: application/mbms-schedule+xml +Content-Transfer-Encoding: 7bit +Content-Location: file:///TMGI-0x1009f165schedule.xml + + + + 1 + + + 2021-09-02T07:45:33Z + 2051-08-26T07:45:33Z + 0 + 0 + 0 + + + +--xxx.yyy.zzz.--Rohde&Schwarz-BSCC--zzz.yyy.xxx-- +```` diff --git a/pages/lte-based-5g-broadcast/rt-mbms-modem/GPS-time-synchronization.md b/pages/lte-based-5g-broadcast/rt-mbms-modem/GPS-time-synchronization.md new file mode 100644 index 00000000..e6c34243 --- /dev/null +++ b/pages/lte-based-5g-broadcast/rt-mbms-modem/GPS-time-synchronization.md @@ -0,0 +1,63 @@ +--- +layout: default +title: GPS Time Synchonization for rt-mbms-modem +parent: Tutorials +grand_parent: MBMS and LTE-based 5G Broadcast +has_children: false +nav_order: 0 +--- + +# GPS Time Synchonization for rt-mbms-modem + +Preconditions: install, configure and enable ``gpsd`` by +following [this guide.](https://github.com/5G-MAG/rt-mbms-modem#measurement-recording-and-gps) + +## Install chrony + +```` +sudo apt install chrony +```` + +## Edit ``/etc/chrony/chrony.conf``, and add the following line at the end: + +```` +refclock SHM 0 delay 0.5 refid NMEA +```` + +## Edit ``/etc/default/gpsd``, and add options '-n -b': + +```` +# Devices gpsd should collect to at boot time. +# They need to be read/writeable, either by user gpsd or the group dialout. +DEVICES="/dev/ttyACM0" +# Other options you want to pass to gpsd +GPSD_OPTIONS="-n -b" +```` + +## Restart gpsd and chrony: + +```` +sudo systemctl restart gpsd +sudo systemctl restart chrony +```` + +## Done! + +You can check if chrony receives time data from your GPS devices with ``chronyc sources`` + +After around 30 seconds, you should see last sample data in the NMEA line, e.g: + +```` +210 Number of sources = 9 +MS Name/IP address Stratum Poll Reach LastRx Last sample +=============================================================================== +#- NMEA 0 4 377 11 +73ms[ +73ms] +/- 251ms +^- pugot.canonical.com 2 6 377 53 +394us[ +394us] +/- 54ms +^+ chilipepper.canonical.com 2 6 377 54 +156us[ +185us] +/- 44ms +^- alphyn.canonical.com 2 6 377 52 +624us[ +624us] +/- 127ms +^+ golem.canonical.com 2 6 377 55 +324us[ +352us] +/- 53ms +^+ extern4.nemox.net 2 6 377 54 -177us[ -149us] +/- 48ms +^+ 194.112.182.172 2 6 377 55 -151us[ -123us] +/- 29ms +^* ntp.candystore.at 2 6 377 53 +132us[ +160us] +/- 23ms +^+ svn.mediainvent.at 2 6 377 55 -94us[ -66us] +/- 36ms +```` diff --git a/pages/lte-based-5g-broadcast/specifications.md b/pages/lte-based-5g-broadcast/specifications.md index b4ea0397..1efb71be 100644 --- a/pages/lte-based-5g-broadcast/specifications.md +++ b/pages/lte-based-5g-broadcast/specifications.md @@ -5,6 +5,10 @@ parent: MBMS and LTE-based 5G Broadcast has_children: false nav_order: 0 --- -# 📑 Specifications and relevant references -* Information about relevant specifications can be found at the [Standards Wiki](https://github.com/5G-MAG/Standards/wiki/MBMS-&-LTE-based-5G-Broadcast:-Relevant-Specifications) -* A list of relevant 3GPP Work Items can be found at [Standards Wiki](https://github.com/5G-MAG/Standards/wiki/MBMS-&-LTE-based-5G-Broadcast:-Relevant-Work-Items) + +# MBMS and LTE-based 5G Broadcast +## 📑 Specifications and relevant references +* Information about relevant specifications can be found in this [page](https://5g-mag.github.io/Standards/pages/lte-based-5g-broadcast/lte-based-5g-broadcast-specifications.html) + +## 📑 Relevant Work Items +* A list of relevant 3GPP Work Items can be found in this [page](https://5g-mag.github.io/Standards/pages/lte-based-5g-broadcast/lte-based-5g-broadcast-workitems.html) diff --git a/pages/xr-media-integration-in-5g/repositories.md b/pages/xr-media-integration-in-5g/repositories.md index b247e733..841459a5 100644 --- a/pages/xr-media-integration-in-5g/repositories.md +++ b/pages/xr-media-integration-in-5g/repositories.md @@ -2,7 +2,7 @@ layout: default title: Repositories parent: XR Media Integration in 5G -has_children: false +has_children: true nav_order: 3 --- @@ -19,11 +19,15 @@ See also: [xr-player-overview](tutorials/xr-player-overview) * [Information and how to download, build, install and run](https://github.com/5G-MAG/rt-xr-gITFast#readme) * [Releases](https://github.com/5G-MAG/rt-xr-gITFast/releases) -## Media pipelines factory and plugins implementing the MAF API: [rt-xr-maf-native](https://github.com/5G-MAG/rt-xr-maf-native) +## Media pipelines factory and plugins implementing the Media Access Function (MAF) API: [rt-xr-maf-native](https://github.com/5G-MAG/rt-xr-maf-native) * [Information and how to download, build, install and run](https://github.com/5G-MAG/rt-xr-maf-native#readme) * [Releases](https://github.com/5G-MAG/rt-xr-maf-native/tags) -## Test content: [rt-xr-content](https://github.com/5G-MAG/rt-xr-content) +## Media Access Function (MAF) API Unity3D packager: [rt-xr-maf-plugin](https://github.com/5G-MAG/rt-xr-maf-plugin) +* [Information and how to download, build, install and run](https://github.com/5G-MAG/rt-xr-maf-plugin#readme) +* [Releases](https://github.com/5G-MAG/rt-xr-maf-plugin/tags) + +## Content for the XR Unity Player: [rt-xr-content](https://github.com/5G-MAG/rt-xr-content) * [Information](https://github.com/5G-MAG/rt-xr-content#readme) * [Releases](https://github.com/5G-MAG/rt-xr-maf-native/tags) diff --git a/pages/xr-media-integration-in-5g/features.md b/pages/xr-media-integration-in-5g/repositories/featuresXRplayer.md similarity index 99% rename from pages/xr-media-integration-in-5g/features.md rename to pages/xr-media-integration-in-5g/repositories/featuresXRplayer.md index 9eda1b99..f8620fb6 100644 --- a/pages/xr-media-integration-in-5g/features.md +++ b/pages/xr-media-integration-in-5g/repositories/featuresXRplayer.md @@ -1,12 +1,13 @@ --- layout: default -title: Features -parent: XR Media Integration in 5G +title: Features XR Player +grand_parent: XR Media Integration in 5G +parent: Repositories has_children: false nav_order: 0 --- -## XR Player Features +## XR Unity Player Features ### Planned for v1.0.0 diff --git a/pages/xr-media-integration-in-5g/specifications.md b/pages/xr-media-integration-in-5g/specifications.md index 4b242d05..ef399a97 100644 --- a/pages/xr-media-integration-in-5g/specifications.md +++ b/pages/xr-media-integration-in-5g/specifications.md @@ -6,6 +6,9 @@ has_children: false nav_order: 0 --- -# 📑 Specifications and relevant references -* Information about relevant specifications can be found at the [Standards Wiki](https://github.com/5G-MAG/Standards/wiki/XR-(eXtended-Reality):-Relevant-Specifications) -* A list of relevant 3GPP Work Items can be found at [Standards Wiki](https://github.com/5G-MAG/Standards/wiki/XR-(eXtended-Reality):-Relevant-Work-Items) +# eXtended Reality (XR) +## 📑 Specifications and relevant references +* Information about relevant specifications can be found in this [page](https://5g-mag.github.io/Standards/pages/xr/xr-specifications.html) + +## 📑 Relevant Work Items +* A list of relevant 3GPP Work Items can be found in this [page](https://5g-mag.github.io/Standards/pages/xr/xr-workitems.html) diff --git a/pages/xr-media-integration-in-5g/xr-player-overview.md b/pages/xr-media-integration-in-5g/tutorials/xr-player-overview.md similarity index 90% rename from pages/xr-media-integration-in-5g/xr-player-overview.md rename to pages/xr-media-integration-in-5g/tutorials/xr-player-overview.md index 6b8c07a6..d47cab66 100644 --- a/pages/xr-media-integration-in-5g/xr-player-overview.md +++ b/pages/xr-media-integration-in-5g/tutorials/xr-player-overview.md @@ -1,12 +1,13 @@ --- layout: default -title: XR Player project overview -parent: XR Media Integration in 5G +title: XR Player Project overview +grand_parent: XR Media Integration in 5G +parent: Tutorials has_children: false nav_order: 0 --- -# XR Player : project overview +# XR Unity Player: Project overview ## Scene description format @@ -14,7 +15,6 @@ The Scene Description format standardized by [ISO/IEC JTC 1/SC29/WG03](https://w It establishes interfaces like the Media Access Function (MAF) API to enable cross-platform interoperability, ensuring efficient retrieval and processing of media data, by decoupling the Presentation Engine from media pipeline. - ## XR Player implementation ![Alt text](../images/rt-xr-overview.jpg) @@ -25,16 +25,11 @@ The unity project builds on the following dependencies: * [rt-xr-glTFast](https://github.com/5G-MAG/rt-xr-gITFast): parsing and instantiating of 3D scenes in Unity. * [rt-xr-maf-native](https://github.com/5G-MAG/rt-xr-maf-native): a C++ Media Access Functions (MAF) API implementation, extensible with custom media pipeline plugins. - - - ### Test content * [rt-xr-content](https://github.com/5G-MAG/rt-xr-content): test content implementing the scene description format. - -See the [features page](features) for implementation status of the scene description format. - +See the [features page](../repositories/featuresXRplayer.md) for implementation status of the scene description format. ## MAF API & Media pipelines @@ -44,11 +39,8 @@ It's purpose is to decouple the presentation engine from media pipeline manageme - pass View informations to the media pipelines (eg. to optimize fetching media ) - read media buffers updated by the media pipelines - The MAF API is protocol and codec agnostic, media can be fetched a remote URL. - - ### Media player implementation #### MediaPlayer component diff --git a/pages/xr-media-integration-in-5g/tutorials/xr-player-win11-openXR.md b/pages/xr-media-integration-in-5g/tutorials/xr-player-win11-openXR.md index 55baffe9..3e2a68f9 100644 --- a/pages/xr-media-integration-in-5g/tutorials/xr-player-win11-openXR.md +++ b/pages/xr-media-integration-in-5g/tutorials/xr-player-win11-openXR.md @@ -1,13 +1,13 @@ --- layout: default -title: Using the XR Player on windows +title: XR Player on Windows parent: Tutorials grand_parent: XR Media Integration in 5G has_children: false -nav_order: 0 +nav_order: 1 --- - +## Introduction This tutorial covers usage of XR Player on Windows. 1. table of content @@ -21,13 +21,10 @@ The Meta Quest HMDs connected to a PC through Meta Quest Link provide such OpenX {: .highlight } If you do not have a Meta Quest HMD, simply ignore the requirements and optional steps related to Meta Quest in this tutorial. - - ## Requirements - Windows 11 - In order to use the Meta Quest link: {: .d-inline-block } optional @@ -102,4 +99,4 @@ pause ### Keyboard / Mouse controls -Refer to the github repository for the [up to date list of available keyboard and mouse controls](https://github.com/5G-MAG/rt-xr-unity-player?tab=readme-ov-file#usage). \ No newline at end of file +Refer to the github repository for the [up to date list of available keyboard and mouse controls](https://github.com/5G-MAG/rt-xr-unity-player?tab=readme-ov-file#usage).