Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/*
* RED5 Open Source Media Server - https://github.com/Red5/
*
* Copyright 2006-2016 by respective authors (see below). All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.red5.server.stream;
import java.io.File;
import java.io.IOException;
import java.lang.management.ManagementFactory;
import java.lang.ref.WeakReference;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;
import javax.management.InstanceAlreadyExistsException;
import javax.management.MBeanServer;
import javax.management.ObjectName;
import javax.management.StandardMBean;
import org.apache.commons.lang3.StringUtils;
import org.apache.mina.core.buffer.IoBuffer;
import org.red5.codec.IAudioStreamCodec;
import org.red5.codec.IStreamCodecInfo;
import org.red5.codec.IVideoStreamCodec;
import org.red5.codec.StreamCodecInfo;
import org.red5.io.object.DataTypes;
import org.red5.io.object.Input;
import org.red5.server.api.IConnection;
import org.red5.server.api.IContext;
import org.red5.server.api.Red5;
import org.red5.server.api.event.IEvent;
import org.red5.server.api.event.IEventDispatcher;
import org.red5.server.api.event.IEventListener;
import org.red5.server.api.scope.IScope;
import org.red5.server.api.statistics.IClientBroadcastStreamStatistics;
import org.red5.server.api.statistics.support.StatisticsCounter;
import org.red5.server.api.stream.IClientBroadcastStream;
import org.red5.server.api.stream.IStreamAwareScopeHandler;
import org.red5.server.api.stream.IStreamCapableConnection;
import org.red5.server.api.stream.IStreamListener;
import org.red5.server.api.stream.IStreamPacket;
import org.red5.server.jmx.mxbeans.ClientBroadcastStreamMXBean;
import org.red5.server.messaging.IConsumer;
import org.red5.server.messaging.IFilter;
import org.red5.server.messaging.IMessage;
import org.red5.server.messaging.IMessageComponent;
import org.red5.server.messaging.IMessageOutput;
import org.red5.server.messaging.IPipe;
import org.red5.server.messaging.IPipeConnectionListener;
import org.red5.server.messaging.IProvider;
import org.red5.server.messaging.IPushableConsumer;
import org.red5.server.messaging.OOBControlMessage;
import org.red5.server.messaging.PipeConnectionEvent;
import org.red5.server.net.rtmp.event.AudioData;
import org.red5.server.net.rtmp.event.IRTMPEvent;
import org.red5.server.net.rtmp.event.Invoke;
import org.red5.server.net.rtmp.event.Notify;
import org.red5.server.net.rtmp.event.VideoData;
import org.red5.server.net.rtmp.status.Status;
import org.red5.server.net.rtmp.status.StatusCodes;
import org.red5.server.stream.message.RTMPMessage;
import org.red5.server.stream.message.StatusMessage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.jmx.export.annotation.ManagedResource;
import io.antmedia.cluster.IClusterNotifier;
import io.antmedia.datastore.db.DataStore;
import io.antmedia.datastore.db.IDataStoreFactory;
import io.antmedia.datastore.db.types.Broadcast;
import io.antmedia.datastore.db.types.Endpoint;
import io.antmedia.muxer.MuxAdaptor;
import io.antmedia.muxer.RtmpMuxer;
/**
* Represents live stream broadcasted from client. As Flash Media Server, Red5 supports recording mode for live streams, that is,
* broadcasted stream has broadcast mode. It can be either "live" or "record" and latter causes server-side application to record
* broadcasted stream.
*
* Note that recorded streams are recorded as FLV files.
*
* This type of stream uses two different pipes for live streaming and recording.
*
* @author The Red5 Project
* @author Steven Gong
* @author Paul Gregoire ([email protected])
* @author Vladimir Hmelyoff ([email protected])
*/
@ManagedResource(objectName = "org.red5.server:type=ClientBroadcastStream", description = "ClientBroadcastStream")
public class ClientBroadcastStream extends AbstractClientStream implements IClientBroadcastStream, IFilter, IPushableConsumer, IPipeConnectionListener, IEventDispatcher, IClientBroadcastStreamStatistics, ClientBroadcastStreamMXBean {
private static final Logger log = LoggerFactory.getLogger(ClientBroadcastStream.class);
/**
* Whether or not to automatically record the associated stream.
*/
protected boolean automaticRecording;
/**
* Total number of bytes received.
*/
protected long bytesReceived;
/**
* Is there need to check video codec?
*/
protected boolean checkVideoCodec = false;
/**
* Is there need to check audio codec?
*/
protected boolean checkAudioCodec = false;
/**
* Data is sent by chunks, each of them has size
*/
protected int chunkSize;
/**
* Is this stream still active?
*/
protected volatile boolean closed;
/**
* Output endpoint that providers use
*/
protected transient IMessageOutput connMsgOut;
/**
* Stores timestamp of first packet
*/
protected long firstPacketTime = -1;
/**
* Pipe for live streaming
*/
protected transient IPipe livePipe;
/**
* Stream published name
*/
protected String publishedName;
/**
* Streaming parameters
*/
protected Map parameters;
/**
* Is there need to send start notification?
*/
protected boolean sendStartNotification = true;
/**
* Stores statistics about subscribers.
*/
private transient StatisticsCounter subscriberStats = new StatisticsCounter();
/**
* Listeners to get notified about received packets.
*/
protected transient Set listeners = new CopyOnWriteArraySet();
/**
* Recording listener
*/
private transient WeakReference recordingListener;
protected long latestTimeStamp = -1;
/**
* Whether or not to register with JMX.
*/
private boolean registerJMX = true;
/**
* Whether or not automatically record incoming stream as mp4
*/
private boolean automaticMp4Recording;
/**
* Whether or not automatically record incoming stream as mp4
*/
private boolean automaticHlsRecording;
private WeakReference muxAdaptor;
private IClusterNotifier clusterNotifier;
//private WeakReference endPointMuxAdaptor;
/**
* Check and send notification if necessary
*
* @param event
* Event
*/
private void checkSendNotifications(IEvent event) {
IEventListener source = event.getSource();
sendStartNotifications(source);
}
/**
* Closes stream, unsubscribes provides, sends stoppage notifications and broadcast close notification.
*/
public void close() {
log.debug("Stream close: {}", publishedName);
if (closed) {
log.debug("{} already closed", publishedName);
return;
}
closed = true;
if (livePipe != null) {
livePipe.unsubscribe((IProvider) this);
}
// if we have a recording listener, inform that this stream is done
if (recordingListener != null) {
sendRecordStopNotify();
notifyRecordingStop();
// inform the listener to finish and close
recordingListener.get().stop();
}
log.info("Checking mux adaptor to stop {}", publishedName);
if (muxAdaptor != null) {
MuxAdaptor realAdaptor = muxAdaptor.get();
if (realAdaptor != null) {
realAdaptor.stop();
}
else {
log.warn("Mux adaptor reference is null");
}
}
log.info("Mux Adaptor stop called {}", publishedName);
sendPublishStopNotify();
// TODO: can we send the client something to make sure he stops sending data?
if (connMsgOut != null) {
connMsgOut.unsubscribe(this);
}
notifyBroadcastClose();
// clear the listener after all the notifications have been sent
if (recordingListener != null) {
recordingListener.clear();
}
if (muxAdaptor != null) {
muxAdaptor.clear();
muxAdaptor = null;
}
// clear listeners
if (!listeners.isEmpty()) {
listeners.clear();
}
// deregister with jmx
unregisterJMX();
}
/**
* Dispatches event
*
* @param event
* Event to dispatch
*/
public void dispatchEvent(IEvent event) {
if (event instanceof IRTMPEvent && !closed) {
switch (event.getType()) {
case STREAM_CONTROL:
case STREAM_DATA:
// create the event
IRTMPEvent rtmpEvent;
try {
rtmpEvent = (IRTMPEvent) event;
} catch (ClassCastException e) {
log.error("Class cast exception in event dispatch", e);
return;
}
int eventTime = -1;
if (log.isTraceEnabled()) {
// If this is first packet save its timestamp; expect it is
// absolute? no matter: it's never used!
if (firstPacketTime == -1) {
firstPacketTime = rtmpEvent.getTimestamp();
log.trace(String.format("CBS=@%08x: rtmpEvent=%s creation=%s firstPacketTime=%d", System.identityHashCode(this), rtmpEvent.getClass().getSimpleName(), creationTime, firstPacketTime));
} else {
log.trace(String.format("CBS=@%08x: rtmpEvent=%s creation=%s firstPacketTime=%d timestamp=%d", System.identityHashCode(this), rtmpEvent.getClass().getSimpleName(), creationTime, firstPacketTime, rtmpEvent.getTimestamp()));
}
}
//get the buffer only once per call
IoBuffer buf = null;
if (rtmpEvent instanceof IStreamData && (buf = ((IStreamData>) rtmpEvent).getData()) != null) {
bytesReceived += buf.limit();
}
// get stream codec
IStreamCodecInfo codecInfo = getCodecInfo();
StreamCodecInfo info = null;
if (codecInfo instanceof StreamCodecInfo) {
info = (StreamCodecInfo) codecInfo;
}
//log.trace("Stream codec info: {}", info);
if (rtmpEvent instanceof AudioData) {
IAudioStreamCodec audioStreamCodec = null;
if (checkAudioCodec) {
// dont try to read codec info from 0 length audio packets
if (buf.limit() > 0) {
audioStreamCodec = AudioCodecFactory.getAudioCodec(buf);
if (info != null) {
info.setAudioCodec(audioStreamCodec);
}
checkAudioCodec = false;
}
} else if (codecInfo != null) {
audioStreamCodec = codecInfo.getAudioCodec();
}
if (audioStreamCodec != null) {
audioStreamCodec.addData(buf);
}
if (info != null) {
info.setHasAudio(true);
}
eventTime = rtmpEvent.getTimestamp();
log.trace("Audio: {}", eventTime);
} else if (rtmpEvent instanceof VideoData) {
IVideoStreamCodec videoStreamCodec = null;
if (checkVideoCodec) {
videoStreamCodec = VideoCodecFactory.getVideoCodec(buf);
if (info != null) {
info.setVideoCodec(videoStreamCodec);
}
checkVideoCodec = false;
} else if (codecInfo != null) {
videoStreamCodec = codecInfo.getVideoCodec();
}
if (videoStreamCodec != null) {
videoStreamCodec.addData(buf);
}
if (info != null) {
info.setHasVideo(true);
}
eventTime = rtmpEvent.getTimestamp();
log.trace("Video: {}", eventTime);
} else if (rtmpEvent instanceof Invoke) {
Invoke invokeEvent = (Invoke) rtmpEvent;
log.debug("Invoke action: {}", invokeEvent.getAction());
eventTime = rtmpEvent.getTimestamp();
// event / stream listeners will not be notified of invokes
return;
} else if (rtmpEvent instanceof Notify) {
Notify notifyEvent = (Notify) rtmpEvent;
String action = notifyEvent.getAction();
if (log.isDebugEnabled()) {
log.debug("Notify action: {}", action);
}
if ("onMetaData".equals(action)) {
// store the metadata
try {
log.debug("Setting metadata");
setMetaData(notifyEvent.duplicate());
} catch (Exception e) {
log.warn("Metadata could not be duplicated for this stream", e);
}
}
else if ("onFI".equals(action)) {
try {
Notify timeCodeNotify = notifyEvent.duplicate();
Input input = new org.red5.io.amf.Input(timeCodeNotify.getData());
byte object = input.readDataType();
if (object == DataTypes.CORE_SWITCH) {
log.trace("Switching decoding to AMF3");
input = new org.red5.io.amf3.Input(timeCodeNotify.getData());
((org.red5.io.amf3.Input) input).enforceAMF3();
// re-read data type after switching decode
object = input.readDataType();
}
String actionOnFI = input.readString();
input.readDataType();
Map