mirror of
https://github.com/classilla/tenfourfox.git
synced 2025-02-08 01:31:00 +00:00
614 lines
16 KiB
C++
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
|