tenfourfox/netwerk/protocol/rtsp/controller/RtspControllerChild.cpp
Cameron Kaiser c9b2922b70 hello FPR
2017-04-19 00:56:45 -07:00

614 lines
16 KiB
C++

/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set sw=2 ts=8 et tw=80 : */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "RtspControllerChild.h"
#include "RtspMetaData.h"
#include "mozilla/dom/TabChild.h"
#include "mozilla/net/NeckoChild.h"
#include "nsITabChild.h"
#include "nsILoadContext.h"
#include "mozilla/ipc/InputStreamUtils.h"
#include "mozilla/ipc/URIUtils.h"
#include "nsStringStream.h"
#include "mozilla/Logging.h"
const uint32_t kRtspTotalTracks = 2;
const unsigned long kRtspCommandDelayMs = 200;
using namespace mozilla::ipc;
namespace mozilla {
namespace net {
static LazyLogModule gRtspChildLog("nsRtspChild");
#undef LOG
#define LOG(args) MOZ_LOG(mozilla::net::gRtspChildLog, mozilla::LogLevel::Debug, args)
NS_IMPL_ADDREF(RtspControllerChild)
NS_IMETHODIMP_(nsrefcnt) RtspControllerChild::Release()
{
NS_PRECONDITION(0 != mRefCnt, "dup release");
// Enable this to find non-threadsafe destructors:
// NS_ASSERT_OWNINGTHREAD(RtspControllerChild);
--mRefCnt;
NS_LOG_RELEASE(this, mRefCnt, "RtspControllerChild");
if (mRefCnt == 1 && mIPCOpen) {
Send__delete__(this);
return mRefCnt;
}
if (mRefCnt == 0) {
mRefCnt = 1; /* stabilize */
delete this;
return 0;
}
return mRefCnt;
}
NS_INTERFACE_MAP_BEGIN(RtspControllerChild)
NS_INTERFACE_MAP_ENTRY(nsIStreamingProtocolController)
NS_INTERFACE_MAP_ENTRY(nsIStreamingProtocolListener)
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIStreamingProtocolController)
NS_INTERFACE_MAP_END
//-----------------------------------------------------------------------------
// RtspControllerChild methods
//-----------------------------------------------------------------------------
RtspControllerChild::RtspControllerChild(nsIChannel *channel)
: mIPCOpen(false)
, mIPCAllowed(false)
, mChannel(channel)
, mTotalTracks(0)
, mSuspendCount(0)
, mTimerLock("RtspControllerChild.mTimerLock")
, mPlayTimer(nullptr)
, mPauseTimer(nullptr)
{
AddIPDLReference();
gNeckoChild->SendPRtspControllerConstructor(this);
}
RtspControllerChild::~RtspControllerChild()
{
LOG(("RtspControllerChild::~RtspControllerChild()"));
}
void
RtspControllerChild::ReleaseChannel()
{
static_cast<RtspChannelChild*>(mChannel.get())->ReleaseController();
}
bool
RtspControllerChild::OKToSendIPC()
{
MOZ_ASSERT(NS_IsMainThread());
if (mIPCOpen == false) {
return false;
}
return mIPCAllowed;
}
void
RtspControllerChild::AllowIPC()
{
MOZ_ASSERT(NS_IsMainThread());
mIPCAllowed = true;
}
void
RtspControllerChild::DisallowIPC()
{
MOZ_ASSERT(NS_IsMainThread());
mIPCAllowed = false;
}
void
RtspControllerChild::StopPlayAndPauseTimer()
{
MutexAutoLock lock(mTimerLock);
if (mPlayTimer) {
mPlayTimer->Cancel();
mPlayTimer = nullptr;
}
if (mPauseTimer) {
mPauseTimer->Cancel();
mPauseTimer = nullptr;
}
}
//-----------------------------------------------------------------------------
// RtspControllerChild::PRtspControllerChild
//-----------------------------------------------------------------------------
bool
RtspControllerChild::RecvOnMediaDataAvailable(
const uint8_t& index,
const nsCString& data,
const uint32_t& length,
const uint32_t& offset,
InfallibleTArray<RtspMetadataParam>&& metaArray)
{
RefPtr<RtspMetaData> meta = new RtspMetaData();
nsresult rv = meta->DeserializeRtspMetaData(metaArray);
NS_ENSURE_SUCCESS(rv, true);
if (mListener) {
mListener->OnMediaDataAvailable(index, data, length, offset, meta.get());
}
return true;
}
void
RtspControllerChild::AddMetaData(
already_AddRefed<nsIStreamingProtocolMetaData>&& meta)
{
mMetaArray.AppendElement(mozilla::Move(meta));
}
int
RtspControllerChild::GetMetaDataLength()
{
return mMetaArray.Length();
}
bool
RtspControllerChild::RecvOnConnected(
const uint8_t& index,
InfallibleTArray<RtspMetadataParam>&& metaArray)
{
// Deserialize meta data.
RefPtr<RtspMetaData> meta = new RtspMetaData();
nsresult rv = meta->DeserializeRtspMetaData(metaArray);
NS_ENSURE_SUCCESS(rv, true);
meta->GetTotalTracks(&mTotalTracks);
if (mTotalTracks <= 0) {
LOG(("RtspControllerChild::RecvOnConnected invalid tracks %d", mTotalTracks));
// Set the default value.
mTotalTracks = kRtspTotalTracks;
}
AddMetaData(meta.forget().downcast<nsIStreamingProtocolMetaData>());
// Notify the listener when meta data of tracks are available.
if ((static_cast<uint32_t>(index) + 1) == mTotalTracks) {
// The controller provide |GetTrackMetaData| method for his client.
if (mListener) {
mListener->OnConnected(index, nullptr);
}
}
return true;
}
bool
RtspControllerChild::RecvOnDisconnected(
const uint8_t& index,
const nsresult& reason)
{
StopPlayAndPauseTimer();
DisallowIPC();
LOG(("RtspControllerChild::RecvOnDisconnected for track %d reason = 0x%x", index, reason));
if (mListener) {
mListener->OnDisconnected(index, reason);
}
ReleaseChannel();
return true;
}
bool
RtspControllerChild::RecvAsyncOpenFailed(const nsresult& reason)
{
StopPlayAndPauseTimer();
DisallowIPC();
LOG(("RtspControllerChild::RecvAsyncOpenFailed reason = 0x%x", reason));
if (mListener) {
mListener->OnDisconnected(0, NS_ERROR_CONNECTION_REFUSED);
}
ReleaseChannel();
return true;
}
void
RtspControllerChild::AddIPDLReference()
{
MOZ_ASSERT(!mIPCOpen,
"Attempt to retain more than one IPDL reference");
mIPCOpen = true;
AllowIPC();
AddRef();
}
void
RtspControllerChild::ReleaseIPDLReference()
{
MOZ_ASSERT(mIPCOpen, "Attempt to release nonexistent IPDL reference");
mIPCOpen = false;
DisallowIPC();
Release();
}
NS_IMETHODIMP
RtspControllerChild::GetTrackMetaData(
uint8_t index,
nsIStreamingProtocolMetaData **result)
{
if (GetMetaDataLength() <= 0 || index >= GetMetaDataLength()) {
LOG(("RtspControllerChild:: meta data is not available"));
return NS_ERROR_NOT_INITIALIZED;
}
LOG(("RtspControllerChild::GetTrackMetaData() %d", index));
NS_IF_ADDREF(*result = mMetaArray[index]);
return NS_OK;
}
enum IPCEvent
{
SendNoneEvent = 0,
SendPlayEvent,
SendPauseEvent,
SendSeekEvent,
SendStopEvent,
SendPlaybackEndedEvent
};
class SendIPCEvent : public nsRunnable
{
public:
SendIPCEvent(RtspControllerChild *aController, IPCEvent aEvent)
: mController(aController)
, mEvent(aEvent)
, mSeekTime(0)
{
}
SendIPCEvent(RtspControllerChild *aController,
IPCEvent aEvent,
uint64_t aSeekTime)
: mController(aController)
, mEvent(aEvent)
, mSeekTime(aSeekTime)
{
}
NS_IMETHOD Run()
{
MOZ_ASSERT(NS_IsMainThread());
if (mController->OKToSendIPC() == false) {
// Don't send any more IPC events; no guarantee that parent objects are
// still alive.
return NS_ERROR_FAILURE;
}
bool rv = true;
if (mEvent == SendPlayEvent) {
rv = mController->SendPlay();
} else if (mEvent == SendPauseEvent) {
rv = mController->SendPause();
} else if (mEvent == SendSeekEvent) {
rv = mController->SendSeek(mSeekTime);
} else if (mEvent == SendStopEvent) {
rv = mController->SendStop();
} else if (mEvent == SendPlaybackEndedEvent) {
rv = mController->SendPlaybackEnded();
} else {
LOG(("RtspControllerChild::SendIPCEvent"));
}
if (!rv) {
return NS_ERROR_FAILURE;
}
return NS_OK;
}
private:
RefPtr<RtspControllerChild> mController;
IPCEvent mEvent;
uint64_t mSeekTime;
};
//-----------------------------------------------------------------------------
// RtspControllerChild::nsIStreamingProtocolController
//-----------------------------------------------------------------------------
NS_IMETHODIMP
RtspControllerChild::Play(void)
{
LOG(("RtspControllerChild::Play()"));
MutexAutoLock lock(mTimerLock);
// Cancel the pause timer if it is active because successive pause-play in a
// short duration is unncessary but could impair playback smoothing.
if (mPauseTimer) {
mPauseTimer->Cancel();
mPauseTimer = nullptr;
}
// Start a timer to delay the play operation for a short duration.
if (!mPlayTimer) {
mPlayTimer = do_CreateInstance("@mozilla.org/timer;1");
if (!mPlayTimer) {
return NS_ERROR_NOT_INITIALIZED;
}
// We have to dispatch the timer callback to the main thread because the
// decoder thread is a thread from nsIThreadPool and cannot be the timer
// target. Furthermore, IPC send functions should only be called from the
// main thread.
nsCOMPtr<nsIThread> mainThread = do_GetMainThread();
mPlayTimer->SetTarget(mainThread);
mPlayTimer->InitWithFuncCallback(
RtspControllerChild::PlayTimerCallback,
this, kRtspCommandDelayMs,
nsITimer::TYPE_ONE_SHOT);
}
return NS_OK;
}
NS_IMETHODIMP
RtspControllerChild::Pause(void)
{
LOG(("RtspControllerChild::Pause()"));
MutexAutoLock lock(mTimerLock);
// Cancel the play timer if it is active because successive play-pause in a
// shrot duration is unnecessary but could impair playback smoothing.
if (mPlayTimer) {
mPlayTimer->Cancel();
mPlayTimer = nullptr;
}
// Start a timer to delay the pause operation for a short duration.
if (!mPauseTimer) {
mPauseTimer = do_CreateInstance("@mozilla.org/timer;1");
if (!mPauseTimer) {
return NS_ERROR_NOT_INITIALIZED;
}
// We have to dispatch the timer callback to the main thread because the
// decoder thread is a thread from nsIThreadPool and cannot be the timer
// target.
nsCOMPtr<nsIThread> mainThread = do_GetMainThread();
mPauseTimer->SetTarget(mainThread);
mPauseTimer->InitWithFuncCallback(
RtspControllerChild::PauseTimerCallback,
this, kRtspCommandDelayMs,
nsITimer::TYPE_ONE_SHOT);
}
return NS_OK;
}
NS_IMETHODIMP
RtspControllerChild::Resume(void)
{
LOG(("RtspControllerChild::Resume()"));
NS_ENSURE_TRUE(mSuspendCount > 0, NS_ERROR_UNEXPECTED);
if (!--mSuspendCount) {
return Play();
}
return NS_OK;
}
NS_IMETHODIMP
RtspControllerChild::Suspend(void)
{
LOG(("RtspControllerChild::Suspend()"));
if (!mSuspendCount++) {
return Pause();
}
return NS_OK;
}
NS_IMETHODIMP
RtspControllerChild::Seek(uint64_t seekTimeUs)
{
LOG(("RtspControllerChild::Seek() %llu", seekTimeUs));
if (NS_IsMainThread()) {
if (!OKToSendIPC() || !SendSeek(seekTimeUs)) {
return NS_ERROR_FAILURE;
}
} else {
nsresult rv = NS_DispatchToMainThread(
new SendIPCEvent(this, SendSeekEvent, seekTimeUs));
NS_ENSURE_SUCCESS(rv, rv);
}
return NS_OK;
}
NS_IMETHODIMP
RtspControllerChild::Stop()
{
LOG(("RtspControllerChild::Stop()"));
StopPlayAndPauseTimer();
if (NS_IsMainThread()) {
if (!OKToSendIPC() || !SendStop()) {
return NS_ERROR_FAILURE;
}
DisallowIPC();
} else {
nsresult rv = NS_DispatchToMainThread(
new SendIPCEvent(this, SendStopEvent));
NS_ENSURE_SUCCESS(rv, rv);
}
return NS_OK;
}
NS_IMETHODIMP
RtspControllerChild::GetTotalTracks(uint8_t *aTracks)
{
NS_ENSURE_ARG_POINTER(aTracks);
*aTracks = kRtspTotalTracks;
if (mTotalTracks) {
*aTracks = mTotalTracks;
}
LOG(("RtspControllerChild::GetTracks() %d", *aTracks));
return NS_OK;
}
NS_IMETHODIMP
RtspControllerChild::PlaybackEnded()
{
LOG(("RtspControllerChild::PlaybackEnded"));
StopPlayAndPauseTimer();
if (NS_IsMainThread()) {
if (!OKToSendIPC() || !SendPlaybackEnded()) {
return NS_ERROR_FAILURE;
}
} else {
nsresult rv = NS_DispatchToMainThread(
new SendIPCEvent(this, SendPlaybackEndedEvent));
NS_ENSURE_SUCCESS(rv, rv);
}
return NS_OK;
}
//-----------------------------------------------------------------------------
// RtspControllerChild::nsIStreamingProtocolListener
//-----------------------------------------------------------------------------
NS_IMETHODIMP
RtspControllerChild::OnMediaDataAvailable(uint8_t index,
const nsACString & data,
uint32_t length,
uint32_t offset,
nsIStreamingProtocolMetaData *meta)
{
LOG(("RtspControllerChild::OnMediaDataAvailable()"));
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
RtspControllerChild::OnConnected(uint8_t index,
nsIStreamingProtocolMetaData *meta)
{
LOG(("RtspControllerChild::OnConnected()"));
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
RtspControllerChild::OnDisconnected(uint8_t index,
nsresult reason)
{
LOG(("RtspControllerChild::OnDisconnected() reason = 0x%x", reason));
return NS_ERROR_NOT_IMPLEMENTED;
}
//-----------------------------------------------------------------------------
// RtspControllerChild::nsIStreamingProtocoController
//-----------------------------------------------------------------------------
NS_IMETHODIMP
RtspControllerChild::Init(nsIURI *aURI)
{
nsresult rv;
if (!aURI) {
LOG(("RtspControllerChild::Init() - invalid URI"));
return NS_ERROR_NOT_INITIALIZED;
}
nsAutoCString host;
int32_t port = -1;
rv = aURI->GetAsciiHost(host);
if (NS_FAILED(rv)) return rv;
// Reject the URL if it doesn't specify a host
if (host.IsEmpty())
return NS_ERROR_MALFORMED_URI;
rv = aURI->GetPort(&port);
if (NS_FAILED(rv)) return rv;
rv = aURI->GetAsciiSpec(mSpec);
if (NS_FAILED(rv)) return rv;
if (!strncmp(mSpec.get(), "rtsp:", 5) == 0)
return NS_ERROR_UNEXPECTED;
mURI = aURI;
return NS_OK;
}
NS_IMETHODIMP
RtspControllerChild::AsyncOpen(nsIStreamingProtocolListener *aListener)
{
LOG(("RtspControllerChild::AsyncOpen()"));
if (!aListener) {
LOG(("RtspControllerChild::AsyncOpen() - invalid listener"));
return NS_ERROR_NOT_INITIALIZED;
}
mListener = aListener;
if (!mChannel) {
LOG(("RtspControllerChild::AsyncOpen() - invalid URI"));
return NS_ERROR_NOT_INITIALIZED;
}
nsCOMPtr<nsIURI> uri;
URIParams uriParams;
mChannel->GetURI(getter_AddRefs(uri));
if (!uri) {
LOG(("RtspControllerChild::AsyncOpen() - invalid URI"));
return NS_ERROR_NOT_INITIALIZED;
}
SerializeURI(uri, uriParams);
if (!OKToSendIPC() || !SendAsyncOpen(uriParams)) {
return NS_ERROR_FAILURE;
}
return NS_OK;
}
//-----------------------------------------------------------------------------
// RtspControllerChild static member methods
//-----------------------------------------------------------------------------
//static
void
RtspControllerChild::PlayTimerCallback(nsITimer *aTimer, void *aClosure)
{
MOZ_ASSERT(aTimer);
MOZ_ASSERT(aClosure);
MOZ_ASSERT(NS_IsMainThread());
RtspControllerChild *self = static_cast<RtspControllerChild*>(aClosure);
MutexAutoLock lock(self->mTimerLock);
if (!self->mPlayTimer || !self->OKToSendIPC()) {
return;
}
self->SendPlay();
self->mPlayTimer = nullptr;
}
//static
void
RtspControllerChild::PauseTimerCallback(nsITimer *aTimer, void *aClosure)
{
MOZ_ASSERT(aTimer);
MOZ_ASSERT(aClosure);
MOZ_ASSERT(NS_IsMainThread());
RtspControllerChild *self = static_cast<RtspControllerChild*>(aClosure);
MutexAutoLock lock(self->mTimerLock);
if (!self->mPauseTimer || !self->OKToSendIPC()) {
return;
}
self->SendPause();
self->mPauseTimer = nullptr;
}
} // namespace net
} // namespace mozilla