1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/content/media/omx/RtspOmxReader.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,342 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* vim:set ts=2 sw=2 sts=2 et cindent: */ 1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this file, 1.8 + * You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +#include "RtspOmxReader.h" 1.11 + 1.12 +#include "AbstractMediaDecoder.h" 1.13 +#include "MediaDecoderStateMachine.h" 1.14 +#include "MPAPI.h" 1.15 +#include "mozilla/dom/TimeRanges.h" 1.16 +#include "mozilla/Mutex.h" 1.17 +#include "mozilla/TimeStamp.h" 1.18 +#include "OmxDecoder.h" 1.19 +#include "RtspMediaResource.h" 1.20 +#include "RtspOmxDecoder.h" 1.21 +#include "VideoUtils.h" 1.22 + 1.23 +#include <stagefright/MediaExtractor.h> 1.24 +#include <stagefright/MediaBufferGroup.h> 1.25 +#include <stagefright/MetaData.h> 1.26 + 1.27 +#define FRAME_DEFAULT_SIZE 1024 1.28 + 1.29 +using namespace android; 1.30 + 1.31 +namespace mozilla { 1.32 + 1.33 +/* class RtspMediaSource : implements MediaSource for OMX. 1.34 + * The decoder thread will trigger the MediaDecodeStateMachine to read a/v frame. 1.35 + * Then RtspOmxReader calls OMX decoder to decode a/v frame. Finally the code 1.36 + * path run into the read() here, it reads un-decoded frame data from mResource 1.37 + * and construct a MediaBuffer for output to OMX decoder. 1.38 + * */ 1.39 +class RtspMediaSource : public android::MediaSource { 1.40 +public: 1.41 + RtspMediaSource(RtspMediaResource *aRtspMediaResource, 1.42 + ssize_t aTrackIdx, 1.43 + uint32_t aFrameMaxSize, 1.44 + const sp<MetaData>& aMeta) 1.45 + : mRtspResource(aRtspMediaResource) 1.46 + , mFormat(aMeta) 1.47 + , mTrackIdx(aTrackIdx) 1.48 + , mMonitor("RtspMediaSource.mMonitor") 1.49 + , mIsStarted(false) 1.50 + , mGroup(nullptr) 1.51 + , mBuffer(nullptr) 1.52 + , mFrameMaxSize(aFrameMaxSize) { 1.53 + MOZ_COUNT_CTOR(RtspMediaSource); 1.54 + }; 1.55 + virtual ~RtspMediaSource() { 1.56 + MOZ_COUNT_DTOR(RtspMediaSource); 1.57 + } 1.58 + virtual status_t start(MetaData *params = nullptr) MOZ_FINAL MOZ_OVERRIDE; 1.59 + virtual status_t stop() MOZ_FINAL MOZ_OVERRIDE; 1.60 + virtual sp<MetaData> getFormat() MOZ_FINAL MOZ_OVERRIDE { 1.61 + ReentrantMonitorAutoEnter mon(mMonitor); 1.62 + return mFormat; 1.63 + }; 1.64 + virtual status_t read(MediaBuffer **buffer, 1.65 + const ReadOptions *options = nullptr) MOZ_FINAL MOZ_OVERRIDE ; 1.66 +private: 1.67 + nsRefPtr<RtspMediaResource> mRtspResource; 1.68 + sp<MetaData> mFormat; 1.69 + uint32_t mTrackIdx; 1.70 + ReentrantMonitor mMonitor; 1.71 + bool mIsStarted; 1.72 + 1.73 + // mGroup owns the mBuffer. mFrameMaxSize is the mBuffer size. 1.74 + // mBuffer is the input buffer for omx decoder. 1.75 + nsAutoPtr<MediaBufferGroup> mGroup; 1.76 + MediaBuffer* mBuffer; 1.77 + uint32_t mFrameMaxSize; 1.78 +}; 1.79 + 1.80 +status_t RtspMediaSource::start(MetaData *params) 1.81 +{ 1.82 + ReentrantMonitorAutoEnter mon(mMonitor); 1.83 + if (!mIsStarted) { 1.84 + // RtspMediaSource relinquish the ownership of MediaBuffer |buf| to mGroup. 1.85 + mGroup = new MediaBufferGroup(); 1.86 + MediaBuffer* buf = new MediaBuffer(mFrameMaxSize); 1.87 + mGroup->add_buffer(buf); 1.88 + mIsStarted = true; 1.89 + } 1.90 + return OK; 1.91 +} 1.92 + 1.93 +status_t RtspMediaSource::stop() 1.94 +{ 1.95 + ReentrantMonitorAutoEnter mon(mMonitor); 1.96 + if (mIsStarted) { 1.97 + if (mBuffer) { 1.98 + mBuffer->release(); 1.99 + mBuffer = nullptr; 1.100 + } 1.101 + mGroup = nullptr; 1.102 + mIsStarted = false; 1.103 + } 1.104 + return OK; 1.105 +} 1.106 + 1.107 +status_t RtspMediaSource::read(MediaBuffer **out, const ReadOptions *options) 1.108 +{ 1.109 + ReentrantMonitorAutoEnter mon(mMonitor); 1.110 + NS_ENSURE_TRUE(mIsStarted, MEDIA_ERROR_BASE); 1.111 + NS_ENSURE_TRUE(out, MEDIA_ERROR_BASE); 1.112 + *out = nullptr; 1.113 + 1.114 + // Video/audio track's initial frame size is FRAME_DEFAULT_SIZE. 1.115 + // We need to realloc the mBuffer if the mBuffer doesn't have enough space 1.116 + // for next ReadFrameFromTrack function. (actualFrameSize > mFrameMaxSize) 1.117 + status_t err; 1.118 + uint32_t readCount; 1.119 + uint32_t actualFrameSize; 1.120 + uint64_t time; 1.121 + nsresult rv; 1.122 + 1.123 + while (1) { 1.124 + err = mGroup->acquire_buffer(&mBuffer); 1.125 + NS_ENSURE_TRUE(err == OK, err); 1.126 + 1.127 + rv = mRtspResource->ReadFrameFromTrack((uint8_t *)mBuffer->data(), 1.128 + mFrameMaxSize, mTrackIdx, readCount, 1.129 + time, actualFrameSize); 1.130 + if (NS_FAILED(rv)) { 1.131 + // Release mGroup and mBuffer. 1.132 + stop(); 1.133 + // Since RtspMediaSource is an implementation of Android media source, 1.134 + // it's held by OMXCodec and isn't released yet. So we have to re-construct 1.135 + // mGroup and mBuffer. 1.136 + start(); 1.137 + NS_WARNING("ReadFrameFromTrack failed; releasing buffers and returning."); 1.138 + return ERROR_CONNECTION_LOST; 1.139 + } 1.140 + if (actualFrameSize > mFrameMaxSize) { 1.141 + // release mGroup and mBuffer 1.142 + stop(); 1.143 + // re-construct mGroup and mBuffer 1.144 + mFrameMaxSize = actualFrameSize; 1.145 + err = start(); 1.146 + NS_ENSURE_TRUE(err == OK, err); 1.147 + } else { 1.148 + // ReadFrameFromTrack success, break the while loop. 1.149 + break; 1.150 + } 1.151 + } 1.152 + mBuffer->set_range(0, readCount); 1.153 + if (NS_SUCCEEDED(rv)) { 1.154 + mBuffer->meta_data()->clear(); 1.155 + // fill the meta data 1.156 + mBuffer->meta_data()->setInt64(kKeyTime, time); 1.157 + *out = mBuffer; 1.158 + mBuffer = nullptr; 1.159 + return OK; 1.160 + } 1.161 + 1.162 + return ERROR_END_OF_STREAM; 1.163 +} 1.164 + 1.165 + 1.166 +// RtspExtractor is a custom extractor for Rtsp stream, whereas the other 1.167 +// XXXExtractors are made for container media content. 1.168 +// The extractor is used for |OmxDecoder::Init|, it provides the essential 1.169 +// information for creating OMXCodec instance. 1.170 +// For example, the |getTrackMetaData| returns metadata that includes the 1.171 +// codec type. 1.172 +class RtspExtractor: public MediaExtractor 1.173 +{ 1.174 +public: 1.175 + virtual size_t countTracks() MOZ_FINAL MOZ_OVERRIDE; 1.176 + virtual sp<android::MediaSource> getTrack(size_t index) 1.177 + MOZ_FINAL MOZ_OVERRIDE; 1.178 + virtual sp<MetaData> getTrackMetaData( 1.179 + size_t index, uint32_t flag = 0) MOZ_FINAL MOZ_OVERRIDE; 1.180 + virtual uint32_t flags() const MOZ_FINAL MOZ_OVERRIDE; 1.181 + 1.182 + RtspExtractor(RtspMediaResource *aResource) 1.183 + : mRtspResource(aResource) { 1.184 + MOZ_COUNT_CTOR(RtspExtractor); 1.185 + MOZ_ASSERT(aResource); 1.186 + mController = mRtspResource->GetMediaStreamController(); 1.187 + MOZ_ASSERT(mController); 1.188 + } 1.189 + virtual ~RtspExtractor() MOZ_OVERRIDE { 1.190 + MOZ_COUNT_DTOR(RtspExtractor); 1.191 + } 1.192 +private: 1.193 + // mRtspResource is a pointer to RtspMediaResource. When |getTrack| is called 1.194 + // we use mRtspResource to construct a RtspMediaSource. 1.195 + RtspMediaResource* mRtspResource; 1.196 + // Through the mController in mRtspResource, we can get the essential 1.197 + // information for the extractor. 1.198 + nsRefPtr<nsIStreamingProtocolController> mController; 1.199 +}; 1.200 + 1.201 +size_t RtspExtractor::countTracks() 1.202 +{ 1.203 + uint8_t tracks = 0; 1.204 + if (mController) { 1.205 + mController->GetTotalTracks(&tracks); 1.206 + } 1.207 + return size_t(tracks); 1.208 +} 1.209 + 1.210 +sp<android::MediaSource> RtspExtractor::getTrack(size_t index) 1.211 +{ 1.212 + NS_ENSURE_TRUE(index < countTracks(), nullptr); 1.213 + 1.214 + sp<MetaData> meta = getTrackMetaData(index); 1.215 + sp<android::MediaSource> source = new RtspMediaSource(mRtspResource, 1.216 + index, 1.217 + FRAME_DEFAULT_SIZE, 1.218 + meta); 1.219 + return source; 1.220 +} 1.221 + 1.222 +sp<MetaData> RtspExtractor::getTrackMetaData(size_t index, uint32_t flag) 1.223 +{ 1.224 + NS_ENSURE_TRUE(index < countTracks(), nullptr);; 1.225 + 1.226 + sp<MetaData> meta = new MetaData(); 1.227 + nsCOMPtr<nsIStreamingProtocolMetaData> rtspMetadata; 1.228 + mController->GetTrackMetaData(index, getter_AddRefs(rtspMetadata)); 1.229 + 1.230 + if (rtspMetadata) { 1.231 + // Convert msMeta into meta. 1.232 + // The getter function of nsIStreamingProtocolMetaData will initialize the 1.233 + // metadata values to 0 before setting them. 1.234 + nsCString mime; 1.235 + rtspMetadata->GetMimeType(mime); 1.236 + meta->setCString(kKeyMIMEType, mime.get()); 1.237 + uint32_t temp32; 1.238 + rtspMetadata->GetWidth(&temp32); 1.239 + meta->setInt32(kKeyWidth, temp32); 1.240 + rtspMetadata->GetHeight(&temp32); 1.241 + meta->setInt32(kKeyHeight, temp32); 1.242 + rtspMetadata->GetSampleRate(&temp32); 1.243 + meta->setInt32(kKeySampleRate, temp32); 1.244 + rtspMetadata->GetChannelCount(&temp32); 1.245 + meta->setInt32(kKeyChannelCount, temp32); 1.246 + uint64_t temp64; 1.247 + rtspMetadata->GetDuration(&temp64); 1.248 + meta->setInt64(kKeyDuration, temp64); 1.249 + 1.250 + nsCString tempCString; 1.251 + rtspMetadata->GetEsdsData(tempCString); 1.252 + if (tempCString.Length()) { 1.253 + meta->setData(kKeyESDS, 0, tempCString.get(), tempCString.Length()); 1.254 + } 1.255 + rtspMetadata->GetAvccData(tempCString); 1.256 + if (tempCString.Length()) { 1.257 + meta->setData(kKeyAVCC, 0, tempCString.get(), tempCString.Length()); 1.258 + } 1.259 + } 1.260 + return meta; 1.261 +} 1.262 + 1.263 +uint32_t RtspExtractor::flags() const 1.264 +{ 1.265 + if (mRtspResource->IsRealTime()) { 1.266 + return 0; 1.267 + } else { 1.268 + return MediaExtractor::CAN_SEEK; 1.269 + } 1.270 +} 1.271 + 1.272 +nsresult RtspOmxReader::InitOmxDecoder() 1.273 +{ 1.274 + if (!mOmxDecoder.get()) { 1.275 + NS_ASSERTION(mDecoder, "RtspOmxReader mDecoder is null."); 1.276 + NS_ASSERTION(mDecoder->GetResource(), 1.277 + "RtspOmxReader mDecoder->GetResource() is null."); 1.278 + mExtractor = new RtspExtractor(mRtspResource); 1.279 + mOmxDecoder = new OmxDecoder(mDecoder->GetResource(), mDecoder); 1.280 + if (!mOmxDecoder->Init(mExtractor)) { 1.281 + return NS_ERROR_FAILURE; 1.282 + } 1.283 + } 1.284 + return NS_OK; 1.285 +} 1.286 + 1.287 +nsresult RtspOmxReader::Seek(int64_t aTime, int64_t aStartTime, 1.288 + int64_t aEndTime, int64_t aCurrentTime) 1.289 +{ 1.290 + // The seek function of Rtsp is time-based, we call the SeekTime function in 1.291 + // RtspMediaResource. The SeekTime function finally send a seek command to 1.292 + // Rtsp stream server through network and also clear the buffer data in 1.293 + // RtspMediaResource. 1.294 + if (mRtspResource) { 1.295 + mRtspResource->SeekTime(aTime); 1.296 + } 1.297 + 1.298 + // Call |MediaOmxReader::Seek| to notify the OMX decoder we are performing a 1.299 + // seek operation. The function will clear the |mVideoQueue| and |mAudioQueue| 1.300 + // that store the decoded data and also call the |DecodeToTarget| to pass 1.301 + // the seek time to OMX a/v decoders. 1.302 + return MediaOmxReader::Seek(aTime, aStartTime, aEndTime, aCurrentTime); 1.303 +} 1.304 + 1.305 +nsresult 1.306 +RtspOmxReader::ReadMetadata(MediaInfo* aInfo, 1.307 + MetadataTags** aTags) 1.308 +{ 1.309 + SetActive(); 1.310 + 1.311 + nsresult rv = MediaOmxReader::ReadMetadata(aInfo, aTags); 1.312 + NS_ENSURE_SUCCESS(rv, rv); 1.313 + 1.314 + return NS_OK; 1.315 +} 1.316 + 1.317 +void RtspOmxReader::SetIdle() { 1.318 + // Call parent class to set OMXCodec idle. 1.319 + MediaOmxReader::SetIdle(); 1.320 + 1.321 + // Need to pause RTSP streaming OMXCodec decoding. 1.322 + if (mRtspResource) { 1.323 + nsIStreamingProtocolController* controller = 1.324 + mRtspResource->GetMediaStreamController(); 1.325 + if (controller) { 1.326 + controller->Pause(); 1.327 + } 1.328 + } 1.329 +} 1.330 + 1.331 +void RtspOmxReader::SetActive() { 1.332 + // Need to start RTSP streaming OMXCodec decoding. 1.333 + if (mRtspResource) { 1.334 + nsIStreamingProtocolController* controller = 1.335 + mRtspResource->GetMediaStreamController(); 1.336 + if (controller) { 1.337 + controller->Play(); 1.338 + } 1.339 + } 1.340 + 1.341 + // Call parent class to set OMXCodec active. 1.342 + MediaOmxReader::SetActive(); 1.343 +} 1.344 + 1.345 +} // namespace mozilla