/

Implementation


When the InterstitialREST servlet receives an InterstitialRequest, first it performs some validation. It verifies that the user and digest fields are present. It then verifies that either resume or the inserts array are present.

Assuming the request is valid, either the resume parameter, or if not present the inserts array, is delegated to each of the servlet's registered IInterstitialRequestHandlers by calling either .resume(...) or .newRequest(...), respectively. If there are no registered handlers, the servlet has no effect and the message is ignored.

IInterstitialRequestHandler

The interface IInterstitialRequestHandler contains its own list of registered handlers, and it defines the .resume(...) and .newRequest(...) method signatures.

public interface IInterstitialRequestHandler {
	static final CopyOnWriteArraySet<IInterstitialRequestHandler> handlers = new CopyOnWriteArraySet<>();

	void newRequest(String user, String digest, List<InterstitialInsert> inserts) throws InterstitialException;

	void resume(String user, String digest, String path) throws InterstitialException;
}

IInterstitialConfiguration

The interface IInterstitialConfiguration allows you to adjust the URI target after video resolution and audio sample rates are discovered (configure(...)). It also allows you to be notified that an interstitial session is next in schedule so you can acquire and prepare any remote or unusual media sources (queueSession(...) is called when an interstitial is about to be activated).

public interface IInterstitialConfiguration {
	public InterstitialSession configure(IInterstitialStream stream, InterstitialSession session);

	public void queueSession(InterstitialSession session);
}

InterstitialRequestHandlerImpl

In the red5pro-server-examples live webapp, in the class com.infrared5.red5pro.live.InterstitialRequestHandlerImpl demonstrates a functional implementation of IInterstitialRequestHandler. It is tightly coupled to Red5ProLive in the same package, which is an example application adapter that most importantly maintains a map of active live streams.

When the webapp initializes, Red5ProLive.appStart(...) is called, which first performs some unrelated initialization and then constructs the InterstitialRequestHandlerImpl and registers it in IInterstitialRequestHandler.handlers. Thereafter, the InterstitialREST servlet will call either newRequest(...) or resume(...) when a valid InterstitialRequest is received.

InterstitialRequestHandlerImpl.newRequest(...)

The InterstitialREST servlet calls newRequest(...) when it receives an InterstitialRequest containing no resume field and containing an insert array with at least one element.

The example implementation ignores user and digest for simplicity.

The List<InterstitialInserts> from the request is iterated, and for each insert definition we determine if the insert is a static file or a live stream using the simplistic check:

if (insert.interstitial.contains(".flv")) {

Static File

To insert static content from a file, we construct an FLVInterstitial, which is a kind of InterstitialSession. Importantly, we override the open(), process(...) and dispose() methods. This FLVInterstitial object locates the FLV file and streams its packets.

The InterstitialDurationControl details are handled by FLVInterstitial's parent class, InterstitialSession which implements duration control functionality in the compareTo(...) method.

With the FLVInterstitial prepared, we look up the target stream as an InterstitialStream (see Configuration, above), and we add the FLVInterstitial to the target stream's InterstitialEngine.

FLVInterstitial.open()

The FLV file is located using the IProviderService to get the VOD provider, and then it subscribes for OOB messages for illustrative purposes (the messages are not used in this implementation).

FLVInterstitial.process()

When events are dispatched on the target InterstitialStream, it calls FLVInterstitial.process().

The core of process() is to pull an event from the FLV stream and then to call InterstitialSession.dispatchEvent(...) with both the FLV event and the event from the target stream. dispatchEvent(...) encapsulates the decision about whether this packet should be dispatched on to the output or not, based on the packet type and whether audio and/or video is being forwarded (inserted).

FLVInterstitial.dispose()

Unsubscribes as IConsumer at lifecycle completion.

Live Stream

To insert content from a live stream in newRequest(...), first we look up both the target stream and the new stream to insert. Then we construct a LiveInterstitial with these two streams. LiveInterstitial is a kind of InterstitialSession.

While LiveInterstitial does override the open(), process(...) and dispose() methods, it also implements IStreamListener and receives packets from the new stream in packetReceived(...).

LiveInterstitial.open(...)

In open(...), we add the LiveInterstitial to the new stream as an IStreamListener so that events on the new stream flow through packetReceived(...).

LiveInterstitial.packetReceived(...)

The logic in packetReceived(...) is designed to discard video packets until a keyframe arrives. It essentially filters packets and then finally, if the packet has not been eliminated, it calls dispatchEvent(...) to pass packets from the new stream to InterstitialSession.

LiveInterstitial.process(...)

Simply dispatch events from the target stream to the InterstitialSession.

LiveInterstitial.streamStopped(...)

This method is called when the new stream ends. We flag the stream as dead so that we can throw an exception to the InterstitialEngine when it next calls.

Also, we remove this LiveInterstitial from the new stream's IStreamListeners to stop further calls to process(...).

LiveInterstitial.dispose()

Remove this LiveInterstitial from the new stream's IStreamListeners.

InterstitialRequestHandlerImpl.resume(...)

The implementation of resume(...) is straightforward. First we look up the target stream by path, and then we call stream.getInterstitialEngine().resumeProgram().