michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* vim:set ts=2 sw=2 sts=2 et cindent: */ 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 file, michael@0: * You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "RtspOmxReader.h" michael@0: michael@0: #include "AbstractMediaDecoder.h" michael@0: #include "MediaDecoderStateMachine.h" michael@0: #include "MPAPI.h" michael@0: #include "mozilla/dom/TimeRanges.h" michael@0: #include "mozilla/Mutex.h" michael@0: #include "mozilla/TimeStamp.h" michael@0: #include "OmxDecoder.h" michael@0: #include "RtspMediaResource.h" michael@0: #include "RtspOmxDecoder.h" michael@0: #include "VideoUtils.h" michael@0: michael@0: #include michael@0: #include michael@0: #include michael@0: michael@0: #define FRAME_DEFAULT_SIZE 1024 michael@0: michael@0: using namespace android; michael@0: michael@0: namespace mozilla { michael@0: michael@0: /* class RtspMediaSource : implements MediaSource for OMX. michael@0: * The decoder thread will trigger the MediaDecodeStateMachine to read a/v frame. michael@0: * Then RtspOmxReader calls OMX decoder to decode a/v frame. Finally the code michael@0: * path run into the read() here, it reads un-decoded frame data from mResource michael@0: * and construct a MediaBuffer for output to OMX decoder. michael@0: * */ michael@0: class RtspMediaSource : public android::MediaSource { michael@0: public: michael@0: RtspMediaSource(RtspMediaResource *aRtspMediaResource, michael@0: ssize_t aTrackIdx, michael@0: uint32_t aFrameMaxSize, michael@0: const sp& aMeta) michael@0: : mRtspResource(aRtspMediaResource) michael@0: , mFormat(aMeta) michael@0: , mTrackIdx(aTrackIdx) michael@0: , mMonitor("RtspMediaSource.mMonitor") michael@0: , mIsStarted(false) michael@0: , mGroup(nullptr) michael@0: , mBuffer(nullptr) michael@0: , mFrameMaxSize(aFrameMaxSize) { michael@0: MOZ_COUNT_CTOR(RtspMediaSource); michael@0: }; michael@0: virtual ~RtspMediaSource() { michael@0: MOZ_COUNT_DTOR(RtspMediaSource); michael@0: } michael@0: virtual status_t start(MetaData *params = nullptr) MOZ_FINAL MOZ_OVERRIDE; michael@0: virtual status_t stop() MOZ_FINAL MOZ_OVERRIDE; michael@0: virtual sp getFormat() MOZ_FINAL MOZ_OVERRIDE { michael@0: ReentrantMonitorAutoEnter mon(mMonitor); michael@0: return mFormat; michael@0: }; michael@0: virtual status_t read(MediaBuffer **buffer, michael@0: const ReadOptions *options = nullptr) MOZ_FINAL MOZ_OVERRIDE ; michael@0: private: michael@0: nsRefPtr mRtspResource; michael@0: sp mFormat; michael@0: uint32_t mTrackIdx; michael@0: ReentrantMonitor mMonitor; michael@0: bool mIsStarted; michael@0: michael@0: // mGroup owns the mBuffer. mFrameMaxSize is the mBuffer size. michael@0: // mBuffer is the input buffer for omx decoder. michael@0: nsAutoPtr mGroup; michael@0: MediaBuffer* mBuffer; michael@0: uint32_t mFrameMaxSize; michael@0: }; michael@0: michael@0: status_t RtspMediaSource::start(MetaData *params) michael@0: { michael@0: ReentrantMonitorAutoEnter mon(mMonitor); michael@0: if (!mIsStarted) { michael@0: // RtspMediaSource relinquish the ownership of MediaBuffer |buf| to mGroup. michael@0: mGroup = new MediaBufferGroup(); michael@0: MediaBuffer* buf = new MediaBuffer(mFrameMaxSize); michael@0: mGroup->add_buffer(buf); michael@0: mIsStarted = true; michael@0: } michael@0: return OK; michael@0: } michael@0: michael@0: status_t RtspMediaSource::stop() michael@0: { michael@0: ReentrantMonitorAutoEnter mon(mMonitor); michael@0: if (mIsStarted) { michael@0: if (mBuffer) { michael@0: mBuffer->release(); michael@0: mBuffer = nullptr; michael@0: } michael@0: mGroup = nullptr; michael@0: mIsStarted = false; michael@0: } michael@0: return OK; michael@0: } michael@0: michael@0: status_t RtspMediaSource::read(MediaBuffer **out, const ReadOptions *options) michael@0: { michael@0: ReentrantMonitorAutoEnter mon(mMonitor); michael@0: NS_ENSURE_TRUE(mIsStarted, MEDIA_ERROR_BASE); michael@0: NS_ENSURE_TRUE(out, MEDIA_ERROR_BASE); michael@0: *out = nullptr; michael@0: michael@0: // Video/audio track's initial frame size is FRAME_DEFAULT_SIZE. michael@0: // We need to realloc the mBuffer if the mBuffer doesn't have enough space michael@0: // for next ReadFrameFromTrack function. (actualFrameSize > mFrameMaxSize) michael@0: status_t err; michael@0: uint32_t readCount; michael@0: uint32_t actualFrameSize; michael@0: uint64_t time; michael@0: nsresult rv; michael@0: michael@0: while (1) { michael@0: err = mGroup->acquire_buffer(&mBuffer); michael@0: NS_ENSURE_TRUE(err == OK, err); michael@0: michael@0: rv = mRtspResource->ReadFrameFromTrack((uint8_t *)mBuffer->data(), michael@0: mFrameMaxSize, mTrackIdx, readCount, michael@0: time, actualFrameSize); michael@0: if (NS_FAILED(rv)) { michael@0: // Release mGroup and mBuffer. michael@0: stop(); michael@0: // Since RtspMediaSource is an implementation of Android media source, michael@0: // it's held by OMXCodec and isn't released yet. So we have to re-construct michael@0: // mGroup and mBuffer. michael@0: start(); michael@0: NS_WARNING("ReadFrameFromTrack failed; releasing buffers and returning."); michael@0: return ERROR_CONNECTION_LOST; michael@0: } michael@0: if (actualFrameSize > mFrameMaxSize) { michael@0: // release mGroup and mBuffer michael@0: stop(); michael@0: // re-construct mGroup and mBuffer michael@0: mFrameMaxSize = actualFrameSize; michael@0: err = start(); michael@0: NS_ENSURE_TRUE(err == OK, err); michael@0: } else { michael@0: // ReadFrameFromTrack success, break the while loop. michael@0: break; michael@0: } michael@0: } michael@0: mBuffer->set_range(0, readCount); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: mBuffer->meta_data()->clear(); michael@0: // fill the meta data michael@0: mBuffer->meta_data()->setInt64(kKeyTime, time); michael@0: *out = mBuffer; michael@0: mBuffer = nullptr; michael@0: return OK; michael@0: } michael@0: michael@0: return ERROR_END_OF_STREAM; michael@0: } michael@0: michael@0: michael@0: // RtspExtractor is a custom extractor for Rtsp stream, whereas the other michael@0: // XXXExtractors are made for container media content. michael@0: // The extractor is used for |OmxDecoder::Init|, it provides the essential michael@0: // information for creating OMXCodec instance. michael@0: // For example, the |getTrackMetaData| returns metadata that includes the michael@0: // codec type. michael@0: class RtspExtractor: public MediaExtractor michael@0: { michael@0: public: michael@0: virtual size_t countTracks() MOZ_FINAL MOZ_OVERRIDE; michael@0: virtual sp getTrack(size_t index) michael@0: MOZ_FINAL MOZ_OVERRIDE; michael@0: virtual sp getTrackMetaData( michael@0: size_t index, uint32_t flag = 0) MOZ_FINAL MOZ_OVERRIDE; michael@0: virtual uint32_t flags() const MOZ_FINAL MOZ_OVERRIDE; michael@0: michael@0: RtspExtractor(RtspMediaResource *aResource) michael@0: : mRtspResource(aResource) { michael@0: MOZ_COUNT_CTOR(RtspExtractor); michael@0: MOZ_ASSERT(aResource); michael@0: mController = mRtspResource->GetMediaStreamController(); michael@0: MOZ_ASSERT(mController); michael@0: } michael@0: virtual ~RtspExtractor() MOZ_OVERRIDE { michael@0: MOZ_COUNT_DTOR(RtspExtractor); michael@0: } michael@0: private: michael@0: // mRtspResource is a pointer to RtspMediaResource. When |getTrack| is called michael@0: // we use mRtspResource to construct a RtspMediaSource. michael@0: RtspMediaResource* mRtspResource; michael@0: // Through the mController in mRtspResource, we can get the essential michael@0: // information for the extractor. michael@0: nsRefPtr mController; michael@0: }; michael@0: michael@0: size_t RtspExtractor::countTracks() michael@0: { michael@0: uint8_t tracks = 0; michael@0: if (mController) { michael@0: mController->GetTotalTracks(&tracks); michael@0: } michael@0: return size_t(tracks); michael@0: } michael@0: michael@0: sp RtspExtractor::getTrack(size_t index) michael@0: { michael@0: NS_ENSURE_TRUE(index < countTracks(), nullptr); michael@0: michael@0: sp meta = getTrackMetaData(index); michael@0: sp source = new RtspMediaSource(mRtspResource, michael@0: index, michael@0: FRAME_DEFAULT_SIZE, michael@0: meta); michael@0: return source; michael@0: } michael@0: michael@0: sp RtspExtractor::getTrackMetaData(size_t index, uint32_t flag) michael@0: { michael@0: NS_ENSURE_TRUE(index < countTracks(), nullptr);; michael@0: michael@0: sp meta = new MetaData(); michael@0: nsCOMPtr rtspMetadata; michael@0: mController->GetTrackMetaData(index, getter_AddRefs(rtspMetadata)); michael@0: michael@0: if (rtspMetadata) { michael@0: // Convert msMeta into meta. michael@0: // The getter function of nsIStreamingProtocolMetaData will initialize the michael@0: // metadata values to 0 before setting them. michael@0: nsCString mime; michael@0: rtspMetadata->GetMimeType(mime); michael@0: meta->setCString(kKeyMIMEType, mime.get()); michael@0: uint32_t temp32; michael@0: rtspMetadata->GetWidth(&temp32); michael@0: meta->setInt32(kKeyWidth, temp32); michael@0: rtspMetadata->GetHeight(&temp32); michael@0: meta->setInt32(kKeyHeight, temp32); michael@0: rtspMetadata->GetSampleRate(&temp32); michael@0: meta->setInt32(kKeySampleRate, temp32); michael@0: rtspMetadata->GetChannelCount(&temp32); michael@0: meta->setInt32(kKeyChannelCount, temp32); michael@0: uint64_t temp64; michael@0: rtspMetadata->GetDuration(&temp64); michael@0: meta->setInt64(kKeyDuration, temp64); michael@0: michael@0: nsCString tempCString; michael@0: rtspMetadata->GetEsdsData(tempCString); michael@0: if (tempCString.Length()) { michael@0: meta->setData(kKeyESDS, 0, tempCString.get(), tempCString.Length()); michael@0: } michael@0: rtspMetadata->GetAvccData(tempCString); michael@0: if (tempCString.Length()) { michael@0: meta->setData(kKeyAVCC, 0, tempCString.get(), tempCString.Length()); michael@0: } michael@0: } michael@0: return meta; michael@0: } michael@0: michael@0: uint32_t RtspExtractor::flags() const michael@0: { michael@0: if (mRtspResource->IsRealTime()) { michael@0: return 0; michael@0: } else { michael@0: return MediaExtractor::CAN_SEEK; michael@0: } michael@0: } michael@0: michael@0: nsresult RtspOmxReader::InitOmxDecoder() michael@0: { michael@0: if (!mOmxDecoder.get()) { michael@0: NS_ASSERTION(mDecoder, "RtspOmxReader mDecoder is null."); michael@0: NS_ASSERTION(mDecoder->GetResource(), michael@0: "RtspOmxReader mDecoder->GetResource() is null."); michael@0: mExtractor = new RtspExtractor(mRtspResource); michael@0: mOmxDecoder = new OmxDecoder(mDecoder->GetResource(), mDecoder); michael@0: if (!mOmxDecoder->Init(mExtractor)) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult RtspOmxReader::Seek(int64_t aTime, int64_t aStartTime, michael@0: int64_t aEndTime, int64_t aCurrentTime) michael@0: { michael@0: // The seek function of Rtsp is time-based, we call the SeekTime function in michael@0: // RtspMediaResource. The SeekTime function finally send a seek command to michael@0: // Rtsp stream server through network and also clear the buffer data in michael@0: // RtspMediaResource. michael@0: if (mRtspResource) { michael@0: mRtspResource->SeekTime(aTime); michael@0: } michael@0: michael@0: // Call |MediaOmxReader::Seek| to notify the OMX decoder we are performing a michael@0: // seek operation. The function will clear the |mVideoQueue| and |mAudioQueue| michael@0: // that store the decoded data and also call the |DecodeToTarget| to pass michael@0: // the seek time to OMX a/v decoders. michael@0: return MediaOmxReader::Seek(aTime, aStartTime, aEndTime, aCurrentTime); michael@0: } michael@0: michael@0: nsresult michael@0: RtspOmxReader::ReadMetadata(MediaInfo* aInfo, michael@0: MetadataTags** aTags) michael@0: { michael@0: SetActive(); michael@0: michael@0: nsresult rv = MediaOmxReader::ReadMetadata(aInfo, aTags); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: void RtspOmxReader::SetIdle() { michael@0: // Call parent class to set OMXCodec idle. michael@0: MediaOmxReader::SetIdle(); michael@0: michael@0: // Need to pause RTSP streaming OMXCodec decoding. michael@0: if (mRtspResource) { michael@0: nsIStreamingProtocolController* controller = michael@0: mRtspResource->GetMediaStreamController(); michael@0: if (controller) { michael@0: controller->Pause(); michael@0: } michael@0: } michael@0: } michael@0: michael@0: void RtspOmxReader::SetActive() { michael@0: // Need to start RTSP streaming OMXCodec decoding. michael@0: if (mRtspResource) { michael@0: nsIStreamingProtocolController* controller = michael@0: mRtspResource->GetMediaStreamController(); michael@0: if (controller) { michael@0: controller->Play(); michael@0: } michael@0: } michael@0: michael@0: // Call parent class to set OMXCodec active. michael@0: MediaOmxReader::SetActive(); michael@0: } michael@0: michael@0: } // namespace mozilla