Skip to content

Commit d71ee72

Browse files
authored
Initial files
1 parent 46e2e8b commit d71ee72

19 files changed

+2378
-1
lines changed

.github/workflows/release.yml

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
# This workflow uses actions that are not certified by GitHub.
2+
# They are provided by a third-party and are governed by
3+
# separate terms of service, privacy policy, and support
4+
# documentation.
5+
6+
# GitHub recommends pinning actions to a commit SHA.
7+
# To get a newer version, you will need to update the SHA.
8+
# You can also reference a tag or branch, but the action may change without warning.
9+
10+
name: Java CI
11+
12+
on:
13+
push:
14+
tags: ['*.*.*']
15+
16+
jobs:
17+
build:
18+
runs-on: ubuntu-latest
19+
20+
permissions:
21+
id-token: write # This is required for requesting the JWT
22+
contents: write
23+
discussions: write
24+
steps:
25+
- uses: actions/checkout@v4
26+
- name: Set up JDK 17
27+
uses: actions/setup-java@v4
28+
with:
29+
java-version: 17
30+
distribution: 'temurin'
31+
# TODO (mod-squad): Add S3 fetching routine to pull in WSE dependencies.
32+
- name: Configure AWS credentials for hub account
33+
uses: aws-actions/configure-aws-credentials@v3
34+
with:
35+
aws-region: ${{ secrets.AWS_REGION }}
36+
role-to-assume: ${{ secrets.HUB_ACCOUNT_ROLE_ARN }}
37+
role-session-name: ${{ secrets.HUB_ACCOUNT_ROLE_SESSION_NAME }}
38+
- name: Configure AWS Credentials for spoke account
39+
uses: aws-actions/configure-aws-credentials@v3
40+
with:
41+
aws-region: ${{ secrets.AWS_REGION }}
42+
role-to-assume: ${{ secrets.SPOKE_ACCOUNT_ROLE_ARN }}
43+
role-session-name: ${{ secrets.SPOKE_ACCOUNT_ROLE_SESSION_NAME }}
44+
role-chaining: true
45+
role-skip-session-tagging: true
46+
- name: Copy WSE distribution from S3
47+
run: |
48+
aws s3 cp s3://${{ secrets.WOWZA_DISTRIBUTION_BUCKET }}/${{ vars.WOWZA_RELEASE_CHANNEL }}/${{ vars.WOWZA_VERSION }}/WowzaStreamingEngine-Update-${{ vars.WOWZA_VERSION }}.zip .
49+
- name: Unzip WSE distribution
50+
run: |
51+
unzip WowzaStreamingEngine-Update-${{ vars.WOWZA_VERSION }}.zip -d ${{ vars.WSE_HOME }}
52+
- name: Validate Gradle wrapper
53+
uses: gradle/actions/wrapper-validation@v4
54+
- name: Setup Gradle
55+
uses: gradle/actions/setup-gradle@v4
56+
- name: Build Gradle
57+
run: gradle build -Pversion=${{ github.ref_name }} -PwseLibDir=${{ vars.WSE_HOME }}/files/lib
58+
- name: Release
59+
uses: softprops/action-gh-release@v2
60+
with:
61+
discussion_category_name: announcements
62+
generate_release_notes: true
63+
files: |
64+
build/libs/*

.gitignore

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
.gradle
2+
**/build/
3+
!src/**/build/
4+
5+
# Ignore Gradle GUI config
6+
gradle-app.setting
7+
8+
# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored)
9+
!gradle-wrapper.jar
10+
11+
# Avoid ignore Gradle wrappper properties
12+
!gradle-wrapper.properties
13+
14+
# Cache of project
15+
.gradletasknamecache
16+
17+
# Eclipse Gradle plugin generated files
18+
# Eclipse Core
19+
.project
20+
# JDT-specific (Eclipse Java Development Tools)
21+
.classpath
22+
23+
.env
24+
wse
25+
model_cache

LICENSE.txt

Lines changed: 234 additions & 0 deletions
Large diffs are not rendered by default.

README.md

Lines changed: 139 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,139 @@
1-
# wse-plugin-interstitials-rest-api
1+
# Wowza Streaming Engine HLS interstitials REST API
2+
3+
With the **HLS Interstitials REST API** module for [Wowza Streaming Engine™ media server software](https://www.wowza.com/products/streaming-engine), you can use a REST API to add HLS interstitials to a live video feed by inserting an `#EXT-DATE-RANGE` tag in the HLS manifest.
4+
5+
For more details about HLS interstitials, see [Getting Started with HLS Interstitials](https://developer.apple.com/streaming/GettingStartedWithHLSInterstitials.pdf).
6+
7+
## Prerequisites
8+
9+
* Wowza Streaming Engine™ 4.9.4 or later is required
10+
* Java 21
11+
* Gradle (to build)
12+
13+
## Build instructions
14+
15+
1. Clone this repository to your local filesystem.
16+
2. Update the `wseLibDir` variable in the `gradle.properties` file to point to the local Wowza Streaming Engine `lib` folder.
17+
3. Run `./gradlew build` to build the jar file.
18+
19+
## Install
20+
21+
1. Copy `wse-plugin-cloud-interstitials-rest-api-x.x.x.jar` into the `lib` directory.
22+
2. Add the HTTPProvider to `VHost.xml`:
23+
24+
```xml
25+
<HTTPProvider>
26+
<BaseClass>com.wowza.wms.plugin.interstitialsrestapi.http.HTTPProviderInterstitialsRestApi</BaseClass>
27+
<RequestFilters>v1/interstitials/*</RequestFilters>
28+
<AuthenticationMethod>none</AuthenticationMethod>
29+
</HTTPProvider>
30+
```
31+
32+
3. Add the following property to `VHost.xml`:
33+
34+
```xml
35+
<Property>
36+
<Name>optionsCORSHeadersAddMain</Name>
37+
<Value>Access-Control-Allow-Methods:DELETE</Value>
38+
<Type>String</Type>
39+
</Property>
40+
```
41+
42+
4. Add the following module to the Application.xml:
43+
44+
```xml
45+
<Module>
46+
<Name>ModuleInterstitialsRestApi</Name>
47+
<Description>ModuleInterstitialsRestApi</Description>
48+
<Class>com.wowza.wms.plugin.interstitialsrestapi.module.ModuleInterstitialsRestApi</Class>
49+
</Module>
50+
```
51+
52+
5. Add the following property to the `HTTPStreamer > Properties` block in the Application.xml file:
53+
54+
```xml
55+
<Property>
56+
<Name>cupertinoEnableProgramDateTime</Name>
57+
<Value>true</Value>
58+
<Type>Boolean</Type>
59+
</Property>
60+
```
61+
62+
## API details
63+
64+
### API pattern
65+
66+
* `/v1/interstitials/applications/{appName}/streams/{streamName}`
67+
68+
### API supported methods
69+
70+
* `POST`
71+
* `DELETE`
72+
73+
### Metadata
74+
75+
A JSON object can be passed into the video stream using the properties outlined in the following table.
76+
77+
#### Properties
78+
79+
| Property | Description |
80+
| :-------------- | :----------------------------------------------------------------------- |
81+
| `id` | Specify an identifier to use for the ad. Default value is `ad1`. |
82+
| `start_date` | Define an absolute start date in ISO8601 format, or +seconds from now. Defaults to the current time. |
83+
| `duration` | Specify duration for the ad. Default value is 30 seconds. |
84+
| `asset_list` | Define a URL for an assets list. If not defined, must have `asset_uri`. |
85+
| `asset_uri` | Define a URL for a single asset. If not defined, must have `asset_list`. |
86+
| `resume_offset` | Determine when primary playback should resume following the playback of the interstitial. Default value is 0 seconds. |
87+
| `restrict` | Create a list of navigation restrictions. Default value is `SKIP,JUMP`. |
88+
89+
## Examples and demo
90+
91+
After building the module, start Wowza Streaming Engine and Wowza Streaming Engine Manager using the docker-compose.yaml file in this repository. It includes a pre-configured Wowza Streaming Engine instance and sample `live` and `simu-live` applications.
92+
93+
1. Run the following command:
94+
95+
```bash
96+
docker compose up
97+
```
98+
99+
2. Insert an HLS interstitial for the `simu-live` application with a 10 second ad break, five seconds from now:
100+
101+
```shell
102+
curl -X POST -H "Content-Type: application/json" -d '{
103+
"id": "ad1",
104+
"start_date": "+5",
105+
"duration": 10.0,
106+
"asset_uri": "https://wv-cdn-00-00.flowplayer.com/7bb18344-08f9-4c1e-84a7-80c1007aa99b/cmaf/6089d839-d699-424b-b914-445152e25115/playlist.m3u8"
107+
}' http://localhost/v1/interstitials/applications/simu-live/streams/myStream
108+
```
109+
110+
3. To test playback, go to:
111+
112+
```text
113+
https://hlsjs.video-dev.org/demo/?src=https://wse-trial.wowza.com/simu-live/myStream/playlist.m3u8
114+
```
115+
116+
4. To view the HLS interstitial in the HLS manifest, run:
117+
118+
```bash
119+
curl http://localhost/simu-live/myStream/chunklist_w2003968828.m3u8
120+
```
121+
122+
## HLS output example
123+
124+
An HLS output example looks similar to:
125+
126+
```text
127+
#EXTM3U
128+
#EXT-X-VERSION:3
129+
#EXT-X-TARGETDURATION:4
130+
#EXT-X-MEDIA-SEQUENCE:60897
131+
#EXT-X-DISCONTINUITY-SEQUENCE:0
132+
#EXT-X-PROGRAM-DATE-TIME:2025-02-13T17:03:19.368Z
133+
#EXT-X-DATERANGE:ID="ad1-5",CLASS="com.apple.hls.interstitial",START-DATE="2025-06-30T20:14:26.497Z",DURATION=10.000,X-ASSET-URI="https://wv-cdn-00-00.flowplayer.com/7bb18344-08f9-4c1e-84a7-80c1007aa99b/cmaf/6089d839-d699-424b-b914-445152e25115/playlist.m3u8",X-RESUME-OFFSET=0,X-RESTRICT="SKIP,JUMP"
134+
#EXTINF:4.0,
135+
media_10.ts
136+
#EXTINF:4.0,
137+
media_11.ts
138+
#EXTINF:4.0,
139+
media_12.ts

build.gradle

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
import java.text.SimpleDateFormat
2+
import java.time.LocalDateTime
3+
import java.time.format.DateTimeFormatter
4+
5+
plugins {
6+
id 'java-library'
7+
id "com.gorylenko.gradle-git-properties" version "2.4.0-rc1"
8+
}
9+
10+
group 'com.wowza.wms.plugin.interstitialsrestapi'
11+
version '1.0.0'
12+
13+
java {
14+
toolchain {
15+
languageVersion = JavaLanguageVersion.of(17)
16+
}
17+
}
18+
19+
repositories {
20+
mavenCentral()
21+
flatDir {
22+
dirs "$wseLibDir"
23+
}
24+
}
25+
26+
configurations {
27+
plugin
28+
compileClasspath.extendsFrom(plugin)
29+
runtimeClasspath.extendsFrom(plugin)
30+
testCompileClasspath.extendsFrom(plugin)
31+
testRuntimeClasspath.extendsFrom(plugin)
32+
}
33+
34+
dependencies {
35+
implementation name: 'wms-server'
36+
implementation name: 'wms-stream-live'
37+
implementation name: 'wms-stream-publish'
38+
implementation name: 'wms-transcoder'
39+
implementation name: 'wms-httpstreamer-cupertinostreaming'
40+
implementation name: 'wms-httpstreamer-mpegdashstreaming'
41+
implementation 'org.apache.logging.log4j:log4j-core:2.17.2'
42+
implementation 'org.apache.logging.log4j:log4j-api:2.17.2'
43+
implementation 'com.fasterxml.jackson.core:jackson-databind:2.14.0'
44+
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.8.1'
45+
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.8.1'
46+
}
47+
48+
tasks.register('copyDeps', Copy) {
49+
from (configurations.plugin)
50+
into layout.buildDirectory.dir("libs")
51+
}
52+
53+
jar.configure {
54+
dependsOn (copyDeps)
55+
}
56+
57+
tasks.withType(Jar).configureEach {
58+
manifest {
59+
attributes(
60+
'Gradle-Version' : "Gradle ${gradle.gradleVersion}",
61+
'Created-By' : "${System.properties['java.version']} (${System.properties['java.vendor']} ${System.properties['java.vm.version']})",
62+
'Name' : "${project.name}",
63+
'Build-Version' : "${project.version}",
64+
'Build-Timestamp' : "${-> project.ext.gitProps['git.commit.time']}",
65+
'Build-Revision' : "${-> project.ext.gitProps['git.commit.id.abbrev']}",
66+
)
67+
}
68+
exclude('git.properties')
69+
metaInf {
70+
from files('LICENSE.txt')
71+
}
72+
archiveBaseName = 'wse-plugin-' + projectName
73+
}
74+
75+
gitProperties {
76+
extProperty = 'gitProps'
77+
dateFormat = "EEE LLL dd HH:mm:ss yyyy Z"
78+
dateFormatTimeZone = "UTC"
79+
}
80+
81+
generateGitProperties.outputs.upToDateWhen { false }
82+
83+
tasks.register('generateReleaseInfo') {
84+
dependsOn generateGitProperties
85+
group = "build"
86+
Provider<Directory> outputDir = layout.buildDirectory.dir('generated/java') as Provider<Directory>
87+
outputs.dir outputDir.get().asFile.absolutePath
88+
doLast {
89+
def now = System.currentTimeMillis()
90+
def packageDotPath = "${project.group}"
91+
def packagePath = packageDotPath.replaceAll('\\.', '/')
92+
Directory dir = outputDir.get().dir(packagePath)
93+
dir.asFile.mkdirs()
94+
dir.file("ReleaseInfo.java").asFile.text =
95+
"""|package $packageDotPath;
96+
|public class ReleaseInfo {
97+
| public static String getProject() { return "${projectName}"; }
98+
| public static String getVersion() { return "${project.version}"; }
99+
| public static String getBuildComitDate() { return "${-> project.ext.gitProps['git.commit.time']}"; }
100+
| public static String getBuildNumber() { return "${-> project.ext.gitProps['git.commit.id.abbrev']}"; }
101+
|}""".stripMargin()
102+
}
103+
}
104+
105+
sourceSets.main.java.srcDir generateReleaseInfo
106+
clean.finalizedBy generateReleaseInfo
107+
compileJava.dependsOn generateReleaseInfo
108+
109+
test {
110+
useJUnitPlatform()
111+
}

0 commit comments

Comments
 (0)