michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* vim: set sw=2 ts=8 et tw=80 : */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "RtspControllerChild.h" michael@0: #include "RtspMetaData.h" michael@0: #include "mozilla/dom/TabChild.h" michael@0: #include "mozilla/net/NeckoChild.h" michael@0: #include "nsITabChild.h" michael@0: #include "nsILoadContext.h" michael@0: #include "nsNetUtil.h" michael@0: #include "mozilla/ipc/InputStreamUtils.h" michael@0: #include "mozilla/ipc/URIUtils.h" michael@0: #include "nsStringStream.h" michael@0: #include "prlog.h" michael@0: michael@0: PRLogModuleInfo* gRtspChildLog = nullptr; michael@0: #undef LOG michael@0: #define LOG(args) PR_LOG(gRtspChildLog, PR_LOG_DEBUG, args) michael@0: michael@0: const uint32_t kRtspTotalTracks = 2; michael@0: using namespace mozilla::ipc; michael@0: michael@0: namespace mozilla { michael@0: namespace net { michael@0: michael@0: NS_IMPL_ADDREF(RtspControllerChild) michael@0: michael@0: NS_IMETHODIMP_(nsrefcnt) RtspControllerChild::Release() michael@0: { michael@0: NS_PRECONDITION(0 != mRefCnt, "dup release"); michael@0: // Enable this to find non-threadsafe destructors: michael@0: // NS_ASSERT_OWNINGTHREAD(RtspControllerChild); michael@0: --mRefCnt; michael@0: NS_LOG_RELEASE(this, mRefCnt, "RtspControllerChild"); michael@0: michael@0: if (mRefCnt == 1 && mIPCOpen) { michael@0: Send__delete__(this); michael@0: return mRefCnt; michael@0: } michael@0: michael@0: if (mRefCnt == 0) { michael@0: mRefCnt = 1; /* stabilize */ michael@0: delete this; michael@0: return 0; michael@0: } michael@0: return mRefCnt; michael@0: } michael@0: michael@0: NS_INTERFACE_MAP_BEGIN(RtspControllerChild) michael@0: NS_INTERFACE_MAP_ENTRY(nsIStreamingProtocolController) michael@0: NS_INTERFACE_MAP_ENTRY(nsIStreamingProtocolListener) michael@0: NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIStreamingProtocolController) michael@0: NS_INTERFACE_MAP_END michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: // RtspControllerChild methods michael@0: //----------------------------------------------------------------------------- michael@0: RtspControllerChild::RtspControllerChild(nsIChannel *channel) michael@0: : mIPCOpen(false) michael@0: , mIPCAllowed(false) michael@0: , mChannel(channel) michael@0: , mTotalTracks(0) michael@0: , mSuspendCount(0) michael@0: { michael@0: #if defined(PR_LOGGING) michael@0: if (!gRtspChildLog) michael@0: gRtspChildLog = PR_NewLogModule("nsRtspChild"); michael@0: #endif michael@0: AddIPDLReference(); michael@0: gNeckoChild->SendPRtspControllerConstructor(this); michael@0: } michael@0: michael@0: RtspControllerChild::~RtspControllerChild() michael@0: { michael@0: LOG(("RtspControllerChild::~RtspControllerChild()")); michael@0: } michael@0: michael@0: void michael@0: RtspControllerChild::ReleaseChannel() michael@0: { michael@0: static_cast(mChannel.get())->ReleaseController(); michael@0: } michael@0: michael@0: bool michael@0: RtspControllerChild::OKToSendIPC() michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: if (mIPCOpen == false) { michael@0: return false; michael@0: } michael@0: return mIPCAllowed; michael@0: } michael@0: michael@0: void michael@0: RtspControllerChild::AllowIPC() michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: mIPCAllowed = true; michael@0: } michael@0: michael@0: void michael@0: RtspControllerChild::DisallowIPC() michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: mIPCAllowed = false; michael@0: } michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: // RtspControllerChild::PRtspControllerChild michael@0: //----------------------------------------------------------------------------- michael@0: bool michael@0: RtspControllerChild::RecvOnMediaDataAvailable( michael@0: const uint8_t& index, michael@0: const nsCString& data, michael@0: const uint32_t& length, michael@0: const uint32_t& offset, michael@0: const InfallibleTArray& metaArray) michael@0: { michael@0: nsRefPtr meta = new RtspMetaData(); michael@0: nsresult rv = meta->DeserializeRtspMetaData(metaArray); michael@0: NS_ENSURE_SUCCESS(rv, true); michael@0: michael@0: if (mListener) { michael@0: mListener->OnMediaDataAvailable(index, data, length, offset, meta.get()); michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: void michael@0: RtspControllerChild::AddMetaData( michael@0: already_AddRefed&& meta) michael@0: { michael@0: nsCOMPtr data = meta; michael@0: mMetaArray.AppendElement(data); michael@0: } michael@0: michael@0: int michael@0: RtspControllerChild::GetMetaDataLength() michael@0: { michael@0: return mMetaArray.Length(); michael@0: } michael@0: michael@0: bool michael@0: RtspControllerChild::RecvOnConnected( michael@0: const uint8_t& index, michael@0: const InfallibleTArray& metaArray) michael@0: { michael@0: // Deserialize meta data. michael@0: nsRefPtr meta = new RtspMetaData(); michael@0: nsresult rv = meta->DeserializeRtspMetaData(metaArray); michael@0: NS_ENSURE_SUCCESS(rv, true); michael@0: meta->GetTotalTracks(&mTotalTracks); michael@0: if (mTotalTracks <= 0) { michael@0: LOG(("RtspControllerChild::RecvOnConnected invalid tracks %d", mTotalTracks)); michael@0: // Set the default value. michael@0: mTotalTracks = kRtspTotalTracks; michael@0: } michael@0: AddMetaData(meta.forget().downcast()); michael@0: michael@0: // Notify the listener when meta data of tracks are available. michael@0: if ((static_cast(index) + 1) == mTotalTracks) { michael@0: // The controller provide |GetTrackMetaData| method for his client. michael@0: if (mListener) { michael@0: mListener->OnConnected(index, nullptr); michael@0: } michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: RtspControllerChild::RecvOnDisconnected( michael@0: const uint8_t& index, michael@0: const nsresult& reason) michael@0: { michael@0: DisallowIPC(); michael@0: LOG(("RtspControllerChild::RecvOnDisconnected for track %d reason = 0x%x", index, reason)); michael@0: if (mListener) { michael@0: mListener->OnDisconnected(index, reason); michael@0: } michael@0: ReleaseChannel(); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: RtspControllerChild::RecvAsyncOpenFailed(const nsresult& reason) michael@0: { michael@0: DisallowIPC(); michael@0: LOG(("RtspControllerChild::RecvAsyncOpenFailed reason = 0x%x", reason)); michael@0: if (mListener) { michael@0: mListener->OnDisconnected(0, NS_ERROR_CONNECTION_REFUSED); michael@0: } michael@0: ReleaseChannel(); michael@0: return true; michael@0: } michael@0: michael@0: void michael@0: RtspControllerChild::AddIPDLReference() michael@0: { michael@0: NS_ABORT_IF_FALSE(!mIPCOpen, michael@0: "Attempt to retain more than one IPDL reference"); michael@0: mIPCOpen = true; michael@0: AllowIPC(); michael@0: AddRef(); michael@0: } michael@0: michael@0: void michael@0: RtspControllerChild::ReleaseIPDLReference() michael@0: { michael@0: NS_ABORT_IF_FALSE(mIPCOpen, "Attempt to release nonexistent IPDL reference"); michael@0: mIPCOpen = false; michael@0: DisallowIPC(); michael@0: Release(); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: RtspControllerChild::GetTrackMetaData( michael@0: uint8_t index, michael@0: nsIStreamingProtocolMetaData **result) michael@0: { michael@0: if (GetMetaDataLength() <= 0 || index >= GetMetaDataLength()) { michael@0: LOG(("RtspControllerChild:: meta data is not available")); michael@0: return NS_ERROR_NOT_INITIALIZED; michael@0: } michael@0: LOG(("RtspControllerChild::GetTrackMetaData() %d", index)); michael@0: NS_IF_ADDREF(*result = mMetaArray[index]); michael@0: return NS_OK; michael@0: } michael@0: michael@0: enum IPCEvent michael@0: { michael@0: SendNoneEvent = 0, michael@0: SendPlayEvent, michael@0: SendPauseEvent, michael@0: SendSeekEvent, michael@0: SendResumeEvent, michael@0: SendSuspendEvent, michael@0: SendStopEvent michael@0: }; michael@0: michael@0: class SendIPCEvent : public nsRunnable michael@0: { michael@0: public: michael@0: SendIPCEvent(RtspControllerChild *aController, IPCEvent aEvent) michael@0: : mController(aController) michael@0: , mEvent(aEvent) michael@0: , mSeekTime(0) michael@0: { michael@0: } michael@0: michael@0: SendIPCEvent(RtspControllerChild *aController, michael@0: IPCEvent aEvent, michael@0: uint64_t aSeekTime) michael@0: : mController(aController) michael@0: , mEvent(aEvent) michael@0: , mSeekTime(aSeekTime) michael@0: { michael@0: } michael@0: michael@0: NS_IMETHOD Run() michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: if (mController->OKToSendIPC() == false) { michael@0: // Don't send any more IPC events; no guarantee that parent objects are michael@0: // still alive. michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: bool rv = true; michael@0: michael@0: if (mEvent == SendPlayEvent) { michael@0: rv = mController->SendPlay(); michael@0: } else if (mEvent == SendPauseEvent) { michael@0: rv = mController->SendPause(); michael@0: } else if (mEvent == SendSeekEvent) { michael@0: rv = mController->SendSeek(mSeekTime); michael@0: } else if (mEvent == SendResumeEvent) { michael@0: rv = mController->SendResume(); michael@0: } else if (mEvent == SendSuspendEvent) { michael@0: rv = mController->SendSuspend(); michael@0: } else if (mEvent == SendStopEvent) { michael@0: rv = mController->SendStop(); michael@0: } else { michael@0: LOG(("RtspControllerChild::SendIPCEvent")); michael@0: } michael@0: if (!rv) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: return NS_OK; michael@0: } michael@0: private: michael@0: nsRefPtr mController; michael@0: IPCEvent mEvent; michael@0: uint64_t mSeekTime; michael@0: }; michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: // RtspControllerChild::nsIStreamingProtocolController michael@0: //----------------------------------------------------------------------------- michael@0: NS_IMETHODIMP michael@0: RtspControllerChild::Play(void) michael@0: { michael@0: LOG(("RtspControllerChild::Play()")); michael@0: michael@0: if (NS_IsMainThread()) { michael@0: if (!OKToSendIPC() || !SendPlay()) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: } else { michael@0: nsresult rv = NS_DispatchToMainThread( michael@0: new SendIPCEvent(this, SendPlayEvent)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: RtspControllerChild::Pause(void) michael@0: { michael@0: LOG(("RtspControllerChild::Pause()")); michael@0: michael@0: if (NS_IsMainThread()) { michael@0: if (!OKToSendIPC() || !SendPause()) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: } else { michael@0: nsresult rv = NS_DispatchToMainThread( michael@0: new SendIPCEvent(this, SendPauseEvent)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: RtspControllerChild::Resume(void) michael@0: { michael@0: LOG(("RtspControllerChild::Resume()")); michael@0: NS_ENSURE_TRUE(mSuspendCount > 0, NS_ERROR_UNEXPECTED); michael@0: michael@0: if (!--mSuspendCount) { michael@0: if (NS_IsMainThread()) { michael@0: if (!OKToSendIPC() || !SendResume()) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: } else { michael@0: nsresult rv = NS_DispatchToMainThread( michael@0: new SendIPCEvent(this, SendResumeEvent)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: RtspControllerChild::Suspend(void) michael@0: { michael@0: LOG(("RtspControllerChild::Suspend()")); michael@0: michael@0: if (!mSuspendCount++) { michael@0: if (NS_IsMainThread()) { michael@0: if (!OKToSendIPC() || !SendSuspend()) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: } else { michael@0: nsresult rv = NS_DispatchToMainThread( michael@0: new SendIPCEvent(this, SendSuspendEvent)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: RtspControllerChild::Seek(uint64_t seekTimeUs) michael@0: { michael@0: LOG(("RtspControllerChild::Seek() %llu", seekTimeUs)); michael@0: michael@0: if (NS_IsMainThread()) { michael@0: if (!OKToSendIPC() || !SendSeek(seekTimeUs)) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: } else { michael@0: nsresult rv = NS_DispatchToMainThread( michael@0: new SendIPCEvent(this, SendSeekEvent, seekTimeUs)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: RtspControllerChild::Stop() michael@0: { michael@0: LOG(("RtspControllerChild::Stop()")); michael@0: michael@0: if (NS_IsMainThread()) { michael@0: if (!OKToSendIPC() || !SendStop()) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: DisallowIPC(); michael@0: } else { michael@0: nsresult rv = NS_DispatchToMainThread( michael@0: new SendIPCEvent(this, SendStopEvent)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: RtspControllerChild::GetTotalTracks(uint8_t *aTracks) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aTracks); michael@0: *aTracks = kRtspTotalTracks; michael@0: if (mTotalTracks) { michael@0: *aTracks = mTotalTracks; michael@0: } michael@0: LOG(("RtspControllerChild::GetTracks() %d", *aTracks)); michael@0: return NS_OK; michael@0: } michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: // RtspControllerChild::nsIStreamingProtocolListener michael@0: //----------------------------------------------------------------------------- michael@0: NS_IMETHODIMP michael@0: RtspControllerChild::OnMediaDataAvailable(uint8_t index, michael@0: const nsACString & data, michael@0: uint32_t length, michael@0: uint32_t offset, michael@0: nsIStreamingProtocolMetaData *meta) michael@0: { michael@0: LOG(("RtspControllerChild::OnMediaDataAvailable()")); michael@0: return NS_ERROR_NOT_IMPLEMENTED; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: RtspControllerChild::OnConnected(uint8_t index, michael@0: nsIStreamingProtocolMetaData *meta) michael@0: michael@0: { michael@0: LOG(("RtspControllerChild::OnConnected()")); michael@0: return NS_ERROR_NOT_IMPLEMENTED; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: RtspControllerChild::OnDisconnected(uint8_t index, michael@0: nsresult reason) michael@0: { michael@0: LOG(("RtspControllerChild::OnDisconnected() reason = 0x%x", reason)); michael@0: return NS_ERROR_NOT_IMPLEMENTED; michael@0: } michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: // RtspControllerChild::nsIStreamingProtocoController michael@0: //----------------------------------------------------------------------------- michael@0: NS_IMETHODIMP michael@0: RtspControllerChild::Init(nsIURI *aURI) michael@0: { michael@0: nsresult rv; michael@0: michael@0: if (!aURI) { michael@0: LOG(("RtspControllerChild::Init() - invalid URI")); michael@0: return NS_ERROR_NOT_INITIALIZED; michael@0: } michael@0: michael@0: nsAutoCString host; michael@0: int32_t port = -1; michael@0: michael@0: rv = aURI->GetAsciiHost(host); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: // Reject the URL if it doesn't specify a host michael@0: if (host.IsEmpty()) michael@0: return NS_ERROR_MALFORMED_URI; michael@0: michael@0: rv = aURI->GetPort(&port); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: rv = aURI->GetAsciiSpec(mSpec); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: if (!strncmp(mSpec.get(), "rtsp:", 5) == 0) michael@0: return NS_ERROR_UNEXPECTED; michael@0: michael@0: mURI = aURI; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: RtspControllerChild::AsyncOpen(nsIStreamingProtocolListener *aListener) michael@0: { michael@0: LOG(("RtspControllerChild::AsyncOpen()")); michael@0: if (!aListener) { michael@0: LOG(("RtspControllerChild::AsyncOpen() - invalid listener")); michael@0: return NS_ERROR_NOT_INITIALIZED; michael@0: } michael@0: mListener = aListener; michael@0: michael@0: if (!mChannel) { michael@0: LOG(("RtspControllerChild::AsyncOpen() - invalid URI")); michael@0: return NS_ERROR_NOT_INITIALIZED; michael@0: } michael@0: michael@0: nsCOMPtr uri; michael@0: URIParams uriParams; michael@0: mChannel->GetURI(getter_AddRefs(uri)); michael@0: if (!uri) { michael@0: LOG(("RtspControllerChild::AsyncOpen() - invalid URI")); michael@0: return NS_ERROR_NOT_INITIALIZED; michael@0: } michael@0: SerializeURI(uri, uriParams); michael@0: michael@0: if (!OKToSendIPC() || !SendAsyncOpen(uriParams)) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: } // namespace net michael@0: } // namespace mozilla