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 "RtspController.h" michael@0: #include "RtspMetaData.h" michael@0: #include "nsIURI.h" michael@0: #include "nsICryptoHash.h" michael@0: #include "nsIRunnable.h" michael@0: #include "nsIPrefBranch.h" michael@0: #include "nsIPrefService.h" michael@0: #include "nsICancelable.h" michael@0: #include "nsIStreamConverterService.h" michael@0: #include "nsIIOService2.h" michael@0: #include "nsIProtocolProxyService.h" michael@0: #include "nsIProxyInfo.h" michael@0: #include "nsIProxiedChannel.h" michael@0: michael@0: #include "nsAutoPtr.h" michael@0: #include "nsStandardURL.h" michael@0: #include "nsNetCID.h" michael@0: #include "nsServiceManagerUtils.h" michael@0: #include "nsXPIDLString.h" michael@0: #include "nsCRT.h" michael@0: #include "nsThreadUtils.h" michael@0: #include "nsError.h" michael@0: #include "nsStringStream.h" michael@0: #include "nsAlgorithm.h" michael@0: #include "nsProxyRelease.h" michael@0: #include "nsNetUtil.h" michael@0: #include "mozilla/Attributes.h" michael@0: #include "mozilla/Telemetry.h" michael@0: #include "mozilla/TimeStamp.h" michael@0: #include "prlog.h" michael@0: michael@0: #include "plbase64.h" michael@0: #include "prmem.h" michael@0: #include "prnetdb.h" michael@0: #include "zlib.h" michael@0: #include michael@0: #include "nsDebug.h" michael@0: michael@0: extern PRLogModuleInfo* gRtspLog; michael@0: #undef LOG michael@0: #define LOG(args) PR_LOG(gRtspLog, PR_LOG_DEBUG, args) michael@0: michael@0: namespace mozilla { michael@0: namespace net { michael@0: michael@0: NS_IMPL_ISUPPORTS(RtspController, michael@0: nsIStreamingProtocolController) michael@0: michael@0: RtspController::RtspController(nsIChannel *channel) michael@0: : mState(INIT) michael@0: { michael@0: LOG(("RtspController::RtspController()")); michael@0: } michael@0: michael@0: RtspController::~RtspController() michael@0: { michael@0: LOG(("RtspController::~RtspController()")); michael@0: if (mRtspSource.get()) { michael@0: mRtspSource.clear(); michael@0: } michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: RtspController::GetTrackMetaData(uint8_t index, michael@0: nsIStreamingProtocolMetaData * *_retval) michael@0: { michael@0: LOG(("RtspController::GetTrackMetaData()")); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: RtspController::Play(void) michael@0: { michael@0: LOG(("RtspController::Play()")); michael@0: if (!mRtspSource.get()) { michael@0: MOZ_ASSERT(mRtspSource.get(), "mRtspSource should not be null!"); michael@0: return NS_ERROR_NOT_INITIALIZED; michael@0: } michael@0: michael@0: if (mState != CONNECTED) { michael@0: return NS_ERROR_NOT_CONNECTED; michael@0: } michael@0: michael@0: mRtspSource->play(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: RtspController::Pause(void) michael@0: { michael@0: LOG(("RtspController::Pause()")); michael@0: if (!mRtspSource.get()) { michael@0: MOZ_ASSERT(mRtspSource.get(), "mRtspSource should not be null!"); michael@0: return NS_ERROR_NOT_INITIALIZED; michael@0: } michael@0: michael@0: if (mState != CONNECTED) { michael@0: return NS_ERROR_NOT_CONNECTED; michael@0: } michael@0: michael@0: mRtspSource->pause(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: RtspController::Resume(void) michael@0: { michael@0: LOG(("RtspController::Resume()")); michael@0: if (!mRtspSource.get()) { michael@0: MOZ_ASSERT(mRtspSource.get(), "mRtspSource should not be null!"); michael@0: return NS_ERROR_NOT_INITIALIZED; michael@0: } michael@0: michael@0: if (mState != CONNECTED) { michael@0: return NS_ERROR_NOT_CONNECTED; michael@0: } michael@0: michael@0: mRtspSource->play(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: RtspController::Suspend(void) michael@0: { michael@0: LOG(("RtspController::Suspend()")); michael@0: if (!mRtspSource.get()) { michael@0: MOZ_ASSERT(mRtspSource.get(), "mRtspSource should not be null!"); michael@0: return NS_ERROR_NOT_INITIALIZED; michael@0: } michael@0: michael@0: if (mState != CONNECTED) { michael@0: return NS_ERROR_NOT_CONNECTED; michael@0: } michael@0: michael@0: mRtspSource->pause(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: RtspController::Seek(uint64_t seekTimeUs) michael@0: { michael@0: LOG(("RtspController::Seek() %llu", seekTimeUs)); michael@0: if (!mRtspSource.get()) { michael@0: MOZ_ASSERT(mRtspSource.get(), "mRtspSource should not be null!"); michael@0: return NS_ERROR_NOT_INITIALIZED; michael@0: } michael@0: michael@0: if (mState != CONNECTED) { michael@0: return NS_ERROR_NOT_CONNECTED; michael@0: } michael@0: michael@0: mRtspSource->seek(seekTimeUs); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: RtspController::Stop() michael@0: { michael@0: LOG(("RtspController::Stop()")); michael@0: mState = INIT; michael@0: if (!mRtspSource.get()) { michael@0: MOZ_ASSERT(mRtspSource.get(), "mRtspSource should not be null!"); michael@0: return NS_ERROR_NOT_INITIALIZED; michael@0: } michael@0: michael@0: mRtspSource->stop(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: RtspController::GetTotalTracks(uint8_t *aTracks) michael@0: { michael@0: LOG(("RtspController::GetTotalTracks()")); michael@0: return NS_ERROR_NOT_IMPLEMENTED; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: RtspController::AsyncOpen(nsIStreamingProtocolListener *aListener) michael@0: { michael@0: if (!aListener) { michael@0: LOG(("RtspController::AsyncOpen() illegal listener")); michael@0: return NS_ERROR_NOT_INITIALIZED; michael@0: } michael@0: michael@0: mListener = aListener; michael@0: michael@0: if (!mURI) { michael@0: LOG(("RtspController::AsyncOpen() illegal URI")); michael@0: return NS_ERROR_ILLEGAL_VALUE; michael@0: } michael@0: michael@0: nsAutoCString uriSpec; michael@0: mURI->GetSpec(uriSpec); michael@0: LOG(("RtspController AsyncOpen uri=%s", uriSpec.get())); michael@0: michael@0: if (!mRtspSource.get()) { michael@0: mRtspSource = new android::RTSPSource(this, uriSpec.get(), false, 0); michael@0: } michael@0: // Connect to Rtsp Server. michael@0: mRtspSource->start(); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: class SendMediaDataTask : public nsRunnable michael@0: { michael@0: public: michael@0: SendMediaDataTask(nsIStreamingProtocolListener *listener, michael@0: uint8_t index, michael@0: const nsACString & data, michael@0: uint32_t length, michael@0: uint32_t offset, michael@0: nsIStreamingProtocolMetaData *meta) michael@0: : mIndex(index) michael@0: , mLength(length) michael@0: , mOffset(offset) michael@0: , mMetaData(meta) michael@0: , mListener(listener) michael@0: { michael@0: mData.Assign(data); michael@0: } michael@0: michael@0: NS_IMETHOD Run() michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: mListener->OnMediaDataAvailable(mIndex, mData, mLength, michael@0: mOffset, mMetaData); michael@0: return NS_OK; michael@0: } michael@0: michael@0: private: michael@0: uint8_t mIndex; michael@0: nsCString mData; michael@0: uint32_t mLength; michael@0: uint32_t mOffset; michael@0: nsRefPtr mMetaData; michael@0: nsCOMPtr mListener; michael@0: }; michael@0: michael@0: NS_IMETHODIMP michael@0: RtspController::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: if (mListener && mState == CONNECTED) { michael@0: nsRefPtr task = michael@0: new SendMediaDataTask(mListener, index, data, length, offset, meta); michael@0: return NS_DispatchToMainThread(task); michael@0: } michael@0: return NS_ERROR_NOT_AVAILABLE; michael@0: } michael@0: michael@0: class SendOnConnectedTask : public nsRunnable michael@0: { michael@0: public: michael@0: SendOnConnectedTask(nsIStreamingProtocolListener *listener, michael@0: uint8_t index, michael@0: nsIStreamingProtocolMetaData *meta) michael@0: : mListener(listener) michael@0: , mIndex(index) michael@0: , mMetaData(meta) michael@0: { } michael@0: michael@0: NS_IMETHOD Run() michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: mListener->OnConnected(mIndex, mMetaData); michael@0: return NS_OK; michael@0: } michael@0: michael@0: private: michael@0: nsCOMPtr mListener; michael@0: uint8_t mIndex; michael@0: nsRefPtr mMetaData; michael@0: }; michael@0: michael@0: michael@0: NS_IMETHODIMP michael@0: RtspController::OnConnected(uint8_t index, michael@0: nsIStreamingProtocolMetaData *meta) michael@0: { michael@0: LOG(("RtspController::OnConnected()")); michael@0: mState = CONNECTED; michael@0: if (mListener) { michael@0: nsRefPtr task = michael@0: new SendOnConnectedTask(mListener, index, meta); michael@0: return NS_DispatchToMainThread(task); michael@0: } michael@0: return NS_ERROR_NOT_AVAILABLE; michael@0: } michael@0: michael@0: class SendOnDisconnectedTask : public nsRunnable michael@0: { michael@0: public: michael@0: SendOnDisconnectedTask(nsIStreamingProtocolListener *listener, michael@0: uint8_t index, michael@0: nsresult reason) michael@0: : mListener(listener) michael@0: , mIndex(index) michael@0: , mReason(reason) michael@0: { } michael@0: michael@0: NS_IMETHOD Run() michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: mListener->OnDisconnected(mIndex, mReason); michael@0: return NS_OK; michael@0: } michael@0: michael@0: private: michael@0: nsCOMPtr mListener; michael@0: uint8_t mIndex; michael@0: nsresult mReason; michael@0: }; michael@0: michael@0: NS_IMETHODIMP michael@0: RtspController::OnDisconnected(uint8_t index, michael@0: nsresult reason) michael@0: { michael@0: LOG(("RtspController::OnDisconnected() for track %d reason = 0x%x", index, reason)); michael@0: mState = DISCONNECTED; michael@0: if (mListener) { michael@0: nsRefPtr task = michael@0: new SendOnDisconnectedTask(mListener, index, reason); michael@0: // Break the cycle reference between the Listener (RtspControllerParent) and michael@0: // us. michael@0: mListener = nullptr; michael@0: return NS_DispatchToMainThread(task); michael@0: } michael@0: return NS_ERROR_NOT_AVAILABLE; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: RtspController::Init(nsIURI *aURI) michael@0: { michael@0: nsresult rv; michael@0: michael@0: if (!aURI) { michael@0: LOG(("RtspController::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: mURI = aURI; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: } // namespace mozilla::net michael@0: } // namespace mozilla