Skip to content

Commit

Permalink
simplify the interfaces - bump to mp4parser 1.9.17
Browse files Browse the repository at this point in the history
  • Loading branch information
sannies committed Sep 11, 2015
1 parent 8774981 commit 1ec782b
Show file tree
Hide file tree
Showing 8 changed files with 87 additions and 45 deletions.
16 changes: 12 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,22 @@ How to Stream and How to Emulate a live encoder
================================================

One format that most encoder support is plain RTP. So this project shows how RTP streams are received and
re-emitted as MPEG-DASH.
re-emitted as MPEG-DASH. It includes a stock Shaka Player to complete the showcase.

The sample server is not configurable in any way from the outside. The configuration is in the code and reflects the
prepared test content.
prepared test content. While the stream is created it is a live stream with a dynamic manifest. Once the stream source
stopped emitting RTP packets the DASH stream will automatically become available as an on demand stream (even though
the profile is still the live profile).

Prerequisites:
---------------

* [java 8](http://www.oracle.com/technetwork/java/javase/downloads/index.html)
* [Rtp2DASH Release 1.0](https://github.com/sannies/rtp2dash/releases/download/v1.0/rtp2dash-1.0.jar)
* [ffmpeg](https://www.ffmpeg.org/download.html)

additionally you can download a prebuilt version and the demo video:

* [Rtp2DASH Release 1.0](https://github.com/sannies/rtp2dash/releases/download/v1.0/rtp2dash-1.0.jar)
* [tos-mbr-in-one-file.mp4](http://com.mp4parser.s3.amazonaws.com/tos-mbr-in-one-file.mp4)


Expand All @@ -30,7 +35,10 @@ Step 2 - Start the stream
Step 3 - Watch Tears of Steel
---------------

Open your browser and go to: http://localhost:8080/index.html
1. Wait a moment (>10s so that at least 1 full video segment is available
2. Open your browser and go to: http://localhost:8080/index.html
3. Select "Tears of Steel" in the selection "Test manifest:"
4. Hit "Load Stream" and enjoy "Tears of Steel"



10 changes: 5 additions & 5 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
<groupId>com.mp4parser.rtp2dash</groupId>
<artifactId>rtp2dash</artifactId>
<packaging>jar</packaging>
<version>1.0</version>
<version>1.0.1</version>
<name>Project - rtp2dash</name>
<url>http://maven.apache.org</url>

Expand All @@ -20,12 +20,12 @@
<dependency>
<groupId>org.mp4parser</groupId>
<artifactId>isoparser</artifactId>
<version>1.9.15</version>
<version>1.9.17</version>
</dependency>
<dependency>
<groupId>org.mp4parser</groupId>
<artifactId>streaming</artifactId>
<version>1.9.15</version>
<version>1.9.17</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
Expand Down Expand Up @@ -103,7 +103,7 @@
<configuration>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>com.github.sannies.isoviewer.IsoViewerFx</mainClass>
<mainClass>com.mp4parser.rtp2dash.Server</mainClass>
</transformer>
</transformers>
</configuration>
Expand All @@ -115,7 +115,7 @@
<repositories>
<repository>
<id>mp4parser-2.x</id>
<url>https://oss.sonatype.org/content/repositories/central_bundles-4921</url>
<url>https://oss.sonatype.org/content/repositories/central_bundles-4932/</url>
</repository>
</repositories>
</project>
21 changes: 17 additions & 4 deletions src/main/java/com/mp4parser/rtp2dash/DashFragmentedMp4Writer.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import com.mp4parser.boxes.sampleentry.VisualSampleEntry;
import com.mp4parser.streaming.MultiTrackFragmentedMp4Writer;
import com.mp4parser.streaming.StreamingTrack;
import com.mp4parser.streaming.extensions.TrackIdTrackExtension;
import com.mp4parser.tools.Path;
import mpeg.dash.schema.mpd._2011.RepresentationType;
import mpeg.dash.schema.mpd._2011.SegmentTemplateType;
Expand All @@ -20,7 +21,9 @@
import java.util.Comparator;
import java.util.logging.Logger;

/**
*/
public class DashFragmentedMp4Writer extends MultiTrackFragmentedMp4Writer {
private static final Logger LOG = Logger.getLogger(DashFragmentedMp4Writer.class.getName());
private File representationBaseDir;
Expand All @@ -29,15 +32,25 @@ public class DashFragmentedMp4Writer extends MultiTrackFragmentedMp4Writer {
private StreamingTrack source;


public DashFragmentedMp4Writer(ReceivingStreamingTrack source, File baseDir, long adaptationSetId, String representationId, OutputStream outputStream) throws IOException {
super(Collections.<StreamingTrack>singletonList(source), outputStream);
public DashFragmentedMp4Writer(ReceivingStreamingTrack source, File baseDir) throws IOException {
super(Collections.<StreamingTrack>singletonList(source), new OutputStream() {
@Override
public void write(int b) throws IOException {

}
});
if (!source.isReceiving()) {
LOG.warning(source + " is not receiving any data. Will not create Representation.");
return;
}
RepresentationIdTrackExtension representationIdTrackExtension = source.getTrackExtension(RepresentationIdTrackExtension.class);
assert representationIdTrackExtension != null;
TrackIdTrackExtension trackIdTrackExtension = source.getTrackExtension(TrackIdTrackExtension.class);
assert trackIdTrackExtension != null;

this.source = source;
this.adaptationSetId = adaptationSetId;
this.representationId = representationId;
this.adaptationSetId = trackIdTrackExtension.getTrackId();
this.representationId = representationIdTrackExtension.getRepresentationId();
representationBaseDir = new File(baseDir, representationId);
representationBaseDir.mkdir();
this.timeOut = 10000;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.mp4parser.rtp2dash;

import com.mp4parser.streaming.TrackExtension;

/**
* TrackExtension to store the representationId that is to be used in Manifest.
*/
public class RepresentationIdTrackExtension implements TrackExtension {
private String representationId;

public RepresentationIdTrackExtension(String representationId) {
this.representationId = representationId;
}

public String getRepresentationId() {
return representationId;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
public class RtpH264StreamingTrack extends H264NalConsumingTrack implements ReceivingStreamingTrack {
private static final Logger LOG = Logger.getLogger(RtpH264StreamingTrack.class.getName());
boolean isReceiving = false;
private int initialTimeout = 10000;
private int initialTimeout = 120000;
private int timeout = 5000;
CountDownLatch countDownLatch = new CountDownLatch(1);
private Phaser started;
Expand Down
56 changes: 32 additions & 24 deletions src/main/java/com/mp4parser/rtp2dash/Server.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.mp4parser.rtp2dash;

import com.mp4parser.streaming.extensions.TrackIdTrackExtension;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelInitializer;
Expand Down Expand Up @@ -62,15 +63,34 @@ public void run() throws Exception {
CompletionService<Void> ecs
= new ExecutorCompletionService<Void>(es);
Phaser waitOnReceivingStarted = new Phaser(1);
final RtpH264StreamingTrack h264_0 = new RtpH264StreamingTrack(waitOnReceivingStarted, "Z2QAFUs2QCAb5/ARAAADAAEAAAMAMI8WLZY=,aEquJyw=", 5000, 96);
final RtpH264StreamingTrack h264_1 = new RtpH264StreamingTrack(waitOnReceivingStarted, "Z2QAFWs2QCcIebwEQAAAAwBAAAAMI8WLZYA=,aG6uJyw=", 5001, 96);
final RtpH264StreamingTrack h264_2 = new RtpH264StreamingTrack(waitOnReceivingStarted, "Z2QAHiLNkAwCmwEQAAADABAAAAMDCPFi2WA=,aCEq4nLA", 5002, 96);
final RtpH264StreamingTrack h264_3 = new RtpH264StreamingTrack(waitOnReceivingStarted, "Z2QAHyrNkASA9sBEAAADAAQAAAMAwjxgxlg=,aClq4nLA", 5003, 96);
// The init data is typically parsed from the SDP. As encoder setups are rather static I decided to
// have a static configuration.
final RtpH264StreamingTrack h264_0 = new RtpH264StreamingTrack(
waitOnReceivingStarted, "Z2QAFUs2QCAb5/ARAAADAAEAAAMAMI8WLZY=,aEquJyw=", 5000, 96);
h264_0.addTrackExtension(new TrackIdTrackExtension(1)); // trackId becomes AdaptationSetId
h264_0.addTrackExtension(new RepresentationIdTrackExtension("h264_0"));
final RtpH264StreamingTrack h264_1 = new RtpH264StreamingTrack(
waitOnReceivingStarted, "Z2QAFWs2QCcIebwEQAAAAwBAAAAMI8WLZYA=,aG6uJyw=", 5001, 96);
h264_1.addTrackExtension(new TrackIdTrackExtension(1));
h264_1.addTrackExtension(new RepresentationIdTrackExtension("h264_1"));
final RtpH264StreamingTrack h264_2 = new RtpH264StreamingTrack(
waitOnReceivingStarted, "Z2QAHiLNkAwCmwEQAAADABAAAAMDCPFi2WA=,aCEq4nLA", 5002, 96);
h264_2.addTrackExtension(new TrackIdTrackExtension(1));
h264_2.addTrackExtension(new RepresentationIdTrackExtension("h264_2"));
final RtpH264StreamingTrack h264_3 = new RtpH264StreamingTrack(
waitOnReceivingStarted, "Z2QAHyrNkASA9sBEAAADAAQAAAMAwjxgxlg=,aClq4nLA", 5003, 96);
h264_3.addTrackExtension(new TrackIdTrackExtension(1));
h264_3.addTrackExtension(new RepresentationIdTrackExtension("h264_3"));

final RtpAacStreamingTrack aac_eng = new RtpAacStreamingTrack(waitOnReceivingStarted, 5004, 97, 128, "profile-level-id=1;mode=AAC-hbr;sizelength=13;indexlength=3;indexdeltalength=3; config=1190", "MPEG4-GENERIC/48000/2");
aac_eng.setLanguage("eng");
aac_eng.addTrackExtension(new TrackIdTrackExtension(2));
aac_eng.addTrackExtension(new RepresentationIdTrackExtension("aac_eng"));

final RtpAacStreamingTrack aac_ita = new RtpAacStreamingTrack(waitOnReceivingStarted, 5005, 97, 128, "profile-level-id=1;mode=AAC-hbr;sizelength=13;indexlength=3;indexdeltalength=3; config=1190", "MPEG4-GENERIC/48000/2");
aac_ita.setLanguage("ita");
aac_ita.addTrackExtension(new TrackIdTrackExtension(3));
aac_ita.addTrackExtension(new RepresentationIdTrackExtension("aac_ita"));

List<Future<Void>> rtpReaders = new ArrayList<Future<Void>>();
rtpReaders.add(ecs.submit(aac_ita));
Expand All @@ -81,19 +101,20 @@ public void run() throws Exception {
rtpReaders.add(ecs.submit(h264_3));

waitOnReceivingStarted.arriveAndAwaitAdvance();
// The execution will only pass this point if each Rtp[H264|Aac]StreamingTrack has received at least a single packet


final List<DashFragmentedMp4Writer> writers = new ArrayList<DashFragmentedMp4Writer>();

writers.add(new DashFragmentedMp4Writer(aac_ita, baseDir, 2, "aac_ita", new ByteArrayOutputStream()));
writers.add(new DashFragmentedMp4Writer(aac_eng, baseDir, 3, "aac_eng", new ByteArrayOutputStream()));
writers.add(new DashFragmentedMp4Writer(h264_0, baseDir, 1, "h264_0", new ByteArrayOutputStream()));
writers.add(new DashFragmentedMp4Writer(h264_1, baseDir, 1, "h264_1", new ByteArrayOutputStream()));
writers.add(new DashFragmentedMp4Writer(h264_2, baseDir, 1, "h264_2", new ByteArrayOutputStream()));
writers.add(new DashFragmentedMp4Writer(h264_3, baseDir, 1, "h264_3", new ByteArrayOutputStream()));
writers.add(new DashFragmentedMp4Writer(aac_ita, baseDir));
writers.add(new DashFragmentedMp4Writer(aac_eng, baseDir));
writers.add(new DashFragmentedMp4Writer(h264_0, baseDir));
writers.add(new DashFragmentedMp4Writer(h264_1, baseDir));
writers.add(new DashFragmentedMp4Writer(h264_2, baseDir));
writers.add(new DashFragmentedMp4Writer(h264_3, baseDir));

for (DashFragmentedMp4Writer writer : writers) {
ecs.submit(new WriterCallable(writer));
ecs.submit(writer);
}


Expand Down Expand Up @@ -166,17 +187,4 @@ public static void main(String[] args) throws Exception {
new Server(8080).run();
}

public static class WriterCallable implements Callable<Void> {
private DashFragmentedMp4Writer writer;

public WriterCallable(DashFragmentedMp4Writer writer) {
this.writer = writer;
}

public Void call() throws Exception {
writer.write();

return null;
}
}
}
2 changes: 1 addition & 1 deletion src/test/java/TestAac.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ public static void main(String[] args) throws IOException, ExecutionException, I
OutputStream os = new FileOutputStream("output.mp4");
final MultiTrackFragmentedMp4Writer streamingMp4Writer = new MultiTrackFragmentedMp4Writer(Collections.<StreamingTrack>singletonList(st), os);
Future<Void> stFuture = es.submit(st);
streamingMp4Writer.write();
streamingMp4Writer.call();
stFuture.get();
streamingMp4Writer.close();
st.close();
Expand Down
7 changes: 1 addition & 6 deletions src/test/java/TestH264.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,7 @@ public static void main(String[] args) throws IOException, ExecutionException, I
OutputStream os = new FileOutputStream("output.mp4");
final MultiTrackFragmentedMp4Writer streamingMp4Writer = new MultiTrackFragmentedMp4Writer(Collections.<StreamingTrack>singletonList(st), os);
Future<Void> stFuture = es.submit(st);
es.submit(new Callable<Void>() {
public Void call() throws Exception {
streamingMp4Writer.write();
return null;
}
});
es.submit(streamingMp4Writer);
System.in.read();
streamingMp4Writer.close();
st.close();
Expand Down

0 comments on commit 1ec782b

Please sign in to comment.