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()
.