Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add HLSMerger Plugin #118

Merged
merged 10 commits into from
Jan 12, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/test_and_deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ jobs:
sudo cp target/clip-creator.jar /usr/local/antmedia/plugins
ls /usr/local/antmedia/plugins
sudo service antmedia restart
mvn test -DskipTests=false --quiet
mvn deploy -DskipTests=false --quiet --settings ../mvn-settings.xml
popd

- name: Build and Test Media Push Plugin
Expand Down Expand Up @@ -136,6 +136,6 @@ jobs:
ls /usr/local/antmedia/plugins
sudo service antmedia restart
sleep 10
mvn test -DskipTests=false --quiet
mvn deploy -DskipTests=false --quiet --settings ../mvn-settings.xml
cd ..

23 changes: 0 additions & 23 deletions ClipCreatorPlugin/mvn-settings.xml

This file was deleted.

249 changes: 197 additions & 52 deletions HLSMergerPlugin/README.md
Original file line number Diff line number Diff line change
@@ -1,53 +1,198 @@
# HLS Merger Plugin
mekya marked this conversation as resolved.
Show resolved Hide resolved
This is a HLS Merger plugin project for Ant Media Server. You can use this a basis for your plugin.
With this plugin you can find:
- Accessing the Ant Media Server ie. AntMediaApplicationAdaptor class
- Registration of the plugin as the PacketListener and/or FrameListener
- Consuming packets and/or frames
- REST interface implementation

# Prerequests
- Install Ant Media Server
- Install Maven

# Quick Start

- Clone the repository and go the Sample Plugin Directory
```sh
git clone https://github.com/ant-media/Plugins.git
cd Plugins/SamplePlugin/
```
- Build the Sample Plugin
```sh
mvn install -Dgpg.skip=true
```
- Copy the generated jar file to your Ant Media Server's plugin directory
```sh
cp target/PluginApp.jar /usr/local/antmedia/plugins
```
- Restart the Ant Media Server
```
sudo service antmedia restart
```
- Publish/unPublish a Live Stream to Ant Media Server with WebRTC/RTMP/RTSP
- Check the logs on the server side
```
tail -f /usr/local/antmedia/log/ant-media-server.log
```
You would see the following logs
```
...
...
...
io.antmedia.plugin.SamplePlugin - *************** Stream Started: streamIdXXXXX ***************
...
...
...
io.antmedia.plugin.SamplePlugin - *************** Stream Finished: streamIdXXXXX ***************
...
...
...
```

For more information about the plugins, [visit this post](https://antmedia.io/plugins-will-make-ant-media-server-more-powerful/)


The **HLS Merger Plugin** enhances your HLS streaming capabilities by providing tools to:

1. Create HLS master playlists with multiple resolutions.
2. Combine video streams with multiple audio tracks for multilingual or alternate audio experiences.

---

## Installation

### Prerequisites
- [Ant Media Server](https://antmedia.io/)
- Java Runtime Environment (JRE)
- FFmpeg (for generating streams)

### Installation Steps

1. **Download the Plugin**
Fetch the latest snapshot from Sonatype:

```bash
wget -O maven-metadata.xml https://oss.sonatype.org/service/local/repositories/snapshots/content/io/antmedia/plugin/HLSMerger/maven-metadata.xml
export LATEST_SNAPSHOT=$(grep -o '<version>[^<]*</version>' maven-metadata.xml | tail -n 1 | sed -e 's/<version>//g' -e 's/<\/version>//g')
wget -O HLSMerger.jar "https://oss.sonatype.org/service/local/artifact/maven/redirect?r=snapshots&g=io.antmedia.plugin&a=HLSMerger&v=${LATEST_SNAPSHOT}&e=jar"
```

2. **Install the Plugin**
Copy the JAR file into the Ant Media Server plugins directory:

```bash
sudo cp HLSMerger.jar /usr/local/antmedia/plugins
```

3. **Restart the Server**
Restart Ant Media Server to activate the plugin:

```bash
sudo service antmedia restart
```

---

## Usage

### 1. Create a HLS Master Playlist with Multiple Resolutions

#### Overview
Adaptive Bitrate Streaming (ABR) enhances the viewing experience by dynamically adjusting video quality to match the viewer's internet speed. The **HLS Merger Plugin** simplifies ABR by merging multiple resolution streams into a single master playlist, reducing server-side CPU usage.

#### Steps

1. **Publish Streams with Different Resolutions**
Use FFmpeg to stream video at different resolutions:

```bash
ffmpeg -re -stream_loop -1 -i src/test/resources/test_video_720p.flv -codec copy -f flv rtmp://localhost/LiveApp/stream1
ffmpeg -re -stream_loop -1 -i src/test/resources/test_video_360p.flv -codec copy -f flv rtmp://localhost/LiveApp/stream2
```

2. **Merge the Streams**
Call the REST API to create a master playlist:

```bash
export MASTER_M3U8=merged_stream
export SERVER_ADDR=localhost

curl -X POST -H "Accept: application/json" -H "Content-Type: application/json" \
http://${SERVER_ADDR}:5080/LiveApp/rest/v1/hls-merger/multi-resolution-stream/$MASTER_M3U8 -d '["stream1", "stream2"]'
```

**Result:**
Access the master playlist at:
`http://localhost:5080/LiveApp/streams/merged_stream.m3u8`

3. **Play the Stream**
- Directly play with Ant Media Server player by visiting the url: `http://localhost:5080/LiveApp/play.html?id=merged_stream&playOrder=hls` in your browser.
- Use a compatible HLS player to play the resultant m3u8 file URL in step 2.
For example in [hls.js](https://hlsjs.video-dev.org/demo/):
1. Copy the master file URL into URL field as in the image.
2. Click "Apply" button. Player should start to play your stream.
3. Click "Quality-levels" button. You should see available resolutions below the player.
4. Choose any resolution to switch the quality.
![multi-resolution-hls](multi-resolution-hls.png)

4. **Stop the Merging Process**
Delete the master playlist when no longer needed:

```bash
curl -X DELETE -H "Accept: application/json" -H "Content-Type: application/json" \
http://${SERVER_ADDR}:5080/LiveApp/rest/v1/hls-merger/multi-resolution-stream/$MASTER_M3U8
```

---

### 2. Create a HLS Stream with Multiple Audio Tracks

#### Overview
Support multilingual or alternate audio streams by merging a video stream with multiple audio tracks into a single HLS playlist. Viewers can switch between audio tracks on HLS-compatible players.

#### Steps

1. **Update HLS settings on Ant Media Server Dashboard**

Set the `Segment Duration` to `10` seconds in LiveApp Application Settings in Ant Media Server Dashdoard.

![segment-duration-setting](segment-duration-setting.png)

2. **Create Streams**
Generate the video and audio streams:

- **Video Stream:**

```bash
ffmpeg -re -stream_loop -1 -i src/test/resources/test_video_720p.flv -an -codec copy -f flv rtmp://localhost/LiveApp/videoStream
```

- **Audio Streams:**

```bash
ffmpeg -re -stream_loop -1 -i src/test/resources/test_video_720p.flv -vn -codec copy -f flv rtmp://localhost/LiveApp/audiostream1
ffmpeg -re -stream_loop -1 -i src/test/resources/test_video_720p.flv -vn -codec copy -f flv rtmp://localhost/LiveApp/audiostream2
```

3. **Merge Streams**
Use the REST API to combine the audio streams with the video stream:

```bash
export MASTER_M3U8=merged_stream
export SERVER_ADDR=localhost

curl -X POST -H "Accept: application/json" -H "Content-Type: application/json" \
http://${SERVER_ADDR}:5080/LiveApp/rest/v1/hls-merger/videoStream/multi-audio-stream/$MASTER_M3U8 -d '["audiostream1", "audiostream2"]'
```

**Result:**
Access the master playlist at:
`http://localhost:5080/LiveApp/streams/merged_stream.m3u8`

4. **Play the Stream**
Use a compatible HLS player to play the resultant m3u8 file URL in step 3.
For example in [hls.js](https://hlsjs.video-dev.org/demo/):
1. Copy the master file URL into URL field as in the image.
2. Click "Apply" button. Player should start to play your stream.
3. Click "Audio-tracks" button. You should see available audio tracks below the player.
4. Choose any audio track to listen.

![multi-audio-hls](multi-audio-hls.png)

5. **Delete the Merged Stream**
Remove the playlist when no longer needed:

```bash
curl -X DELETE -H "Accept: application/json" -H "Content-Type: application/json" \
http://${SERVER_ADDR}:5080/LiveApp/rest/v1/hls-merger/multi-audio-stream/$MASTER_M3U8
```

---

## Build from Source

1. Clone the repository:

```bash
git clone https://github.com/ant-media/Plugins.git
```

2. Navigate to the HLSMerger directory:

```bash
cd Plugins/HLSMergerPlugin
```

3. Build the plugin:

```bash
mvn clean install -Dmaven.javadoc.skip=true -Dmaven.test.skip=true -Dgpg.skip=true
```

4. Install the plugin:

```bash
sudo cp target/HLSMerger.jar /usr/local/antmedia/plugins
```

5. Restart the server:

```bash
sudo service antmedia restart
```

---

## Additional Resources

- [Ant Media Server Documentation](https://antmedia.io/docs/)
- [REST API Guide](https://antmedia.io/docs/guides/developer-sdk-and-api/rest-api-guide/)
- [HLS Overview](https://en.wikipedia.org/wiki/HTTP_Live_Streaming)
Binary file added HLSMergerPlugin/multi-audio-hls.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added HLSMergerPlugin/multi-resolution-hls.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion HLSMergerPlugin/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<parent>
<groupId>io.antmedia</groupId>
<artifactId>parent</artifactId>
<version>2.12.0-SNAPSHOT</version>
<version>3.0.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<groupId>io.antmedia.plugin</groupId>
Expand Down
Binary file added HLSMergerPlugin/segment-duration-setting.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,14 @@ public IAntMediaStreamHandler getApplication() {
}

public boolean mergeStreams(String fileName, String[] streamIds) {
long hlsMergedFileUpdate = vertx.setPeriodic(5000,h -> mergeStreamsInternal(fileName, streamIds));
mergeTimers.put(fileName, hlsMergedFileUpdate);
return true;
if (!mergeTimers.contains(fileName)) {
long hlsMergedFileUpdate = vertx.setPeriodic(5000,h -> mergeStreamsInternal(fileName, streamIds));
mergeTimers.put(fileName, hlsMergedFileUpdate);
return true;
}
else {
return false;
}
}

public void mergeStreamsInternal(String fileName, String[] streamIds) {
Expand Down Expand Up @@ -113,28 +118,36 @@ public boolean stopMerge(String fileName) {
}

public boolean addMultipleAudioStreams(String fileName, String videoStreamId, String[] audioStreamIds) {
long hlsMergedFileUpdate = vertx.setPeriodic(5000,h -> addMultipleAudioInternal(fileName, videoStreamId, audioStreamIds));
mergeTimers.put(fileName, hlsMergedFileUpdate);
return true;
if (!mergeTimers.contains(fileName)) {
long hlsMergedFileUpdate = vertx.setPeriodic(5000,h -> addMultipleAudioInternal(fileName, videoStreamId, audioStreamIds));
mergeTimers.put(fileName, hlsMergedFileUpdate);
return true;
}
else {
return false;
}
}

public void addMultipleAudioInternal(String fileName, String videoStreamId, String[] audioStreamIds) {
StringBuilder streamBuilder = new StringBuilder();
String subfolder = "";
streamBuilder.append("#EXTM3U\n");


boolean defaultSet = false;
// Add audio streams
for (String audioStreamId : audioStreamIds) {
MuxAdaptor muxAdaptor = getMuxAdaptor(audioStreamId);
if (muxAdaptor != null) {
subfolder = muxAdaptor.getBroadcast().getSubFolder();
streamBuilder.append("#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID=\"audio\",NAME=\"")
streamBuilder.append("#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID=\"audio-group\",NAME=\"")
.append(audioStreamId)
.append("\",DEFAULT=NO,AUTOSELECT=YES,URI=\"")
.append("\",DEFAULT="+(defaultSet?"NO":"YES")+",AUTOSELECT=YES,URI=\"")
.append(audioStreamId).append(".m3u8\"\n");

defaultSet = true;
}
}

// Add video stream with reference to audio group
MuxAdaptor videoMuxAdaptor = getMuxAdaptor(videoStreamId);
if (videoMuxAdaptor != null) {
Expand All @@ -148,12 +161,13 @@ public void addMultipleAudioInternal(String fileName, String videoStreamId, Stri
int width = videoMuxAdaptor.getWidth();
int height = videoMuxAdaptor.getHeight();
streamBuilder.append("#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=")
.append(bitrate)
.append("1000000")
.append(",RESOLUTION=").append(width).append("x").append(height)
.append(",CODECS=\"avc1.42e00a,mp4a.40.2\",AUDIO=\"audio\"\n");
.append(",CODECS=\"avc1.42e00a,mp4a.40.2\",AUDIO=\"audio-group\"\n");
streamBuilder.append(videoStreamId).append(".m3u8\n");
}


writeHLSFile(fileName, streamBuilder.toString(), subfolder);
}

Expand Down
Loading