2014-04-14 04:24:00 -07:00
|
|
|
/* -*- mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
2013-09-26 22:22:37 -07:00
|
|
|
/* vim: set ts=8 sts=2 et sw=2 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/. */
|
2014-04-14 04:24:00 -07:00
|
|
|
#include "MediaSourceResource.h"
|
2013-09-26 22:22:37 -07:00
|
|
|
#include "MediaSourceDecoder.h"
|
|
|
|
|
|
|
|
#include "AbstractMediaDecoder.h"
|
|
|
|
#include "MediaDecoderReader.h"
|
|
|
|
#include "MediaDecoderStateMachine.h"
|
|
|
|
#include "mozilla/Assertions.h"
|
2013-10-05 01:04:39 -07:00
|
|
|
#include "mozilla/FloatingPoint.h"
|
2013-09-26 22:22:37 -07:00
|
|
|
#include "mozilla/dom/HTMLMediaElement.h"
|
2013-10-05 01:04:39 -07:00
|
|
|
#include "mozilla/dom/TimeRanges.h"
|
2013-09-26 22:22:37 -07:00
|
|
|
#include "mozilla/mozalloc.h"
|
|
|
|
#include "nsISupports.h"
|
|
|
|
#include "prlog.h"
|
2013-10-05 01:04:39 -07:00
|
|
|
#include "MediaSource.h"
|
2013-09-26 22:22:37 -07:00
|
|
|
#include "SubBufferDecoder.h"
|
|
|
|
#include "SourceBufferResource.h"
|
2014-02-27 16:54:48 -08:00
|
|
|
#include "VideoUtils.h"
|
2013-09-26 22:22:37 -07:00
|
|
|
|
|
|
|
#ifdef PR_LOGGING
|
|
|
|
extern PRLogModuleInfo* gMediaSourceLog;
|
2014-03-04 19:35:46 -08:00
|
|
|
#define MSE_DEBUG(...) PR_LOG(gMediaSourceLog, PR_LOG_DEBUG, (__VA_ARGS__))
|
2013-09-26 22:22:37 -07:00
|
|
|
#else
|
2014-03-04 19:35:46 -08:00
|
|
|
#define MSE_DEBUG(...)
|
2013-09-26 22:22:37 -07:00
|
|
|
#endif
|
|
|
|
|
|
|
|
namespace mozilla {
|
|
|
|
|
|
|
|
namespace dom {
|
|
|
|
|
|
|
|
class TimeRanges;
|
|
|
|
|
|
|
|
} // namespace dom
|
|
|
|
|
|
|
|
class MediaSourceReader : public MediaDecoderReader
|
|
|
|
{
|
|
|
|
public:
|
2013-09-26 22:22:38 -07:00
|
|
|
MediaSourceReader(MediaSourceDecoder* aDecoder)
|
2013-09-26 22:22:37 -07:00
|
|
|
: MediaDecoderReader(aDecoder)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
nsresult Init(MediaDecoderReader* aCloneDonor) MOZ_OVERRIDE
|
|
|
|
{
|
2014-02-17 14:53:51 -08:00
|
|
|
// Although we technically don't implement anything here, we return NS_OK
|
|
|
|
// so that when the state machine initializes and calls this function
|
|
|
|
// we don't return an error code back to the media element.
|
|
|
|
return NS_OK;
|
2013-09-26 22:22:37 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
bool DecodeAudioData() MOZ_OVERRIDE
|
|
|
|
{
|
|
|
|
if (GetAudioReader()) {
|
|
|
|
return GetAudioReader()->DecodeAudioData();
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool DecodeVideoFrame(bool& aKeyFrameSkip, int64_t aTimeThreshold) MOZ_OVERRIDE
|
|
|
|
{
|
|
|
|
if (GetVideoReader()) {
|
|
|
|
return GetVideoReader()->DecodeVideoFrame(aKeyFrameSkip, aTimeThreshold);
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool HasVideo() MOZ_OVERRIDE
|
|
|
|
{
|
2013-09-26 22:22:38 -07:00
|
|
|
return mInfo.HasVideo();
|
2013-09-26 22:22:37 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
bool HasAudio() MOZ_OVERRIDE
|
|
|
|
{
|
2013-09-26 22:22:38 -07:00
|
|
|
return mInfo.HasAudio();
|
2013-09-26 22:22:37 -07:00
|
|
|
}
|
|
|
|
|
2013-09-26 22:22:38 -07:00
|
|
|
nsresult ReadMetadata(MediaInfo* aInfo, MetadataTags** aTags) MOZ_OVERRIDE;
|
2013-09-26 22:22:37 -07:00
|
|
|
|
|
|
|
nsresult Seek(int64_t aTime, int64_t aStartTime, int64_t aEndTime,
|
|
|
|
int64_t aCurrentTime) MOZ_OVERRIDE
|
|
|
|
{
|
|
|
|
return NS_ERROR_NOT_IMPLEMENTED;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsresult GetBuffered(dom::TimeRanges* aBuffered, int64_t aStartTime) MOZ_OVERRIDE
|
|
|
|
{
|
|
|
|
// XXX: Merge result with audio reader.
|
2013-12-15 19:24:44 -08:00
|
|
|
MediaDecoderReader* reader = GetVideoReader() ? GetVideoReader() : GetAudioReader();
|
|
|
|
if (reader) {
|
|
|
|
return reader->GetBuffered(aBuffered, aStartTime);
|
|
|
|
}
|
|
|
|
return NS_OK;
|
2013-09-26 22:22:37 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
MediaQueue<AudioData>& AudioQueue() MOZ_OVERRIDE
|
|
|
|
{
|
|
|
|
// TODO: Share AudioQueue with SubReaders.
|
|
|
|
if (GetAudioReader()) {
|
|
|
|
return GetAudioReader()->AudioQueue();
|
|
|
|
}
|
|
|
|
return MediaDecoderReader::AudioQueue();
|
|
|
|
}
|
|
|
|
|
|
|
|
MediaQueue<VideoData>& VideoQueue() MOZ_OVERRIDE
|
|
|
|
{
|
|
|
|
// TODO: Share VideoQueue with SubReaders.
|
|
|
|
if (GetVideoReader()) {
|
|
|
|
return GetVideoReader()->VideoQueue();
|
|
|
|
}
|
|
|
|
return MediaDecoderReader::VideoQueue();
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
MediaDecoderReader* GetVideoReader()
|
|
|
|
{
|
|
|
|
MediaSourceDecoder* decoder = static_cast<MediaSourceDecoder*>(mDecoder);
|
|
|
|
return decoder->GetVideoReader();
|
|
|
|
}
|
|
|
|
|
|
|
|
MediaDecoderReader* GetAudioReader()
|
|
|
|
{
|
|
|
|
MediaSourceDecoder* decoder = static_cast<MediaSourceDecoder*>(mDecoder);
|
|
|
|
return decoder->GetAudioReader();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2013-11-17 20:22:47 -08:00
|
|
|
MediaSourceDecoder::MediaSourceDecoder(dom::HTMLMediaElement* aElement)
|
2013-09-26 22:22:37 -07:00
|
|
|
: mMediaSource(nullptr)
|
|
|
|
, mVideoReader(nullptr),
|
|
|
|
mAudioReader(nullptr)
|
|
|
|
{
|
|
|
|
Init(aElement);
|
|
|
|
}
|
|
|
|
|
|
|
|
MediaDecoder*
|
|
|
|
MediaSourceDecoder::Clone()
|
|
|
|
{
|
|
|
|
// TODO: Sort out cloning.
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
MediaDecoderStateMachine*
|
|
|
|
MediaSourceDecoder::CreateStateMachine()
|
|
|
|
{
|
|
|
|
return new MediaDecoderStateMachine(this, new MediaSourceReader(this));
|
|
|
|
}
|
|
|
|
|
|
|
|
nsresult
|
|
|
|
MediaSourceDecoder::Load(nsIStreamListener**, MediaDecoder*)
|
|
|
|
{
|
2014-02-17 14:53:51 -08:00
|
|
|
MOZ_ASSERT(!mDecoderStateMachine);
|
|
|
|
mDecoderStateMachine = CreateStateMachine();
|
|
|
|
if (!mDecoderStateMachine) {
|
|
|
|
NS_WARNING("Failed to create state machine!");
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
}
|
|
|
|
|
|
|
|
return mDecoderStateMachine->Init(nullptr);
|
2013-09-26 22:22:37 -07:00
|
|
|
}
|
|
|
|
|
2013-10-05 01:04:39 -07:00
|
|
|
nsresult
|
2013-11-17 20:22:47 -08:00
|
|
|
MediaSourceDecoder::GetSeekable(dom::TimeRanges* aSeekable)
|
2013-10-05 01:04:39 -07:00
|
|
|
{
|
|
|
|
double duration = mMediaSource->Duration();
|
|
|
|
if (IsNaN(duration)) {
|
|
|
|
// Return empty range.
|
|
|
|
} else if (duration > 0 && mozilla::IsInfinite(duration)) {
|
2013-11-17 20:22:47 -08:00
|
|
|
nsRefPtr<dom::TimeRanges> bufferedRanges = new dom::TimeRanges();
|
2013-10-05 01:04:39 -07:00
|
|
|
GetBuffered(bufferedRanges);
|
|
|
|
aSeekable->Add(0, bufferedRanges->GetFinalEndTime());
|
|
|
|
} else {
|
|
|
|
aSeekable->Add(0, duration);
|
|
|
|
}
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2014-04-14 04:24:00 -07:00
|
|
|
/*static*/
|
|
|
|
already_AddRefed<MediaResource>
|
|
|
|
MediaSourceDecoder::CreateResource()
|
|
|
|
{
|
|
|
|
return nsRefPtr<MediaResource>(new MediaSourceResource()).forget();
|
|
|
|
}
|
|
|
|
|
2013-09-26 22:22:37 -07:00
|
|
|
void
|
2013-11-17 20:22:47 -08:00
|
|
|
MediaSourceDecoder::AttachMediaSource(dom::MediaSource* aMediaSource)
|
2013-09-26 22:22:37 -07:00
|
|
|
{
|
|
|
|
MOZ_ASSERT(!mMediaSource && !mDecoderStateMachine);
|
|
|
|
mMediaSource = aMediaSource;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
MediaSourceDecoder::DetachMediaSource()
|
|
|
|
{
|
|
|
|
mMediaSource = nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
SubBufferDecoder*
|
|
|
|
MediaSourceDecoder::CreateSubDecoder(const nsACString& aType)
|
|
|
|
{
|
|
|
|
MediaResource* resource = new SourceBufferResource(nullptr, aType);
|
|
|
|
nsRefPtr<SubBufferDecoder> decoder = new SubBufferDecoder(resource, this);
|
|
|
|
nsAutoPtr<MediaDecoderReader> reader(DecoderTraits::CreateReader(aType, decoder));
|
|
|
|
reader->Init(nullptr);
|
|
|
|
|
|
|
|
ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
|
|
|
|
mDecoders.AppendElement(decoder);
|
|
|
|
mReaders.AppendElement(reader);
|
2014-03-04 19:35:46 -08:00
|
|
|
MSE_DEBUG("Registered subdecoder %p subreader %p", decoder.get(), reader.get());
|
2013-09-26 22:22:37 -07:00
|
|
|
mon.NotifyAll();
|
|
|
|
|
|
|
|
decoder->SetReader(reader.forget());
|
|
|
|
return decoder;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsresult
|
2013-09-26 22:22:38 -07:00
|
|
|
MediaSourceReader::ReadMetadata(MediaInfo* aInfo, MetadataTags** aTags)
|
2013-09-26 22:22:37 -07:00
|
|
|
{
|
|
|
|
mDecoder->SetMediaSeekable(true);
|
|
|
|
mDecoder->SetTransportSeekable(false);
|
|
|
|
|
|
|
|
MediaSourceDecoder* decoder = static_cast<MediaSourceDecoder*>(mDecoder);
|
|
|
|
const nsTArray<MediaDecoderReader*>& readers = decoder->GetReaders();
|
|
|
|
for (uint32_t i = 0; i < readers.Length(); ++i) {
|
|
|
|
MediaDecoderReader* reader = readers[i];
|
2013-09-26 22:22:38 -07:00
|
|
|
MediaInfo mi;
|
|
|
|
nsresult rv = reader->ReadMetadata(&mi, aTags);
|
2014-03-04 19:35:46 -08:00
|
|
|
MSE_DEBUG("ReadMetadata on SB reader %p", reader);
|
2013-09-26 22:22:37 -07:00
|
|
|
if (NS_FAILED(rv)) {
|
|
|
|
return rv;
|
|
|
|
}
|
2013-09-26 22:22:38 -07:00
|
|
|
if (mi.HasVideo() && !mInfo.HasVideo()) {
|
|
|
|
mInfo.mVideo = mi.mVideo;
|
2013-09-26 22:22:37 -07:00
|
|
|
decoder->SetVideoReader(reader);
|
|
|
|
}
|
2013-09-26 22:22:38 -07:00
|
|
|
if (mi.HasAudio() && !mInfo.HasAudio()) {
|
|
|
|
mInfo.mAudio = mi.mAudio;
|
2013-09-26 22:22:37 -07:00
|
|
|
decoder->SetAudioReader(reader);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
*aInfo = mInfo;
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2014-02-27 16:54:48 -08:00
|
|
|
double
|
|
|
|
MediaSourceDecoder::GetMediaSourceDuration()
|
|
|
|
{
|
|
|
|
return mMediaSource ?
|
|
|
|
mMediaSource->Duration() :
|
|
|
|
mDuration / static_cast<double>(USECS_PER_S);
|
|
|
|
}
|
|
|
|
|
2013-09-26 22:22:37 -07:00
|
|
|
} // namespace mozilla
|