Skip to content

Commit

Permalink
Merge branch 'master' into recordSubfolder
Browse files Browse the repository at this point in the history
  • Loading branch information
burak-58 committed May 25, 2024
2 parents d19d6f3 + 88adc23 commit 2b45c36
Show file tree
Hide file tree
Showing 19 changed files with 685 additions and 185 deletions.
21 changes: 17 additions & 4 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -311,10 +311,17 @@
<artifactId>spring-webmvc</artifactId>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>${junit5.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
<version>${junit5.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
Expand Down Expand Up @@ -489,6 +496,12 @@
<groupId>com.googlecode.json-simple</groupId>
<artifactId>json-simple</artifactId>
<version>${json-simple.version}</version>
<exclusions>
<exclusion>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
Expand Down
5 changes: 3 additions & 2 deletions src/main/java/io/antmedia/AppSettings.java
Original file line number Diff line number Diff line change
Expand Up @@ -2137,8 +2137,8 @@ public boolean isWriteStatsToDatastore() {
/**
* Webhook webrtc play authentication url.
*/
@Value("${webhookPlayAuthUrl:#{null}}")
private String webhookPlayAuthUrl;
@Value("${webhookPlayAuthUrl:}")
private String webhookPlayAuthUrl = "";

/**
* Subfolder for the recording files (mp4 and webm)
Expand Down Expand Up @@ -3708,6 +3708,7 @@ public void setWebhookRetryDelay(long webhookRetryDelay) {
this.webhookRetryDelay = webhookRetryDelay;
}

@JsonIgnore
public boolean isWebhookPlayAuthEnabled() {
return getWebhookPlayAuthUrl() != null && !getWebhookPlayAuthUrl().isEmpty();
}
Expand Down
3 changes: 3 additions & 0 deletions src/main/java/io/antmedia/EncoderSettings.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

import java.io.Serializable;

import dev.morphia.annotations.Entity;

@Entity
public class EncoderSettings implements Serializable{

private int height;
Expand Down
6 changes: 4 additions & 2 deletions src/main/java/io/antmedia/datastore/db/DataStore.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@
import io.antmedia.datastore.db.types.ConnectionEvent;
import io.antmedia.datastore.db.types.Endpoint;
import io.antmedia.datastore.db.types.P2PConnection;
import io.antmedia.datastore.db.types.PushNotificationToken;
import io.antmedia.datastore.db.types.StreamInfo;
import io.antmedia.datastore.db.types.Subscriber;
import io.antmedia.datastore.db.types.SubscriberMetadata;
Expand Down Expand Up @@ -916,6 +915,10 @@ protected void updateStreamInfo(Broadcast broadcast, Broadcast newBroadcast)
broadcast.setConferenceMode(newBroadcast.getConferenceMode());
}

if (newBroadcast.getEncoderSettingsList() != null) {
broadcast.setEncoderSettingsList(newBroadcast.getEncoderSettingsList());
}


broadcast.setPlannedStartDate(newBroadcast.getPlannedStartDate());
broadcast.setSeekTimeInMs(newBroadcast.getSeekTimeInMs());
Expand All @@ -930,7 +933,6 @@ protected void updateStreamInfo(Broadcast broadcast, Broadcast newBroadcast)
broadcast.setSubTrackStreamIds(newBroadcast.getSubTrackStreamIds());
broadcast.setPlaylistLoopEnabled(newBroadcast.isPlaylistLoopEnabled());
broadcast.setAutoStartStopEnabled(newBroadcast.isAutoStartStopEnabled());

}


Expand Down
4 changes: 4 additions & 0 deletions src/main/java/io/antmedia/datastore/db/MongoStore.java
Original file line number Diff line number Diff line change
Expand Up @@ -803,6 +803,10 @@ public boolean updateBroadcastFields(String streamId, Broadcast broadcast) {
if (broadcast.getSpeed() != 0) {
updates.add(set("speed", broadcast.getSpeed()));
}

if(broadcast.getEncoderSettingsList() != null){
updates.add(set("encoderSettingsList",broadcast.getEncoderSettingsList()));
}

if (broadcast.getConferenceMode() != null) {
updates.add(set("conferenceMode", broadcast.getConferenceMode()));
Expand Down
18 changes: 17 additions & 1 deletion src/main/java/io/antmedia/datastore/db/types/Broadcast.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import java.util.List;

import dev.morphia.utils.IndexType;
import io.antmedia.EncoderSettings;
import org.bson.types.ObjectId;

import com.fasterxml.jackson.annotation.JsonIgnore;
Expand All @@ -15,6 +16,9 @@
import dev.morphia.annotations.Indexes;
import io.swagger.v3.oas.annotations.media.Schema;

import static io.antmedia.AppSettings.encodersList2Str;
import static io.antmedia.AppSettings.encodersStr2List;


@Schema(description="The basic broadcast class")
@Entity(value = "broadcast")
Expand Down Expand Up @@ -223,6 +227,7 @@ public class Broadcast {
@Schema(description ="Number of subtracks that is allowed to be created for the broadcast. It's usefult for limiting number of conference attendees. Default value is -1 and it means no limit")
private int subtracksLimit = -1;


@Entity
public static class PlayListItem
{
Expand Down Expand Up @@ -372,7 +377,7 @@ public Broadcast() {
private String mainTrackStreamId;

@Schema(description ="If this broadcast is main track. This variable hold sub track ids.")
private List<String> subTrackStreamIds = new ArrayList<String>();
private List<String> subTrackStreamIds = new ArrayList<>();

@Schema(description ="Absolute start time in milliseconds - unix timestamp. It's used for measuring the absolute latency")
private long absoluteStartTimeMs;
Expand Down Expand Up @@ -419,6 +424,9 @@ public Broadcast() {
+ "If there is no viewer after certain amount of seconds, it will stop. If there is an user want to watch the stream, it will start automatically")
private boolean autoStartStopEnabled = false;

@Schema(description ="The list of encoder settings")
private List<EncoderSettings> encoderSettingsList;


public Broadcast(String status, String name) {
this.setStatus(status);
Expand Down Expand Up @@ -921,4 +929,12 @@ public void setSubtracksLimit(int subtracksLimit) {
this.subtracksLimit = subtracksLimit;
}

public List<EncoderSettings> getEncoderSettingsList() {
return encoderSettingsList;
}

public void setEncoderSettingsList(List<EncoderSettings> encoderSettingsList) {
this.encoderSettingsList = encoderSettingsList;
}

}
96 changes: 72 additions & 24 deletions src/main/java/io/antmedia/muxer/HLSMuxer.java
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,12 @@ public class HLSMuxer extends Muxer {
private static final String SEI_UUID = "086f3693-b7b3-4f2c-9653-21492feee5b8+";

private static final String SEGMENT_SUFFIX_TS = "%0"+SEGMENT_INDEX_LENGTH+"d.ts";
private static final String SEGMENT_SUFFIX_FMP4 = "%0"+SEGMENT_INDEX_LENGTH+"d.m4s";

private static final String HLS_SEGMENT_TYPE_MPEGTS = "mpegts";
private static final String HLS_SEGMENT_TYPE_FMP4 = "fmp4";



protected static Logger logger = LoggerFactory.getLogger(HLSMuxer.class);
private String hlsListSize = "20";
Expand All @@ -44,6 +50,8 @@ public class HLSMuxer extends Muxer {

private boolean deleteFileOnExit = true;
private String hlsFlags;

private String segmentInitFilename;;

private String hlsEncryptionKeyInfoFile = null;

Expand All @@ -58,7 +66,7 @@ public class HLSMuxer extends Muxer {
* Note: The generated M3U8 for HEVC can be playable when it's fmp4
* It's not playable when it's mpegts
*/
private String hlsSegmentType = "mpegts";
private String hlsSegmentType = HLS_SEGMENT_TYPE_MPEGTS;

private String httpEndpoint;
public static final int S3_CONSTANT = 0b010;
Expand Down Expand Up @@ -154,21 +162,31 @@ public void init(IScope scope, String name, int resolutionHeight, String subFold

//remove double slashes with single slash because it may cause problems
segmentFilename = replaceDoubleSlashesWithSingleSlash(segmentFilename);
segmentFilename += SEGMENT_SUFFIX_TS;

options.put("hls_segment_type", hlsSegmentType);
if (HLS_SEGMENT_TYPE_FMP4.equals(hlsSegmentType)) {

segmentInitFilename = initialResourceNameWithoutExtension + "_init.mp4";
options.put("hls_fmp4_init_filename", segmentInitFilename);
segmentFilename += SEGMENT_SUFFIX_FMP4;
}
else { //if it's mpegts
segmentFilename += SEGMENT_SUFFIX_TS;
}


options.put("hls_segment_filename", segmentFilename);

if (hlsPlayListType != null && (hlsPlayListType.equals("event") || hlsPlayListType.equals("vod"))) {
if (hlsPlayListType != null && (hlsPlayListType.equals("event") || hlsPlayListType.equals("vod")))
{
options.put("hls_playlist_type", hlsPlayListType);
}

if (this.hlsFlags != null && !this.hlsFlags.isEmpty()) {
options.put("hls_flags", this.hlsFlags);
}

options.put("hls_segment_type", hlsSegmentType);


isInitialized = true;
}
Expand Down Expand Up @@ -296,39 +314,42 @@ public synchronized void writeTrailer() {
{
logger.info("Delete File onexit:{} upload to S3:{} stream:{} hls time:{} hlslist size:{}",
deleteFileOnExit, uploadHLSToS3, streamId, hlsTime, hlsListSize);
vertx.setTimer(Integer.parseInt(hlsTime) * Integer.parseInt(hlsListSize) * 1000l, l -> {

vertx.setTimer(Integer.parseInt(hlsTime) * Integer.parseInt(hlsListSize) * 1000l, l ->
{
final String filenameWithoutExtension = file.getName().substring(0, file.getName().lastIndexOf(extension));

//SEGMENT_SUFFIX_TS is %09d.ts
//convert segmentFileName to regular expression
String segmentFileWithoutSuffixTS = segmentFilename.substring(segmentFilename.lastIndexOf("/")+1, segmentFilename.indexOf(SEGMENT_SUFFIX_TS));
String regularExpression = segmentFileWithoutSuffixTS + "[0-9]*\\.ts$";

int indexOfSuffix = 0;
if (HLS_SEGMENT_TYPE_FMP4.equals(hlsSegmentType)) {
indexOfSuffix = segmentFilename.indexOf(SEGMENT_SUFFIX_FMP4);
}
else {
indexOfSuffix = segmentFilename.indexOf(SEGMENT_SUFFIX_TS);
}

String segmentFileWithoutSuffix = segmentFilename.substring(segmentFilename.lastIndexOf("/")+1, indexOfSuffix);
String regularExpression = segmentFileWithoutSuffix + "[0-9]*\\.(?:ts|m4s)$";
File[] files = getHLSFilesInDirectory(regularExpression);

if (files != null)
{

for (int i = 0; i < files.length; i++)
{
try {
if (!files[i].exists()) {
continue;
}
if(uploadHLSToS3 && storageClient.isEnabled())
{
String path = replaceDoubleSlashesWithSingleSlash(s3StreamsFolderPath + File.separator + (subFolder != null ? subFolder : "" ) + File.separator + files[i].getName());
storageClient.save(path , files[i], deleteFileOnExit);
}
else if (deleteFileOnExit)
{
Files.delete(files[i].toPath());
}
} catch (IOException e) {
logger.error(e.getMessage());
}

handleFinalization(files[i]);
}
}

if (segmentInitFilename != null) {
handleFinalization(new File(file.getParentFile() + File.separator + segmentInitFilename));
}



});
}
else {
Expand All @@ -338,6 +359,22 @@ else if (deleteFileOnExit)

}

private void handleFinalization(File file) {

try {
if (uploadHLSToS3 && storageClient.isEnabled())
{
String path = replaceDoubleSlashesWithSingleSlash(s3StreamsFolderPath + File.separator
+ (subFolder != null ? subFolder : "") + File.separator + file.getName());
storageClient.save(path, file, deleteFileOnExit);
} else if (deleteFileOnExit) {
Files.deleteIfExists(file.toPath());
}
} catch (IOException e) {
logger.error(e.getMessage());
}
}

public File[] getHLSFilesInDirectory(String regularExpression) {
return file.getParentFile().listFiles((dir, name) ->

Expand Down Expand Up @@ -405,7 +442,18 @@ public static void logError(int ret, String message, String streamId) {
@Override
public synchronized boolean addStream(AVCodecParameters codecParameters, AVRational timebase, int streamIndex)
{
setBitstreamFilter("h264_mp4toannexb");

if (codecParameters.codec_id() == AV_CODEC_ID_H264) {
setBitstreamFilter("h264_mp4toannexb");
}
else if (codecParameters.codec_id() == AV_CODEC_ID_H265){
setBitstreamFilter("hevc_mp4toannexb");
}
else if (codecParameters.codec_id() == AV_CODEC_ID_AAC && HLS_SEGMENT_TYPE_FMP4.equals(hlsSegmentType)) {
//we need this conversion for fmp4
setAudioBitreamFilter("aac_adtstoasc");
}

return super.addStream(codecParameters, timebase, streamIndex);
}

Expand Down
33 changes: 22 additions & 11 deletions src/main/java/io/antmedia/muxer/MuxAdaptor.java
Original file line number Diff line number Diff line change
Expand Up @@ -279,24 +279,19 @@ public PacketTime(long packetTimeMs, long systemTimeMs) {

//NOSONAR because we need to keep the reference of the field
protected AVChannelLayout channelLayout;
private long lastTotalByteReceived = 0;


private long lastTotalByteReceived = 0;
public static MuxAdaptor initializeMuxAdaptor(ClientBroadcastStream clientBroadcastStream, Broadcast broadcast, boolean isSource, IScope scope) {


public static MuxAdaptor initializeMuxAdaptor(ClientBroadcastStream clientBroadcastStream, boolean isSource, IScope scope) {
MuxAdaptor muxAdaptor = null;
ApplicationContext applicationContext = scope.getContext().getApplicationContext();
boolean tryEncoderAdaptor = false;
if (applicationContext.containsBean(AppSettings.BEAN_NAME)) {
AppSettings appSettings = (AppSettings) applicationContext.getBean(AppSettings.BEAN_NAME);
List<EncoderSettings> list = appSettings.getEncoderSettings();
if ((list != null && !list.isEmpty()) || appSettings.isWebRTCEnabled() || appSettings.isForceDecoding()) {
/*
* enable encoder adaptor if webrtc enabled because we're supporting forwarding video to end user
* without transcoding. We need encoder adaptor because we need to transcode audio
*/
tryEncoderAdaptor = true;
}

tryEncoderAdaptor = isEncoderAdaptorShouldBeTried(broadcast, appSettings);
}

if (tryEncoderAdaptor) {
Expand All @@ -316,10 +311,22 @@ public static MuxAdaptor initializeMuxAdaptor(ClientBroadcastStream clientBroadc
muxAdaptor = new MuxAdaptor(clientBroadcastStream);
}
muxAdaptor.setStreamSource(isSource);
muxAdaptor.setBroadcast(broadcast);

return muxAdaptor;
}

public static boolean isEncoderAdaptorShouldBeTried(Broadcast broadcast,
AppSettings appSettings)
{
return (broadcast != null && broadcast.getEncoderSettingsList() != null && !broadcast.getEncoderSettingsList().isEmpty())
||
(appSettings.getEncoderSettings() != null && !appSettings.getEncoderSettings().isEmpty())
||
appSettings.isWebRTCEnabled()
|| appSettings.isForceDecoding();
}


protected MuxAdaptor(ClientBroadcastStream clientBroadcastStream) {

Expand Down Expand Up @@ -395,7 +402,11 @@ protected void enableSettings() {
targetLatency = appSettingsLocal.getTargetLatency();

previewOverwrite = appSettingsLocal.isPreviewOverwrite();
encoderSettingsList = appSettingsLocal.getEncoderSettings();

encoderSettingsList = (getBroadcast() != null && getBroadcast().getEncoderSettingsList() != null && !getBroadcast().getEncoderSettingsList().isEmpty())
? getBroadcast().getEncoderSettingsList()
: appSettingsLocal.getEncoderSettings();

previewCreatePeriod = appSettingsLocal.getCreatePreviewPeriod();
maxAnalyzeDurationMS = appSettingsLocal.getMaxAnalyzeDurationMS();
generatePreview = appSettingsLocal.isGeneratePreview();
Expand Down
Loading

0 comments on commit 2b45c36

Please sign in to comment.