gecko-dev/netwerk/protocol/rtsp/controller/RtspController.cpp

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