зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1033910 - Enable RTSP capability while using android::MediaCodec. r=cpearce
This commit is contained in:
Родитель
a34f73bd67
Коммит
835169b7a8
|
@ -48,6 +48,10 @@
|
|||
#endif
|
||||
#endif
|
||||
#ifdef NECKO_PROTOCOL_rtsp
|
||||
#if ANDROID_VERSION >= 18
|
||||
#include "RtspMediaCodecDecoder.h"
|
||||
#include "RtspMediaCodecReader.h"
|
||||
#endif
|
||||
#include "RtspOmxDecoder.h"
|
||||
#include "RtspOmxReader.h"
|
||||
#endif
|
||||
|
@ -555,7 +559,13 @@ InstantiateDecoder(const nsACString& aType, MediaDecoderOwner* aOwner)
|
|||
#endif
|
||||
#ifdef NECKO_PROTOCOL_rtsp
|
||||
if (IsRtspSupportedType(aType)) {
|
||||
#if ANDROID_VERSION >= 18
|
||||
decoder = MediaDecoder::IsOmxAsyncEnabled()
|
||||
? static_cast<MediaDecoder*>(new RtspMediaCodecDecoder())
|
||||
: static_cast<MediaDecoder*>(new RtspOmxDecoder());
|
||||
#else
|
||||
decoder = new RtspOmxDecoder();
|
||||
#endif
|
||||
return decoder.forget();
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -6,8 +6,6 @@
|
|||
|
||||
#include "MediaCodecDecoder.h"
|
||||
|
||||
#include <stagefright/MediaSource.h>
|
||||
|
||||
#include "MediaCodecReader.h"
|
||||
#include "MediaDecoderStateMachine.h"
|
||||
|
||||
|
|
|
@ -196,6 +196,7 @@ MediaCodecReader::CodecBufferInfo::CodecBufferInfo()
|
|||
MediaCodecReader::MediaCodecReader(AbstractMediaDecoder* aDecoder)
|
||||
: MediaOmxCommonReader(aDecoder)
|
||||
, mColorConverterBufferSize(0)
|
||||
, mExtractor(nullptr)
|
||||
{
|
||||
mHandler = new MessageHandler(this);
|
||||
mVideoListener = new VideoResourceListener(this);
|
||||
|
|
|
@ -133,6 +133,10 @@ protected:
|
|||
virtual void codecReserved(Track& aTrack);
|
||||
virtual void codecCanceled(Track& aTrack);
|
||||
|
||||
virtual bool CreateExtractor();
|
||||
|
||||
android::sp<android::MediaExtractor> mExtractor;
|
||||
|
||||
private:
|
||||
// An intermediary class that can be managed by android::sp<T>.
|
||||
// Redirect onMessageReceived() to MediaCodecReader.
|
||||
|
@ -224,7 +228,6 @@ private:
|
|||
bool CreateLooper();
|
||||
void DestroyLooper();
|
||||
|
||||
bool CreateExtractor();
|
||||
void DestroyExtractor();
|
||||
|
||||
bool CreateMediaSources();
|
||||
|
@ -276,7 +279,6 @@ private:
|
|||
android::sp<VideoResourceListener> mVideoListener;
|
||||
|
||||
android::sp<android::ALooper> mLooper;
|
||||
android::sp<android::MediaExtractor> mExtractor;
|
||||
|
||||
// media tracks
|
||||
AudioTrack mAudioTrack;
|
||||
|
|
|
@ -40,6 +40,8 @@ MediaOmxCommonDecoder::MediaOmxCommonDecoder()
|
|||
#endif
|
||||
}
|
||||
|
||||
MediaOmxCommonDecoder::~MediaOmxCommonDecoder() {}
|
||||
|
||||
void
|
||||
MediaOmxCommonDecoder::SetPlatformCanOffloadAudio(bool aCanOffloadAudio)
|
||||
{
|
||||
|
|
|
@ -45,6 +45,7 @@ public:
|
|||
virtual MediaDecoderStateMachine* CreateStateMachine(MediaOmxCommonReader* aReader) = 0;
|
||||
|
||||
protected:
|
||||
virtual ~MediaOmxCommonDecoder();
|
||||
void PauseStateMachine();
|
||||
void ResumeStateMachine();
|
||||
|
||||
|
|
|
@ -0,0 +1,221 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* 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 "RtspExtractor.h"
|
||||
|
||||
#include "mozilla/ReentrantMonitor.h"
|
||||
|
||||
using namespace android;
|
||||
|
||||
#define FRAME_DEFAULT_SIZE 1024
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
/* class RtspMediaSource : implements MediaSource for OMX.
|
||||
* The decoder thread will trigger the MediaDecodeStateMachine to read a/v frame.
|
||||
* Then RtspOmxReader calls OMX decoder to decode a/v frame. Finally the code
|
||||
* path run into the read() here, it reads un-decoded frame data from mResource
|
||||
* and construct a MediaBuffer for output to OMX decoder.
|
||||
* */
|
||||
class RtspMediaSource MOZ_FINAL : public MediaSource {
|
||||
public:
|
||||
RtspMediaSource(RtspMediaResource* aRtspMediaResource,
|
||||
ssize_t aTrackIdx,
|
||||
uint32_t aFrameMaxSize,
|
||||
const sp<MetaData>& aMeta)
|
||||
: mRtspResource(aRtspMediaResource)
|
||||
, mFormat(aMeta)
|
||||
, mTrackIdx(aTrackIdx)
|
||||
, mMonitor("RtspMediaSource.mMonitor")
|
||||
, mIsStarted(false)
|
||||
, mGroup(nullptr)
|
||||
, mBuffer(nullptr)
|
||||
, mFrameMaxSize(aFrameMaxSize) {}
|
||||
virtual ~RtspMediaSource() {}
|
||||
virtual status_t start(MetaData* params = nullptr) MOZ_OVERRIDE;
|
||||
virtual status_t stop() MOZ_OVERRIDE;
|
||||
virtual sp<MetaData> getFormat() MOZ_OVERRIDE {
|
||||
ReentrantMonitorAutoEnter mon(mMonitor);
|
||||
return mFormat;
|
||||
}
|
||||
virtual status_t read(MediaBuffer** buffer,
|
||||
const ReadOptions* options = nullptr) MOZ_OVERRIDE ;
|
||||
private:
|
||||
nsRefPtr<RtspMediaResource> mRtspResource;
|
||||
sp<MetaData> mFormat;
|
||||
uint32_t mTrackIdx;
|
||||
ReentrantMonitor mMonitor;
|
||||
bool mIsStarted;
|
||||
|
||||
// mGroup owns the mBuffer. mFrameMaxSize is the mBuffer size.
|
||||
// mBuffer is the input buffer for omx decoder.
|
||||
nsAutoPtr<MediaBufferGroup> mGroup;
|
||||
MediaBuffer* mBuffer;
|
||||
uint32_t mFrameMaxSize;
|
||||
};
|
||||
|
||||
status_t
|
||||
RtspMediaSource::start(MetaData* params)
|
||||
{
|
||||
ReentrantMonitorAutoEnter mon(mMonitor);
|
||||
if (!mIsStarted) {
|
||||
// RtspMediaSource relinquish the ownership of MediaBuffer |buf| to mGroup.
|
||||
mGroup = new MediaBufferGroup();
|
||||
MediaBuffer* buf = new MediaBuffer(mFrameMaxSize);
|
||||
mGroup->add_buffer(buf);
|
||||
mIsStarted = true;
|
||||
}
|
||||
return OK;
|
||||
}
|
||||
|
||||
status_t
|
||||
RtspMediaSource::stop()
|
||||
{
|
||||
ReentrantMonitorAutoEnter mon(mMonitor);
|
||||
if (mIsStarted) {
|
||||
if (mBuffer) {
|
||||
mBuffer->release();
|
||||
mBuffer = nullptr;
|
||||
}
|
||||
mGroup = nullptr;
|
||||
mIsStarted = false;
|
||||
}
|
||||
return OK;
|
||||
}
|
||||
|
||||
status_t
|
||||
RtspMediaSource::read(MediaBuffer** out, const ReadOptions* options)
|
||||
{
|
||||
ReentrantMonitorAutoEnter mon(mMonitor);
|
||||
NS_ENSURE_TRUE(mIsStarted, MEDIA_ERROR_BASE);
|
||||
NS_ENSURE_TRUE(out, MEDIA_ERROR_BASE);
|
||||
*out = nullptr;
|
||||
|
||||
// Video/audio track's initial frame size is FRAME_DEFAULT_SIZE.
|
||||
// We need to realloc the mBuffer if the mBuffer doesn't have enough space
|
||||
// for next ReadFrameFromTrack function. (actualFrameSize > mFrameMaxSize)
|
||||
status_t err;
|
||||
uint32_t readCount;
|
||||
uint32_t actualFrameSize;
|
||||
uint64_t time;
|
||||
nsresult rv;
|
||||
|
||||
while (1) {
|
||||
err = mGroup->acquire_buffer(&mBuffer);
|
||||
NS_ENSURE_TRUE(err == OK, err);
|
||||
rv = mRtspResource->ReadFrameFromTrack((uint8_t *)mBuffer->data(),
|
||||
mFrameMaxSize, mTrackIdx, readCount,
|
||||
time, actualFrameSize);
|
||||
if (NS_FAILED(rv)) {
|
||||
// Release mGroup and mBuffer.
|
||||
stop();
|
||||
// Since RtspMediaSource is an implementation of Android media source,
|
||||
// it's held by OMXCodec and isn't released yet. So we have to re-construct
|
||||
// mGroup and mBuffer.
|
||||
start();
|
||||
NS_WARNING("ReadFrameFromTrack failed; releasing buffers and returning.");
|
||||
return ERROR_END_OF_STREAM;
|
||||
}
|
||||
if (actualFrameSize > mFrameMaxSize) {
|
||||
// release mGroup and mBuffer
|
||||
stop();
|
||||
// re-construct mGroup and mBuffer
|
||||
mFrameMaxSize = actualFrameSize;
|
||||
err = start();
|
||||
NS_ENSURE_TRUE(err == OK, err);
|
||||
} else {
|
||||
// ReadFrameFromTrack success, break the while loop.
|
||||
break;
|
||||
}
|
||||
}
|
||||
mBuffer->set_range(0, readCount);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
mBuffer->meta_data()->clear();
|
||||
// fill the meta data
|
||||
mBuffer->meta_data()->setInt64(kKeyTime, time);
|
||||
*out = mBuffer;
|
||||
mBuffer = nullptr;
|
||||
return OK;
|
||||
}
|
||||
|
||||
return ERROR_END_OF_STREAM;
|
||||
}
|
||||
|
||||
size_t
|
||||
RtspExtractor::countTracks()
|
||||
{
|
||||
uint8_t tracks = 0;
|
||||
if (mController) {
|
||||
mController->GetTotalTracks(&tracks);
|
||||
}
|
||||
return size_t(tracks);
|
||||
}
|
||||
|
||||
sp<MediaSource>
|
||||
RtspExtractor::getTrack(size_t index)
|
||||
{
|
||||
NS_ENSURE_TRUE(index < countTracks(), nullptr);
|
||||
sp<MetaData> meta = getTrackMetaData(index);
|
||||
sp<MediaSource> source = new RtspMediaSource(mRtspResource,
|
||||
index,
|
||||
FRAME_DEFAULT_SIZE,
|
||||
meta);
|
||||
return source;
|
||||
}
|
||||
|
||||
sp<MetaData>
|
||||
RtspExtractor::getTrackMetaData(size_t index, uint32_t flag)
|
||||
{
|
||||
NS_ENSURE_TRUE(index < countTracks(), nullptr);
|
||||
sp<MetaData> meta = new MetaData();
|
||||
nsCOMPtr<nsIStreamingProtocolMetaData> rtspMetadata;
|
||||
mController->GetTrackMetaData(index, getter_AddRefs(rtspMetadata));
|
||||
|
||||
if (rtspMetadata) {
|
||||
// Convert msMeta into meta.
|
||||
// The getter function of nsIStreamingProtocolMetaData will initialize the
|
||||
// metadata values to 0 before setting them.
|
||||
nsCString mime;
|
||||
rtspMetadata->GetMimeType(mime);
|
||||
meta->setCString(kKeyMIMEType, mime.get());
|
||||
uint32_t temp32;
|
||||
rtspMetadata->GetWidth(&temp32);
|
||||
meta->setInt32(kKeyWidth, temp32);
|
||||
rtspMetadata->GetHeight(&temp32);
|
||||
meta->setInt32(kKeyHeight, temp32);
|
||||
rtspMetadata->GetSampleRate(&temp32);
|
||||
meta->setInt32(kKeySampleRate, temp32);
|
||||
rtspMetadata->GetChannelCount(&temp32);
|
||||
meta->setInt32(kKeyChannelCount, temp32);
|
||||
uint64_t temp64;
|
||||
rtspMetadata->GetDuration(&temp64);
|
||||
meta->setInt64(kKeyDuration, temp64);
|
||||
|
||||
nsCString tempCString;
|
||||
rtspMetadata->GetEsdsData(tempCString);
|
||||
if (tempCString.Length()) {
|
||||
meta->setData(kKeyESDS, 0, tempCString.get(), tempCString.Length());
|
||||
}
|
||||
rtspMetadata->GetAvccData(tempCString);
|
||||
if (tempCString.Length()) {
|
||||
meta->setData(kKeyAVCC, 0, tempCString.get(), tempCString.Length());
|
||||
}
|
||||
}
|
||||
return meta;
|
||||
}
|
||||
|
||||
uint32_t
|
||||
RtspExtractor::flags() const
|
||||
{
|
||||
if (mRtspResource->IsRealTime()) {
|
||||
return 0;
|
||||
} else {
|
||||
return MediaExtractor::CAN_SEEK;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* 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/. */
|
||||
|
||||
#if !defined(RtspExtractor_h_)
|
||||
#define RtspExtractor_h_
|
||||
|
||||
#include "RtspMediaResource.h"
|
||||
|
||||
#include <stagefright/MediaBufferGroup.h>
|
||||
#include <stagefright/MediaExtractor.h>
|
||||
#include <stagefright/MediaSource.h>
|
||||
#include <stagefright/MetaData.h>
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
// RtspExtractor is a custom extractor for Rtsp stream, whereas the other
|
||||
// XXXExtractors are made for container media content.
|
||||
// The extractor is used for |OmxDecoder::Init|, it provides the essential
|
||||
// information for creating OMXCodec instance.
|
||||
// For example, the |getTrackMetaData| returns metadata that includes the
|
||||
// codec type.
|
||||
class RtspExtractor: public android::MediaExtractor
|
||||
{
|
||||
public:
|
||||
virtual size_t countTracks() MOZ_FINAL MOZ_OVERRIDE;
|
||||
virtual android::sp<android::MediaSource> getTrack(size_t index)
|
||||
MOZ_FINAL MOZ_OVERRIDE;
|
||||
virtual android::sp<android::MetaData> getTrackMetaData(
|
||||
size_t index, uint32_t flag = 0) MOZ_FINAL MOZ_OVERRIDE;
|
||||
virtual uint32_t flags() const MOZ_FINAL MOZ_OVERRIDE;
|
||||
|
||||
RtspExtractor(RtspMediaResource* aResource)
|
||||
: mRtspResource(aResource) {
|
||||
MOZ_ASSERT(aResource);
|
||||
mController = mRtspResource->GetMediaStreamController();
|
||||
MOZ_ASSERT(mController);
|
||||
}
|
||||
virtual ~RtspExtractor() MOZ_OVERRIDE {}
|
||||
private:
|
||||
// mRtspResource is a pointer to RtspMediaResource. When |getTrack| is called
|
||||
// we use mRtspResource to construct a RtspMediaSource.
|
||||
RtspMediaResource* mRtspResource;
|
||||
// Through the mController in mRtspResource, we can get the essential
|
||||
// information for the extractor.
|
||||
nsRefPtr<nsIStreamingProtocolController> mController;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif
|
|
@ -0,0 +1,56 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* 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 "RtspMediaCodecDecoder.h"
|
||||
|
||||
#include "MediaDecoderStateMachine.h"
|
||||
#include "RtspMediaResource.h"
|
||||
#include "RtspMediaCodecReader.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
MediaDecoder*
|
||||
RtspMediaCodecDecoder::Clone()
|
||||
{
|
||||
return new RtspMediaCodecDecoder();
|
||||
}
|
||||
|
||||
MediaOmxCommonReader*
|
||||
RtspMediaCodecDecoder::CreateReader()
|
||||
{
|
||||
return new RtspMediaCodecReader(this);
|
||||
}
|
||||
|
||||
MediaDecoderStateMachine*
|
||||
RtspMediaCodecDecoder::CreateStateMachine(MediaOmxCommonReader* aReader)
|
||||
{
|
||||
return new MediaDecoderStateMachine(this, aReader,
|
||||
mResource->IsRealTime());
|
||||
}
|
||||
|
||||
void
|
||||
RtspMediaCodecDecoder::ApplyStateToStateMachine(PlayState aState)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
MediaDecoder::ApplyStateToStateMachine(aState);
|
||||
|
||||
// Notify RTSP controller if the play state is ended.
|
||||
// This is necessary for RTSP controller to transit its own state.
|
||||
if (aState == PLAY_STATE_ENDED) {
|
||||
nsRefPtr<RtspMediaResource> resource = mResource->GetRtspPointer();
|
||||
if (resource) {
|
||||
nsIStreamingProtocolController* controller =
|
||||
resource->GetMediaStreamController();
|
||||
if (controller) {
|
||||
controller->PlaybackEnded();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* 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/. */
|
||||
|
||||
#if !defined(RtspMediaCodecDecoder_h_)
|
||||
#define RtspMediaCodecDecoder_h_
|
||||
|
||||
#include "MediaOmxCommonDecoder.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class RtspMediaCodecDecoder MOZ_FINAL : public MediaOmxCommonDecoder
|
||||
{
|
||||
public:
|
||||
virtual MediaDecoder* Clone() MOZ_OVERRIDE;
|
||||
|
||||
virtual MediaOmxCommonReader* CreateReader() MOZ_OVERRIDE;
|
||||
|
||||
virtual MediaDecoderStateMachine* CreateStateMachine(MediaOmxCommonReader* aReader) MOZ_OVERRIDE;
|
||||
|
||||
virtual void ApplyStateToStateMachine(PlayState aState) MOZ_OVERRIDE;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif
|
|
@ -0,0 +1,116 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* 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 "RtspMediaCodecReader.h"
|
||||
|
||||
#include "RtspExtractor.h"
|
||||
#include "RtspMediaResource.h"
|
||||
#include "RtspMediaCodecDecoder.h"
|
||||
|
||||
using namespace android;
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
RtspMediaCodecReader::RtspMediaCodecReader(AbstractMediaDecoder* aDecoder)
|
||||
: MediaCodecReader(aDecoder)
|
||||
{
|
||||
NS_ASSERTION(mDecoder, "RtspMediaCodecReader mDecoder is null.");
|
||||
NS_ASSERTION(mDecoder->GetResource(),
|
||||
"RtspMediaCodecReader mDecoder->GetResource() is null.");
|
||||
mRtspResource = mDecoder->GetResource()->GetRtspPointer();
|
||||
MOZ_ASSERT(mRtspResource);
|
||||
}
|
||||
|
||||
RtspMediaCodecReader::~RtspMediaCodecReader() {}
|
||||
|
||||
bool
|
||||
RtspMediaCodecReader::CreateExtractor()
|
||||
{
|
||||
if (mExtractor != nullptr) {
|
||||
return true;
|
||||
}
|
||||
|
||||
mExtractor = new RtspExtractor(mRtspResource);
|
||||
|
||||
return mExtractor != nullptr;
|
||||
}
|
||||
|
||||
nsresult
|
||||
RtspMediaCodecReader::Seek(int64_t aTime, int64_t aStartTime,
|
||||
int64_t aEndTime, int64_t aCurrentTime)
|
||||
{
|
||||
// The seek function of Rtsp is time-based, we call the SeekTime function in
|
||||
// RtspMediaResource. The SeekTime function finally send a seek command to
|
||||
// Rtsp stream server through network and also clear the buffer data in
|
||||
// RtspMediaResource.
|
||||
mRtspResource->SeekTime(aTime);
|
||||
|
||||
return MediaCodecReader::Seek(aTime, aStartTime, aEndTime, aCurrentTime);
|
||||
}
|
||||
|
||||
void
|
||||
RtspMediaCodecReader::SetIdle()
|
||||
{
|
||||
nsIStreamingProtocolController* controller =
|
||||
mRtspResource->GetMediaStreamController();
|
||||
if (controller) {
|
||||
controller->Pause();
|
||||
}
|
||||
mRtspResource->SetSuspend(true);
|
||||
}
|
||||
|
||||
void
|
||||
RtspMediaCodecReader::EnsureActive()
|
||||
{
|
||||
// Need to start RTSP streaming OMXCodec decoding.
|
||||
nsIStreamingProtocolController* controller =
|
||||
mRtspResource->GetMediaStreamController();
|
||||
if (controller) {
|
||||
controller->Play();
|
||||
}
|
||||
mRtspResource->SetSuspend(false);
|
||||
}
|
||||
|
||||
void
|
||||
RtspMediaCodecReader::RequestAudioData()
|
||||
{
|
||||
EnsureActive();
|
||||
MediaCodecReader::RequestAudioData();
|
||||
}
|
||||
|
||||
void
|
||||
RtspMediaCodecReader::RequestVideoData(bool aSkipToNextKeyframe,
|
||||
int64_t aTimeThreshold)
|
||||
{
|
||||
EnsureActive();
|
||||
MediaCodecReader::RequestVideoData(aSkipToNextKeyframe, aTimeThreshold);
|
||||
}
|
||||
|
||||
nsresult
|
||||
RtspMediaCodecReader::ReadMetadata(MediaInfo* aInfo,
|
||||
MetadataTags** aTags)
|
||||
{
|
||||
nsresult rv = MediaCodecReader::ReadMetadata(aInfo, aTags);
|
||||
if (rv == NS_OK && !IsWaitingMediaResources()) {
|
||||
EnsureActive();
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
// Called on Binder thread.
|
||||
void
|
||||
RtspMediaCodecReader::codecReserved(Track& aTrack)
|
||||
{
|
||||
// TODO: fix me, we need a SeekTime(0) here because the
|
||||
// MediaDecoderStateMachine will update the mStartTime after ReadMetadata.
|
||||
MediaCodecReader::codecReserved(aTrack);
|
||||
if (aTrack.mCodec != nullptr) {
|
||||
mRtspResource->SeekTime(0);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
|
@ -0,0 +1,80 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* 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/. */
|
||||
|
||||
#if !defined(RtspMediaCodecReader_h_)
|
||||
#define RtspMediaCodecReader_h_
|
||||
|
||||
#include "MediaCodecReader.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
namespace dom {
|
||||
class TimeRanges;
|
||||
}
|
||||
|
||||
class AbstractMediaDecoder;
|
||||
class RtspMediaResource;
|
||||
|
||||
/* RtspMediaCodecReader is a subclass of MediaCodecReader.
|
||||
* The major reason that RtspMediaCodecReader inherit from MediaCodecReader is
|
||||
* the same video/audio decoding logic we can reuse.
|
||||
*/
|
||||
class RtspMediaCodecReader MOZ_FINAL : public MediaCodecReader
|
||||
{
|
||||
protected:
|
||||
// Provide a Rtsp extractor.
|
||||
virtual bool CreateExtractor() MOZ_OVERRIDE;
|
||||
// Ensure the play command is sent to Rtsp server.
|
||||
void EnsureActive();
|
||||
|
||||
public:
|
||||
RtspMediaCodecReader(AbstractMediaDecoder* aDecoder);
|
||||
|
||||
virtual ~RtspMediaCodecReader();
|
||||
|
||||
// Implement a time-based seek instead of byte-based.
|
||||
virtual nsresult Seek(int64_t aTime, int64_t aStartTime, int64_t aEndTime,
|
||||
int64_t aCurrentTime) MOZ_OVERRIDE;
|
||||
|
||||
// Override GetBuffered() to do nothing for below reasons:
|
||||
// 1. Because the Rtsp stream is a/v separated. The buffered data in a/v
|
||||
// tracks are not consistent with time stamp.
|
||||
// For example: audio buffer: 1~2s, video buffer: 1.5~2.5s
|
||||
// 2. Since the Rtsp is a realtime streaming, the buffer we made for
|
||||
// RtspMediaResource is quite small. The small buffer implies the time ranges
|
||||
// we returned are not useful for the MediaDecodeStateMachine. Unlike the
|
||||
// ChannelMediaResource, it has a "cache" that can store the whole streaming
|
||||
// data so the |GetBuffered| function can retrieve useful time ranges.
|
||||
virtual nsresult GetBuffered(dom::TimeRanges* aBuffered,
|
||||
int64_t aStartTime) MOZ_OVERRIDE {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
virtual void SetIdle() MOZ_OVERRIDE;
|
||||
|
||||
// Disptach a DecodeVideoFrameTask to decode video data.
|
||||
virtual void RequestVideoData(bool aSkipToNextKeyframe,
|
||||
int64_t aTimeThreshold) MOZ_OVERRIDE;
|
||||
|
||||
// Disptach a DecodeAudioDataTask to decode audio data.
|
||||
virtual void RequestAudioData() MOZ_OVERRIDE;
|
||||
|
||||
virtual nsresult ReadMetadata(MediaInfo* aInfo,
|
||||
MetadataTags** aTags) MOZ_OVERRIDE;
|
||||
|
||||
virtual void codecReserved(Track& aTrack) MOZ_OVERRIDE;
|
||||
|
||||
private:
|
||||
// A pointer to RtspMediaResource for calling the Rtsp specific function.
|
||||
// The lifetime of mRtspResource is controlled by MediaDecoder. MediaDecoder
|
||||
// holds the MediaDecoderStateMachine and RtspMediaResource.
|
||||
// And MediaDecoderStateMachine holds this RtspMediaCodecReader.
|
||||
RtspMediaResource* mRtspResource;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif
|
|
@ -8,264 +8,15 @@
|
|||
|
||||
#include "AbstractMediaDecoder.h"
|
||||
#include "MediaDecoderStateMachine.h"
|
||||
#include "MPAPI.h"
|
||||
#include "mozilla/dom/TimeRanges.h"
|
||||
#include "mozilla/Mutex.h"
|
||||
#include "mozilla/TimeStamp.h"
|
||||
#include "OmxDecoder.h"
|
||||
#include "RtspExtractor.h"
|
||||
#include "RtspMediaResource.h"
|
||||
#include "RtspOmxDecoder.h"
|
||||
#include "VideoUtils.h"
|
||||
|
||||
#include <stagefright/MediaExtractor.h>
|
||||
#include <stagefright/MediaBufferGroup.h>
|
||||
#include <stagefright/MetaData.h>
|
||||
|
||||
#define FRAME_DEFAULT_SIZE 1024
|
||||
|
||||
using namespace android;
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
/* class RtspMediaSource : implements MediaSource for OMX.
|
||||
* The decoder thread will trigger the MediaDecodeStateMachine to read a/v frame.
|
||||
* Then RtspOmxReader calls OMX decoder to decode a/v frame. Finally the code
|
||||
* path run into the read() here, it reads un-decoded frame data from mResource
|
||||
* and construct a MediaBuffer for output to OMX decoder.
|
||||
* */
|
||||
class RtspMediaSource : public android::MediaSource {
|
||||
public:
|
||||
RtspMediaSource(RtspMediaResource *aRtspMediaResource,
|
||||
ssize_t aTrackIdx,
|
||||
uint32_t aFrameMaxSize,
|
||||
const sp<MetaData>& aMeta)
|
||||
: mRtspResource(aRtspMediaResource)
|
||||
, mFormat(aMeta)
|
||||
, mTrackIdx(aTrackIdx)
|
||||
, mMonitor("RtspMediaSource.mMonitor")
|
||||
, mIsStarted(false)
|
||||
, mGroup(nullptr)
|
||||
, mBuffer(nullptr)
|
||||
, mFrameMaxSize(aFrameMaxSize) {
|
||||
MOZ_COUNT_CTOR(RtspMediaSource);
|
||||
};
|
||||
virtual ~RtspMediaSource() {
|
||||
MOZ_COUNT_DTOR(RtspMediaSource);
|
||||
}
|
||||
virtual status_t start(MetaData *params = nullptr) MOZ_FINAL MOZ_OVERRIDE;
|
||||
virtual status_t stop() MOZ_FINAL MOZ_OVERRIDE;
|
||||
virtual sp<MetaData> getFormat() MOZ_FINAL MOZ_OVERRIDE {
|
||||
ReentrantMonitorAutoEnter mon(mMonitor);
|
||||
return mFormat;
|
||||
};
|
||||
virtual status_t read(MediaBuffer **buffer,
|
||||
const ReadOptions *options = nullptr) MOZ_FINAL MOZ_OVERRIDE ;
|
||||
private:
|
||||
nsRefPtr<RtspMediaResource> mRtspResource;
|
||||
sp<MetaData> mFormat;
|
||||
uint32_t mTrackIdx;
|
||||
ReentrantMonitor mMonitor;
|
||||
bool mIsStarted;
|
||||
|
||||
// mGroup owns the mBuffer. mFrameMaxSize is the mBuffer size.
|
||||
// mBuffer is the input buffer for omx decoder.
|
||||
nsAutoPtr<MediaBufferGroup> mGroup;
|
||||
MediaBuffer* mBuffer;
|
||||
uint32_t mFrameMaxSize;
|
||||
};
|
||||
|
||||
status_t RtspMediaSource::start(MetaData *params)
|
||||
{
|
||||
ReentrantMonitorAutoEnter mon(mMonitor);
|
||||
if (!mIsStarted) {
|
||||
// RtspMediaSource relinquish the ownership of MediaBuffer |buf| to mGroup.
|
||||
mGroup = new MediaBufferGroup();
|
||||
MediaBuffer* buf = new MediaBuffer(mFrameMaxSize);
|
||||
mGroup->add_buffer(buf);
|
||||
mIsStarted = true;
|
||||
}
|
||||
return OK;
|
||||
}
|
||||
|
||||
status_t RtspMediaSource::stop()
|
||||
{
|
||||
ReentrantMonitorAutoEnter mon(mMonitor);
|
||||
if (mIsStarted) {
|
||||
if (mBuffer) {
|
||||
mBuffer->release();
|
||||
mBuffer = nullptr;
|
||||
}
|
||||
mGroup = nullptr;
|
||||
mIsStarted = false;
|
||||
}
|
||||
return OK;
|
||||
}
|
||||
|
||||
status_t RtspMediaSource::read(MediaBuffer **out, const ReadOptions *options)
|
||||
{
|
||||
ReentrantMonitorAutoEnter mon(mMonitor);
|
||||
NS_ENSURE_TRUE(mIsStarted, MEDIA_ERROR_BASE);
|
||||
NS_ENSURE_TRUE(out, MEDIA_ERROR_BASE);
|
||||
*out = nullptr;
|
||||
|
||||
// Video/audio track's initial frame size is FRAME_DEFAULT_SIZE.
|
||||
// We need to realloc the mBuffer if the mBuffer doesn't have enough space
|
||||
// for next ReadFrameFromTrack function. (actualFrameSize > mFrameMaxSize)
|
||||
status_t err;
|
||||
uint32_t readCount;
|
||||
uint32_t actualFrameSize;
|
||||
uint64_t time;
|
||||
nsresult rv;
|
||||
|
||||
while (1) {
|
||||
err = mGroup->acquire_buffer(&mBuffer);
|
||||
NS_ENSURE_TRUE(err == OK, err);
|
||||
|
||||
rv = mRtspResource->ReadFrameFromTrack((uint8_t *)mBuffer->data(),
|
||||
mFrameMaxSize, mTrackIdx, readCount,
|
||||
time, actualFrameSize);
|
||||
if (NS_FAILED(rv)) {
|
||||
// Release mGroup and mBuffer.
|
||||
stop();
|
||||
// Since RtspMediaSource is an implementation of Android media source,
|
||||
// it's held by OMXCodec and isn't released yet. So we have to re-construct
|
||||
// mGroup and mBuffer.
|
||||
start();
|
||||
NS_WARNING("ReadFrameFromTrack failed; releasing buffers and returning.");
|
||||
return ERROR_CONNECTION_LOST;
|
||||
}
|
||||
if (actualFrameSize > mFrameMaxSize) {
|
||||
// release mGroup and mBuffer
|
||||
stop();
|
||||
// re-construct mGroup and mBuffer
|
||||
mFrameMaxSize = actualFrameSize;
|
||||
err = start();
|
||||
NS_ENSURE_TRUE(err == OK, err);
|
||||
} else {
|
||||
// ReadFrameFromTrack success, break the while loop.
|
||||
break;
|
||||
}
|
||||
}
|
||||
mBuffer->set_range(0, readCount);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
mBuffer->meta_data()->clear();
|
||||
// fill the meta data
|
||||
mBuffer->meta_data()->setInt64(kKeyTime, time);
|
||||
*out = mBuffer;
|
||||
mBuffer = nullptr;
|
||||
return OK;
|
||||
}
|
||||
|
||||
return ERROR_END_OF_STREAM;
|
||||
}
|
||||
|
||||
|
||||
// RtspExtractor is a custom extractor for Rtsp stream, whereas the other
|
||||
// XXXExtractors are made for container media content.
|
||||
// The extractor is used for |OmxDecoder::Init|, it provides the essential
|
||||
// information for creating OMXCodec instance.
|
||||
// For example, the |getTrackMetaData| returns metadata that includes the
|
||||
// codec type.
|
||||
class RtspExtractor: public MediaExtractor
|
||||
{
|
||||
public:
|
||||
virtual size_t countTracks() MOZ_FINAL MOZ_OVERRIDE;
|
||||
virtual sp<android::MediaSource> getTrack(size_t index)
|
||||
MOZ_FINAL MOZ_OVERRIDE;
|
||||
virtual sp<MetaData> getTrackMetaData(
|
||||
size_t index, uint32_t flag = 0) MOZ_FINAL MOZ_OVERRIDE;
|
||||
virtual uint32_t flags() const MOZ_FINAL MOZ_OVERRIDE;
|
||||
|
||||
RtspExtractor(RtspMediaResource *aResource)
|
||||
: mRtspResource(aResource) {
|
||||
MOZ_COUNT_CTOR(RtspExtractor);
|
||||
MOZ_ASSERT(aResource);
|
||||
mController = mRtspResource->GetMediaStreamController();
|
||||
MOZ_ASSERT(mController);
|
||||
}
|
||||
virtual ~RtspExtractor() MOZ_OVERRIDE {
|
||||
MOZ_COUNT_DTOR(RtspExtractor);
|
||||
}
|
||||
private:
|
||||
// mRtspResource is a pointer to RtspMediaResource. When |getTrack| is called
|
||||
// we use mRtspResource to construct a RtspMediaSource.
|
||||
RtspMediaResource* mRtspResource;
|
||||
// Through the mController in mRtspResource, we can get the essential
|
||||
// information for the extractor.
|
||||
nsRefPtr<nsIStreamingProtocolController> mController;
|
||||
};
|
||||
|
||||
size_t RtspExtractor::countTracks()
|
||||
{
|
||||
uint8_t tracks = 0;
|
||||
if (mController) {
|
||||
mController->GetTotalTracks(&tracks);
|
||||
}
|
||||
return size_t(tracks);
|
||||
}
|
||||
|
||||
sp<android::MediaSource> RtspExtractor::getTrack(size_t index)
|
||||
{
|
||||
NS_ENSURE_TRUE(index < countTracks(), nullptr);
|
||||
|
||||
sp<MetaData> meta = getTrackMetaData(index);
|
||||
sp<android::MediaSource> source = new RtspMediaSource(mRtspResource,
|
||||
index,
|
||||
FRAME_DEFAULT_SIZE,
|
||||
meta);
|
||||
return source;
|
||||
}
|
||||
|
||||
sp<MetaData> RtspExtractor::getTrackMetaData(size_t index, uint32_t flag)
|
||||
{
|
||||
NS_ENSURE_TRUE(index < countTracks(), nullptr);;
|
||||
|
||||
sp<MetaData> meta = new MetaData();
|
||||
nsCOMPtr<nsIStreamingProtocolMetaData> rtspMetadata;
|
||||
mController->GetTrackMetaData(index, getter_AddRefs(rtspMetadata));
|
||||
|
||||
if (rtspMetadata) {
|
||||
// Convert msMeta into meta.
|
||||
// The getter function of nsIStreamingProtocolMetaData will initialize the
|
||||
// metadata values to 0 before setting them.
|
||||
nsCString mime;
|
||||
rtspMetadata->GetMimeType(mime);
|
||||
meta->setCString(kKeyMIMEType, mime.get());
|
||||
uint32_t temp32;
|
||||
rtspMetadata->GetWidth(&temp32);
|
||||
meta->setInt32(kKeyWidth, temp32);
|
||||
rtspMetadata->GetHeight(&temp32);
|
||||
meta->setInt32(kKeyHeight, temp32);
|
||||
rtspMetadata->GetSampleRate(&temp32);
|
||||
meta->setInt32(kKeySampleRate, temp32);
|
||||
rtspMetadata->GetChannelCount(&temp32);
|
||||
meta->setInt32(kKeyChannelCount, temp32);
|
||||
uint64_t temp64;
|
||||
rtspMetadata->GetDuration(&temp64);
|
||||
meta->setInt64(kKeyDuration, temp64);
|
||||
|
||||
nsCString tempCString;
|
||||
rtspMetadata->GetEsdsData(tempCString);
|
||||
if (tempCString.Length()) {
|
||||
meta->setData(kKeyESDS, 0, tempCString.get(), tempCString.Length());
|
||||
}
|
||||
rtspMetadata->GetAvccData(tempCString);
|
||||
if (tempCString.Length()) {
|
||||
meta->setData(kKeyAVCC, 0, tempCString.get(), tempCString.Length());
|
||||
}
|
||||
}
|
||||
return meta;
|
||||
}
|
||||
|
||||
uint32_t RtspExtractor::flags() const
|
||||
{
|
||||
if (mRtspResource->IsRealTime()) {
|
||||
return 0;
|
||||
} else {
|
||||
return MediaExtractor::CAN_SEEK;
|
||||
}
|
||||
}
|
||||
|
||||
nsresult RtspOmxReader::InitOmxDecoder()
|
||||
{
|
||||
if (!mOmxDecoder.get()) {
|
||||
|
|
|
@ -44,13 +44,24 @@ if CONFIG['MOZ_OMX_ENCODER']:
|
|||
|
||||
if 'rtsp' in CONFIG['NECKO_PROTOCOLS']:
|
||||
EXPORTS += [
|
||||
'RtspExtractor.h',
|
||||
'RtspOmxDecoder.h',
|
||||
'RtspOmxReader.h',
|
||||
]
|
||||
SOURCES += [
|
||||
'RtspExtractor.cpp',
|
||||
'RtspOmxDecoder.cpp',
|
||||
'RtspOmxReader.cpp',
|
||||
]
|
||||
if CONFIG['ANDROID_VERSION'] >= '18':
|
||||
EXPORTS += [
|
||||
'RtspMediaCodecDecoder.h',
|
||||
'RtspMediaCodecReader.h',
|
||||
]
|
||||
SOURCES += [
|
||||
'RtspMediaCodecDecoder.cpp',
|
||||
'RtspMediaCodecReader.cpp',
|
||||
]
|
||||
|
||||
if CONFIG['ANDROID_VERSION'] >= '18':
|
||||
EXPORTS += [
|
||||
|
|
Загрузка…
Ссылка в новой задаче