From a85e2a6f6d51db33fa06d8bdbaf952f347988770 Mon Sep 17 00:00:00 2001 From: mekya Date: Sat, 7 Dec 2024 13:12:14 +0300 Subject: [PATCH 1/5] Refactor Subscriber & Add setting to write events to datastore Subscriber type is now mongodb friendly and add setting to control writing Subscriber's ConnectionEvents to the datastore --- src/main/java/io/antmedia/AppSettings.java | 25 ++ .../io/antmedia/datastore/db/DataStore.java | 330 ++++++++++-------- .../datastore/db/DataStoreFactory.java | 7 +- .../datastore/db/InMemoryDataStore.java | 151 ++++---- .../datastore/db/MapBasedDataStore.java | 227 ++++++------ .../io/antmedia/datastore/db/MapDBStore.java | 5 +- .../io/antmedia/datastore/db/MongoStore.java | 83 +++-- .../io/antmedia/datastore/db/RedisStore.java | 1 + .../datastore/db/types/ConnectionEvent.java | 42 +++ .../datastore/db/types/Subscriber.java | 43 ++- .../datastore/db/types/SubscriberStats.java | 7 +- .../antmedia/rest/BroadcastRestService.java | 32 +- .../io/antmedia/test/AppSettingsUnitTest.java | 4 +- .../io/antmedia/test/db/DBStoresUnitTest.java | 30 +- 14 files changed, 616 insertions(+), 371 deletions(-) diff --git a/src/main/java/io/antmedia/AppSettings.java b/src/main/java/io/antmedia/AppSettings.java index 23238f34e..9cff2dbe9 100644 --- a/src/main/java/io/antmedia/AppSettings.java +++ b/src/main/java/io/antmedia/AppSettings.java @@ -1451,6 +1451,10 @@ public class AppSettings implements Serializable{ private int previewQuality = 75; + /** + * Whether to write viewers(HLS, WebRTC) count to the data store, it's true by default. + * If you set it to false, it decreases the number of write operations to the data store and you don't see the viewer count in datastore + */ @Value( "${writeStatsToDatastore:${" + SETTINGS_WRITE_STATS_TO_DATASTORE +":true}}") private boolean writeStatsToDatastore = true; @@ -2326,6 +2330,13 @@ public boolean isWriteStatsToDatastore() { */ @Value("${encodingQueueSize:150}") private int encodingQueueSize = 150; + + /** + * Write subscriber events to datastore. It's false by default + * Subscriber events are when they are connected/disconnected. Alternatively, you can get these events from analytics logs by default + */ + @Value("${writeSubscriberEventsToDatastore:false}") + private boolean writeSubscriberEventsToDatastore = false; //Make sure you have a default constructor because it's populated by MongoDB public AppSettings() { @@ -4066,4 +4077,18 @@ public int getPreviewQuality() { public void setPreviewQuality(int previewQuality) { this.previewQuality = previewQuality; } + + /** + * @return the writeSubscriberEventsToDatastore + */ + public boolean isWriteSubscriberEventsToDatastore() { + return writeSubscriberEventsToDatastore; + } + + /** + * @param writeSubscriberEventsToDatastore the writeSubscriberEventsToDatastore to set + */ + public void setWriteSubscriberEventsToDatastore(boolean writeSubscriberEventsToDatastore) { + this.writeSubscriberEventsToDatastore = writeSubscriberEventsToDatastore; + } } diff --git a/src/main/java/io/antmedia/datastore/db/DataStore.java b/src/main/java/io/antmedia/datastore/db/DataStore.java index 5ead51e73..c62041636 100644 --- a/src/main/java/io/antmedia/datastore/db/DataStore.java +++ b/src/main/java/io/antmedia/datastore/db/DataStore.java @@ -10,6 +10,8 @@ import java.util.List; import java.util.Map; +import javax.annotation.Nullable; + import org.apache.commons.lang3.RandomStringUtils; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.exception.ExceptionUtils; @@ -20,6 +22,7 @@ import com.google.gson.reflect.TypeToken; import io.antmedia.AntMediaApplicationAdapter; +import io.antmedia.AppSettings; import io.antmedia.datastore.db.types.Broadcast; import io.antmedia.datastore.db.types.BroadcastUpdate; import io.antmedia.datastore.db.types.ConferenceRoom; @@ -44,37 +47,45 @@ public abstract class DataStore { public static final int MAX_ITEM_IN_ONE_LIST = 250; private static final String REPLACE_CHARS_REGEX = "[\n|\r|\t]"; + /** + * If it's true, it writes the viewers updates to the data store + */ private boolean writeStatsToDatastore = true; + + /** + * If it's true, it writes the subscriber events to the data store + */ + private boolean writeSubscriberEventsToDatastore = false; public long executedQueryCount = 0; protected volatile boolean available = false; protected static Logger logger = LoggerFactory.getLogger(DataStore.class); - - + + public abstract String save(Broadcast broadcast); //TODO: In rare scenarios, streamId can not be unique public Broadcast saveBroadcast(Broadcast broadcast) { String streamId = null; try { - if (broadcast.getStreamId() == null || broadcast.getStreamId().isEmpty()) { - streamId = RandomStringUtils.randomAlphanumeric(16) + System.nanoTime(); - broadcast.setStreamId(streamId); - } - streamId = broadcast.getStreamId(); - String rtmpURL = broadcast.getRtmpURL(); - if (rtmpURL != null) { - rtmpURL += streamId; - } - broadcast.setRtmpURL(rtmpURL); - if (broadcast.getStatus() == null) { - broadcast.setStatus(IAntMediaStreamHandler.BROADCAST_STATUS_CREATED); + if (broadcast.getStreamId() == null || broadcast.getStreamId().isEmpty()) { + streamId = RandomStringUtils.randomAlphanumeric(16) + System.nanoTime(); + broadcast.setStreamId(streamId); + } + streamId = broadcast.getStreamId(); + String rtmpURL = broadcast.getRtmpURL(); + if (rtmpURL != null) { + rtmpURL += streamId; + } + broadcast.setRtmpURL(rtmpURL); + if (broadcast.getStatus() == null) { + broadcast.setStatus(IAntMediaStreamHandler.BROADCAST_STATUS_CREATED); + } + } catch (Exception e) { + logger.error(ExceptionUtils.getStackTrace(e)); } - } catch (Exception e) { - logger.error(ExceptionUtils.getStackTrace(e)); - } return broadcast; } @@ -84,7 +95,7 @@ public Broadcast saveBroadcast(Broadcast broadcast) { * @return broadcast */ public abstract Broadcast get(String id); - + public Broadcast get(Map broadcastMap, String streamId, Gson gson) { synchronized (this) { Broadcast broadcast = null; @@ -106,7 +117,7 @@ public Broadcast get(Map broadcastMap, String streamId, Gson gso * @return Vod object */ public abstract VoD getVoD(String id); - + public VoD getVoD(Map vodMap, String vodId, Gson gson) { synchronized (this) { if (vodId != null) { @@ -127,16 +138,6 @@ public VoD getVoD(Map vodMap, String vodId, Gson gson) { protected int totalWebRTCViewerCount = 0; protected long totalWebRTCViewerCountLastUpdateTime = 0; - public boolean updateSourceQualityParameters(String id, String quality, double speed, int pendingPacketQueue) { - if(writeStatsToDatastore) { - return updateSourceQualityParametersLocal(id, quality, speed, pendingPacketQueue); - } - return false; - } - - protected abstract boolean updateSourceQualityParametersLocal(String id, String quality, double speed, int pendingPacketQueue); - - public abstract boolean updateDuration(String id, long duration); /** * Returns the number of vods which contains searched string @@ -167,7 +168,7 @@ public boolean updateSourceQualityParameters(String id, String quality, double s */ @Deprecated public abstract long getBroadcastCount(); - + public long getBroadcastCount(Map broadcastMap) { synchronized (this) { return broadcastMap.size(); @@ -177,7 +178,7 @@ public long getBroadcastCount(Map broadcastMap) { public abstract boolean delete(String id); public abstract boolean deleteVod(String id); - + public abstract boolean updateVoDProcessStatus(String id, String status); /** @@ -195,9 +196,9 @@ public long getBroadcastCount(Map broadcastMap) { public abstract boolean removeEndpoint(String id, Endpoint endpoint, boolean checkRTMPUrl); - + public abstract List getExternalStreamsList(); - + /** * Closes the database * @param deleteDB if it's true, it also deletes the db and closes @@ -260,7 +261,7 @@ public long getTotalVodNumber(Map broadcastMap) { } public abstract long getTotalBroadcastNumber(); - + public long getTotalBroadcastNumber(Map broadcastMap) { synchronized (this) { return broadcastMap.size(); @@ -305,7 +306,7 @@ public List getDetectionList(Map detectionMap, } public abstract List getDetection(String id); - + public List getDetection(Map detectionMap, String id, Gson gson){ synchronized (this) { if (id != null) { @@ -485,7 +486,7 @@ public List listAllSubscriberStats(String streamId, int offset, List subscribers= listAllSubscribers(streamId, offset, size); List subscriberStats = new ArrayList<>(); - + for(Subscriber subscriber : subscribers) { SubscriberStats stat = subscriber.getStats(); stat.setStreamId(subscriber.getStreamId()); @@ -576,28 +577,40 @@ public boolean isSubscriberConnected(String streamId, String subscriberId) { */ public boolean addSubscriberConnectionEvent(String streamId, String subscriberId, ConnectionEvent event) { boolean result = false; - Subscriber subscriber = getSubscriber(streamId, subscriberId); - if (subscriber != null) { - handleConnectionEvent(subscriber, event); - - addSubscriber(streamId, subscriber); + if (writeSubscriberEventsToDatastore) + { + Subscriber subscriber = getSubscriber(streamId, subscriberId); + + if (subscriber != null && !StringUtils.isBlank(subscriber.getSubscriberId())) + { + if(ConnectionEvent.CONNECTED_EVENT.equals(event.getEventType())) { + subscriber.setConnected(true); + subscriber.setCurrentConcurrentConnections(subscriber.getCurrentConcurrentConnections()+1); + } else if(ConnectionEvent.DISCONNECTED_EVENT.equals(event.getEventType())) { + subscriber.setConnected(false); + subscriber.setCurrentConcurrentConnections(subscriber.getCurrentConcurrentConnections()-1); + } + addSubscriber(streamId, subscriber); + } + + handleConnectionEvent(streamId, subscriberId, event); result = true; } - return result; + + } // helper method used by all datastores - protected void handleConnectionEvent(Subscriber subscriber, ConnectionEvent event) { - if(ConnectionEvent.CONNECTED_EVENT.equals(event.getEventType())) { - subscriber.setConnected(true); - subscriber.setCurrentConcurrentConnections(subscriber.getCurrentConcurrentConnections()+1); - } else if(ConnectionEvent.DISCONNECTED_EVENT.equals(event.getEventType())) { - subscriber.setConnected(false); - subscriber.setCurrentConcurrentConnections(subscriber.getCurrentConcurrentConnections()-1); - } - subscriber.getStats().addConnectionEvent(event); - } + protected void handleConnectionEvent(String streamId, String subscriberId, ConnectionEvent event) + { + event.setStreamId(streamId); + event.setSubscriberId(subscriberId); + + addConnectionEvent(event); + } + + protected abstract boolean addConnectionEvent(ConnectionEvent event); /** * sets the avarage bitrate of the subscriber in the datastore @@ -609,12 +622,15 @@ protected void handleConnectionEvent(Subscriber subscriber, ConnectionEvent even public boolean updateSubscriberBitrateEvent(String streamId, String subscriberId, long avgVideoBitrate, long avgAudioBitrate) { boolean result = false; - Subscriber subscriber = getSubscriber(streamId, subscriberId); - if (subscriber != null) { - subscriber.getStats().setAvgVideoBitrate(avgVideoBitrate); - subscriber.getStats().setAvgAudioBitrate(avgAudioBitrate); - addSubscriber(streamId, subscriber); - result = true; + if (writeSubscriberEventsToDatastore) + { + Subscriber subscriber = getSubscriber(streamId, subscriberId); + if (subscriber != null) { + subscriber.setAvgVideoBitrate(avgVideoBitrate); + subscriber.setAvgAudioBitrate(avgAudioBitrate); + addSubscriber(streamId, subscriber); + result = true; + } } return result; @@ -663,7 +679,7 @@ public boolean updateSubscriberBitrateEvent(String streamId, String subscriberId public long getActiveBroadcastCount(Map broadcastMap, Gson gson, String hostAddress) { int activeBroadcastCount = 0; synchronized (this) { - + Collection values = broadcastMap.values(); for (String broadcastString : values) { @@ -678,25 +694,25 @@ public long getActiveBroadcastCount(Map broadcastMap, Gson gson, } return activeBroadcastCount; } - + public List getActiveBroadcastList(Map broadcastMap, Gson gson, String hostAddress) { List broadcastList = new ArrayList<>(); synchronized (this) { - + Collection values = broadcastMap.values(); for (String broadcastString : values) { Broadcast broadcast = gson.fromJson(broadcastString, Broadcast.class); - + String status = broadcast.getStatus(); if (IAntMediaStreamHandler.BROADCAST_STATUS_BROADCASTING.equals(status) && - (StringUtils.isBlank(hostAddress) || hostAddress.equals(broadcast.getOriginAdress()))) + (StringUtils.isBlank(hostAddress) || hostAddress.equals(broadcast.getOriginAdress()))) { broadcastList.add(broadcast); } } } - + return broadcastList; } @@ -720,9 +736,9 @@ public boolean updateHLSViewerCount(String streamId, int diffCount) { } return false; } - + protected abstract boolean updateHLSViewerCountLocal(String streamId, int diffCount); - + /** * Add or subtract the DASH viewer count from current value * @param streamId @@ -890,11 +906,11 @@ protected void updateStreamInfo(Broadcast broadcast, BroadcastUpdate newBroadcas if (newBroadcast.getAbsoluteStartTimeMs() != null) { broadcast.setAbsoluteStartTimeMs(newBroadcast.getAbsoluteStartTimeMs()); } - + if (newBroadcast.getUpdateTime() != null) { broadcast.setUpdateTime(newBroadcast.getUpdateTime()); } - + if (newBroadcast.getPlayListItemList() != null) { broadcast.setPlayListItemList(newBroadcast.getPlayListItemList()); } @@ -912,7 +928,7 @@ protected void updateStreamInfo(Broadcast broadcast, BroadcastUpdate newBroadcas if (newBroadcast.getListenerHookURL() != null && !newBroadcast.getListenerHookURL().isEmpty()) { broadcast.setListenerHookURL(newBroadcast.getListenerHookURL()); } - + if (newBroadcast.getSpeed() != null) { broadcast.setSpeed(newBroadcast.getSpeed()); } @@ -920,75 +936,75 @@ protected void updateStreamInfo(Broadcast broadcast, BroadcastUpdate newBroadcas if (newBroadcast.getMetaData() != null) { broadcast.setMetaData(newBroadcast.getMetaData()); } - + if (newBroadcast.getConferenceMode() != null) { broadcast.setConferenceMode(newBroadcast.getConferenceMode()); } - + if (newBroadcast.getEncoderSettingsList() != null) { broadcast.setEncoderSettingsList(newBroadcast.getEncoderSettingsList()); } - + if (newBroadcast.getPlannedStartDate() != null) { broadcast.setPlannedStartDate(newBroadcast.getPlannedStartDate()); } - + if (newBroadcast.getPlannedEndDate() != null) { broadcast.setPlannedEndDate(newBroadcast.getPlannedEndDate()); } - + if (newBroadcast.getSeekTimeInMs() != null) { broadcast.setSeekTimeInMs(newBroadcast.getSeekTimeInMs()); } - + if (newBroadcast.getReceivedBytes() != null) { broadcast.setReceivedBytes(newBroadcast.getReceivedBytes()); } - + if (newBroadcast.getDuration() != null) { - broadcast.setDuration(newBroadcast.getDuration()); - } - + broadcast.setDuration(newBroadcast.getDuration()); + } + if (newBroadcast.getBitrate() != null) { broadcast.setBitrate(newBroadcast.getBitrate()); } - + if (newBroadcast.getUserAgent() != null) { broadcast.setUserAgent(newBroadcast.getUserAgent()); } - + if (newBroadcast.getWebRTCViewerLimit() != null) { broadcast.setWebRTCViewerLimit(newBroadcast.getWebRTCViewerLimit()); } - + if (newBroadcast.getHlsViewerLimit() != null) { broadcast.setHlsViewerLimit(newBroadcast.getHlsViewerLimit()); } - + if (newBroadcast.getDashViewerCount() != null) { broadcast.setDashViewerCount(newBroadcast.getDashViewerCount()); } - + if (newBroadcast.getSubTrackStreamIds() != null) { broadcast.setSubTrackStreamIds(newBroadcast.getSubTrackStreamIds()); } - + if (newBroadcast.getPlaylistLoopEnabled() != null) { broadcast.setPlaylistLoopEnabled(newBroadcast.getPlaylistLoopEnabled()); } - + if (newBroadcast.getAutoStartStopEnabled() != null) { broadcast.setAutoStartStopEnabled(newBroadcast.getAutoStartStopEnabled()); - } - + } + if (newBroadcast.getCurrentPlayIndex() != null) { broadcast.setCurrentPlayIndex(newBroadcast.getCurrentPlayIndex()); } - + if (newBroadcast.getSubtracksLimit() != null) { broadcast.setSubtracksLimit(newBroadcast.getSubtracksLimit()); } - + if (newBroadcast.getPendingPacketSize() != null) { broadcast.setPendingPacketSize(newBroadcast.getPendingPacketSize()); } @@ -1001,7 +1017,7 @@ protected void updateStreamInfo(Broadcast broadcast, BroadcastUpdate newBroadcas public abstract long getLocalLiveBroadcastCount(String hostAddress); - + public abstract List getLocalLiveBroadcasts(String hostAddress); /** @@ -1065,7 +1081,7 @@ else if (sortBy.contentEquals("date")) else { result = c1.compareTo(c2); } - + } return result; }); @@ -1293,7 +1309,7 @@ protected List sortAndCropWebRTCViewerInfoList(List getWebRTCViewerList(Map webRTCView String search, Gson gson) { ArrayList list = new ArrayList<>(); synchronized (this) { - + Collection webRTCViewers = webRTCViewerMap.values(); for (String infoString : webRTCViewers) { WebRTCViewerInfo info = gson.fromJson(infoString, WebRTCViewerInfo.class); @@ -1364,7 +1380,7 @@ public List getWebRTCViewerList(Map webRTCView * @return */ public abstract SubscriberMetadata getSubscriberMetaData(String subscriberId); - + /** * This is a helper method to remove the ConferenceRoom in later versions * @@ -1410,7 +1426,7 @@ public static BroadcastUpdate conferenceUpdateToBroadcastUpdate(ConferenceRoom c return broadcast; } - + public static Broadcast conferenceToBroadcast(ConferenceRoom conferenceRoom) throws Exception { Broadcast broadcast = new Broadcast(); broadcast.setStreamId(conferenceRoom.getRoomId()); @@ -1439,9 +1455,9 @@ public static Broadcast conferenceToBroadcast(ConferenceRoom conferenceRoom) thr * @param status the status of the stream broadcasting, finished etc. It can be null * @return */ - public abstract List getSubtracks(String mainTrackId, int offset, int size, String role, String status); - - /** + public abstract List getSubtracks(String mainTrackId, int offset, int size, String role, String status); + + /** * Get the subtracks of the main track * @param mainTrackId the main track to get the subtracks * @param offset the offset to get the subtracks @@ -1449,52 +1465,52 @@ public static Broadcast conferenceToBroadcast(ConferenceRoom conferenceRoom) thr * @param role the role of the subtracks for role based streaming especially in conferences. It can be null * @return */ - public abstract List getSubtracks(String mainTrackId, int offset, int size, String role); - - /** - * Get the count of subtracks - * @param mainTrackId the main track to get the subtracks - * @param role the role of the subtracks for role based streaming especially in conferences - * @return number of subtracks - */ - public abstract long getSubtrackCount(String mainTrackId, String role, String status); - - /** - * Get the count of active subtracks. If subtrack is stucked in broadcasting or preparing, it will not count it. - * @param mainTrackId - * @param role - * @return - */ - public abstract long getActiveSubtracksCount(String mainTrackId, String role); - - /** - * Get of active subtracks. If subtrack is stucked in broadcasting or preparing, it will not return it. - * This method is generally not recommended to use because it can be very costly. - * It's implemented for the poll mechanism in Subtracks and poll mechanismi will be replaced with event mechanism - * @param mainTrackId - * @param role - * @return - */ - public abstract List getActiveSubtracks(String mainTrackId, String role); - - - /** - * - * @param streamId - * @return If the stream has subtracks, it return true. If not, it returns false - */ - public abstract boolean hasSubtracks(String streamId); - - - //************************************** + public abstract List getSubtracks(String mainTrackId, int offset, int size, String role); + + /** + * Get the count of subtracks + * @param mainTrackId the main track to get the subtracks + * @param role the role of the subtracks for role based streaming especially in conferences + * @return number of subtracks + */ + public abstract long getSubtrackCount(String mainTrackId, String role, String status); + + /** + * Get the count of active subtracks. If subtrack is stucked in broadcasting or preparing, it will not count it. + * @param mainTrackId + * @param role + * @return + */ + public abstract long getActiveSubtracksCount(String mainTrackId, String role); + + /** + * Get of active subtracks. If subtrack is stucked in broadcasting or preparing, it will not return it. + * This method is generally not recommended to use because it can be very costly. + * It's implemented for the poll mechanism in Subtracks and poll mechanismi will be replaced with event mechanism + * @param mainTrackId + * @param role + * @return + */ + public abstract List getActiveSubtracks(String mainTrackId, String role); + + + /** + * + * @param streamId + * @return If the stream has subtracks, it return true. If not, it returns false + */ + public abstract boolean hasSubtracks(String streamId); + + + //************************************** //ATTENTION: Write function descriptions while adding new functions //************************************** - - //************************************** - //ATTENTION 2: What is the reason you don't add descriptions to the functions? - // Ignore this message if you have added descriptions to the new functions. - // I'm writing to the one who is ignoring this first message - mekya - //************************************** + + //************************************** + //ATTENTION 2: What is the reason you don't add descriptions to the functions? + // Ignore this message if you have added descriptions to the new functions. + // I'm writing to the one who is ignoring this first message - mekya + //************************************** /** * @@ -1505,5 +1521,33 @@ public long getExecutedQueryCount() { return executedQueryCount; } + /** + * Setter for writeSubscriberEventsToDatastore + * @param writeSubscriberEventsToDatastore + */ + public void setWriteSubscriberEventsToDatastore(boolean writeSubscriberEventsToDatastore) { + this.writeSubscriberEventsToDatastore = writeSubscriberEventsToDatastore; + } + + /** + * Getter for writeSubscriberEventsToDatastore + * @return + */ + public boolean isWriteSubscriberEventsToDatastore() { + return writeSubscriberEventsToDatastore; + } + + /** + * Get connection events for a specific streamId and subscriberId + * + * ConnectionEvents are recorded if {@link AppSettings#isWriteSubscriberEventsToDatastore()} is true + * + * @param streamId + * @param subscriberId + * @param offset + * @param size + * @return + */ + public abstract List getConnectionEvents(String streamId, @Nullable String subscriberId, int offset, int size); } diff --git a/src/main/java/io/antmedia/datastore/db/DataStoreFactory.java b/src/main/java/io/antmedia/datastore/db/DataStoreFactory.java index 3a2ddf913..3b2379677 100644 --- a/src/main/java/io/antmedia/datastore/db/DataStoreFactory.java +++ b/src/main/java/io/antmedia/datastore/db/DataStoreFactory.java @@ -56,6 +56,7 @@ public class DataStoreFactory implements IDataStoreFactory, ApplicationContextAw private Vertx vertx; private boolean writeStatsToDatastore; + private boolean writeSubscriberEventsToDatastore; public String getDbName() { return dbName; @@ -119,6 +120,7 @@ else if(dbType .contentEquals(DB_TYPE_MEMORYDB)) if(dataStore != null) { dataStore.setWriteStatsToDatastore(writeStatsToDatastore); + dataStore.setWriteSubscriberEventsToDatastore(writeSubscriberEventsToDatastore); } } @@ -144,7 +146,10 @@ public void setApplicationContext(ApplicationContext applicationContext) throws ServerSettings serverSettings = (ServerSettings) applicationContext.getBean(ServerSettings.BEAN_NAME); hostAddress = serverSettings.getHostAddress(); - writeStatsToDatastore = ((AppSettings) applicationContext.getBean(AppSettings.BEAN_NAME)).isWriteStatsToDatastore(); + AppSettings appSettings = ((AppSettings) applicationContext.getBean(AppSettings.BEAN_NAME)); + writeStatsToDatastore = appSettings.isWriteStatsToDatastore(); + writeSubscriberEventsToDatastore = appSettings.isWriteSubscriberEventsToDatastore(); + init(); } diff --git a/src/main/java/io/antmedia/datastore/db/InMemoryDataStore.java b/src/main/java/io/antmedia/datastore/db/InMemoryDataStore.java index 648014e2d..486c30e7a 100644 --- a/src/main/java/io/antmedia/datastore/db/InMemoryDataStore.java +++ b/src/main/java/io/antmedia/datastore/db/InMemoryDataStore.java @@ -24,6 +24,7 @@ import io.antmedia.AntMediaApplicationAdapter; import io.antmedia.datastore.db.types.Broadcast; import io.antmedia.datastore.db.types.BroadcastUpdate; +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.StreamInfo; @@ -44,6 +45,7 @@ public class InMemoryDataStore extends DataStore { private Map> detectionMap = new LinkedHashMap<>(); private Map tokenMap = new LinkedHashMap<>(); private Map subscriberMap = new LinkedHashMap<>(); + private Map connectionEvents = new LinkedHashMap<>(); private Map subscriberMetadataMap = new LinkedHashMap<>(); private Map webRTCViewerMap = new LinkedHashMap<>(); @@ -112,18 +114,6 @@ else if(status.equals(IAntMediaStreamHandler.BROADCAST_STATUS_FINISHED)) { } return result; } - - @Override - public boolean updateDuration(String id, long duration) { - Broadcast broadcast = broadcastMap.get(id); - boolean result = false; - if (broadcast != null) { - broadcast.setDuration(duration); - broadcastMap.put(id, broadcast); - result = true; - } - return result; - } @Override public boolean addEndpoint(String id, Endpoint endpoint) { @@ -187,12 +177,12 @@ public long getActiveBroadcastCount() { } return activeBroadcastCount; } - - + + public long getLocalLiveBroadcastCount(String hostAddress) { return getActiveBroadcastCount(); } - + public List getLocalLiveBroadcasts(String hostAddress) { List broadcastList = new ArrayList<>(); @@ -219,11 +209,11 @@ public boolean delete(String id) { @Override public List getBroadcastList(int offset, int size, String type, String sortBy, String orderBy, String search) { - + Collection values = broadcastMap.values(); ArrayList list = new ArrayList<>(); - + if(type != null && !type.isEmpty()) { for (Broadcast broadcast : values) { @@ -296,7 +286,7 @@ public String addVod(VoD vod) { } return id; } - + @Override public boolean updateVoDProcessStatus(String id, String status) { VoD vod = vodMap.get(id); @@ -319,18 +309,18 @@ else if (VoD.PROCESS_STATUS_FAILED.equals(status) || VoD.PROCESS_STATUS_FINISHED public List getVodList(int offset, int size, String sortBy, String orderBy, String filterStreamId, String search) { ArrayList vods = null; - + if (filterStreamId != null && !filterStreamId.isEmpty()) { vods = new ArrayList<>(); - + for (VoD vod : vodMap.values()) { if(vod.getStreamId().equals(filterStreamId)) { vods.add(vod); } } - + } else { vods = new ArrayList<>(vodMap.values()); @@ -401,7 +391,7 @@ public int fetchUserVodList(File userfile) { long unixTime = System.currentTimeMillis(); String filePath = file.getPath(); - + String[] subDirs = filePath.split(Pattern.quote(File.separator)); String relativePath= "streams/" + subDirs[subDirs.length-2] +'/' +subDirs[subDirs.length-1]; @@ -420,26 +410,6 @@ public int fetchUserVodList(File userfile) { } - - - @Override - public boolean updateSourceQualityParametersLocal(String id, String quality, double speed, int pendingPacketSize) { - boolean result = false; - if (id != null) { - Broadcast broadcast = broadcastMap.get(id); - if (broadcast != null) { - if (quality != null) { - broadcast.setQuality(quality); - } - broadcast.setSpeed(speed); - broadcast.setPendingPacketSize(pendingPacketSize); - broadcastMap.replace(id, broadcast); - result = true; - } - } - return result; - } - @Override public long getTotalBroadcastNumber() { return broadcastMap.size(); @@ -561,7 +531,7 @@ public synchronized boolean updateHLSViewerCountLocal(String streamId, int diffC } return result; } - + @Override public synchronized boolean updateDASHViewerCountLocal(String streamId, int diffCount) { boolean result = false; @@ -779,14 +749,67 @@ public boolean addSubscriber(String streamId, Subscriber subscriber) { } return result; } + + @Override + public List getConnectionEvents(String streamId, String subscriberId, int offset, int size) { + Collection values = connectionEvents.values(); + List list = new ArrayList<>(); + List returnList = new ArrayList<>(); + + int t = 0; + int itemCount = 0; + if (size > MAX_ITEM_IN_ONE_LIST) { + size = MAX_ITEM_IN_ONE_LIST; + } + if (offset < 0) { + offset = 0; + } + + for(ConnectionEvent event: values) { + if (streamId.equals(event.getStreamId()) && (StringUtils.isBlank(subscriberId) || subscriberId.equals(event.getSubscriberId()))) { + list.add(event); + } + } + + Iterator iterator = list.iterator(); + + while(itemCount < size && iterator.hasNext()) { + if (t < offset) { + t++; + iterator.next(); + } + else { + + returnList.add(iterator.next()); + itemCount++; + } + } + + return returnList; + } + + @Override + public boolean addConnectionEvent(ConnectionEvent connectionEvent) { + boolean result = false; + if (connectionEvent != null && StringUtils.isNoneBlank(connectionEvent.getStreamId(), connectionEvent.getSubscriberId())) { + try { + connectionEvents.put(Subscriber.getDBKey(connectionEvent.getStreamId(), connectionEvent.getSubscriberId()), connectionEvent); + result = true; + } catch (Exception e) { + logger.error(ExceptionUtils.getStackTrace(e)); + } + } + + return result; + } @Override public boolean deleteSubscriber(String streamId, String subscriberId) { - + boolean result = false; if(streamId != null && subscriberId != null) { try { - Subscriber sub = subscriberMap.remove(Subscriber.getDBKey(streamId, subscriberId)); + Subscriber sub = subscriberMap.remove(Subscriber.getDBKey(streamId, subscriberId)); result = sub != null; } catch (Exception e) { logger.error(ExceptionUtils.getStackTrace(e)); @@ -840,12 +863,12 @@ public boolean revokeSubscribers(String streamId) { } return result; } - + @Override public Subscriber getSubscriber(String streamId, String subscriberId) { return subscriberMap.get(Subscriber.getDBKey(streamId, subscriberId)); } - + @Override public boolean resetSubscribersConnectedStatus() { for(Subscriber subscriber: subscriberMap.values()) { @@ -880,7 +903,7 @@ public boolean setMp4Muxing(String streamId, int enabled) { return result; } - + @Override public boolean setWebMMuxing(String streamId, int enabled) { boolean result = false; @@ -940,11 +963,11 @@ public boolean addSubTrack(String mainTrackId, String subTrackId) { Broadcast mainTrack = broadcastMap.get(mainTrackId); if (mainTrack != null && subTrackId != null) { List subTracks = mainTrack.getSubTrackStreamIds(); - + if (subTracks == null) { subTracks = new ArrayList<>(); } - + if (!subTracks.contains(subTrackId)) { subTracks.add(subTrackId); @@ -952,7 +975,7 @@ public boolean addSubTrack(String mainTrackId, String subTrackId) { broadcastMap.put(mainTrackId, mainTrack); } result = true; - + } return result; } @@ -971,11 +994,11 @@ public boolean removeSubTrack(String mainTrackId, String subTrackId) { } return result; } - + @Override public int resetBroadcasts(String hostAddress) { Set> entrySet = broadcastMap.entrySet(); - + Iterator> iterator = entrySet.iterator(); int i = 0; while (iterator.hasNext()) { @@ -994,8 +1017,8 @@ public int resetBroadcasts(String hostAddress) { i++; } } - - + + return i; } @@ -1043,7 +1066,7 @@ public boolean deleteWebRTCViewerInfo(String viewerId) { webRTCViewerMap.remove(viewerId); return true; } - + @Override public boolean updateStreamMetaData(String streamId, String metaData) { Broadcast broadcast = broadcastMap.get(streamId); @@ -1055,12 +1078,12 @@ public boolean updateStreamMetaData(String streamId, String metaData) { } return result; } - + @Override public SubscriberMetadata getSubscriberMetaData(String subscriberId) { return subscriberMetadataMap.get(subscriberId); } - + @Override public void putSubscriberMetaData(String subscriberId, SubscriberMetadata subscriberMetadata) { subscriberMetadata.setSubscriberId(subscriberId); @@ -1076,7 +1099,7 @@ public void migrateConferenceRoomsToBroadcasts() { public List getSubtracks(String mainTrackId, int offset, int size, String role) { return getSubtracks(mainTrackId, offset, size, role, null); } - + @Override public List getSubtracks(String mainTrackId, int offset, int size, String role, String status) { List subtracks = new ArrayList<>(); @@ -1090,7 +1113,7 @@ public List getSubtracks(String mainTrackId, int offset, int size, St } return subtracks.subList(offset, Math.min(offset + size, subtracks.size())); } - + @Override public long getSubtrackCount(@Nonnull String mainTrackId, String role, String status) { int count = 0; @@ -1105,7 +1128,7 @@ public long getSubtrackCount(@Nonnull String mainTrackId, String role, String st } return count; } - + @Override public long getActiveSubtracksCount(String mainTrackId, String role) { int count = 0; @@ -1121,7 +1144,7 @@ public long getActiveSubtracksCount(String mainTrackId, String role) { } return count; } - + @Override public List getActiveSubtracks(String mainTrackId, String role) { @@ -1138,10 +1161,10 @@ public List getActiveSubtracks(String mainTrackId, String role) } return subtracks; } - + @Override public boolean hasSubtracks(String streamId) { - + for (Broadcast broadcast : broadcastMap.values()) { if (streamId.equals(broadcast.getMainTrackStreamId()) ) diff --git a/src/main/java/io/antmedia/datastore/db/MapBasedDataStore.java b/src/main/java/io/antmedia/datastore/db/MapBasedDataStore.java index 0cada38cf..b2b5200ec 100644 --- a/src/main/java/io/antmedia/datastore/db/MapBasedDataStore.java +++ b/src/main/java/io/antmedia/datastore/db/MapBasedDataStore.java @@ -25,6 +25,7 @@ import io.antmedia.datastore.db.types.Broadcast; import io.antmedia.datastore.db.types.BroadcastUpdate; import io.antmedia.datastore.db.types.ConferenceRoom; +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.StreamInfo; @@ -46,6 +47,7 @@ public abstract class MapBasedDataStore extends DataStore { protected Map detectionMap; protected Map tokenMap; protected Map subscriberMap; + protected Map connectionEventsMap; protected Map conferenceRoomMap; protected Map webRTCViewerMap; protected Map subscriberMetadataMap; @@ -54,13 +56,13 @@ public abstract class MapBasedDataStore extends DataStore { protected Gson gson; protected String dbName; - + protected static Logger logger = LoggerFactory.getLogger(MapBasedDataStore.class); public MapBasedDataStore(String dbName) { this.dbName = dbName; - + GsonBuilder builder = new GsonBuilder(); gson = builder.create(); @@ -70,14 +72,14 @@ public MapBasedDataStore(String dbName) { @Override public String save(Broadcast broadcast) { - String streamId = null; + String streamId = null; synchronized (this) { if (broadcast != null) { Broadcast updatedBroadcast = super.saveBroadcast(broadcast); streamId = updatedBroadcast.getStreamId(); map.put(updatedBroadcast.getStreamId(), gson.toJson(updatedBroadcast)); } - return streamId; + return streamId; } } @@ -108,14 +110,14 @@ public boolean updateStatus(String id, String status) { broadcast.setDashViewerCount(0); } setBroadcastToMap(broadcast, id); - + result = true; } } } return result; } - + @Override public boolean updateVoDProcessStatus(String id, String status) { boolean result = false; @@ -134,23 +136,7 @@ else if (VoD.PROCESS_STATUS_FAILED.equals(status) || VoD.PROCESS_STATUS_FINISHED result = true; } } - - return result; - } - @Override - public boolean updateDuration(String id, long duration) { - boolean result = false; - synchronized (this) { - if (id != null) { - Broadcast broadcast = getBroadcastFromMap(id); - if (broadcast != null) { - broadcast.setDuration(duration); - setBroadcastToMap(broadcast, id); - result = true; - } - } - } return result; } @@ -180,7 +166,7 @@ public boolean removeEndpoint(String id, Endpoint endpoint, boolean checkRTMPUrl boolean result = false; synchronized (this) { if (id != null && endpoint != null) { - + Broadcast broadcast = getBroadcastFromMap(id); if (broadcast != null) { @@ -240,7 +226,7 @@ public long getBroadcastCount() { public long getActiveBroadcastCount() { return super.getActiveBroadcastCount(map, gson, null); } - + public List getActiveBroadcastList(String hostAddress) { return super.getActiveBroadcastList(map, gson, hostAddress); } @@ -461,26 +447,6 @@ public int fetchUserVodList(File filedir) { return numberOfSavedFiles; } - @Override - protected boolean updateSourceQualityParametersLocal(String id, String quality, double speed, int pendingPacketQueue) { - boolean result = false; - synchronized (this) { - if (id != null) { - Broadcast broadcast = getBroadcastFromMap(id); - if(broadcast != null) { - broadcast.setSpeed(speed); - if (quality != null) { - broadcast.setQuality(quality); - } - broadcast.setPendingPacketSize(pendingPacketQueue); - setBroadcastToMap(broadcast, id); - result = true; - } - } - } - return result; - } - @Override public long getTotalBroadcastNumber() { return super.getTotalBroadcastNumber(map); @@ -490,7 +456,7 @@ public long getTotalBroadcastNumber() { public long getPartialBroadcastNumber(String search) { return getBroadcastListV2(null ,search).size(); } - + @Override public void saveDetection(String id, long timeElapsed, List detectedObjects) { synchronized (this) { @@ -563,7 +529,7 @@ protected synchronized boolean updateHLSViewerCountLocal(String streamId, int di int hlsViewerCount = broadcast.getHlsViewerCount(); hlsViewerCount += diffCount; broadcast.setHlsViewerCount(hlsViewerCount); - + setBroadcastToMap(broadcast, streamId); result = true; } @@ -571,7 +537,7 @@ protected synchronized boolean updateHLSViewerCountLocal(String streamId, int di } return result; } - + @Override protected synchronized boolean updateDASHViewerCountLocal(String streamId, int diffCount) { boolean result = false; @@ -580,7 +546,7 @@ protected synchronized boolean updateDASHViewerCountLocal(String streamId, int d if (streamId != null) { Broadcast broadcast = get(streamId); if (broadcast != null) { - + int dashViewerCount = broadcast.getDashViewerCount(); dashViewerCount += diffCount; broadcast.setDashViewerCount(dashViewerCount); @@ -599,7 +565,7 @@ protected synchronized boolean updateWebRTCViewerCountLocal(String streamId, boo if (streamId != null) { Broadcast broadcast = get(streamId); if (broadcast != null) { - + int webRTCViewerCount = broadcast.getWebRTCViewerCount(); if (increment) { webRTCViewerCount++; @@ -640,12 +606,12 @@ protected synchronized boolean updateRtmpViewerCountLocal(String streamId, boole } return result; } - + public void clearStreamInfoList(String streamId) { //used in mongo for cluster mode. useless here. } - - + + public List getStreamInfoList(String streamId) { return new ArrayList<>(); } @@ -759,31 +725,92 @@ public boolean addSubscriber(String streamId, Subscriber subscriber) { return result; } + + @Override + public List getConnectionEvents(String streamId, String subscriberId, int offset, int size) { + Collection values = connectionEventsMap.values(); + List list = new ArrayList<>(); + List returnList = new ArrayList<>(); + + int t = 0; + int itemCount = 0; + if (size > MAX_ITEM_IN_ONE_LIST) { + size = MAX_ITEM_IN_ONE_LIST; + } + if (offset < 0) { + offset = 0; + } + + Iterator iterator = values.iterator(); - public boolean blockSubscriber(String streamId, String subscriberId, String blockedType, int seconds) { + while (iterator.hasNext()) { + ConnectionEvent event = gson.fromJson(iterator.next(), ConnectionEvent.class); + + if (streamId.equals(event.getStreamId()) + && (StringUtils.isBlank(subscriberId) || subscriberId.equals(event.getSubscriberId()))) { + list.add(event); + } + } + + Iterator listIterator = list.iterator(); + + while (itemCount < size && listIterator.hasNext()) { + if (t < offset) { + t++; + listIterator.next(); + } else { + + returnList.add(listIterator.next()); + itemCount++; + + } + } + + return returnList; + } + + + + @Override + protected boolean addConnectionEvent(ConnectionEvent event) { boolean result = false; + if (event != null && StringUtils.isNoneBlank(event.getStreamId(), event.getSubscriberId())) { synchronized (this) { + try { + connectionEventsMap.put(Subscriber.getDBKey(event.getStreamId(), event.getSubscriberId()), gson.toJson(event)); + result = true; + } catch (Exception e) { + logger.error(ExceptionUtils.getStackTrace(e)); + } + } + } + return result; + } - if (streamId != null && subscriberId != null) { - try { - Subscriber subscriber = gson.fromJson(subscriberMap.get(Subscriber.getDBKey(streamId, subscriberId)), Subscriber.class); - if (subscriber == null) { - subscriber = new Subscriber(); - subscriber.setStreamId(streamId); - subscriber.setSubscriberId(subscriberId); - } - subscriber.setBlockedType(blockedType); - subscriber.setBlockedUntilUnitTimeStampMs(System.currentTimeMillis() + (seconds * 1000)); + public boolean blockSubscriber(String streamId, String subscriberId, String blockedType, int seconds) { + boolean result = false; + synchronized (this) { + if (streamId != null && subscriberId != null) { + try { + Subscriber subscriber = gson.fromJson(subscriberMap.get(Subscriber.getDBKey(streamId, subscriberId)), Subscriber.class); + if (subscriber == null) { + subscriber = new Subscriber(); + subscriber.setStreamId(streamId); + subscriber.setSubscriberId(subscriberId); + } + subscriber.setBlockedType(blockedType); + subscriber.setBlockedUntilUnitTimeStampMs(System.currentTimeMillis() + (seconds * 1000)); - subscriberMap.put(subscriber.getSubscriberKey(), gson.toJson(subscriber)); - result = true; - } catch (Exception e) { - logger.error(ExceptionUtils.getStackTrace(e)); - } + subscriberMap.put(subscriber.getSubscriberKey(), gson.toJson(subscriber)); + + result = true; + } catch (Exception e) { + logger.error(ExceptionUtils.getStackTrace(e)); } } + } return result; @@ -859,26 +886,26 @@ public boolean resetSubscribersConnectedStatus() { } } } - + @Override public int resetBroadcasts(String hostAddress) { synchronized (this) { - + int size = map.size(); int updateOperations = 0; int zombieStreamCount = 0; - + Set> entrySet = map.entrySet(); - + Iterator> iterator = entrySet.iterator(); int i = 0; while (iterator.hasNext()) { Entry next = iterator.next(); - + if (next != null) { Broadcast broadcast = gson.fromJson(next.getValue(), Broadcast.class); i++; - + if (broadcast.getOriginAdress() == null || broadcast.getOriginAdress().isEmpty() || hostAddress.equals(broadcast.getOriginAdress())) { @@ -898,7 +925,7 @@ public int resetBroadcasts(String hostAddress) { } } } - + if (i > size) { logger.error( "Inconsistency in DB found in resetting broadcasts for dbName:{}", @@ -906,14 +933,14 @@ public int resetBroadcasts(String hostAddress) { break; } } - + logger.info("Reset broadcasts result in deleting {} zombi streams and {} update operations", zombieStreamCount, updateOperations); - + return updateOperations + zombieStreamCount; } } - + @Override public void saveStreamInfo(StreamInfo streamInfo) { //no need to implement this method, it is used in cluster mode @@ -1061,7 +1088,7 @@ public boolean deleteWebRTCViewerInfo(String viewerId) { return webRTCViewerMap.remove(viewerId) != null; } } - + @Override public boolean updateStreamMetaData(String streamId, String metaData) { boolean result = false; @@ -1077,20 +1104,20 @@ public boolean updateStreamMetaData(String streamId, String metaData) { } return result; } - - + + public void setBroadcastToMap(Broadcast broadcast, String streamId){ - + String jsonVal = gson.toJson(broadcast); String previousValue = null; previousValue = map.replace(streamId, jsonVal); - + streamId = streamId.replaceAll(REPLACE_CHARS_REGEX, "_"); logger.debug("replacing id {} having value {} to {}", streamId, previousValue, jsonVal); } - + public Broadcast getBroadcastFromMap(String streamId) { String jsonString = map.get(streamId); @@ -1099,13 +1126,13 @@ public Broadcast getBroadcastFromMap(String streamId) } return null; } - + @Override public void putSubscriberMetaData(String subscriberId, SubscriberMetadata metadata) { metadata.setSubscriberId(subscriberId); subscriberMetadataMap.put(subscriberId, gson.toJson(metadata)); } - + @Override public SubscriberMetadata getSubscriberMetaData(String subscriberId) { String jsonString = subscriberMetadataMap.get(subscriberId); @@ -1114,7 +1141,7 @@ public SubscriberMetadata getSubscriberMetaData(String subscriberId) { } return null; } - + public void migrateConferenceRoomsToBroadcasts() { if (conferenceRoomMap.values() != null) { @@ -1122,7 +1149,7 @@ public void migrateConferenceRoomsToBroadcasts() for (String conferenceString : conferenceRoomMap.values()) { ConferenceRoom room = gson.fromJson(conferenceString, ConferenceRoom.class); - + try { Broadcast broadcast = conferenceToBroadcast(room); if (get(broadcast.getStreamId()) == null) @@ -1135,7 +1162,7 @@ public void migrateConferenceRoomsToBroadcasts() logger.error(ExceptionUtils.getStackTrace(e)); } } - + for (String roomId : roomIdList) { conferenceRoomMap.remove(roomId); } @@ -1145,7 +1172,7 @@ public void migrateConferenceRoomsToBroadcasts() public Map getConferenceRoomMap() { return conferenceRoomMap; } - + @Override public List getSubtracks(String mainTrackId, int offset, int size, String role) { return getSubtracks(mainTrackId, offset, size, role, null); @@ -1167,10 +1194,10 @@ public List getSubtracks(String mainTrackId, int offset, int size, St } return subtracks.subList(offset, Math.min(offset + size, subtracks.size())); } - + @Override public long getSubtrackCount(String mainTrackId, String role, String status) { - + int count = 0; synchronized (this) { for (String broadcastString : map.values()) { @@ -1183,9 +1210,9 @@ public long getSubtrackCount(String mainTrackId, String role, String status) { } } return count; - + } - + @Override public long getActiveSubtracksCount(String mainTrackId, String role) { List subtracks = new ArrayList<>(); @@ -1203,10 +1230,10 @@ public long getActiveSubtracksCount(String mainTrackId, String role) { } } } - + return count; } - + @Override public List getActiveSubtracks(String mainTrackId, String role) { List subtracks = new ArrayList<>(); @@ -1224,13 +1251,13 @@ public List getActiveSubtracks(String mainTrackId, String role) { } } } - + return subtracks; } - + @Override public boolean hasSubtracks(String streamId) { - + synchronized (this) { for (String broadcastString : map.values()) { @@ -1241,7 +1268,7 @@ public boolean hasSubtracks(String streamId) { } } } - + return false; } } diff --git a/src/main/java/io/antmedia/datastore/db/MapDBStore.java b/src/main/java/io/antmedia/datastore/db/MapDBStore.java index 3558a72a2..fda32f4b2 100644 --- a/src/main/java/io/antmedia/datastore/db/MapDBStore.java +++ b/src/main/java/io/antmedia/datastore/db/MapDBStore.java @@ -44,7 +44,7 @@ public class MapDBStore extends MapBasedDataStore { private static final String CONFERENCE_ROOM_MAP_NAME = "CONFERENCE_ROOM"; private static final String WEBRTC_VIEWER = "WEBRTC_VIEWER"; private static final String SUBSCRIBER_METADATA = "SUBSCRIBER_METADATA"; - + private static final String CONNECTION_EVENTS = "CONNECTION_EVENTS"; public MapDBStore(String dbName, Vertx vertx) { @@ -84,6 +84,9 @@ public MapDBStore(String dbName, Vertx vertx) { subscriberMetadataMap = db.treeMap(SUBSCRIBER_METADATA).keySerializer(Serializer.STRING).valueSerializer(Serializer.STRING) .counterEnable().createOrOpen(); + + connectionEventsMap = db.treeMap(CONNECTION_EVENTS).keySerializer(Serializer.STRING) + .valueSerializer(Serializer.STRING).counterEnable().createOrOpen(); timerId = vertx.setPeriodic(5000, id -> diff --git a/src/main/java/io/antmedia/datastore/db/MongoStore.java b/src/main/java/io/antmedia/datastore/db/MongoStore.java index 0083f43bd..1fe51f12a 100644 --- a/src/main/java/io/antmedia/datastore/db/MongoStore.java +++ b/src/main/java/io/antmedia/datastore/db/MongoStore.java @@ -53,6 +53,7 @@ import io.antmedia.datastore.db.types.Broadcast; import io.antmedia.datastore.db.types.BroadcastUpdate; import io.antmedia.datastore.db.types.ConferenceRoom; +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; @@ -375,25 +376,6 @@ else if (VoD.PROCESS_STATUS_FAILED.equals(status) || VoD.PROCESS_STATUS_FINISHED return false; } - /* - * (non-Javadoc) - * - * @see io.antmedia.datastore.db.IDataStore#updateDuration(java.lang.String, - * long) - */ - @Override - public boolean updateDuration(String id, long duration) { - synchronized(this) { - try { - executedQueryCount+=2; - Query query = datastore.find(Broadcast.class).filter(Filters.eq(STREAM_ID, id)); - return query.update(set(DURATION, duration)).execute().getMatchedCount() == 1; - } catch (Exception e) { - logger.error(ExceptionUtils.getStackTrace(e)); - } - } - return false; - } /* * (non-Javadoc) @@ -487,6 +469,29 @@ private boolean checkIfRegexValid(String regex) { return false; } } + + @Override + public List getConnectionEvents(String streamId, String subscriberId, int offset, int size) { + synchronized (this) { + try { + executedQueryCount++; + Query query = subscriberDatastore.find(ConnectionEvent.class) + .filter(Filters.eq(STREAM_ID, streamId)); + + + if (StringUtils.isNotBlank(subscriberId)) { + query.filter(Filters.eq(SUBSCRIBER_ID, subscriberId)); + } + + FindOptions findingOptions = new FindOptions().skip(offset).limit(size); + + return query.iterator(findingOptions).toList(); + } catch (Exception e) { + logger.error(ExceptionUtils.getStackTrace(e)); + } + } + return null; + } @Override @@ -728,29 +733,6 @@ public int fetchUserVodList(File userfile) { } - - - @Override - public boolean updateSourceQualityParametersLocal(String id, String quality, double speed, int pendingPacketQueue) { - synchronized(this) { - try { - executedQueryCount+=2; - Query query = datastore.find(Broadcast.class).filter(Filters.eq(STREAM_ID, id)); - List updateOperators = new ArrayList<>(); - updateOperators.add(set("speed", speed)); - updateOperators.add(set("pendingPacketSize", pendingPacketQueue)); - - if (quality != null) { - updateOperators.add(set("quality", quality)); - } - return query.update(updateOperators).execute().getModifiedCount() == 1; - } catch (Exception e) { - logger.error(ExceptionUtils.getStackTrace(e)); - } - } - return false; - } - @Override public long getTotalBroadcastNumber() { synchronized(this) { @@ -1279,6 +1261,23 @@ public boolean addSubscriber(String streamId, Subscriber subscriber) { return result; } + + @Override + protected boolean addConnectionEvent(ConnectionEvent event) { + boolean result = false; + if (event != null && StringUtils.isNoneBlank(event.getStreamId(), event.getSubscriberId())) { + + try { + executedQueryCount++; + subscriberDatastore.save(event); + result = true; + } + catch (Exception e) { + logger.error(ExceptionUtils.getStackTrace(e)); + } + } + return result; + } @Override public boolean deleteSubscriber(String streamId, String subscriberId) { diff --git a/src/main/java/io/antmedia/datastore/db/RedisStore.java b/src/main/java/io/antmedia/datastore/db/RedisStore.java index 90addd0f2..df5d4ad95 100644 --- a/src/main/java/io/antmedia/datastore/db/RedisStore.java +++ b/src/main/java/io/antmedia/datastore/db/RedisStore.java @@ -64,6 +64,7 @@ public RedisStore(String redisConnectionUrl, String dbName) { streamInfoMap = redisson.getMap(dbName+"StreamInfo"); p2pMap = redisson.getMap(dbName+"P2P"); subscriberMetadataMap = redisson.getMap(dbName+"SubscriberMetaData"); + connectionEventsMap = redisson.getMap(dbName+"ConnectionEvents"); available = true; } diff --git a/src/main/java/io/antmedia/datastore/db/types/ConnectionEvent.java b/src/main/java/io/antmedia/datastore/db/types/ConnectionEvent.java index afa207658..d54f2cc49 100644 --- a/src/main/java/io/antmedia/datastore/db/types/ConnectionEvent.java +++ b/src/main/java/io/antmedia/datastore/db/types/ConnectionEvent.java @@ -3,11 +3,19 @@ import com.fasterxml.jackson.annotation.JsonIgnore; import dev.morphia.annotations.Entity; +import dev.morphia.annotations.Field; +import dev.morphia.annotations.Index; +import dev.morphia.annotations.IndexOptions; +import dev.morphia.annotations.Indexes; +import dev.morphia.utils.IndexType; import io.swagger.v3.oas.annotations.media.Schema; @Schema(description = "Connection Event for the subscriber") @Entity +@Indexes({ + @Index(fields = @Field("streamId")), + @Index(fields = @Field("subscriberId")) }) public class ConnectionEvent { @JsonIgnore public static final String CONNECTED_EVENT = "connected"; @@ -33,6 +41,12 @@ public class ConnectionEvent { @Schema(description = "Event protocol. It can be webrtc, hls, dash") private String eventProtocol; + + @Schema(description = "Stream id of the event") + private String streamId; + + @Schema(description = "Subscriber id of the event") + private String subscriberId; public long getTimestamp() { return timestamp; @@ -73,4 +87,32 @@ public String getEventProtocol() { public void setEventProtocol(String eventProtocol) { this.eventProtocol = eventProtocol; } + + /** + * @return the subscriberId + */ + public String getSubscriberId() { + return subscriberId; + } + + /** + * @param subscriberId the subscriberId to set + */ + public void setSubscriberId(String subscriberId) { + this.subscriberId = subscriberId; + } + + /** + * @return the streamId + */ + public String getStreamId() { + return streamId; + } + + /** + * @param streamId the streamId to set + */ + public void setStreamId(String streamId) { + this.streamId = streamId; + } } diff --git a/src/main/java/io/antmedia/datastore/db/types/Subscriber.java b/src/main/java/io/antmedia/datastore/db/types/Subscriber.java index 3cadf9efa..e51a3bc81 100644 --- a/src/main/java/io/antmedia/datastore/db/types/Subscriber.java +++ b/src/main/java/io/antmedia/datastore/db/types/Subscriber.java @@ -45,9 +45,10 @@ public class Subscriber { private String streamId; /** - * Stats for this subscriber + * Stats for this subscriber. Get connection events directly instead of SubscriberStats */ @Schema(description = "Stats for this subscriber") + @Deprecated (forRemoval = true, since = "2.12.0") private SubscriberStats stats = new SubscriberStats(); /** @@ -97,6 +98,18 @@ public class Subscriber { private long blockedUntilUnitTimeStampMs = 0; private String registeredNodeIp; + + /** + * The average video bitrate for a subscriber. + */ + @Schema(description = "Average video bitrate for a subscriber") + private long avgVideoBitrate; + + /** + * The average audio bitrate for a subscriber. + */ + @Schema(description = "Average audio bitrate for a subscriber") + private long avgAudioBitrate; public String getSubscriberId() { return subscriberId; @@ -214,4 +227,32 @@ public long getBlockedUntilUnitTimeStampMs() { public void setBlockedUntilUnitTimeStampMs(long blockedUntilUnitTimeStampMs) { this.blockedUntilUnitTimeStampMs = blockedUntilUnitTimeStampMs; } + + /** + * @return the avgVideoBitrate + */ + public long getAvgVideoBitrate() { + return avgVideoBitrate; + } + + /** + * @param avgVideoBitrate the avgVideoBitrate to set + */ + public void setAvgVideoBitrate(long avgVideoBitrate) { + this.avgVideoBitrate = avgVideoBitrate; + } + + /** + * @return the avgAudioBitrate + */ + public long getAvgAudioBitrate() { + return avgAudioBitrate; + } + + /** + * @param avgAudioBitrate the avgAudioBitrate to set + */ + public void setAvgAudioBitrate(long avgAudioBitrate) { + this.avgAudioBitrate = avgAudioBitrate; + } } diff --git a/src/main/java/io/antmedia/datastore/db/types/SubscriberStats.java b/src/main/java/io/antmedia/datastore/db/types/SubscriberStats.java index 2fd485441..5ad6753d4 100644 --- a/src/main/java/io/antmedia/datastore/db/types/SubscriberStats.java +++ b/src/main/java/io/antmedia/datastore/db/types/SubscriberStats.java @@ -6,8 +6,12 @@ import dev.morphia.annotations.Entity; import io.swagger.v3.oas.annotations.media.Schema; +/** + * Use Connection Event and Subscriber class instead of this class. No need to use this class anymore + */ @Schema(description="Statistics for each subsciber to the stream") @Entity +@Deprecated public class SubscriberStats { /** @@ -23,9 +27,10 @@ public class SubscriberStats { private String streamId; /** - * The connection events happened for this subscriber. + * The connection events happened for this subscriber. Use ConnectionEvent class instead of this field */ @Schema(description = "List of connection events") + @Deprecated(since = "2.12.0", forRemoval = true) private List connectionEvents = new ArrayList<>(); /** diff --git a/src/main/java/io/antmedia/rest/BroadcastRestService.java b/src/main/java/io/antmedia/rest/BroadcastRestService.java index 3e013c711..e67717a53 100644 --- a/src/main/java/io/antmedia/rest/BroadcastRestService.java +++ b/src/main/java/io/antmedia/rest/BroadcastRestService.java @@ -3,6 +3,7 @@ import java.net.URI; import java.time.Instant; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import java.util.concurrent.CompletableFuture; @@ -26,6 +27,7 @@ import io.antmedia.datastore.db.types.BroadcastUpdate; import io.antmedia.datastore.db.types.Broadcast.PlayListItem; import io.antmedia.datastore.db.types.ConferenceRoom; +import io.antmedia.datastore.db.types.ConnectionEvent; import io.antmedia.datastore.db.types.Endpoint; import io.antmedia.datastore.db.types.Subscriber; import io.antmedia.datastore.db.types.SubscriberStats; @@ -890,8 +892,8 @@ public List listSubscriberV2(@Parameter(description = "the id of the return subscribers; } - @Operation(summary = "Retrieve all subscriber statistics of the requested stream", - description = "Fetches comprehensive statistics for all subscribers of the specified stream.", + @Operation(summary = "Retrieve all subscriber statistics of the requested stream. Deprecated use connection-events method. ", + description = "Fetches comprehensive statistics for all subscribers of the specified stream. Deprecated use connection-events method. This method is kept for backward compatibility and getting old records. New records saved and retrieved with connection-events method because there is a schema design causes performance issues", responses = { @ApiResponse(responseCode = "200", description = "List of subscriber statistics", content = @Content( @@ -903,6 +905,7 @@ public List listSubscriberV2(@Parameter(description = "the id of the @GET @Path("/{id}/subscriber-stats/list/{offset}/{size}") @Produces(MediaType.APPLICATION_JSON) + @Deprecated(since="2.12.0", forRemoval = true) public List listSubscriberStatsV2(@Parameter(description = "the id of the stream", required = true) @PathParam("id") String streamId, @Parameter(description = "the starting point of the list", required = true) @PathParam("offset") int offset, @Parameter(description = "size of the return list (max:50 )", required = true) @PathParam("size") int size) { @@ -912,6 +915,31 @@ public List listSubscriberStatsV2(@Parameter(description = "the } return subscriberStats; } + + @Operation(summary = "Retrieve all subscriber statistics of the requested stream. Deprecated ", + description = "Fetches comprehensive statistics for all subscribers of the specified stream.", + responses = { + @ApiResponse(responseCode = "200", description = "List of subscriber statistics", + content = @Content( + mediaType = "application/json", + schema = @Schema(implementation = SubscriberStats.class, type = "array") + )) + } + ) + @GET + @Path("/{id}/connection-events/{offset}/{size}") + @Produces(MediaType.APPLICATION_JSON) + public List getConnectionEvents(@Parameter(description = "the id of the stream", required = true) @PathParam("id") String streamId, + @Parameter(description = "the starting point of the list", required = true) @PathParam("offset") int offset, + @Parameter(description = "size of the return list (max:50 )", required = true) @PathParam("size") int size, + @Parameter(description = "subscriberId to filter the connections events", required=false) @QueryParam("subscriberId") String subscriberId) { + List connectionEvents = new ArrayList<>(); + if(StringUtils.isNotBlank(streamId)) { + connectionEvents = getDataStore().getConnectionEvents(streamId, subscriberId, offset, size); + } + return connectionEvents; + } + @Operation(summary = "Add Subscriber to the requested stream", description = "Adds a subscriber to the requested stream. If the subscriber's type is 'publish', they can also play the stream, which is critical in conferencing. If the subscriber's type is 'play', they can only play the stream. If 'b32Secret' is not set, it will default to the AppSettings. The length of 'b32Secret' should be a multiple of 8 and use base32 characters A–Z, 2–7.", diff --git a/src/test/java/io/antmedia/test/AppSettingsUnitTest.java b/src/test/java/io/antmedia/test/AppSettingsUnitTest.java index 9c74ce83a..c9130d042 100644 --- a/src/test/java/io/antmedia/test/AppSettingsUnitTest.java +++ b/src/test/java/io/antmedia/test/AppSettingsUnitTest.java @@ -643,6 +643,8 @@ public void testUnsetAppSettings(AppSettings appSettings) { assertEquals("", appSettings.getSubFolder()); appSettings.setSubFolder("test/folder"); assertEquals("test/folder", appSettings.getSubFolder()); + + assertFalse(appSettings.isWriteSubscriberEventsToDatastore()); //if we add a new field, we just need to check its default value in this test @@ -650,7 +652,7 @@ public void testUnsetAppSettings(AppSettings appSettings) { //by also checking its default value. assertEquals("New field is added to settings. PAY ATTENTION: Please CHECK ITS DEFAULT VALUE and fix the number of fields.", - 193, numberOfFields); + 194, numberOfFields); } diff --git a/src/test/java/io/antmedia/test/db/DBStoresUnitTest.java b/src/test/java/io/antmedia/test/db/DBStoresUnitTest.java index 710a0524b..22f7e3435 100644 --- a/src/test/java/io/antmedia/test/db/DBStoresUnitTest.java +++ b/src/test/java/io/antmedia/test/db/DBStoresUnitTest.java @@ -941,8 +941,6 @@ public void testNullCheck(DataStore dataStore) { assertNull(dataStore.get(null)); - assertFalse(dataStore.updateDuration(null, 100000)); - assertFalse(dataStore.updateStatus(null, "created")); assertFalse(dataStore.addEndpoint(null, null)); @@ -1757,8 +1755,11 @@ public void testSimpleOperations(DataStore dataStore) { assertEquals(listenerHookURL, broadcast2.getListenerHookURL()); assertFalse(broadcast2.isPlaylistLoopEnabled()); assertEquals(speed, broadcast2.getSpeed(), 0.1); + + BroadcastUpdate update = new BroadcastUpdate(); + update.setDuration(100000L); - result = dataStore.updateDuration(broadcast.getStreamId().toString(), 100000); + result = dataStore.updateBroadcastFields(broadcast.getStreamId().toString(), update); assertTrue(result); broadcast2 = dataStore.get(key); @@ -1828,13 +1829,22 @@ public void testSimpleOperations(DataStore dataStore) { assertEquals(broadcast3.getStreamId(), dataStore.get(broadcast3.getStreamId()).getStreamId()); - result = dataStore.updateSourceQualityParameters(broadcast3.getStreamId(), null, 0.1, 0); + update = new BroadcastUpdate(); + update.setQuality("poor"); + update.setSpeed(0.1); + update.setPendingPacketSize(0); + + result = dataStore.updateBroadcastFields(broadcast.getStreamId().toString(), update); assertTrue(result); //it's poor because it's not updated because of null assertEquals("poor", dataStore.get(broadcast3.getStreamId()).getQuality()); - result = dataStore.updateSourceQualityParameters(broadcast3.getStreamId(), "good", 0, 0); + update = new BroadcastUpdate(); + update.setQuality("good"); + update.setSpeed(0.0); + update.setPendingPacketSize(0); + result = dataStore.updateBroadcastFields(broadcast.getStreamId().toString(), update); assertTrue(result); assertEquals("good", dataStore.get(broadcast3.getStreamId()).getQuality()); @@ -2373,7 +2383,6 @@ public void testDontWriteStatsToDB (DataStore dataStore) { testDontUpdateHLSViewerStats(dataStore); testDontUpdateDASHViewerStats(dataStore); testDontUpdateWebRTCViewerStats(dataStore); - testDontUpdateSourceQualityParameters(dataStore); } public void testDontUpdateRtmpViewerStats(DataStore dataStore) { @@ -2412,15 +2421,6 @@ public void testDontUpdateWebRTCViewerStats(DataStore dataStore) { assertEquals(0, dataStore.get(key).getWebRTCViewerCount()); } - public void testDontUpdateSourceQualityParameters(DataStore dataStore) { - Broadcast broadcast = new Broadcast(); - broadcast.setName("test"); - broadcast.setQuality("poor"); - String key = dataStore.save(broadcast); - assertFalse(dataStore.updateSourceQualityParameters(key, "good", 0, 0)); - assertEquals("poor", dataStore.get(key).getQuality()); - } - private DataStore createDB(String type, boolean writeStats) { DataStoreFactory dsf = new DataStoreFactory(); From ec4bba0af5ad60352e2f0e3be7d5a46e8a9f4774 Mon Sep 17 00:00:00 2001 From: mekya Date: Sat, 7 Dec 2024 23:19:44 +0300 Subject: [PATCH 2/5] Fix failing test and add test codes --- .../io/antmedia/datastore/db/DataStore.java | 63 +++++++++++----- .../datastore/db/DataStoreFactory.java | 8 ++ .../datastore/db/InMemoryDataStore.java | 41 +++++++--- .../datastore/db/MapBasedDataStore.java | 75 ++++++++++++++----- .../io/antmedia/datastore/db/MongoStore.java | 16 +++- .../datastore/db/types/ConferenceRoom.java | 1 + .../datastore/db/types/ConnectionEvent.java | 8 ++ .../antmedia/rest/BroadcastRestService.java | 2 +- .../io/antmedia/test/db/DBStoresUnitTest.java | 65 ++++++++++++++-- .../test/statistic/DashViewerStatsTest.java | 20 ++--- .../test/statistic/HlsViewerStatsTest.java | 7 +- 11 files changed, 237 insertions(+), 69 deletions(-) diff --git a/src/main/java/io/antmedia/datastore/db/DataStore.java b/src/main/java/io/antmedia/datastore/db/DataStore.java index c62041636..9d6acccdf 100644 --- a/src/main/java/io/antmedia/datastore/db/DataStore.java +++ b/src/main/java/io/antmedia/datastore/db/DataStore.java @@ -577,8 +577,9 @@ public boolean isSubscriberConnected(String streamId, String subscriberId) { */ public boolean addSubscriberConnectionEvent(String streamId, String subscriberId, ConnectionEvent event) { boolean result = false; - if (writeSubscriberEventsToDatastore) + if (writeSubscriberEventsToDatastore && event != null) { + Subscriber subscriber = getSubscriber(streamId, subscriberId); if (subscriber != null && !StringUtils.isBlank(subscriber.getSubscriberId())) @@ -593,8 +594,11 @@ public boolean addSubscriberConnectionEvent(String streamId, String subscriberId addSubscriber(streamId, subscriber); } - handleConnectionEvent(streamId, subscriberId, event); - result = true; + result = handleConnectionEvent(streamId, subscriberId, event); + } + else { + logger.debug("Not saving subscriber events to datastore because either writeSubscriberEventsToDatastore are false in the settings or event is null." + + "writeSubscriberEventsToDatastore:{} and event is {} null", writeSubscriberEventsToDatastore, event == null ? "" : "not"); } return result; @@ -602,12 +606,15 @@ public boolean addSubscriberConnectionEvent(String streamId, String subscriberId } // helper method used by all datastores - protected void handleConnectionEvent(String streamId, String subscriberId, ConnectionEvent event) + protected boolean handleConnectionEvent(String streamId, String subscriberId, ConnectionEvent event) { - event.setStreamId(streamId); - event.setSubscriberId(subscriberId); - - addConnectionEvent(event); + if (StringUtils.isNoneBlank(subscriberId, streamId)) { + event.setStreamId(streamId); + event.setSubscriberId(subscriberId); + + return addConnectionEvent(event); + } + return false; } protected abstract boolean addConnectionEvent(ConnectionEvent event); @@ -1008,6 +1015,10 @@ protected void updateStreamInfo(Broadcast broadcast, BroadcastUpdate newBroadcas if (newBroadcast.getPendingPacketSize() != null) { broadcast.setPendingPacketSize(newBroadcast.getPendingPacketSize()); } + + if (newBroadcast.getQuality() != null) { + broadcast.setQuality(newBroadcast.getQuality()); + } if (newBroadcast.getRole() != null) { broadcast.setRole(newBroadcast.getRole()); @@ -1502,15 +1513,7 @@ public static Broadcast conferenceToBroadcast(ConferenceRoom conferenceRoom) thr public abstract boolean hasSubtracks(String streamId); - //************************************** - //ATTENTION: Write function descriptions while adding new functions - //************************************** - - //************************************** - //ATTENTION 2: What is the reason you don't add descriptions to the functions? - // Ignore this message if you have added descriptions to the new functions. - // I'm writing to the one who is ignoring this first message - mekya - //************************************** + /** * @@ -1549,5 +1552,31 @@ public boolean isWriteSubscriberEventsToDatastore() { * @return */ public abstract List getConnectionEvents(String streamId, @Nullable String subscriberId, int offset, int size); + + /** + * Simple converter from Collection to List + * @param values + * @return + */ + protected static List getConnectionEventListFromCollection(Collection values) { + List list = new ArrayList<>(); + + for(ConnectionEvent event: values) { + list.add(event); + } + + return list; + + } + + //************************************** + //ATTENTION: Write function above with descriptions while adding new functions + //************************************** + + //************************************** + //ATTENTION 2: What is the reason you don't add descriptions to the functions? + // Ignore this message if you have added descriptions to the new functions. + // I'm writing to the one who is ignoring this first message - mekya + //************************************** } diff --git a/src/main/java/io/antmedia/datastore/db/DataStoreFactory.java b/src/main/java/io/antmedia/datastore/db/DataStoreFactory.java index 3b2379677..39e1634b3 100644 --- a/src/main/java/io/antmedia/datastore/db/DataStoreFactory.java +++ b/src/main/java/io/antmedia/datastore/db/DataStoreFactory.java @@ -152,6 +152,14 @@ public void setApplicationContext(ApplicationContext applicationContext) throws init(); } + + public void setWriteSubscriberEventsToDatastore(boolean writeSubscriberEventsToDatastore) { + this.writeSubscriberEventsToDatastore = writeSubscriberEventsToDatastore; + } + + public boolean isWriteSubscriberEventsToDatastore() { + return writeSubscriberEventsToDatastore; + } } diff --git a/src/main/java/io/antmedia/datastore/db/InMemoryDataStore.java b/src/main/java/io/antmedia/datastore/db/InMemoryDataStore.java index 486c30e7a..b10d4641a 100644 --- a/src/main/java/io/antmedia/datastore/db/InMemoryDataStore.java +++ b/src/main/java/io/antmedia/datastore/db/InMemoryDataStore.java @@ -9,7 +9,9 @@ import java.util.List; import java.util.Map; import java.util.Map.Entry; +import java.util.Queue; import java.util.Set; +import java.util.concurrent.ConcurrentLinkedQueue; import java.util.regex.Pattern; import javax.annotation.Nonnull; @@ -45,7 +47,7 @@ public class InMemoryDataStore extends DataStore { private Map> detectionMap = new LinkedHashMap<>(); private Map tokenMap = new LinkedHashMap<>(); private Map subscriberMap = new LinkedHashMap<>(); - private Map connectionEvents = new LinkedHashMap<>(); + private Map> connectionEvents = new LinkedHashMap<>(); private Map subscriberMetadataMap = new LinkedHashMap<>(); private Map webRTCViewerMap = new LinkedHashMap<>(); @@ -752,8 +754,26 @@ public boolean addSubscriber(String streamId, Subscriber subscriber) { @Override public List getConnectionEvents(String streamId, String subscriberId, int offset, int size) { - Collection values = connectionEvents.values(); - List list = new ArrayList<>(); + + String key = null; + if (StringUtils.isNotBlank(subscriberId)) { + key = Subscriber.getDBKey(streamId, subscriberId); + } + + List list = new ArrayList<>(); + if (key != null) { + Collection values = connectionEvents.get(key); + if (values != null) { + list = getConnectionEventListFromCollection(values); + } + } + else { + Collection> values = connectionEvents.values(); + for (Queue queue : values) { + list.addAll(getConnectionEventListFromCollection(queue)); + } + } + List returnList = new ArrayList<>(); int t = 0; @@ -765,12 +785,6 @@ public List getConnectionEvents(String streamId, String subscri offset = 0; } - for(ConnectionEvent event: values) { - if (streamId.equals(event.getStreamId()) && (StringUtils.isBlank(subscriberId) || subscriberId.equals(event.getSubscriberId()))) { - list.add(event); - } - } - Iterator iterator = list.iterator(); while(itemCount < size && iterator.hasNext()) { @@ -779,7 +793,6 @@ public List getConnectionEvents(String streamId, String subscri iterator.next(); } else { - returnList.add(iterator.next()); itemCount++; } @@ -787,13 +800,17 @@ public List getConnectionEvents(String streamId, String subscri return returnList; } - + @Override public boolean addConnectionEvent(ConnectionEvent connectionEvent) { boolean result = false; if (connectionEvent != null && StringUtils.isNoneBlank(connectionEvent.getStreamId(), connectionEvent.getSubscriberId())) { try { - connectionEvents.put(Subscriber.getDBKey(connectionEvent.getStreamId(), connectionEvent.getSubscriberId()), connectionEvent); + String key = Subscriber.getDBKey(connectionEvent.getStreamId(), connectionEvent.getSubscriberId()); + if (!connectionEvents.containsKey(key)) { + connectionEvents.put(key, new ConcurrentLinkedQueue()); + } + connectionEvents.get(key).add(connectionEvent); result = true; } catch (Exception e) { logger.error(ExceptionUtils.getStackTrace(e)); diff --git a/src/main/java/io/antmedia/datastore/db/MapBasedDataStore.java b/src/main/java/io/antmedia/datastore/db/MapBasedDataStore.java index b2b5200ec..aa1845a2a 100644 --- a/src/main/java/io/antmedia/datastore/db/MapBasedDataStore.java +++ b/src/main/java/io/antmedia/datastore/db/MapBasedDataStore.java @@ -1,6 +1,7 @@ package io.antmedia.datastore.db; import java.io.File; +import java.lang.reflect.Type; import java.time.Instant; import java.util.ArrayList; import java.util.Collection; @@ -8,7 +9,9 @@ import java.util.List; import java.util.Map; import java.util.Map.Entry; +import java.util.Queue; import java.util.Set; +import java.util.concurrent.ConcurrentLinkedQueue; import java.util.regex.Pattern; import org.apache.commons.io.FilenameUtils; @@ -20,6 +23,7 @@ import com.google.gson.Gson; import com.google.gson.GsonBuilder; +import com.google.gson.reflect.TypeToken; import io.antmedia.AntMediaApplicationAdapter; import io.antmedia.datastore.db.types.Broadcast; @@ -725,13 +729,38 @@ public boolean addSubscriber(String streamId, Subscriber subscriber) { return result; } - + @Override public List getConnectionEvents(String streamId, String subscriberId, int offset, int size) { - Collection values = connectionEventsMap.values(); - List list = new ArrayList<>(); - List returnList = new ArrayList<>(); + String key = null; + if (StringUtils.isNotBlank(subscriberId)) { + key = Subscriber.getDBKey(streamId, subscriberId); + } + + List list = new ArrayList<>(); + if (key != null) + { + Type queueType = new TypeToken>() { + }.getType(); + Queue values = gson.fromJson(connectionEventsMap.get(key), queueType); + if (values != null) { + list = getConnectionEventListFromCollection(values); + } + } + else + { + Collection values = connectionEventsMap.values(); + Type queueType = new TypeToken>() { + }.getType(); + + for (String queueString : values) { + Queue queueValues = gson.fromJson(queueString, queueType); + list.addAll(getConnectionEventListFromCollection(queueValues)); + } + } + List returnList = new ArrayList<>(); + int t = 0; int itemCount = 0; if (size > MAX_ITEM_IN_ONE_LIST) { @@ -740,17 +769,6 @@ public List getConnectionEvents(String streamId, String subscri if (offset < 0) { offset = 0; } - - Iterator iterator = values.iterator(); - - while (iterator.hasNext()) { - ConnectionEvent event = gson.fromJson(iterator.next(), ConnectionEvent.class); - - if (streamId.equals(event.getStreamId()) - && (StringUtils.isBlank(subscriberId) || subscriberId.equals(event.getSubscriberId()))) { - list.add(event); - } - } Iterator listIterator = list.iterator(); @@ -765,10 +783,9 @@ public List getConnectionEvents(String streamId, String subscri } } - + return returnList; } - @Override @@ -777,7 +794,24 @@ protected boolean addConnectionEvent(ConnectionEvent event) { if (event != null && StringUtils.isNoneBlank(event.getStreamId(), event.getSubscriberId())) { synchronized (this) { try { - connectionEventsMap.put(Subscriber.getDBKey(event.getStreamId(), event.getSubscriberId()), gson.toJson(event)); + String key = Subscriber.getDBKey(event.getStreamId(), event.getSubscriberId()); + Queue connectionQueue = null; + if (connectionEventsMap.containsKey(key)) + { + String connectionQueueString = connectionEventsMap.get(key); + + Type queueType = new TypeToken>() { + }.getType(); + + connectionQueue = gson.fromJson(connectionQueueString, queueType); + } + else { + connectionQueue = new ConcurrentLinkedQueue<>(); + } + + connectionQueue.add(event); + + connectionEventsMap.put(key, gson.toJson(connectionQueue)); result = true; } catch (Exception e) { logger.error(ExceptionUtils.getStackTrace(e)); @@ -823,6 +857,8 @@ public boolean deleteSubscriber(String streamId, String subscriberId) { synchronized (this) { try { result = subscriberMap.remove(Subscriber.getDBKey(streamId, subscriberId)) != null; + + connectionEventsMap.keySet().removeIf(key -> key.equals(Subscriber.getDBKey(streamId, subscriberId))); } catch (Exception e) { logger.error(ExceptionUtils.getStackTrace(e)); } @@ -850,8 +886,9 @@ public boolean revokeSubscribers(String streamId) { break; } } - } + + connectionEventsMap.keySet().removeIf(key -> key.startsWith(streamId + "-")); } return result; diff --git a/src/main/java/io/antmedia/datastore/db/MongoStore.java b/src/main/java/io/antmedia/datastore/db/MongoStore.java index 1fe51f12a..ba8235244 100644 --- a/src/main/java/io/antmedia/datastore/db/MongoStore.java +++ b/src/main/java/io/antmedia/datastore/db/MongoStore.java @@ -1002,6 +1002,10 @@ public boolean updateBroadcastFields(String streamId, BroadcastUpdate broadcast) updates.add(set(ROLE, broadcast.getRole())); } + if (broadcast.getQuality() != null) { + updates.add(set("quality", broadcast.getQuality())); + } + prepareFields(broadcast, updates); @@ -1286,11 +1290,16 @@ public boolean deleteSubscriber(String streamId, String subscriberId) { try { executedQueryCount++; - Query query = subscriberDatastore.find(Subscriber.class).filter(Filters.eq(STREAM_ID, streamId), Filters.eq("subscriberId", subscriberId)); + Query query = subscriberDatastore.find(Subscriber.class).filter(Filters.eq(STREAM_ID, streamId), Filters.eq(SUBSCRIBER_ID, subscriberId)); result = query.delete().getDeletedCount() == 1; if(result){ getSubscriberCache().evictIfPresent(getSubscriberCacheKey(streamId, subscriberId)); } + + Query queryConnectionEvent = subscriberDatastore.find(ConnectionEvent.class).filter(Filters.eq(STREAM_ID, streamId), Filters.eq(SUBSCRIBER_ID, subscriberId)); + queryConnectionEvent.delete(new DeleteOptions().multi(true)); + + } catch (Exception e) { logger.error(ExceptionUtils.getStackTrace(e)); } @@ -1359,8 +1368,11 @@ public boolean revokeSubscribers(String streamId) { Query query = subscriberDatastore.find(Subscriber.class).filter(Filters.eq(STREAM_ID, streamId)); DeleteResult delete = query.delete(new DeleteOptions().multi(true)); getSubscriberCache().clear(); + + Query queryConnectionEvents = subscriberDatastore.find(ConnectionEvent.class).filter(Filters.eq(STREAM_ID, streamId)); + DeleteResult deleteConnectionEvents = queryConnectionEvents.delete(new DeleteOptions().multi(true)); - return delete.getDeletedCount() >= 1; + return delete.getDeletedCount() >= 1 || deleteConnectionEvents.getDeletedCount() >= 1; } } diff --git a/src/main/java/io/antmedia/datastore/db/types/ConferenceRoom.java b/src/main/java/io/antmedia/datastore/db/types/ConferenceRoom.java index 7e713e63a..a99fcb919 100644 --- a/src/main/java/io/antmedia/datastore/db/types/ConferenceRoom.java +++ b/src/main/java/io/antmedia/datastore/db/types/ConferenceRoom.java @@ -18,6 +18,7 @@ @Entity("ConferenceRoom") @Indexes({ @Index(fields = @Field("roomId")) }) @Schema(description = "The Conference Room class") +@Deprecated(forRemoval = true, since = "2.12.0") public class ConferenceRoom { public static final String MULTI_TRACK_MODE = "multitrack"; diff --git a/src/main/java/io/antmedia/datastore/db/types/ConnectionEvent.java b/src/main/java/io/antmedia/datastore/db/types/ConnectionEvent.java index d54f2cc49..f50412cfa 100644 --- a/src/main/java/io/antmedia/datastore/db/types/ConnectionEvent.java +++ b/src/main/java/io/antmedia/datastore/db/types/ConnectionEvent.java @@ -1,9 +1,12 @@ package io.antmedia.datastore.db.types; +import org.bson.types.ObjectId; + import com.fasterxml.jackson.annotation.JsonIgnore; import dev.morphia.annotations.Entity; import dev.morphia.annotations.Field; +import dev.morphia.annotations.Id; import dev.morphia.annotations.Index; import dev.morphia.annotations.IndexOptions; import dev.morphia.annotations.Indexes; @@ -47,6 +50,11 @@ public class ConnectionEvent { @Schema(description = "Subscriber id of the event") private String subscriberId; + + @JsonIgnore + @Schema(hidden = true) + @Id + private ObjectId dbId; public long getTimestamp() { return timestamp; diff --git a/src/main/java/io/antmedia/rest/BroadcastRestService.java b/src/main/java/io/antmedia/rest/BroadcastRestService.java index e67717a53..9aa890b4d 100644 --- a/src/main/java/io/antmedia/rest/BroadcastRestService.java +++ b/src/main/java/io/antmedia/rest/BroadcastRestService.java @@ -1130,7 +1130,7 @@ public Result blockSubscriber(@Parameter(description = "the id of the stream", r } @Operation(summary = "Removes all subscribers related to the requested stream", - description = "Deletes all subscriber data associated with the specified stream.", + description = "Deletes all subscriber data associated with the specified stream including ConnectionEvents.", responses = { @ApiResponse(responseCode = "200", description = "Result of removing all subscribers", content = @Content( diff --git a/src/test/java/io/antmedia/test/db/DBStoresUnitTest.java b/src/test/java/io/antmedia/test/db/DBStoresUnitTest.java index 22f7e3435..3ce689e10 100644 --- a/src/test/java/io/antmedia/test/db/DBStoresUnitTest.java +++ b/src/test/java/io/antmedia/test/db/DBStoresUnitTest.java @@ -1844,7 +1844,7 @@ public void testSimpleOperations(DataStore dataStore) { update.setQuality("good"); update.setSpeed(0.0); update.setPendingPacketSize(0); - result = dataStore.updateBroadcastFields(broadcast.getStreamId().toString(), update); + result = dataStore.updateBroadcastFields(broadcast3.getStreamId().toString(), update); assertTrue(result); assertEquals("good", dataStore.get(broadcast3.getStreamId()).getQuality()); @@ -2211,6 +2211,13 @@ public void testTimeBasedSubscriberOperations(DataStore store) { // clean db in the begining of the test String streamId = "stream1"; store.revokeSubscribers(streamId); + + //default value must be false + assertFalse(store.isWriteSubscriberEventsToDatastore()); + + + store.setWriteSubscriberEventsToDatastore(true); + // null checks assertFalse(store.addSubscriber("stream1", null)); @@ -2263,25 +2270,48 @@ public void testTimeBasedSubscriberOperations(DataStore store) { assertEquals(subscriberPub.getSubscriberId(), written.getSubscriberId()); assertEquals(subscriberPub.getType(), written.getType()); + ConnectionEvent connected = new ConnectionEvent(); + connected.setEventType(ConnectionEvent.CONNECTED_EVENT); + long eventTime = 20; + connected.setTimestamp(eventTime); + connected.setType(Subscriber.PLAY_TYPE); + String hostAddress = ServerSettings.getLocalHostAddress(); + connected.setInstanceIP(hostAddress); + + + store.addSubscriberConnectionEvent(subscriberPub.getStreamId(), subscriberPub.getSubscriberId(), connected); + + assertEquals(1, store.getConnectionEvents(subscriberPub.getStreamId(), subscriberPub.getSubscriberId(), 0, 50).size()); + assertEquals(1, store.getConnectionEvents(subscriberPub.getStreamId(), null, 0, 50).size()); + + //delete this subscriber - assertTrue(store.deleteSubscriber(streamId, written.getSubscriberId())); + assertTrue(store.deleteSubscriber(subscriberPub.getStreamId(), subscriberPub.getSubscriberId())); + + assertEquals(0, store.getConnectionEvents(subscriberPub.getStreamId(), subscriberPub.getSubscriberId(), 0, 50).size()); + assertEquals(0, store.getConnectionEvents(subscriberPub.getStreamId(), null, 0, 50).size()); subscribers = store.listAllSubscribers(streamId, 0, 10); subscriberStats = store.listAllSubscriberStats(streamId, 0, 10); + //it should be zero because subscriber is deleted assertEquals(0, subscribers.size()); assertEquals(0, subscriberStats.size()); + assertEquals(0, store.getConnectionEvents(streamId, written.getSubscriberId(), 0, 50).size()); + + + //create subscriber again assertTrue(store.addSubscriber(subscriberPlay.getStreamId(), subscriberPlay)); - ConnectionEvent connected = new ConnectionEvent(); + connected = new ConnectionEvent(); connected.setEventType(ConnectionEvent.CONNECTED_EVENT); - long eventTime = 20; + eventTime = 20; connected.setTimestamp(eventTime); connected.setType(Subscriber.PLAY_TYPE); - String hostAddress = ServerSettings.getLocalHostAddress(); + hostAddress = ServerSettings.getLocalHostAddress(); connected.setInstanceIP(hostAddress); ConnectionEvent disconnected = new ConnectionEvent(); @@ -2293,6 +2323,8 @@ public void testTimeBasedSubscriberOperations(DataStore store) { store.addSubscriberConnectionEvent(subscriberPlay.getStreamId(), subscriberPlay.getSubscriberId(), connected); // isConnected should be true assertTrue(store.isSubscriberConnected(subscriberPlay.getStreamId(), subscriberPlay.getSubscriberId())); + assertEquals(1, store.getConnectionEvents(subscriberPlay.getStreamId(), subscriberPlay.getSubscriberId(), 0, 50).size()); + assertEquals(1, store.getConnectionEvents(subscriberPlay.getStreamId(), null, 0, 50).size()); // add disconnected event store.addSubscriberConnectionEvent(subscriberPlay.getStreamId(), subscriberPlay.getSubscriberId(), disconnected); @@ -2304,7 +2336,8 @@ public void testTimeBasedSubscriberOperations(DataStore store) { assertEquals(0, written.getCurrentConcurrentConnections()); // there should be two events with correct order - List events = written.getStats().getConnectionEvents(); + List events = store.getConnectionEvents(written.getStreamId(), written.getSubscriberId(), 0, 50); + assertEquals(2, events.size()); assertEquals(ConnectionEvent.CONNECTED_EVENT, events.get(0).getEventType()); @@ -2313,6 +2346,13 @@ public void testTimeBasedSubscriberOperations(DataStore store) { assertEquals(eventTime, events.get(0).getTimestamp()); assertEquals(ConnectionEvent.DISCONNECTED_EVENT, events.get(1).getEventType()); + connected = new ConnectionEvent(); + connected.setEventType(ConnectionEvent.CONNECTED_EVENT); + eventTime = 20; + connected.setTimestamp(eventTime); + connected.setType(Subscriber.PLAY_TYPE); + hostAddress = ServerSettings.getLocalHostAddress(); + connected.setInstanceIP(hostAddress); // add connected event store.addSubscriberConnectionEvent(subscriberPlay.getStreamId(), subscriberPlay.getSubscriberId(), connected); @@ -2323,9 +2363,22 @@ public void testTimeBasedSubscriberOperations(DataStore store) { assertTrue(store.resetSubscribersConnectedStatus()); // connection status should false again assertFalse(store.isSubscriberConnected(subscriberPlay.getStreamId(), subscriberPlay.getSubscriberId())); + events = store.getConnectionEvents(subscriberPlay.getStreamId(), subscriberPlay.getSubscriberId(), 0, 50); + + assertEquals(3, events.size()); + + events = store.getConnectionEvents(subscriberPlay.getStreamId(), null, 0, 50); + assertEquals(3, events.size()); + store.revokeSubscribers(streamId); + events = store.getConnectionEvents(subscriberPlay.getStreamId(), subscriberPlay.getSubscriberId(), 0, 50); + assertEquals(0, events.size()); + events = store.getConnectionEvents(subscriberPlay.getStreamId(), null, 0, 50); + assertEquals(0, events.size()); + + { //save subscriber again diff --git a/src/test/java/io/antmedia/test/statistic/DashViewerStatsTest.java b/src/test/java/io/antmedia/test/statistic/DashViewerStatsTest.java index 22579954f..3d567ce67 100644 --- a/src/test/java/io/antmedia/test/statistic/DashViewerStatsTest.java +++ b/src/test/java/io/antmedia/test/statistic/DashViewerStatsTest.java @@ -166,7 +166,7 @@ public void testGetTimeout() { public void testSetApplicationContextSubscribers() { ApplicationContext context = mock(ApplicationContext.class); - try { + DataStoreFactory dsf = new DataStoreFactory(); dsf.setDbType("memorydb"); @@ -181,6 +181,7 @@ public void testSetApplicationContextSubscribers() { //set dash fragment duration to 0.5 settings.setDashFragmentDuration("0.5"); + settings.setWriteSubscriberEventsToDatastore(true); when(context.getBean(AppSettings.BEAN_NAME)).thenReturn(settings); when(context.getBean(ServerSettings.BEAN_NAME)).thenReturn(new ServerSettings()); @@ -203,6 +204,8 @@ public void testSetApplicationContextSubscribers() { doReturn(true).when(viewerStats).isStreaming(Mockito.any()); dsf.setWriteStatsToDatastore(true); + dsf.setWriteSubscriberEventsToDatastore(true); + dsf.setApplicationContext(context); String streamId = dsf.getDataStore().save(broadcast); @@ -256,12 +259,14 @@ public void testSetApplicationContextSubscribers() { boolean eventExist = false; Subscriber subData = dsf.getDataStore().getSubscriber(streamId, subscriberPlay.getSubscriberId()); - List events = subData.getStats().getConnectionEvents(); + List events = dsf.getDataStore().getConnectionEvents(streamId, subscriberPlay.getSubscriberId(), 0, 50); if(events.size() == 2) { ConnectionEvent event = events.get(0); - eventExist = ConnectionEvent.CONNECTED_EVENT == event.getEventType(); + + eventExist = ConnectionEvent.CONNECTED_EVENT == events.get(0).getEventType(); } + log.info("isConnected: {} currentConcurrentConnections: {} events.size:{} eventExist: {}", subData.isConnected(), subData.getCurrentConcurrentConnections(), events.size(), eventExist); return subData.isConnected() && subData.getCurrentConcurrentConnections() == 2 && eventExist; }); @@ -286,7 +291,7 @@ public void testSetApplicationContextSubscribers() { // a disconnection event should be added Subscriber subData = dsf.getDataStore().getSubscriber(streamId, subscriberPlay2.getSubscriberId()); - List events = subData.getStats().getConnectionEvents(); + List events = dsf.getDataStore().getConnectionEvents(streamId, subscriberPlay2.getSubscriberId(), 0, 50); assertEquals(4, events.size()); ConnectionEvent eventDis = events.get(3); @@ -329,16 +334,13 @@ public void testSetApplicationContextSubscribers() { Subscriber subData2 = dsf.getDataStore().getSubscriber(streamId, subscriberPlay3.getSubscriberId()); - List events2 = subData2.getStats().getConnectionEvents(); + List events2 = dsf.getDataStore().getConnectionEvents(streamId, subscriberPlay3.getSubscriberId(), 0, 50); assertEquals(2, events2.size()); ConnectionEvent eventDis2 = events2.get(1); assertSame(ConnectionEvent.DISCONNECTED_EVENT, eventDis2.getEventType()); - } catch (Exception e) { - e.printStackTrace(); - fail(e.getMessage()); - } + } diff --git a/src/test/java/io/antmedia/test/statistic/HlsViewerStatsTest.java b/src/test/java/io/antmedia/test/statistic/HlsViewerStatsTest.java index ec251fdea..fe0e47772 100644 --- a/src/test/java/io/antmedia/test/statistic/HlsViewerStatsTest.java +++ b/src/test/java/io/antmedia/test/statistic/HlsViewerStatsTest.java @@ -176,6 +176,7 @@ public void testSetApplicationContextSubscribers() { //set hls time to 1 settings.setHlsTime("1"); + settings.setWriteSubscriberEventsToDatastore(true); when(context.getBean(AppSettings.BEAN_NAME)).thenReturn(settings); when(context.getBean(ServerSettings.BEAN_NAME)).thenReturn(new ServerSettings()); @@ -246,7 +247,7 @@ public void testSetApplicationContextSubscribers() { boolean eventExist = false; Subscriber subData = dsf.getDataStore().getSubscriber(streamId, subscriberPlay.getSubscriberId()); - List events = subData.getStats().getConnectionEvents(); + List events = dsf.getDataStore().getConnectionEvents(streamId, subscriberPlay.getSubscriberId(), 0, 50); if(events.size() == 1) { ConnectionEvent event = events.get(0); @@ -271,7 +272,7 @@ public void testSetApplicationContextSubscribers() { // a disconnection event should be added Subscriber subData = dsf.getDataStore().getSubscriber(streamId, subscriberPlay2.getSubscriberId()); - List events = subData.getStats().getConnectionEvents(); + List events = dsf.getDataStore().getConnectionEvents(streamId, subscriberPlay2.getSubscriberId(), 0, 50); assertEquals(2, events.size()); ConnectionEvent eventDis = events.get(1); @@ -313,7 +314,7 @@ public void testSetApplicationContextSubscribers() { Subscriber subData2 = dsf.getDataStore().getSubscriber(streamId, subscriberPlay3.getSubscriberId()); - List events2 = subData2.getStats().getConnectionEvents(); + List events2 = dsf.getDataStore().getConnectionEvents(streamId, subscriberPlay3.getSubscriberId(), 0, 50); assertEquals(2, events2.size()); ConnectionEvent eventDis2 = events.get(1); From bf43490274a49fbbd658111220b3c7985df782bf Mon Sep 17 00:00:00 2001 From: mekya Date: Sat, 7 Dec 2024 23:44:32 +0300 Subject: [PATCH 3/5] Fix test cases --- .../java/io/antmedia/test/statistic/DashViewerStatsTest.java | 3 ++- .../java/io/antmedia/test/statistic/HlsViewerStatsTest.java | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/test/java/io/antmedia/test/statistic/DashViewerStatsTest.java b/src/test/java/io/antmedia/test/statistic/DashViewerStatsTest.java index 3d567ce67..d4447360f 100644 --- a/src/test/java/io/antmedia/test/statistic/DashViewerStatsTest.java +++ b/src/test/java/io/antmedia/test/statistic/DashViewerStatsTest.java @@ -108,6 +108,7 @@ public void testSubscriberEvents() { viewerStats.setVertx(vertx); DataStore dataStore = new InMemoryDataStore("datastore"); + dataStore.setWriteSubscriberEventsToDatastore(true); viewerStats.setDataStore(dataStore); viewerStats.setServerSettings(new ServerSettings()); @@ -131,7 +132,7 @@ public void testSubscriberEvents() { boolean eventExist = false; Subscriber subData = dataStore.getSubscriber(streamId, subscriberPlay.getSubscriberId()); - List events = subData.getStats().getConnectionEvents(); + List events = dataStore.getConnectionEvents(streamId, subscriberPlay.getSubscriberId(), 0, 50); if(events.size() == 1) { ConnectionEvent event2 = events.get(0); diff --git a/src/test/java/io/antmedia/test/statistic/HlsViewerStatsTest.java b/src/test/java/io/antmedia/test/statistic/HlsViewerStatsTest.java index fe0e47772..fffc0a95f 100644 --- a/src/test/java/io/antmedia/test/statistic/HlsViewerStatsTest.java +++ b/src/test/java/io/antmedia/test/statistic/HlsViewerStatsTest.java @@ -102,6 +102,7 @@ public void testSubscriberEvents() { viewerStats.setVertx(vertx); DataStore dataStore = new InMemoryDataStore("datastore"); + dataStore.setWriteSubscriberEventsToDatastore(true); viewerStats.setDataStore(dataStore); String streamId = "stream1"; @@ -127,7 +128,7 @@ public void testSubscriberEvents() { boolean eventExist = false; Subscriber subData = dataStore.getSubscriber(streamId, subscriberPlay.getSubscriberId()); - List events = subData.getStats().getConnectionEvents(); + List events = dataStore.getConnectionEvents(streamId, subscriberPlay.getSubscriberId(), 0, 50); if(events.size() == 1) { ConnectionEvent event2 = events.get(0); From 0e7bbdd1035c7dc972836aca3170041922c8f844 Mon Sep 17 00:00:00 2001 From: mekya Date: Sun, 8 Dec 2024 08:58:40 +0300 Subject: [PATCH 4/5] Fix test scenario --- src/main/java/io/antmedia/datastore/db/InMemoryDataStore.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/java/io/antmedia/datastore/db/InMemoryDataStore.java b/src/main/java/io/antmedia/datastore/db/InMemoryDataStore.java index b10d4641a..0190ff5ac 100644 --- a/src/main/java/io/antmedia/datastore/db/InMemoryDataStore.java +++ b/src/main/java/io/antmedia/datastore/db/InMemoryDataStore.java @@ -828,6 +828,8 @@ public boolean deleteSubscriber(String streamId, String subscriberId) { try { Subscriber sub = subscriberMap.remove(Subscriber.getDBKey(streamId, subscriberId)); result = sub != null; + connectionEvents.keySet().removeIf(key -> key.equals(Subscriber.getDBKey(streamId, subscriberId))); + } catch (Exception e) { logger.error(ExceptionUtils.getStackTrace(e)); } @@ -875,6 +877,8 @@ public boolean revokeSubscribers(String streamId) { iterator.remove(); subscriberMap.remove(subscriber.getSubscriberKey()); } + connectionEvents.keySet().removeIf(key -> key.startsWith(streamId+ "-")); + result = true; } From 4dfabcf0efa341c3dfd5d5841c10819dee2dc030 Mon Sep 17 00:00:00 2001 From: mekya Date: Sun, 8 Dec 2024 10:17:00 +0300 Subject: [PATCH 5/5] Add test code and fix quality gate issues --- .../datastore/db/InMemoryDataStore.java | 33 +- .../datastore/db/MapBasedDataStore.java | 56 +- .../io/antmedia/datastore/db/MongoStore.java | 5 +- .../datastore/db/types/Subscriber.java | 21 +- .../io/antmedia/test/db/DBStoresUnitTest.java | 944 +++++++++--------- .../rest/BroadcastRestServiceV2UnitTest.java | 20 + 6 files changed, 557 insertions(+), 522 deletions(-) diff --git a/src/main/java/io/antmedia/datastore/db/InMemoryDataStore.java b/src/main/java/io/antmedia/datastore/db/InMemoryDataStore.java index 0190ff5ac..fb0c35bed 100644 --- a/src/main/java/io/antmedia/datastore/db/InMemoryDataStore.java +++ b/src/main/java/io/antmedia/datastore/db/InMemoryDataStore.java @@ -755,10 +755,8 @@ public boolean addSubscriber(String streamId, Subscriber subscriber) { @Override public List getConnectionEvents(String streamId, String subscriberId, int offset, int size) { - String key = null; - if (StringUtils.isNotBlank(subscriberId)) { - key = Subscriber.getDBKey(streamId, subscriberId); - } + String key = Subscriber.getDBKey(streamId, subscriberId); + List list = new ArrayList<>(); if (key != null) { @@ -773,32 +771,7 @@ public List getConnectionEvents(String streamId, String subscri list.addAll(getConnectionEventListFromCollection(queue)); } } - - List returnList = new ArrayList<>(); - - int t = 0; - int itemCount = 0; - if (size > MAX_ITEM_IN_ONE_LIST) { - size = MAX_ITEM_IN_ONE_LIST; - } - if (offset < 0) { - offset = 0; - } - - Iterator iterator = list.iterator(); - - while(itemCount < size && iterator.hasNext()) { - if (t < offset) { - t++; - iterator.next(); - } - else { - returnList.add(iterator.next()); - itemCount++; - } - } - - return returnList; + return MapBasedDataStore.getReturningConnectionEventsList(offset, size, list); } @Override diff --git a/src/main/java/io/antmedia/datastore/db/MapBasedDataStore.java b/src/main/java/io/antmedia/datastore/db/MapBasedDataStore.java index aa1845a2a..1f41dc928 100644 --- a/src/main/java/io/antmedia/datastore/db/MapBasedDataStore.java +++ b/src/main/java/io/antmedia/datastore/db/MapBasedDataStore.java @@ -732,33 +732,37 @@ public boolean addSubscriber(String streamId, Subscriber subscriber) { @Override public List getConnectionEvents(String streamId, String subscriberId, int offset, int size) { - String key = null; - if (StringUtils.isNotBlank(subscriberId)) { - key = Subscriber.getDBKey(streamId, subscriberId); - } - List list = new ArrayList<>(); - if (key != null) - { - Type queueType = new TypeToken>() { - }.getType(); - Queue values = gson.fromJson(connectionEventsMap.get(key), queueType); - if (values != null) { - list = getConnectionEventListFromCollection(values); + synchronized (this) { + String key = Subscriber.getDBKey(streamId, subscriberId); + + if (key != null) + { + Type queueType = new TypeToken>() { + }.getType(); + Queue values = gson.fromJson(connectionEventsMap.get(key), queueType); + if (values != null) { + list = getConnectionEventListFromCollection(values); + } } - } - else - { - Collection values = connectionEventsMap.values(); - Type queueType = new TypeToken>() { - }.getType(); - - for (String queueString : values) { - Queue queueValues = gson.fromJson(queueString, queueType); - list.addAll(getConnectionEventListFromCollection(queueValues)); + else + { + Collection values = connectionEventsMap.values(); + Type queueType = new TypeToken>() { + }.getType(); + + for (String queueString : values) { + Queue queueValues = gson.fromJson(queueString, queueType); + list.addAll(getConnectionEventListFromCollection(queueValues)); + } } } - + + return getReturningConnectionEventsList(offset, size, list); + + } + + public static List getReturningConnectionEventsList(int offset, int size, List list) { List returnList = new ArrayList<>(); int t = 0; @@ -808,7 +812,7 @@ protected boolean addConnectionEvent(ConnectionEvent event) { else { connectionQueue = new ConcurrentLinkedQueue<>(); } - + connectionQueue.add(event); connectionEventsMap.put(key, gson.toJson(connectionQueue)); @@ -857,7 +861,7 @@ public boolean deleteSubscriber(String streamId, String subscriberId) { synchronized (this) { try { result = subscriberMap.remove(Subscriber.getDBKey(streamId, subscriberId)) != null; - + connectionEventsMap.keySet().removeIf(key -> key.equals(Subscriber.getDBKey(streamId, subscriberId))); } catch (Exception e) { logger.error(ExceptionUtils.getStackTrace(e)); @@ -887,7 +891,7 @@ public boolean revokeSubscribers(String streamId) { } } } - + connectionEventsMap.keySet().removeIf(key -> key.startsWith(streamId + "-")); } diff --git a/src/main/java/io/antmedia/datastore/db/MongoStore.java b/src/main/java/io/antmedia/datastore/db/MongoStore.java index ba8235244..a0d7606e0 100644 --- a/src/main/java/io/antmedia/datastore/db/MongoStore.java +++ b/src/main/java/io/antmedia/datastore/db/MongoStore.java @@ -472,6 +472,7 @@ private boolean checkIfRegexValid(String regex) { @Override public List getConnectionEvents(String streamId, String subscriberId, int offset, int size) { + List connectionEvents = new ArrayList<>(); synchronized (this) { try { executedQueryCount++; @@ -485,12 +486,12 @@ public List getConnectionEvents(String streamId, String subscri FindOptions findingOptions = new FindOptions().skip(offset).limit(size); - return query.iterator(findingOptions).toList(); + connectionEvents = query.iterator(findingOptions).toList(); } catch (Exception e) { logger.error(ExceptionUtils.getStackTrace(e)); } } - return null; + return connectionEvents; } diff --git a/src/main/java/io/antmedia/datastore/db/types/Subscriber.java b/src/main/java/io/antmedia/datastore/db/types/Subscriber.java index e51a3bc81..78d084355 100644 --- a/src/main/java/io/antmedia/datastore/db/types/Subscriber.java +++ b/src/main/java/io/antmedia/datastore/db/types/Subscriber.java @@ -1,4 +1,5 @@ package io.antmedia.datastore.db.types; +import org.apache.commons.lang3.StringUtils; import org.bson.types.ObjectId; import com.fasterxml.jackson.annotation.JsonIgnore; @@ -111,21 +112,22 @@ public class Subscriber { @Schema(description = "Average audio bitrate for a subscriber") private long avgAudioBitrate; - public String getSubscriberId() { - return subscriberId; - } - + public void setSubscriberId(String subscriberId) { this.subscriberId = subscriberId; } - - public String getStreamId() { - return streamId; + + public String getSubscriberId() { + return subscriberId; } public void setStreamId(String streamId) { this.streamId = streamId; } + + public String getStreamId() { + return streamId; + } public String getB32Secret() { return b32Secret; @@ -155,7 +157,10 @@ public String getSubscriberKey() { } public static String getDBKey(String streamId, String subscriberId) { - return streamId + "-" +subscriberId; + if (StringUtils.isNoneBlank(streamId, subscriberId)) { + return streamId + "-" +subscriberId; + } + return null; } public String getType() { diff --git a/src/test/java/io/antmedia/test/db/DBStoresUnitTest.java b/src/test/java/io/antmedia/test/db/DBStoresUnitTest.java index 3ce689e10..9a5499651 100644 --- a/src/test/java/io/antmedia/test/db/DBStoresUnitTest.java +++ b/src/test/java/io/antmedia/test/db/DBStoresUnitTest.java @@ -91,10 +91,10 @@ public void deleteMapDBFile() { } } } - + @Test public void testConferenceRoomWrapper() throws Exception { - + ConferenceRoom conferenceRoom = new ConferenceRoom(); conferenceRoom.setRoomId("roomId"); long startDate = Instant.now().getEpochSecond(); @@ -109,7 +109,7 @@ public void testConferenceRoomWrapper() throws Exception { assertEquals(ConferenceRoom.LEGACY_MODE, conferenceRoom.getMode()); assertEquals("originAdress", conferenceRoom.getOriginAdress()); assertEquals(2, conferenceRoom.getRoomStreamList().size()); - + Broadcast broadcast = DataStore.conferenceToBroadcast(conferenceRoom); assertEquals("roomId", broadcast.getStreamId()); assertEquals("originAdress", broadcast.getOriginAdress()); @@ -119,8 +119,8 @@ public void testConferenceRoomWrapper() throws Exception { assertEquals("stream2", broadcast.getSubTrackStreamIds().get(1)); assertEquals(startDate, broadcast.getPlannedStartDate()); assertEquals(endDate, broadcast.getPlannedEndDate()); - - + + ConferenceRoom conferenceRoom2 = DataStore.broadcastToConference(broadcast); assertEquals("roomId", conferenceRoom2.getRoomId()); assertEquals(ConferenceRoom.LEGACY_MODE, conferenceRoom2.getMode()); @@ -131,16 +131,17 @@ public void testConferenceRoomWrapper() throws Exception { assertEquals(startDate, conferenceRoom2.getStartDate()); assertEquals(endDate, conferenceRoom2.getEndDate()); } - - - - + + + + @Test public void testMapDBStore() throws Exception { DataStore dataStore = new MapDBStore("testdb", vertx); - + + testSubscriberAvgBitrate(dataStore); testLocalLiveBroadcast(dataStore); testUpdateBroadcastEncoderSettings(dataStore); testSubscriberMetaData(dataStore); @@ -149,7 +150,7 @@ public void testMapDBStore() throws Exception { testBugFreeStreamId(dataStore); testUnexpectedBroadcastOffset(dataStore); testUnexpectedVodOffset(dataStore); - + testBugGetExternalStreamsList(dataStore); testGetPagination(dataStore); testNullCheck(dataStore); @@ -177,8 +178,8 @@ public void testMapDBStore() throws Exception { testAddTrack(dataStore); testRemoveTrack(dataStore); testClearAtStart(dataStore); - testGetVoDIdByStreamId(dataStore); - testBroadcastListSorting(dataStore); + testGetVoDIdByStreamId(dataStore); + testBroadcastListSorting(dataStore); testFullTextSearch(dataStore); testTotalWebRTCViewerCount(dataStore); testBroadcastListSearch(dataStore); @@ -190,19 +191,19 @@ public void testMapDBStore() throws Exception { testGetSubtracks(dataStore); testGetSubtracksWithStatus(dataStore); - + dataStore.close(false); - + } - + @Test public void testMapDBPersistent() { DataStore dataStore = new MapDBStore("testdb", vertx); - + Broadcast broadcast = new Broadcast(null, null); String key = dataStore.save(broadcast); - + assertNotNull(key); assertNotNull(broadcast.getStreamId()); @@ -212,25 +213,25 @@ public void testMapDBPersistent() { Broadcast broadcast2 = dataStore.get(key); assertEquals(broadcast.getStreamId(), broadcast2.getStreamId()); assertTrue(broadcast2.isPublish()); - - + + dataStore.close(false); - + dataStore = new MapDBStore("testdb", vertx); Broadcast broadcast3 = dataStore.get(key); assertEquals(broadcast.getStreamId(), broadcast3.getStreamId()); assertTrue(broadcast3.isPublish()); - + dataStore.close(false); - + } @Test public void testMemoryDataStore() throws Exception { DataStore dataStore = new InMemoryDataStore("testdb"); - - testVoDFunctions(dataStore); + testSubscriberAvgBitrate(dataStore); + testVoDFunctions(dataStore); testLocalLiveBroadcast(dataStore); testUpdateBroadcastEncoderSettings(dataStore); testSubscriberMetaData(dataStore); @@ -266,8 +267,8 @@ public void testMemoryDataStore() throws Exception { testAddTrack(dataStore); testRemoveTrack(dataStore); testClearAtStart(dataStore); - testGetVoDIdByStreamId(dataStore); - testBroadcastListSorting(dataStore); + testGetVoDIdByStreamId(dataStore); + testBroadcastListSorting(dataStore); testFullTextSearch(dataStore); testTotalWebRTCViewerCount(dataStore); testBroadcastListSearch(dataStore); @@ -291,10 +292,10 @@ public void testMongoStore() throws Exception { DataStore dataStore = new MongoStore("127.0.0.1", "", "", "testdb"); //delete db dataStore.close(true); - + dataStore = new MongoStore("127.0.0.1", "", "", "testdb"); - + testSubscriberAvgBitrate(dataStore); testSaveDuplicateStreamId((MongoStore)dataStore); testLocalLiveBroadcast(dataStore); @@ -344,7 +345,7 @@ public void testMongoStore() throws Exception { testWebRTCViewerOperations(dataStore); testUpdateMetaData(dataStore); - + testGetSubtracks(dataStore); testGetSubtracksWithStatus(dataStore); @@ -353,7 +354,7 @@ public void testMongoStore() throws Exception { dataStore.close(true); } - + @Test public void testRedisStore() throws Exception { @@ -361,7 +362,8 @@ public void testRedisStore() throws Exception { //delete db dataStore.close(true); dataStore = new RedisStore("redis://127.0.0.1:6379", "testdb"); - + + testSubscriberAvgBitrate(dataStore); testLocalLiveBroadcast(dataStore); testUpdateBroadcastEncoderSettings(dataStore); testSubscriberMetaData(dataStore); @@ -407,48 +409,48 @@ public void testRedisStore() throws Exception { testUpdateEndpointStatus(dataStore); testWebRTCViewerOperations(dataStore); testUpdateMetaData(dataStore); - + testGetSubtracks(dataStore); testGetSubtracksWithStatus(dataStore); - + dataStore.close(true); } - - + + public void testSaveDuplicateStreamId(MongoStore mongoStore ) throws Exception { Broadcast broadcast = new Broadcast(null, null); assertNotNull(mongoStore.save(broadcast)); - + Broadcast broadcast2 = new Broadcast(null, null); broadcast2.setStreamId(broadcast.getStreamId()); assertNull(mongoStore.save(broadcast2)); - - + + mongoStore.deleteDuplicateStreamIds(mongoStore.getDataStore().getCollection(Broadcast.class)); } - - + + @Test public void testBug() { - + MapDBStore dataStore = new MapDBStore("src/test/resources/damaged_webrtcappee.db", vertx); - + //Following methods does not return before the bug is fixed dataStore.fetchUserVodList(new File("")); - + dataStore.getVodList(0, 10, "name", "asc", null, null); dataStore.getBroadcastList(0, 10, "asc", null, null, null); } - + @Test public void testConferenceRoomMigrationMapBased() { - + MapDBStore dataStore = new MapDBStore("testdb" + RandomStringUtils.randomAlphanumeric(12) , vertx); - + Map conferenceRoomMap = dataStore.getConferenceRoomMap(); - + Gson gson = new Gson(); ConferenceRoom conferenceRoom = new ConferenceRoom(); conferenceRoom.setRoomId("roomId"); @@ -459,10 +461,10 @@ public void testConferenceRoomMigrationMapBased() { conferenceRoom.setMode(ConferenceRoom.LEGACY_MODE); conferenceRoom.setRoomStreamList(Arrays.asList("stream1", "stream2")); conferenceRoom.setOriginAdress("originAdress"); - + conferenceRoomMap.put("roomId", gson.toJson(conferenceRoom)); - - + + ConferenceRoom conferenceRoom2 = new ConferenceRoom(); conferenceRoom2.setRoomId("roomId2"); long startDate2 = Instant.now().getEpochSecond(); @@ -472,12 +474,12 @@ public void testConferenceRoomMigrationMapBased() { conferenceRoom2.setMode(ConferenceRoom.MULTI_TRACK_MODE); conferenceRoom2.setRoomStreamList(Arrays.asList("stream3", "stream4")); conferenceRoom2.setOriginAdress("originAdress2"); - + conferenceRoomMap.put("roomId2", gson.toJson(conferenceRoom2)); - - + + dataStore.migrateConferenceRoomsToBroadcasts(); - + assertEquals(2, dataStore.getTotalBroadcastNumber()); Broadcast broadcast = dataStore.get("roomId"); assertNotNull(broadcast); @@ -487,24 +489,24 @@ public void testConferenceRoomMigrationMapBased() { assertEquals(2, broadcast.getSubTrackStreamIds().size()); assertEquals("stream1", broadcast.getSubTrackStreamIds().get(0)); assertEquals("stream2", broadcast.getSubTrackStreamIds().get(1)); - - + + assertEquals(0, conferenceRoomMap.size()); - + } - + @Test public void testConferenceRoomMigrationMongo() { - + MongoStore dataStore = new MongoStore("127.0.0.1", "", "", "testdb"); - + //delete db dataStore.close(true); - + dataStore = new MongoStore("127.0.0.1", "", "", "testdb"); - + Datastore conferenceRoomMap = dataStore.getConferenceRoomDatastore(); - + Gson gson = new Gson(); ConferenceRoom conferenceRoom = new ConferenceRoom(); conferenceRoom.setRoomId("roomId"); @@ -515,10 +517,10 @@ public void testConferenceRoomMigrationMongo() { conferenceRoom.setMode(ConferenceRoom.LEGACY_MODE); conferenceRoom.setRoomStreamList(Arrays.asList("stream1", "stream2")); conferenceRoom.setOriginAdress("originAdress"); - + conferenceRoomMap.save(conferenceRoom); - - + + ConferenceRoom conferenceRoom2 = new ConferenceRoom(); conferenceRoom2.setRoomId("roomId2"); long startDate2 = Instant.now().getEpochSecond(); @@ -528,12 +530,12 @@ public void testConferenceRoomMigrationMongo() { conferenceRoom2.setMode(ConferenceRoom.MULTI_TRACK_MODE); conferenceRoom2.setRoomStreamList(Arrays.asList("stream3", "stream4")); conferenceRoom2.setOriginAdress("originAdress2"); - + conferenceRoomMap.save(conferenceRoom2); - - + + dataStore.migrateConferenceRoomsToBroadcasts(); - + assertEquals(2, dataStore.getTotalBroadcastNumber()); Broadcast broadcast = dataStore.get("roomId"); assertNotNull(broadcast); @@ -543,10 +545,10 @@ public void testConferenceRoomMigrationMongo() { assertEquals(2, broadcast.getSubTrackStreamIds().size()); assertEquals("stream1", broadcast.getSubTrackStreamIds().get(0)); assertEquals("stream2", broadcast.getSubTrackStreamIds().get(1)); - - + + assertEquals(0, conferenceRoomMap.find(ConferenceRoom.class).count()); - + } public void clear(DataStore dataStore) @@ -563,8 +565,8 @@ public void clear(DataStore dataStore) } totalBroadcastList.addAll(broadcastList); } - - + + for (Broadcast broadcast : totalBroadcastList) { numberOfCall++; @@ -572,7 +574,7 @@ public void clear(DataStore dataStore) } assertEquals(numberOfCall, numberOfStreams); - + numberOfCall = 0; List totalVoDList = new ArrayList<>(); for (int i = 0; i < Integer.MAX_VALUE; i++) { @@ -582,77 +584,77 @@ public void clear(DataStore dataStore) } totalVoDList.addAll(vodList); } - + for (VoD vod : totalVoDList) { numberOfCall++; assertTrue(dataStore.deleteVod(vod.getVodId())); } } - - + + public void testUnexpectedVodOffset(DataStore dataStore) { clear(dataStore); - + assertEquals(0, dataStore.getTotalVodNumber()); - - + + List vodList = dataStore.getVodList(50, 50, null, null, null, null); assertNotNull(vodList); assertEquals(0, vodList.size()); - - + + vodList = dataStore.getVodList(50, 0, null, null, null, null); assertNotNull(vodList); assertEquals(0, vodList.size()); - + for (int i = 0; i < 10; i++) { assertNotNull(dataStore.addVod(new VoD("stream", "111223" + (int)(Math.random() * 9100000), "path", "vod", 1517239808, 111, 17933, 1190525, VoD.STREAM_VOD, "1112233" + (int)(Math.random() * 91000), null))); } - + vodList = dataStore.getVodList(6, 4, null, null, null, null); assertNotNull(vodList); assertEquals(4, vodList.size()); - + vodList = dataStore.getVodList(20, 5, null, null, null, null); assertNotNull(vodList); assertEquals(0, vodList.size()); } - + public void testUnexpectedBroadcastOffset(DataStore dataStore) { clear(dataStore); - + assertEquals(0, dataStore.getBroadcastCount()); - - + + List broadcastList = dataStore.getBroadcastList(50, 50, null, null, null, null); assertNotNull(broadcastList); assertEquals(0, broadcastList.size()); - - + + broadcastList = dataStore.getBroadcastList(50, 0, null, null, null, null); assertNotNull(broadcastList); assertEquals(0, broadcastList.size()); - + for (int i = 0; i < 10; i++) { dataStore.save(new Broadcast(null, null)); } - + broadcastList = dataStore.getBroadcastList(6, 4, null, null, null, null); assertNotNull(broadcastList); assertEquals(4, broadcastList.size()); - + broadcastList = dataStore.getBroadcastList(20, 5, null, null, null, null); assertNotNull(broadcastList); assertEquals(0, broadcastList.size()); } - + public void testLocalLiveBroadcast(DataStore dataStore) { clear(dataStore); assertEquals(0, dataStore.getBroadcastCount()); - + long streamCount = 10; String streamId = null; for (int i = 0; i < streamCount; i++) { @@ -662,28 +664,28 @@ public void testLocalLiveBroadcast(DataStore dataStore) { streamId = dataStore.save(broadcast); logger.info("Saved streamId:{}", streamId); } - - + + assertEquals(streamCount, dataStore.getActiveBroadcastCount()); - + if (dataStore instanceof MapDBStore || dataStore instanceof InMemoryDataStore) { assertEquals(streamCount, dataStore.getLocalLiveBroadcastCount(ServerSettings.getLocalHostAddress())); - + List localLiveBroadcasts = dataStore.getLocalLiveBroadcasts(ServerSettings.getLocalHostAddress()); - assertEquals(streamCount, localLiveBroadcasts.size()); + assertEquals(streamCount, localLiveBroadcasts.size()); } else { - + //because there is no origin address registered assertEquals(0, dataStore.getLocalLiveBroadcastCount(ServerSettings.getLocalHostAddress())); List localLiveBroadcasts = dataStore.getLocalLiveBroadcasts(ServerSettings.getLocalHostAddress()); assertEquals(0, localLiveBroadcasts.size()); } - + clear(dataStore); assertEquals(0, dataStore.getBroadcastCount()); - - streamCount = 15; + + streamCount = 15; for (int i = 0; i < streamCount; i++) { Broadcast broadcast = new Broadcast(null, null); broadcast.setStatus(AntMediaApplicationAdapter.BROADCAST_STATUS_BROADCASTING); @@ -692,12 +694,12 @@ public void testLocalLiveBroadcast(DataStore dataStore) { streamId = dataStore.save(broadcast); logger.info("Saved streamId:{}", streamId); } - + assertEquals(streamCount, dataStore.getLocalLiveBroadcastCount(ServerSettings.getLocalHostAddress())); - + List localLiveBroadcasts = dataStore.getLocalLiveBroadcasts(ServerSettings.getLocalHostAddress()); - assertEquals(streamCount, localLiveBroadcasts.size()); - + assertEquals(streamCount, localLiveBroadcasts.size()); + } public void testGetActiveBroadcastCount(DataStore dataStore) { @@ -753,13 +755,13 @@ public void testGetActiveBroadcastCount(DataStore dataStore) { assertTrue(numberOfCall > 0); assertEquals(numberOfCall, numberOfStatusChangeStreams); //check that active broadcast exactly the same as changed above - + //////this test is sometimes failing below, I think streamId may not be unique so I logged above to confirm it - mekya //yes the streamId is not unique, we need to improve - mekya Aug 11, 2024 assertEquals(numberOfStatusChangeStreams, dataStore.getActiveBroadcastCount()); - + assertEquals(numberOfStatusChangeStreams, dataStore.getLocalLiveBroadcastCount(ServerSettings.getLocalHostAddress())); - + List localLiveBroadcasts = dataStore.getLocalLiveBroadcasts(ServerSettings.getLocalHostAddress()); assertEquals(numberOfStatusChangeStreams, localLiveBroadcasts.size()); @@ -784,14 +786,14 @@ public void testGetActiveBroadcastCount(DataStore dataStore) { assertEquals(0, localLiveBroadcasts.size()); - + } - - + + public void testBugFreeStreamId(DataStore datastore) { // add ip camera Broadcast broadcast = new Broadcast(); - + try { broadcast.setStreamId(""); @@ -799,11 +801,11 @@ public void testBugFreeStreamId(DataStore datastore) { e.printStackTrace(); fail(e.getMessage()); } - + String streamId = datastore.save(broadcast); assertNotEquals("", streamId); - - + + } @@ -825,7 +827,7 @@ public void testBugGetExternalStreamsList(DataStore datastore) { assertNotNull(streamsList); assertEquals(2, streamsList.size()); - + streamsList = datastore.getExternalStreamsList(); assertNotNull(streamsList); @@ -992,29 +994,29 @@ public void testVoDFunctions(DataStore datastore) { assertEquals(streamVod.getFileSize(), voD.getFileSize()); assertEquals(streamVod.getCreationDate(), voD.getCreationDate()); assertEquals(userVod.getType(), voD.getType()); - + assertNull(voD.getProcessStatus()); datastore.updateVoDProcessStatus(voD.getVodId(), VoD.PROCESS_STATUS_INQUEUE); - + + voD = datastore.getVoD(userVod.getVodId()); + assertEquals(VoD.PROCESS_STATUS_INQUEUE, voD.getProcessStatus()); + + assertEquals(0, voD.getProcessStartTime()); + assertEquals(0, voD.getProcessEndTime()); + + datastore.updateVoDProcessStatus(voD.getVodId(), VoD.PROCESS_STATUS_PROCESSING); + voD = datastore.getVoD(userVod.getVodId()); + assertNotEquals(0, voD.getProcessStartTime()); + assertEquals(0, voD.getProcessEndTime()); + + datastore.updateVoDProcessStatus(voD.getVodId(), VoD.PROCESS_STATUS_FAILED); voD = datastore.getVoD(userVod.getVodId()); - assertEquals(VoD.PROCESS_STATUS_INQUEUE, voD.getProcessStatus()); - - assertEquals(0, voD.getProcessStartTime()); - assertEquals(0, voD.getProcessEndTime()); - - datastore.updateVoDProcessStatus(voD.getVodId(), VoD.PROCESS_STATUS_PROCESSING); - voD = datastore.getVoD(userVod.getVodId()); - assertNotEquals(0, voD.getProcessStartTime()); - assertEquals(0, voD.getProcessEndTime()); - - datastore.updateVoDProcessStatus(voD.getVodId(), VoD.PROCESS_STATUS_FAILED); - voD = datastore.getVoD(userVod.getVodId()); - assertNotEquals(0, voD.getProcessStartTime()); - assertNotEquals(0, voD.getProcessEndTime()); - - - - + assertNotEquals(0, voD.getProcessStartTime()); + assertNotEquals(0, voD.getProcessEndTime()); + + + + //delete streamVod datastore.deleteVod(streamVod.getVodId()); assertNull(datastore.getVoD(streamVod.getVodId())); @@ -1027,23 +1029,23 @@ public void testVoDFunctions(DataStore datastore) { //check vod number assertEquals(0, datastore.getTotalVodNumber()); - - + + //check finished time - VoD userVod2 =new VoD("streamName", "streamId", "filePath", "vodName", 111, 111, 111, 111, VoD.USER_VOD,vodId,null); + VoD userVod2 =new VoD("streamName", "streamId", "filePath", "vodName", 111, 111, 111, 111, VoD.USER_VOD,vodId,null); datastore.addVod(userVod2); voD = datastore.getVoD(userVod2.getVodId()); - assertEquals(0, voD.getProcessStartTime()); - assertEquals(0, voD.getProcessEndTime()); - - datastore.updateVoDProcessStatus(userVod2.getVodId(), VoD.PROCESS_STATUS_FINISHED); - voD = datastore.getVoD(userVod2.getVodId()); - assertEquals(0, voD.getProcessStartTime()); - assertNotEquals(0, voD.getProcessEndTime()); - - datastore.deleteVod(userVod2.getVodId()); - assertEquals(0, datastore.getTotalVodNumber()); + assertEquals(0, voD.getProcessStartTime()); + assertEquals(0, voD.getProcessEndTime()); + + datastore.updateVoDProcessStatus(userVod2.getVodId(), VoD.PROCESS_STATUS_FINISHED); + voD = datastore.getVoD(userVod2.getVodId()); + assertEquals(0, voD.getProcessStartTime()); + assertNotEquals(0, voD.getProcessEndTime()); + + datastore.deleteVod(userVod2.getVodId()); + assertEquals(0, datastore.getTotalVodNumber()); VoD userVod3 =new VoD("streamName", "streamId", "filePath", "vodName", 111, 111, 111, 111, VoD.USER_VOD,vodId,null); userVod3.setLatitude("10"); @@ -1081,11 +1083,11 @@ public void testEditCameraInfo(DataStore datastore) { //change cam info camera.setName("new_name"); camera.setIpAddr("1.1.1.1"); - + BroadcastUpdate cameraUpdate = new BroadcastUpdate(); cameraUpdate.setName("new_name"); cameraUpdate.setIpAddr("1.1.1.1"); - + datastore.updateBroadcastFields(camera.getStreamId(), cameraUpdate); @@ -1160,10 +1162,10 @@ public void testUpdateHLSViewerCount(DataStore dataStore) { assertEquals(totalCountFor1, dataStore.get(key).getHlsViewerCount()); assertEquals(totalCountFor2, dataStore.get(key2).getHlsViewerCount()); - + // If broadcast finished - - + + } } @@ -1192,7 +1194,7 @@ public void testWebRTCViewerCount(DataStore dataStore) { else { totalViewerCountFor1--; } - + if(dataStore.get(key).getWebRTCViewerCount()>0 || (dataStore.get(key).getWebRTCViewerCount()==0 && increment)) { assertTrue(dataStore.updateWebRTCViewerCount(key, increment)); } @@ -1249,7 +1251,7 @@ public void testRTMPViewerCount(DataStore dataStore) { else { totalViewerCountFor1--; } - + if(dataStore.get(key).getRtmpViewerCount()>0 || (dataStore.get(key).getRtmpViewerCount()==0 && increment)) { assertTrue(dataStore.updateRtmpViewerCount(key, increment)); } @@ -1257,7 +1259,7 @@ public void testRTMPViewerCount(DataStore dataStore) { assertFalse(dataStore.updateRtmpViewerCount(key, increment)); totalViewerCountFor1 = 0; } - + increment = false; randomValue = (int)(Math.random()*99999); if (randomValue % 2 == 0) { @@ -1267,7 +1269,7 @@ public void testRTMPViewerCount(DataStore dataStore) { else { totalViewerCountFor2--; } - + if(dataStore.get(key2).getRtmpViewerCount()>0 || (dataStore.get(key2).getRtmpViewerCount()==0 && increment)) { assertTrue(dataStore.updateRtmpViewerCount(key2, increment)); } @@ -1275,7 +1277,7 @@ public void testRTMPViewerCount(DataStore dataStore) { assertFalse(dataStore.updateRtmpViewerCount(key2, increment)); totalViewerCountFor2 = 0; } - + assertEquals(totalViewerCountFor1, dataStore.get(key).getRtmpViewerCount()); assertEquals(totalViewerCountFor2, dataStore.get(key2).getRtmpViewerCount()); } @@ -1353,7 +1355,7 @@ public void testGetPagination(DataStore dataStore) { public void testBroadcastListSearch(DataStore dataStore){ long broadcastCount = dataStore.getBroadcastCount(); int pageCount = (int)(broadcastCount/50 + 1); - + for (int i = 0; i < pageCount; i++) { List broadcastList2 = dataStore.getBroadcastList(0, 50, null, null, null, null); for (Iterator iterator = broadcastList2.iterator(); iterator.hasNext();) { @@ -1361,8 +1363,8 @@ public void testBroadcastListSearch(DataStore dataStore){ dataStore.delete(broadcast.getStreamId()); } } - - + + Broadcast broadcast1 = new Broadcast(AntMediaApplicationAdapter.BROADCAST_STATUS_BROADCASTING, "bbbStream"); broadcast1.setDate(1000); @@ -1431,9 +1433,9 @@ public void testFullTextSearch(DataStore dataStore) { List broadcastList1 = dataStore.getBroadcastList(0, 50, null, null, null, searchQueryNotMatched); assertTrue(broadcastList1.isEmpty()); } - + public void testBroadcastListSorting(DataStore dataStore) { - + List broadcastList2 = dataStore.getBroadcastList(0, 50, null, null, null, null); for (Iterator iterator = broadcastList2.iterator(); iterator.hasNext();) { Broadcast broadcast = (Broadcast) iterator.next(); @@ -1449,17 +1451,17 @@ public void testBroadcastListSorting(DataStore dataStore) { Broadcast broadcast3 = new Broadcast(AntMediaApplicationAdapter.BROADCAST_STATUS_PREPARING, "cccStream"); broadcast3.setDate(100000000); broadcast3.setType(AntMediaApplicationAdapter.STREAM_SOURCE); //Stream Source - + dataStore.save(broadcast1); dataStore.save(broadcast2); dataStore.save(broadcast3); - + List broadcastList = dataStore.getBroadcastList(0, 50, null, null, null, null); assertEquals(3, broadcastList.size()); - + broadcastList = dataStore.getBroadcastList(0, 50, "", "", "", ""); assertEquals(3, broadcastList.size()); - + broadcastList = dataStore.getBroadcastList(0, 50, null, "name", "asc", null); assertEquals(3, broadcastList.size()); assertEquals(broadcastList.get(0).getStreamId(), broadcast2.getStreamId()); @@ -1471,8 +1473,8 @@ public void testBroadcastListSorting(DataStore dataStore) { assertEquals(broadcastList.get(0).getStreamId(), broadcast2.getStreamId()); assertEquals(broadcastList.get(1).getStreamId(), broadcast1.getStreamId()); assertEquals(broadcastList.get(2).getStreamId(), broadcast3.getStreamId()); - - + + broadcastList = dataStore.getBroadcastList(0, 50, null, "name", "desc", null); assertEquals(3, broadcastList.size()); assertEquals(broadcastList.get(0).getStreamId(), broadcast3.getStreamId()); @@ -1485,8 +1487,8 @@ public void testBroadcastListSorting(DataStore dataStore) { assertEquals(broadcastList.get(0).getStreamId(), broadcast3.getStreamId()); assertEquals(broadcastList.get(1).getStreamId(), broadcast1.getStreamId()); assertEquals(broadcastList.get(2).getStreamId(), broadcast2.getStreamId()); - - + + broadcastList = dataStore.getBroadcastList(0, 50, null, "date", "asc" , null); assertEquals(3, broadcastList.size()); assertEquals(broadcastList.get(0).getStreamId(), broadcast1.getStreamId()); @@ -1498,27 +1500,27 @@ public void testBroadcastListSorting(DataStore dataStore) { assertEquals(broadcastList.get(0).getStreamId(), broadcast1.getStreamId()); assertEquals(broadcastList.get(1).getStreamId(), broadcast2.getStreamId()); assertEquals(broadcastList.get(2).getStreamId(), broadcast3.getStreamId()); - + broadcastList = dataStore.getBroadcastList(0, 50, null, "date", "desc", null); assertEquals(3, broadcastList.size()); assertEquals(broadcastList.get(0).getStreamId(), broadcast3.getStreamId()); assertEquals(broadcastList.get(1).getStreamId(), broadcast2.getStreamId()); assertEquals(broadcastList.get(2).getStreamId(), broadcast1.getStreamId()); - - + + broadcastList = dataStore.getBroadcastList(0, 50, null, "status", "asc", null); assertEquals(3, broadcastList.size()); assertEquals(broadcastList.get(0).getStreamId(), broadcast1.getStreamId()); assertEquals(broadcastList.get(1).getStreamId(), broadcast2.getStreamId()); assertEquals(broadcastList.get(2).getStreamId(), broadcast3.getStreamId()); - + broadcastList = dataStore.getBroadcastList(0, 50, null, "status", "desc", null); assertEquals(3, broadcastList.size()); assertEquals(broadcastList.get(0).getStreamId(), broadcast3.getStreamId()); assertEquals(broadcastList.get(1).getStreamId(), broadcast2.getStreamId()); assertEquals(broadcastList.get(2).getStreamId(), broadcast1.getStreamId()); - - + + broadcastList = dataStore.getBroadcastList(0, 2, null, "status", "desc", null); assertEquals(2, broadcastList.size()); assertEquals(broadcastList.get(0).getStreamId(), broadcast3.getStreamId()); @@ -1528,20 +1530,20 @@ public void testBroadcastListSorting(DataStore dataStore) { assertEquals(2, broadcastList.size()); assertEquals(broadcastList.get(0).getStreamId(), broadcast3.getStreamId()); assertEquals(broadcastList.get(1).getStreamId(), broadcast2.getStreamId()); - + broadcastList = dataStore.getBroadcastList(2, 3, null, "status", "desc" ,null); assertEquals(1, broadcastList.size()); assertEquals(broadcastList.get(0).getStreamId(), broadcast1.getStreamId()); - - + + broadcastList = dataStore.getBroadcastList(-10, 100, AntMediaApplicationAdapter.IP_CAMERA, "status", "desc", null); assertEquals(1, broadcastList.size()); assertEquals(broadcastList.get(0).getStreamId(), broadcast2.getStreamId()); - + dataStore.delete(broadcast1.getStreamId()); dataStore.delete(broadcast2.getStreamId()); dataStore.delete(broadcast3.getStreamId()); - + broadcastList = dataStore.getBroadcastList(0, 50, null, null, null, null); assertEquals(0, broadcastList.size()); } @@ -1620,7 +1622,7 @@ public void testRemoveEndpoint(DataStore dataStore) { assertTrue(broadcast2.getEndPointList() == null || broadcast2.getEndPointList().size() == 0); } - + public void testRemoveEndpointWithServiceEndpoint(DataStore dataStore) { Broadcast broadcast = new Broadcast(null, null); String name = "name 1"; @@ -1695,7 +1697,7 @@ public void testRemoveEndpointWithServiceEndpoint(DataStore dataStore) { assertTrue(broadcast2.getEndPointList() == null || broadcast2.getEndPointList().size() == 0); } - + public void testSimpleOperations(DataStore dataStore) { @@ -1703,7 +1705,7 @@ public void testSimpleOperations(DataStore dataStore) { Broadcast broadcast = new Broadcast(null, null); String key = dataStore.save(broadcast); - + assertNotNull(key); assertNotNull(broadcast.getStreamId()); @@ -1718,7 +1720,7 @@ public void testSimpleOperations(DataStore dataStore) { assertEquals(AntMediaApplicationAdapter.BROADCAST_STATUS_CREATED, broadcast.getStatus()); assertEquals(0, broadcast.getStartTime()); assertNull(broadcast.getOriginAdress()); - + String name = "name 1"; String description = "description 2"; long now = System.currentTimeMillis(); @@ -1738,8 +1740,8 @@ public void testSimpleOperations(DataStore dataStore) { double speed = 1.0; tmp.setSpeed(speed); tmp.setSeekTimeInMs(136); - - + + boolean result = dataStore.updateBroadcastFields(broadcast.getStreamId(), tmp); assertTrue(result); @@ -1755,7 +1757,7 @@ public void testSimpleOperations(DataStore dataStore) { assertEquals(listenerHookURL, broadcast2.getListenerHookURL()); assertFalse(broadcast2.isPlaylistLoopEnabled()); assertEquals(speed, broadcast2.getSpeed(), 0.1); - + BroadcastUpdate update = new BroadcastUpdate(); update.setDuration(100000L); @@ -1824,22 +1826,22 @@ public void testSimpleOperations(DataStore dataStore) { broadcast3.setQuality("poor"); assertNotNull(broadcast3.getQuality()); dataStore.save(broadcast3); - + logger.info("Saved id {}", broadcast3.getStreamId()); - + assertEquals(broadcast3.getStreamId(), dataStore.get(broadcast3.getStreamId()).getStreamId()); - + update = new BroadcastUpdate(); update.setQuality("poor"); update.setSpeed(0.1); update.setPendingPacketSize(0); - + result = dataStore.updateBroadcastFields(broadcast.getStreamId().toString(), update); assertTrue(result); //it's poor because it's not updated because of null assertEquals("poor", dataStore.get(broadcast3.getStreamId()).getQuality()); - + update = new BroadcastUpdate(); update.setQuality("good"); update.setSpeed(0.0); @@ -1898,7 +1900,7 @@ public void testSimpleOperations(DataStore dataStore) { //check that setting is saved correctly assertEquals(MuxAdaptor.RECORDING_DISABLED_FOR_STREAM, dataStore.get(key).getWebMEnabled()); - + result = dataStore.delete(key); assertTrue(result); @@ -1989,7 +1991,7 @@ private void testVodSearch(DataStore dataStore){ private void testFilterSearchOperations(DataStore dataStore) { - + clear(dataStore); Broadcast cameraBroadcast = new Broadcast("test", "192.168.1.100", "admin", "admin", "rtspUrl", "ipCamera"); @@ -2060,7 +2062,7 @@ public void testSaveDetection(DataStore dataStore){ String item1 = "item1"; long detectionTime = 434234L; float probability1 = 0.1f; - + double minX = 5.5; double minY = 4.4; double maxX = 3.3; @@ -2081,7 +2083,7 @@ public void testSaveDetection(DataStore dataStore){ assertEquals(item1, list.get(0).getObjectName()); assertEquals(probability1, list.get(0).getProbability(),0.1F); assertEquals(detectionTime, list.get(0).getDetectionTime()); - + assertEquals(minX, list.get(0).getMinX(), 0.0001); assertEquals(minY, list.get(0).getMinY(), 0.0001); assertEquals(maxX, list.get(0).getMaxX(), 0.0001); @@ -2092,7 +2094,7 @@ public void testTokenOperations(DataStore store) { //create token Token testToken = new Token(); - + //define a valid expire date long expireDate = Instant.now().getEpochSecond() + 1000; @@ -2132,24 +2134,24 @@ public void testTokenOperations(DataStore store) { testToken.setRoomId("testRoom"); store.saveToken(testToken); - + //get this token Token retrievedToken = store.getToken(testToken.getTokenId()); - + assertNotNull(retrievedToken); assertEquals("testRoom", retrievedToken.getRoomId()); - - + + //delete this token assertTrue(store.deleteToken(testToken.getTokenId())); - + tokens = store.listAllTokens(testToken.getStreamId(),0 , 10); - + //it should be zero because all tokens are revoked assertEquals(0, tokens.size()); - - - + + + //create token again testToken = new Token(); @@ -2160,7 +2162,7 @@ public void testTokenOperations(DataStore store) { store.saveToken(testToken); - + //validate token Token validatedToken = store.validateToken(testToken); @@ -2172,7 +2174,7 @@ public void testTokenOperations(DataStore store) { assertNull(expiredToken); - + //create token again, this time create a room token testToken = new Token(); @@ -2183,7 +2185,7 @@ public void testTokenOperations(DataStore store) { testToken.setRoomId("testRoom"); store.saveToken(testToken); - + //validate token validatedToken = store.validateToken(testToken); @@ -2194,38 +2196,64 @@ public void testTokenOperations(DataStore store) { expiredToken = store.validateToken(testToken); assertNotNull(expiredToken); - + //change stream id of token - + testToken.setStreamId("changed"); - + //validate token validatedToken = store.validateToken(testToken); //token should be validated and returned assertNotNull(validatedToken); - + } + + public void testSubscriberAvgBitrate(DataStore store) { + String streamId = "stream1"; + + store.setWriteSubscriberEventsToDatastore(true); + + // create a subscriber play + Subscriber subscriberPlay = new Subscriber(); + subscriberPlay.setStreamId(streamId); + subscriberPlay.setSubscriberId("subscriber1"); + subscriberPlay.setB32Secret("6qsp6qhndryqs56zjmvs37i6gqtjsdvc"); + subscriberPlay.setType(Subscriber.PLAY_TYPE); + assertTrue(store.addSubscriber(subscriberPlay.getStreamId(), subscriberPlay)); + + assertTrue(store.updateSubscriberBitrateEvent(streamId, subscriberPlay.getSubscriberId(), 1000, 2000)); + + Subscriber subscriber = store.getSubscriber(streamId, subscriberPlay.getSubscriberId()); + assertEquals(1000, subscriber.getAvgVideoBitrate()); + assertEquals(2000, subscriber.getAvgAudioBitrate()); + + + store.setWriteSubscriberEventsToDatastore(false); + + + } + public void testTimeBasedSubscriberOperations(DataStore store) { // clean db in the begining of the test String streamId = "stream1"; store.revokeSubscribers(streamId); - + //default value must be false assertFalse(store.isWriteSubscriberEventsToDatastore()); - - + + store.setWriteSubscriberEventsToDatastore(true); - + // null checks assertFalse(store.addSubscriber("stream1", null)); - + assertFalse(store.isSubscriberConnected("stream1", null)); assertNull(store.getSubscriber("stream1", null)); assertFalse(store.addSubscriberConnectionEvent("stream1", null, null)); - - + + // create a subscriber play Subscriber subscriberPlay = new Subscriber(); subscriberPlay.setStreamId(streamId); @@ -2233,7 +2261,7 @@ public void testTimeBasedSubscriberOperations(DataStore store) { subscriberPlay.setB32Secret("6qsp6qhndryqs56zjmvs37i6gqtjsdvc"); subscriberPlay.setType(Subscriber.PLAY_TYPE); assertTrue(store.addSubscriber(subscriberPlay.getStreamId(), subscriberPlay)); - + // create a subscriber publish Subscriber subscriberPub = new Subscriber(); subscriberPub.setStreamId(streamId); @@ -2241,35 +2269,35 @@ public void testTimeBasedSubscriberOperations(DataStore store) { subscriberPub.setB32Secret("6qsp6qhndryqs56zjmvs37i6gqtjsdvc"); subscriberPub.setType(Subscriber.PUBLISH_TYPE); assertTrue(store.addSubscriber(subscriberPub.getStreamId(), subscriberPub)); - + //get subscribers of stream List subscribers = store.listAllSubscribers(streamId, 0, 10); assertEquals(2, subscribers.size()); List subscriberStats = store.listAllSubscriberStats(streamId, 0, 10); assertEquals(2, subscriberStats.size()); - + //revoke subscribers store.revokeSubscribers(subscriberPlay.getStreamId()); //get subscribers of stream subscribers = store.listAllSubscribers(streamId, 0, 10); subscriberStats = store.listAllSubscriberStats(streamId, 0, 10); - - + + //it should be zero because all subscribers are revoked assertEquals(0, subscribers.size()); assertEquals(0, subscriberStats.size()); - + //create subscriber again assertTrue(store.addSubscriber(subscriberPub.getStreamId(), subscriberPub)); //get this subscriber Subscriber written = store.getSubscriber(subscriberPub.getStreamId(), subscriberPub.getSubscriberId()); - + assertNotNull(written); assertEquals(subscriberPub.getSubscriberId(), written.getSubscriberId()); assertEquals(subscriberPub.getType(), written.getType()); - + ConnectionEvent connected = new ConnectionEvent(); connected.setEventType(ConnectionEvent.CONNECTED_EVENT); long eventTime = 20; @@ -2277,32 +2305,32 @@ public void testTimeBasedSubscriberOperations(DataStore store) { connected.setType(Subscriber.PLAY_TYPE); String hostAddress = ServerSettings.getLocalHostAddress(); connected.setInstanceIP(hostAddress); - - + + store.addSubscriberConnectionEvent(subscriberPub.getStreamId(), subscriberPub.getSubscriberId(), connected); - + assertEquals(1, store.getConnectionEvents(subscriberPub.getStreamId(), subscriberPub.getSubscriberId(), 0, 50).size()); assertEquals(1, store.getConnectionEvents(subscriberPub.getStreamId(), null, 0, 50).size()); //delete this subscriber assertTrue(store.deleteSubscriber(subscriberPub.getStreamId(), subscriberPub.getSubscriberId())); - + assertEquals(0, store.getConnectionEvents(subscriberPub.getStreamId(), subscriberPub.getSubscriberId(), 0, 50).size()); assertEquals(0, store.getConnectionEvents(subscriberPub.getStreamId(), null, 0, 50).size()); - + subscribers = store.listAllSubscribers(streamId, 0, 10); subscriberStats = store.listAllSubscriberStats(streamId, 0, 10); - - + + //it should be zero because subscriber is deleted assertEquals(0, subscribers.size()); assertEquals(0, subscriberStats.size()); assertEquals(0, store.getConnectionEvents(streamId, written.getSubscriberId(), 0, 50).size()); - - - - + + + + //create subscriber again assertTrue(store.addSubscriber(subscriberPlay.getStreamId(), subscriberPlay)); @@ -2313,39 +2341,39 @@ public void testTimeBasedSubscriberOperations(DataStore store) { connected.setType(Subscriber.PLAY_TYPE); hostAddress = ServerSettings.getLocalHostAddress(); connected.setInstanceIP(hostAddress); - + ConnectionEvent disconnected = new ConnectionEvent(); disconnected.setEventType(ConnectionEvent.DISCONNECTED_EVENT); long eventTimeDisconnect = 21; disconnected.setTimestamp(eventTimeDisconnect); - + // add connected event store.addSubscriberConnectionEvent(subscriberPlay.getStreamId(), subscriberPlay.getSubscriberId(), connected); // isConnected should be true assertTrue(store.isSubscriberConnected(subscriberPlay.getStreamId(), subscriberPlay.getSubscriberId())); assertEquals(1, store.getConnectionEvents(subscriberPlay.getStreamId(), subscriberPlay.getSubscriberId(), 0, 50).size()); assertEquals(1, store.getConnectionEvents(subscriberPlay.getStreamId(), null, 0, 50).size()); - + // add disconnected event store.addSubscriberConnectionEvent(subscriberPlay.getStreamId(), subscriberPlay.getSubscriberId(), disconnected); written = store.getSubscriber(subscriberPlay.getStreamId(), subscriberPlay.getSubscriberId()); - + // isConnected should return false assertFalse(store.isSubscriberConnected(subscriberPlay.getStreamId(), subscriberPlay.getSubscriberId())); assertFalse(written.isConnected()); assertEquals(0, written.getCurrentConcurrentConnections()); - + // there should be two events with correct order List events = store.getConnectionEvents(written.getStreamId(), written.getSubscriberId(), 0, 50); - + assertEquals(2, events.size()); - + assertEquals(ConnectionEvent.CONNECTED_EVENT, events.get(0).getEventType()); assertEquals(Subscriber.PLAY_TYPE, events.get(0).getType()); assertEquals(hostAddress, events.get(0).getInstanceIP()); assertEquals(eventTime, events.get(0).getTimestamp()); assertEquals(ConnectionEvent.DISCONNECTED_EVENT, events.get(1).getEventType()); - + connected = new ConnectionEvent(); connected.setEventType(ConnectionEvent.CONNECTED_EVENT); eventTime = 20; @@ -2353,7 +2381,7 @@ public void testTimeBasedSubscriberOperations(DataStore store) { connected.setType(Subscriber.PLAY_TYPE); hostAddress = ServerSettings.getLocalHostAddress(); connected.setInstanceIP(hostAddress); - + // add connected event store.addSubscriberConnectionEvent(subscriberPlay.getStreamId(), subscriberPlay.getSubscriberId(), connected); // isConnected should be true again @@ -2366,22 +2394,22 @@ public void testTimeBasedSubscriberOperations(DataStore store) { events = store.getConnectionEvents(subscriberPlay.getStreamId(), subscriberPlay.getSubscriberId(), 0, 50); assertEquals(3, events.size()); - + events = store.getConnectionEvents(subscriberPlay.getStreamId(), null, 0, 50); assertEquals(3, events.size()); - + store.revokeSubscribers(streamId); - + events = store.getConnectionEvents(subscriberPlay.getStreamId(), subscriberPlay.getSubscriberId(), 0, 50); assertEquals(0, events.size()); events = store.getConnectionEvents(subscriberPlay.getStreamId(), null, 0, 50); assertEquals(0, events.size()); - + { //save subscriber again - + String subscriberId = "subscriberId" + (int)(Math.random()*112313); Subscriber subscriber = new Subscriber(); subscriber.setStreamId(streamId); @@ -2389,36 +2417,40 @@ public void testTimeBasedSubscriberOperations(DataStore store) { subscriber.setB32Secret("6qsp6qhndryqs56zjmvs37i6gqtjsdvc"); subscriber.setType(Subscriber.PLAY_TYPE); assertTrue(store.addSubscriber(subscriber.getStreamId(), subscriber)); - + Subscriber subscriberFromDB = store.getSubscriber(streamId, subscriberId); - + assertNull(subscriberFromDB.getRegisteredNodeIp()); String nodeIp = "nodeip" + (int)(Math.random()*112313); subscriberFromDB.setRegisteredNodeIp(nodeIp); - + assertTrue(store.addSubscriber(subscriber.getStreamId(), subscriberFromDB)); - + subscriberFromDB = store.getSubscriber(streamId, subscriberId); assertEquals(nodeIp, subscriberFromDB.getRegisteredNodeIp()); - + if (store instanceof MongoStore) { MongoStore mongoStore = (MongoStore) store; Datastore subscriberDatastore = mongoStore.getSubscriberDatastore(); long count = subscriberDatastore.find(Subscriber.class).filter(Filters.eq("streamId", streamId), Filters.eq("subscriberId", subscriberId)).count(); - + assertEquals(1, count); } - + } + + + store.setWriteSubscriberEventsToDatastore(false); + } - + @Test public void testDontWriteStatsToDB () { /* DataStore ds = createDB("memorydb", false); assertTrue(ds instanceof InMemoryDataStore); testDontWriteStatsToDB(ds); - */ + */ DataStore ds = createDB("mapdb", false); assertTrue(ds instanceof MapDBStore); @@ -2437,7 +2469,7 @@ public void testDontWriteStatsToDB (DataStore dataStore) { testDontUpdateDASHViewerStats(dataStore); testDontUpdateWebRTCViewerStats(dataStore); } - + public void testDontUpdateRtmpViewerStats(DataStore dataStore) { Broadcast broadcast = new Broadcast(); broadcast.setName("test"); @@ -2455,7 +2487,7 @@ public void testDontUpdateHLSViewerStats(DataStore dataStore) { assertFalse(dataStore.updateHLSViewerCount(key, 1)); assertEquals(0, dataStore.get(key).getHlsViewerCount()); } - + public void testDontUpdateDASHViewerStats(DataStore dataStore) { Broadcast broadcast = new Broadcast(); broadcast.setName("test"); @@ -2476,7 +2508,7 @@ public void testDontUpdateWebRTCViewerStats(DataStore dataStore) { private DataStore createDB(String type, boolean writeStats) { DataStoreFactory dsf = new DataStoreFactory(); - + dsf.setWriteStatsToDatastore(writeStats); dsf.setDbType(type); dsf.setDbName("testdb"); @@ -2487,14 +2519,14 @@ private DataStore createDB(String type, boolean writeStats) { appSettings.setWriteStatsToDatastore(writeStats); when(context.getBean(AppSettings.BEAN_NAME)).thenReturn(appSettings); when(context.getBean(ServerSettings.BEAN_NAME)).thenReturn(new ServerSettings()); - - + + dsf.setApplicationContext(context); return dsf.getDataStore(); } public void testClearAtStart(DataStore dataStore) { - + if (dataStore instanceof MongoStore) { deleteBroadcast((MongoStore) dataStore); assertEquals(0, dataStore.getBroadcastCount()); @@ -2511,12 +2543,12 @@ public void testClearAtStart(DataStore dataStore) { } for (Broadcast broadcast : broadcastList) { assertTrue(dataStore.delete(broadcast.getStreamId())); - + } } - + } - + assertEquals(0, dataStore.getBroadcastCount()); Broadcast broadcast = new Broadcast(); broadcast.setName("test1"); @@ -2527,23 +2559,23 @@ public void testClearAtStart(DataStore dataStore) { broadcast2.setName("test2"); broadcast2.setZombi(true); dataStore.save(broadcast2); - + Broadcast broadcast3 = new Broadcast(); broadcast3.setStatus(AntMediaApplicationAdapter.BROADCAST_STATUS_BROADCASTING); broadcast3.setWebRTCViewerCount(104); broadcast3.setHlsViewerCount(305); broadcast3.setRtmpViewerCount(506); dataStore.save(broadcast3); - + Broadcast broadcast4 = new Broadcast(); broadcast4.setStatus(AntMediaApplicationAdapter.BROADCAST_STATUS_PREPARING); broadcast4.setWebRTCViewerCount(10); broadcast4.setHlsViewerCount(30); broadcast4.setRtmpViewerCount(50); dataStore.save(broadcast4); - + assertEquals(4, dataStore.getBroadcastCount()); - + dataStore.resetBroadcasts(ServerSettings.getLocalHostAddress()); assertEquals(2, dataStore.getBroadcastCount()); @@ -2554,12 +2586,12 @@ public void testClearAtStart(DataStore dataStore) { assertEquals(0, tmp.getHlsViewerCount()); assertEquals(0, tmp.getRtmpViewerCount()); } - + } public void testClearAtStartCluster(DataStore dataStore) { - - + + if (dataStore instanceof MongoStore) { deleteBroadcast((MongoStore) dataStore); assertEquals(0, dataStore.getBroadcastCount()); @@ -2576,12 +2608,12 @@ public void testClearAtStartCluster(DataStore dataStore) { } for (Broadcast broadcast : broadcastList) { assertTrue(dataStore.delete(broadcast.getStreamId())); - + } } - + } - + Broadcast broadcast = new Broadcast(); broadcast.setOriginAdress(ServerSettings.getLocalHostAddress()); broadcast.setName("test1"); @@ -2614,7 +2646,7 @@ public void testClearAtStartCluster(DataStore dataStore) { assertEquals(0, dataStore.getBroadcastCount()); assertEquals(0, dataStore.getStreamInfoList(broadcast.getStreamId()).size()); - + } @Test @@ -2636,13 +2668,13 @@ public void testMongoDBSaveStreamInfo() { public void deleteStreamInfos(MongoStore datastore) { datastore.getDataStore().find(StreamInfo.class).delete(new DeleteOptions() - .multi(true)); - + .multi(true)); + } - + public void deleteBroadcast(MongoStore dataStore) { dataStore.getDataStore().find(Broadcast.class).delete(new DeleteOptions() - .multi(true)); + .multi(true)); } public void saveStreamInfo(DataStore dataStore, String host1, int videoPort1, int audioPort1, int dataPort1, @@ -2653,11 +2685,11 @@ public void saveStreamInfo(DataStore dataStore, String host1, int videoPort1, in si.setStreamId("test1"); si.setOriginPort(5858); dataStore.saveStreamInfo(si); - + List siList = dataStore.getStreamInfoList("test1"); assertEquals(1, siList.size()); - + assertEquals(host1, siList.get(0).getHost()); assertEquals(5858, siList.get(0).getOriginPort()); @@ -2766,24 +2798,24 @@ private void testUpdateEndpointStatus(DataStore dataStore) Broadcast tmpBroadcast = dataStore.get(broadcast.getStreamId()); List endPointList = tmpBroadcast.getEndPointList(); - + BroadcastUpdate updateData = new BroadcastUpdate(); for (Endpoint tmpEndpoint : endPointList) { if (tmpEndpoint.getRtmpUrl().equals(rtmpUrl)) { tmpEndpoint.setStatus(IAntMediaStreamHandler.BROADCAST_STATUS_FAILED); - + break; } } - + updateData.setEndPointList(endPointList); - + //update rtmpurl result = dataStore.updateBroadcastFields(broadcast.getStreamId(), updateData); assertTrue(result); - - - + + + tmpBroadcast = dataStore.get(broadcast.getStreamId()); endPointList = tmpBroadcast.getEndPointList(); for (Endpoint tmpEndpoint : endPointList) { @@ -2797,9 +2829,9 @@ private void testUpdateEndpointStatus(DataStore dataStore) result = dataStore.updateBroadcastFields(broadcast.getStreamId(), updateData); assertTrue(result); - - - + + + Broadcast updated = dataStore.get(broadcast.getStreamId()); List endpList = updated.getEndPointList(); for(int i = 0; i < endpList.size(); i++){ @@ -2815,7 +2847,7 @@ else if(e.getRtmpUrl().equals(rtmpUrl2)){ } } } - + private void testUpdateStatus(DataStore dataStore) { String streamId = "test"; Broadcast broadcast = new Broadcast(); @@ -2824,14 +2856,14 @@ private void testUpdateStatus(DataStore dataStore) { } catch (Exception e1) { e1.printStackTrace(); } - + broadcast.setWebRTCViewerCount(10); broadcast.setHlsViewerCount(1000); broadcast.setRtmpViewerCount(100); - + broadcast.setStatus(AntMediaApplicationAdapter.BROADCAST_STATUS_CREATED); dataStore.save(broadcast); - + Broadcast broadcastFromStore = dataStore.get(streamId); assertNotNull(broadcastFromStore); assertEquals(AntMediaApplicationAdapter.BROADCAST_STATUS_CREATED, broadcastFromStore.getStatus()); @@ -2839,29 +2871,29 @@ private void testUpdateStatus(DataStore dataStore) { long now = System.currentTimeMillis(); dataStore.updateStatus(streamId, AntMediaApplicationAdapter.BROADCAST_STATUS_BROADCASTING); - + broadcastFromStore = dataStore.get(streamId); assertEquals(AntMediaApplicationAdapter.BROADCAST_STATUS_BROADCASTING, broadcastFromStore.getStatus()); assertTrue(Math.abs(now-broadcastFromStore.getStartTime()) < 100); - + //wait to be sure time changed from we set now try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } - + dataStore.updateStatus(streamId, AntMediaApplicationAdapter.BROADCAST_STATUS_FINISHED); - + broadcastFromStore = dataStore.get(streamId); assertEquals(AntMediaApplicationAdapter.BROADCAST_STATUS_FINISHED, broadcastFromStore.getStatus()); assertTrue(Math.abs(now-broadcastFromStore.getStartTime()) < 100); - + assertEquals(0, broadcastFromStore.getWebRTCViewerCount()); assertEquals(0, broadcastFromStore.getRtmpViewerCount()); assertEquals(0, broadcastFromStore.getHlsViewerCount()); } - + private void testP2PConnection(DataStore dataStore) { String streamId = "p2pstream"+Math.random()*100; P2PConnection p2pConn = new P2PConnection(streamId, "dummy"); @@ -2874,13 +2906,13 @@ private void testP2PConnection(DataStore dataStore) { assertEquals("dummy", conn.getOriginNode()); assertTrue(dataStore.deleteP2PConnection(streamId)); assertNull(dataStore.getP2PConnection(streamId)); - - + + assertFalse(dataStore.createP2PConnection(null)); assertNull(dataStore.getP2PConnection(streamId)); assertFalse(dataStore.deleteP2PConnection(streamId)); - - + + } else { assertFalse(dataStore.createP2PConnection(p2pConn)); @@ -2888,7 +2920,7 @@ private void testP2PConnection(DataStore dataStore) { assertFalse(dataStore.deleteP2PConnection(streamId)); } } - + public void testUpdateLocationParams(DataStore dataStore) { logger.info("testUpdateLocationParams for {}", dataStore.getClass()); @@ -2900,76 +2932,76 @@ public void testUpdateLocationParams(DataStore dataStore) { e1.printStackTrace(); } dataStore.save(broadcast); - + Broadcast broadcastFromStore = dataStore.get(streamId); assertNull(broadcastFromStore.getLatitude()); assertNull(broadcastFromStore.getLongitude()); assertNull(broadcastFromStore.getAltitude()); - + String latitude = "51.507351"; String longitude = "-0.127758"; String altitude = "58.58"; - + assertEquals(AntMediaApplicationAdapter.BROADCAST_STATUS_CREATED, broadcastFromStore.getStatus()); - + broadcastFromStore.setLatitude(latitude); broadcastFromStore.setLongitude(longitude); broadcastFromStore.setAltitude(altitude); broadcastFromStore.setStatus(null); - + BroadcastUpdate updateData = new BroadcastUpdate(); updateData.setLatitude(latitude); updateData.setLongitude(longitude); updateData.setAltitude(altitude); updateData.setStatus(null); assertTrue(dataStore.updateBroadcastFields(streamId, updateData)); - + Broadcast broadcastFromStore2 = dataStore.get(streamId); assertEquals(latitude, broadcastFromStore2.getLatitude()); assertEquals(longitude, broadcastFromStore2.getLongitude()); assertEquals(altitude, broadcastFromStore2.getAltitude()); - + if (!(dataStore instanceof InMemoryDataStore)) { assertEquals(AntMediaApplicationAdapter.BROADCAST_STATUS_CREATED, broadcastFromStore2.getStatus()); } } - + public void testPlaylist(DataStore dataStore) { - + //create a broadcast List broadcastList = new ArrayList<>(); - + broadcastList.add(new PlayListItem("", null)); - + Broadcast broadcast = new Broadcast(); broadcast.setName("playlistName"); broadcast.setType(AntMediaApplicationAdapter.PLAY_LIST); broadcast.setPlayListItemList(broadcastList); - + //create playlist String streamId = dataStore.save(broadcast); - + Broadcast broadcast2 = dataStore.get(streamId); assertNotNull(streamId); assertEquals(AntMediaApplicationAdapter.PLAY_LIST, broadcast2.getType()); assertEquals(1, broadcast2.getPlayListItemList().size()); assertNull(broadcast2.getPlayListStatus()); - + //update playlist - + broadcast.setPlayListStatus(AntMediaApplicationAdapter.BROADCAST_STATUS_FINISHED); broadcastList.clear(); broadcast.setPlayListItemList(broadcastList); broadcast.setCurrentPlayIndex(10); - + BroadcastUpdate updateData = new BroadcastUpdate(); updateData.setPlayListStatus(AntMediaApplicationAdapter.BROADCAST_STATUS_FINISHED); updateData.setPlayListItemList(broadcastList); updateData.setCurrentPlayIndex(10); assertTrue(dataStore.updateBroadcastFields(streamId, updateData)); - + broadcast2 = dataStore.get(streamId); assertTrue(broadcast2.getPlayListItemList() == null || broadcast2.getPlayListItemList().isEmpty()); assertEquals(AntMediaApplicationAdapter.BROADCAST_STATUS_FINISHED, broadcast2.getPlayListStatus()); @@ -2979,14 +3011,14 @@ public void testPlaylist(DataStore dataStore) { Broadcast playlist2 = dataStore.get(streamId); assertNotNull(playlist2); - + assertEquals("playlistName", broadcast.getName()); //delete playlist assertTrue(dataStore.delete(streamId)); assertNull(dataStore.get(streamId)); - + } public void testAddTrack(DataStore dataStore) { @@ -3026,14 +3058,14 @@ public void testAddTrack(DataStore dataStore) { assertEquals(1, mainTrack.getSubTrackStreamIds().size()); assertEquals(subTrackId, mainTrack.getSubTrackStreamIds().get(0)); assertEquals(mainTrackId, subtrack.getMainTrackStreamId()); - - + + result = dataStore.addSubTrack("Not exists", subTrackId); assertFalse(result); - + result = dataStore.addSubTrack(mainTrackId, null); assertFalse(result); - + } @@ -3127,7 +3159,7 @@ else if (vod.getVodId().equals(vod3.getVodId())) { assertTrue(vod2Match); } - + public void testTotalWebRTCViewerCount(DataStore dataStore) { int total = 0; for (int i = 0; i < 150; i++) { @@ -3139,9 +3171,9 @@ public void testTotalWebRTCViewerCount(DataStore dataStore) { broadcast.setWebRTCViewerCount(count); dataStore.save(broadcast); } - + assertEquals(total, dataStore.getTotalWebRTCViewersCount()); - + int total2 = 0; for (int i = 0; i < 150; i++) { Broadcast broadcast = new Broadcast(); @@ -3152,19 +3184,19 @@ public void testTotalWebRTCViewerCount(DataStore dataStore) { broadcast.setWebRTCViewerCount(count); dataStore.save(broadcast); } - + //totalWebRTCViewersCount is still total but not total+total2 due to cache assertEquals(total, dataStore.getTotalWebRTCViewersCount()); - + int finalTotal = total+total2; - + //Alter cache time it solud be total+total2 Awaitility.await().atMost(DataStore.TOTAL_WEBRTC_VIEWER_COUNT_CACHE_TIME+1100, TimeUnit.MILLISECONDS) - .pollDelay(1000, TimeUnit.MILLISECONDS) - .until(() -> (finalTotal == dataStore.getTotalWebRTCViewersCount())); + .pollDelay(1000, TimeUnit.MILLISECONDS) + .until(() -> (finalTotal == dataStore.getTotalWebRTCViewersCount())); } - + @Test public void testDeleteMapDB() { String dbName = "deleteMapdb"; @@ -3173,20 +3205,20 @@ public void testDeleteMapDB() { dataStore.close(true); assertFalse(new File(dbName).exists()); } - + @Test public void testDeleteMongoDBCollection() { String dbName = "deleteMapdb"; MongoStore dataStore = new MongoStore("127.0.0.1", "", "", dbName); - + MongoClientURI mongoUri = new MongoClientURI(dataStore.getMongoConnectionUri("127.0.0.1", "", "")); MongoClient client = new MongoClient(mongoUri); - - + + ArrayList dbNames = new ArrayList(); client.listDatabaseNames().forEach(c-> dbNames.add(c)); assertTrue(dbNames.contains(dbName)); - + dataStore.close(true); dbNames.clear(); @@ -3194,12 +3226,12 @@ public void testDeleteMongoDBCollection() { assertFalse(dbNames.contains(dbName)); } - - + + public void testWebRTCViewerOperations(DataStore dataStore) { - + ArrayList idList = new ArrayList(); - + int total = RandomUtils.nextInt(10, DataStore.MAX_ITEM_IN_ONE_LIST); for (int i = 0; i < total; i++) { WebRTCViewerInfo info = new WebRTCViewerInfo(); @@ -3207,37 +3239,37 @@ public void testWebRTCViewerOperations(DataStore dataStore) { info.setStreamId(streamId); String id = RandomStringUtils.randomAlphabetic(5); info.setViewerId(id); - + dataStore.saveViewerInfo(info); - + idList.add(id); } - + List returningList = dataStore.getWebRTCViewerList(0, DataStore.MAX_ITEM_IN_ONE_LIST+10, "viewerId", "asc", ""); assertEquals(total, returningList.size()); - - - Collections.sort(idList); - - for (int i = 0; i < total; i++) { + + + Collections.sort(idList); + + for (int i = 0; i < total; i++) { assertEquals(idList.get(i), returningList.get(i).getViewerId()); } - + List returningList2 = dataStore.getWebRTCViewerList(0, total, "viewerId", "asc", "a"); for (WebRTCViewerInfo webRTCViewerInfo : returningList2) { assertTrue(webRTCViewerInfo.getViewerId().contains("a")||webRTCViewerInfo.getViewerId().contains("A")); } - - - int deleted = 0; - for (String id : idList) { + + + int deleted = 0; + for (String id : idList) { dataStore.deleteWebRTCViewerInfo(id); - List tempList = dataStore.getWebRTCViewerList(0, total, "viewerId", "asc", ""); - + List tempList = dataStore.getWebRTCViewerList(0, total, "viewerId", "asc", ""); + assertEquals(total - (++deleted), tempList.size()); } } - + public void testUpdateMetaData(DataStore dataStore) { final String INITIAL_DATA = "initial meta data"; @@ -3256,11 +3288,11 @@ public void testUpdateMetaData(DataStore dataStore) { dataStore.save(broadcast); assertEquals(INITIAL_DATA, dataStore.get(id).getMetaData()); - + assertTrue(dataStore.updateStreamMetaData(id, UPDATED_DATA)); - + assertEquals(UPDATED_DATA, dataStore.get(id).getMetaData()); - + assertFalse(dataStore.updateStreamMetaData("someDummyStream"+RandomStringUtils.randomAlphanumeric(8), UPDATED_DATA)); } @@ -3313,9 +3345,9 @@ public void testBlockSubscriber(DataStore dataStore){ long currTime = System.currentTimeMillis(); dataStore.addSubscriber(streamId, subscriber); - + Subscriber subscriberFromDB = dataStore.getSubscriber(streamId, subscriberId); - + assertNull(subscriberFromDB.getBlockedType()); assertFalse(subscriberFromDB.isBlocked(Subscriber.PLAY_TYPE)); assertFalse(subscriberFromDB.isBlocked(Subscriber.PUBLISH_TYPE)); @@ -3324,20 +3356,20 @@ public void testBlockSubscriber(DataStore dataStore){ assertTrue(dataStore.blockSubscriber(streamId, subscriberId, Subscriber.PLAY_TYPE, 10)); subscriberFromDB = dataStore.getSubscriber(streamId, subscriberId); assertEquals(Subscriber.PLAY_TYPE, subscriberFromDB.getBlockedType()); - + assertTrue(subscriberFromDB.isBlocked(Subscriber.PLAY_TYPE)); - + assertFalse(subscriberFromDB.isBlocked(Subscriber.PUBLISH_TYPE)); assertFalse(subscriberFromDB.isBlocked(Subscriber.PUBLISH_AND_PLAY_TYPE)); assertTrue(subscriberFromDB.getBlockedUntilUnitTimeStampMs() - System.currentTimeMillis() <= 10000); - - + + assertFalse(dataStore.blockSubscriber(null, subscriberId, Subscriber.PLAY_TYPE, 10)); assertFalse(dataStore.blockSubscriber(streamId, null, Subscriber.PLAY_TYPE, 10)); - + assertTrue(dataStore.blockSubscriber(streamId, subscriberId, Subscriber.PUBLISH_TYPE, 50)); subscriberFromDB = dataStore.getSubscriber(streamId, subscriberId); assertEquals(Subscriber.PUBLISH_TYPE, subscriberFromDB.getBlockedType()); @@ -3356,8 +3388,8 @@ public void testBlockSubscriber(DataStore dataStore){ assertTrue(subscriberFromDB.isBlocked(Subscriber.PUBLISH_AND_PLAY_TYPE)); assertTrue(subscriberFromDB.getBlockedUntilUnitTimeStampMs() - System.currentTimeMillis() <= 50000); - - + + //if subscriber is not in datastore, still blockUser should be supported subscriberId = "subscriberNotInDB"; assertTrue(dataStore.blockSubscriber(streamId, subscriberId, Subscriber.PUBLISH_AND_PLAY_TYPE, 50)); @@ -3369,25 +3401,25 @@ public void testBlockSubscriber(DataStore dataStore){ } - + private void testSubscriberMetaData(DataStore dataStore) { //save subscriberMetadata to the data store - + String subscriberId = RandomStringUtils.randomAlphanumeric(12); - + SubscriberMetadata subscriberMetaData = dataStore.getSubscriberMetaData(subscriberId); assertNull(subscriberMetaData); - + SubscriberMetadata metadata = new SubscriberMetadata(); Map pushNotificationTokens = new HashMap<>(); String tokenValue = RandomStringUtils.randomAlphabetic(65); - + PushNotificationToken token = new PushNotificationToken(tokenValue, PushNotificationServiceTypes.FIREBASE_CLOUD_MESSAGING.toString()); pushNotificationTokens.put(tokenValue, token); - + metadata.setPushNotificationTokens(pushNotificationTokens); dataStore.putSubscriberMetaData(subscriberId, metadata); - + //get the value with the id subscriberMetaData = dataStore.getSubscriberMetaData(subscriberId); assertNotNull(subscriberMetaData); @@ -3397,30 +3429,30 @@ private void testSubscriberMetaData(DataStore dataStore) { assertEquals("fcm", subscriberMetaData.getPushNotificationTokens().get(tokenValue).getServiceName()); assertNull(subscriberMetaData.getPushNotificationTokens().get(tokenValue).getExtraData()); - + String tokenValue2 = RandomStringUtils.randomAlphabetic(65); - + PushNotificationToken token2 = new PushNotificationToken(tokenValue2, PushNotificationServiceTypes.APPLE_PUSH_NOTIFICATION.toString()); String extraData = RandomStringUtils.randomAlphanumeric(12); token2.setExtraData(extraData); subscriberMetaData.getPushNotificationTokens().put(tokenValue2, token2); - - + + dataStore.putSubscriberMetaData(subscriberId, subscriberMetaData); - + subscriberMetaData = dataStore.getSubscriberMetaData(subscriberId); - + assertNotNull(subscriberMetaData); assertEquals(subscriberId, subscriberMetaData.getSubscriberId()); assertEquals(2, subscriberMetaData.getPushNotificationTokens().size()); assertEquals(tokenValue, subscriberMetaData.getPushNotificationTokens().get(tokenValue).getToken()); assertEquals("fcm", subscriberMetaData.getPushNotificationTokens().get(tokenValue).getServiceName()); assertNull(subscriberMetaData.getPushNotificationTokens().get(tokenValue).getExtraData()); - + assertEquals(tokenValue2, subscriberMetaData.getPushNotificationTokens().get(tokenValue2).getToken()); assertEquals("apn", subscriberMetaData.getPushNotificationTokens().get(tokenValue2).getServiceName()); assertEquals(extraData, subscriberMetaData.getPushNotificationTokens().get(tokenValue2).getExtraData()); - + } public void testUpdateBroadcastEncoderSettings(DataStore dataStore) { @@ -3437,20 +3469,20 @@ public void testUpdateBroadcastEncoderSettings(DataStore dataStore) { assertNotNull(dataStore.save(broadcast)); - assertNull(dataStore.get(id).getEncoderSettingsList()); + assertNull(dataStore.get(id).getEncoderSettingsList()); - List settingsList = new ArrayList<>(); - settingsList.add(new EncoderSettings(720, 50000, 32000, true)); + List settingsList = new ArrayList<>(); + settingsList.add(new EncoderSettings(720, 50000, 32000, true)); broadcast.setEncoderSettingsList(settingsList); - + BroadcastUpdate updateData = new BroadcastUpdate(); updateData.setEncoderSettingsList(settingsList); assertTrue(dataStore.updateBroadcastFields(id, updateData)); assertEquals(32000, dataStore.get(id).getEncoderSettingsList().get(0).getAudioBitrate()); - assertEquals(50000, dataStore.get(id).getEncoderSettingsList().get(0).getVideoBitrate()); + assertEquals(50000, dataStore.get(id).getEncoderSettingsList().get(0).getVideoBitrate()); if (! (dataStore instanceof InMemoryDataStore) ) { //because inmemorydata store just keeps the reference, it will be updated @@ -3459,7 +3491,7 @@ public void testUpdateBroadcastEncoderSettings(DataStore dataStore) { //it will not be updated because encoder settings is null assertEquals(32000, dataStore.get(id).getEncoderSettingsList().get(0).getAudioBitrate()); - assertEquals(50000, dataStore.get(id).getEncoderSettingsList().get(0).getVideoBitrate()); + assertEquals(50000, dataStore.get(id).getEncoderSettingsList().get(0).getVideoBitrate()); updateData.setEncoderSettingsList(new ArrayList<>()); @@ -3511,14 +3543,14 @@ private void testGetSubtracks(DataStore dataStore) Broadcast broadcast = new Broadcast(); broadcast.setName("subtrackTrackName"+i); broadcast.setType(AntMediaApplicationAdapter.LIVE_STREAM); - try { - broadcast.setStreamId("subtrackTrackId"+i); - } catch (Exception e) { - throw new RuntimeException(e); - } + try { + broadcast.setStreamId("subtrackTrackId"+i); + } catch (Exception e) { + throw new RuntimeException(e); + } broadcast.setMainTrackStreamId(mainTrackId); broadcast.setRole(i%2 == 0 ? role1 : role2); - dataStore.save(broadcast); + dataStore.save(broadcast); } List subtracks = dataStore.getSubtracks(mainTrackId, 0, 3, null); @@ -3562,39 +3594,39 @@ private void testGetSubtracks(DataStore dataStore) assertEquals(mainTrackId, broadcast.getMainTrackStreamId()); } } - - + + public void testGetSubtracksWithStatus(DataStore dataStore) { - + String role1 = "role1"; String role2 = "role2"; - + String mainTrackId = RandomStringUtils.randomAlphanumeric(8); - + for (int i = 0; i < 100; i++) { Broadcast broadcast = new Broadcast(); broadcast.setType(AntMediaApplicationAdapter.LIVE_STREAM); - try { - broadcast.setStreamId("subtrack" + RandomStringUtils.randomAlphanumeric(24)); - } catch (Exception e) { - throw new RuntimeException(e); - } - - if (i < 50) { - broadcast.setStatus(AntMediaApplicationAdapter.BROADCAST_STATUS_BROADCASTING); - - if (i % 2 == 0) { - broadcast.setUpdateTime(System.currentTimeMillis()); - } - } - else { - broadcast.setStatus(AntMediaApplicationAdapter.BROADCAST_STATUS_FINISHED); - } + try { + broadcast.setStreamId("subtrack" + RandomStringUtils.randomAlphanumeric(24)); + } catch (Exception e) { + throw new RuntimeException(e); + } + + if (i < 50) { + broadcast.setStatus(AntMediaApplicationAdapter.BROADCAST_STATUS_BROADCASTING); + + if (i % 2 == 0) { + broadcast.setUpdateTime(System.currentTimeMillis()); + } + } + else { + broadcast.setStatus(AntMediaApplicationAdapter.BROADCAST_STATUS_FINISHED); + } broadcast.setMainTrackStreamId(mainTrackId); broadcast.setRole(i%2 == 0 ? role1 : role2); - dataStore.save(broadcast); + dataStore.save(broadcast); } - + assertEquals(100, dataStore.getSubtrackCount(mainTrackId, null, null)); assertEquals(50, dataStore.getSubtrackCount(mainTrackId, null, AntMediaApplicationAdapter.BROADCAST_STATUS_BROADCASTING)); assertEquals(25, dataStore.getSubtrackCount(mainTrackId, role1, AntMediaApplicationAdapter.BROADCAST_STATUS_BROADCASTING)); @@ -3603,35 +3635,35 @@ public void testGetSubtracksWithStatus(DataStore dataStore) { assertEquals(50, dataStore.getSubtrackCount(mainTrackId, null, AntMediaApplicationAdapter.BROADCAST_STATUS_FINISHED)); assertEquals(25, dataStore.getSubtrackCount(mainTrackId, role1, AntMediaApplicationAdapter.BROADCAST_STATUS_FINISHED)); assertEquals(25, dataStore.getSubtrackCount(mainTrackId, role2, AntMediaApplicationAdapter.BROADCAST_STATUS_FINISHED)); - - + + assertEquals(0, dataStore.getSubtrackCount("nonExistentMainTrack", null, null)); - + // it is 25 because 25 subtracks has broadcasting status and their update time is up to date assertEquals(25, dataStore.getActiveSubtracksCount(mainTrackId, null)); - + assertEquals(25, dataStore.getActiveSubtracksCount(mainTrackId, role1)); assertEquals(0, dataStore.getActiveSubtracksCount(mainTrackId, role2)); assertEquals(0, dataStore.getActiveSubtracksCount("nonExistentMainTrack", null)); - - + + List activeSubtracks = dataStore.getActiveSubtracks(mainTrackId, null); assertEquals(25, activeSubtracks.size()); - + activeSubtracks = dataStore.getActiveSubtracks(mainTrackId, role1); assertEquals(25, activeSubtracks.size()); - + activeSubtracks = dataStore.getActiveSubtracks(mainTrackId, role2); assertEquals(0, activeSubtracks.size()); - + activeSubtracks = dataStore.getActiveSubtracks("nonExistentMainTrack", null); assertEquals(0, activeSubtracks.size()); - + assertTrue(dataStore.hasSubtracks(mainTrackId)); assertFalse(dataStore.hasSubtracks("nonExistentMainTrack")); - - + + } public void testSubscriberCache(DataStore dataStore) { @@ -3650,7 +3682,7 @@ public void testSubscriberCache(DataStore dataStore) { long executedQueryCount = dataStore.getExecutedQueryCount(); - assertEquals(initalExecutedQueryCount + 1, executedQueryCount ); + assertEquals(initalExecutedQueryCount + 1, executedQueryCount ); String subscriberCacheKey = ((MongoStore) dataStore).getSubscriberCacheKey(streamId,subscriberId1); @@ -3687,7 +3719,7 @@ public void testSubscriberCache(DataStore dataStore) { assertNotNull(subscriberFromCache); - assertNull(subscriberFromCache.getSubscriberId()); + assertNull(subscriberFromCache.getSubscriberId()); executedQueryCount = dataStore.getExecutedQueryCount(); diff --git a/src/test/java/io/antmedia/test/rest/BroadcastRestServiceV2UnitTest.java b/src/test/java/io/antmedia/test/rest/BroadcastRestServiceV2UnitTest.java index 504163104..8895f0fa6 100644 --- a/src/test/java/io/antmedia/test/rest/BroadcastRestServiceV2UnitTest.java +++ b/src/test/java/io/antmedia/test/rest/BroadcastRestServiceV2UnitTest.java @@ -64,6 +64,7 @@ import io.antmedia.datastore.db.types.Broadcast.PlayListItem; import io.antmedia.datastore.db.types.BroadcastUpdate; import io.antmedia.datastore.db.types.ConferenceRoom; +import io.antmedia.datastore.db.types.ConnectionEvent; import io.antmedia.datastore.db.types.Endpoint; import io.antmedia.datastore.db.types.StreamInfo; import io.antmedia.datastore.db.types.Subscriber; @@ -1924,6 +1925,7 @@ public void testSubscriberNone32BitSecret() { public void testTimeBasedSubscriberOperations() { DataStore store = new MapDBStore(RandomStringUtils.randomAlphanumeric(6) + ".db", vertx); + store.setWriteSubscriberEventsToDatastore(true); restServiceReal.setDataStore(store); @@ -1948,6 +1950,24 @@ public void testTimeBasedSubscriberOperations() { assertEquals(2, subscribers.size()); assertEquals(2, subscriberStats.size()); + + List connectionEvents = restServiceReal.getConnectionEvents(subscriber.getStreamId(), 0, 10, null); + assertEquals(0, connectionEvents.size()); + + ConnectionEvent event = new ConnectionEvent(); + event.setEventType(ConnectionEvent.CONNECTED_EVENT); + event.setType(Subscriber.PLAY_TYPE); + + assertTrue(store.addSubscriberConnectionEvent(subscriber.getStreamId(), subscriber.getSubscriberId(), event)); + + connectionEvents = restServiceReal.getConnectionEvents(null, 0, 10, null); + assertEquals(0, connectionEvents.size()); + + connectionEvents = restServiceReal.getConnectionEvents(subscriber.getStreamId(), 0, 10, null); + assertEquals(1, connectionEvents.size()); + + connectionEvents = restServiceReal.getConnectionEvents(subscriber.getStreamId(), 0, 10, subscriber.getSubscriberId()); + assertEquals(1, connectionEvents.size()); assertEquals("stream1", subscriberStats.get(0).getStreamId()); assertEquals("timeSubscriber", subscriberStats.get(0).getSubscriberId());