1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/netwerk/protocol/device/AndroidCaptureProvider.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,301 @@ 1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +#include "base/basictypes.h" 1.10 +#include "AndroidCaptureProvider.h" 1.11 +#include "nsXULAppAPI.h" 1.12 +#include "AndroidBridge.h" 1.13 +#include "nsStreamUtils.h" 1.14 +#include "nsThreadUtils.h" 1.15 +#include "nsMemory.h" 1.16 +#include "RawStructs.h" 1.17 + 1.18 +// The maximum number of frames we keep in our queue. Don't live in the past. 1.19 +#define MAX_FRAMES_QUEUED 10 1.20 + 1.21 +using namespace mozilla::net; 1.22 + 1.23 +NS_IMPL_ISUPPORTS(AndroidCameraInputStream, nsIInputStream, nsIAsyncInputStream) 1.24 + 1.25 +AndroidCameraInputStream::AndroidCameraInputStream() : 1.26 + mWidth(0), mHeight(0), mCamera(0), mHeaderSent(false), mClosed(true), mFrameSize(0), 1.27 + mMonitor("AndroidCamera.Monitor") 1.28 +{ 1.29 + mAvailable = sizeof(RawVideoHeader); 1.30 + mFrameQueue = new nsDeque(); 1.31 +} 1.32 + 1.33 +AndroidCameraInputStream::~AndroidCameraInputStream() { 1.34 + // clear the frame queue 1.35 + while (mFrameQueue->GetSize() > 0) { 1.36 + nsMemory::Free(mFrameQueue->PopFront()); 1.37 + } 1.38 + delete mFrameQueue; 1.39 +} 1.40 + 1.41 +NS_IMETHODIMP 1.42 +AndroidCameraInputStream::Init(nsACString& aContentType, nsCaptureParams* aParams) 1.43 +{ 1.44 + if (XRE_GetProcessType() != GeckoProcessType_Default) 1.45 + return NS_ERROR_NOT_IMPLEMENTED; 1.46 + 1.47 + mContentType = aContentType; 1.48 + mWidth = aParams->width; 1.49 + mHeight = aParams->height; 1.50 + mCamera = aParams->camera; 1.51 + 1.52 + CameraStreamImpl *impl = CameraStreamImpl::GetInstance(0); 1.53 + if (!impl) 1.54 + return NS_ERROR_OUT_OF_MEMORY; 1.55 + if (impl->Init(mContentType, mCamera, mWidth, mHeight, this)) { 1.56 + mWidth = impl->GetWidth(); 1.57 + mHeight = impl->GetHeight(); 1.58 + mClosed = false; 1.59 + } 1.60 + return NS_OK; 1.61 +} 1.62 + 1.63 +void AndroidCameraInputStream::ReceiveFrame(char* frame, uint32_t length) { 1.64 + { 1.65 + mozilla::ReentrantMonitorAutoEnter autoMonitor(mMonitor); 1.66 + if (mFrameQueue->GetSize() > MAX_FRAMES_QUEUED) { 1.67 + nsMemory::Free(mFrameQueue->PopFront()); 1.68 + mAvailable -= mFrameSize; 1.69 + } 1.70 + } 1.71 + 1.72 + mFrameSize = sizeof(RawPacketHeader) + length; 1.73 + 1.74 + char* fullFrame = (char*)nsMemory::Alloc(mFrameSize); 1.75 + 1.76 + if (!fullFrame) 1.77 + return; 1.78 + 1.79 + RawPacketHeader* header = (RawPacketHeader*) fullFrame; 1.80 + header->packetID = 0xFF; 1.81 + header->codecID = 0x595556; // "YUV" 1.82 + 1.83 + // we copy the Y plane, and de-interlace the CrCb 1.84 + 1.85 + uint32_t yFrameSize = mWidth * mHeight; 1.86 + uint32_t uvFrameSize = yFrameSize / 4; 1.87 + 1.88 + memcpy(fullFrame + sizeof(RawPacketHeader), frame, yFrameSize); 1.89 + 1.90 + char* uFrame = fullFrame + yFrameSize; 1.91 + char* vFrame = fullFrame + yFrameSize + uvFrameSize; 1.92 + char* yFrame = frame + yFrameSize; 1.93 + for (uint32_t i = 0; i < uvFrameSize; i++) { 1.94 + uFrame[i] = yFrame[2 * i + 1]; 1.95 + vFrame[i] = yFrame[2 * i]; 1.96 + } 1.97 + 1.98 + { 1.99 + mozilla::ReentrantMonitorAutoEnter autoMonitor(mMonitor); 1.100 + mAvailable += mFrameSize; 1.101 + mFrameQueue->Push((void*)fullFrame); 1.102 + } 1.103 + 1.104 + NotifyListeners(); 1.105 +} 1.106 + 1.107 +NS_IMETHODIMP 1.108 +AndroidCameraInputStream::Available(uint64_t *aAvailable) 1.109 +{ 1.110 + mozilla::ReentrantMonitorAutoEnter autoMonitor(mMonitor); 1.111 + 1.112 + *aAvailable = mAvailable; 1.113 + 1.114 + return NS_OK; 1.115 +} 1.116 + 1.117 +NS_IMETHODIMP AndroidCameraInputStream::IsNonBlocking(bool *aNonBlock) { 1.118 + *aNonBlock = true; 1.119 + return NS_OK; 1.120 +} 1.121 + 1.122 +NS_IMETHODIMP AndroidCameraInputStream::Read(char *aBuffer, uint32_t aCount, uint32_t *aRead) { 1.123 + return ReadSegments(NS_CopySegmentToBuffer, aBuffer, aCount, aRead); 1.124 +} 1.125 + 1.126 +NS_IMETHODIMP AndroidCameraInputStream::ReadSegments(nsWriteSegmentFun aWriter, void *aClosure, uint32_t aCount, uint32_t *aRead) { 1.127 + *aRead = 0; 1.128 + 1.129 + nsresult rv; 1.130 + 1.131 + if (mAvailable == 0) 1.132 + return NS_BASE_STREAM_WOULD_BLOCK; 1.133 + 1.134 + if (aCount > mAvailable) 1.135 + aCount = mAvailable; 1.136 + 1.137 + if (!mHeaderSent) { 1.138 + CameraStreamImpl *impl = CameraStreamImpl::GetInstance(0); 1.139 + RawVideoHeader header; 1.140 + header.headerPacketID = 0; 1.141 + header.codecID = 0x595556; // "YUV" 1.142 + header.majorVersion = 0; 1.143 + header.minorVersion = 1; 1.144 + header.options = 1 | 1 << 1; // color, 4:2:2 1.145 + 1.146 + header.alphaChannelBpp = 0; 1.147 + header.lumaChannelBpp = 8; 1.148 + header.chromaChannelBpp = 4; 1.149 + header.colorspace = 1; 1.150 + 1.151 + header.frameWidth = mWidth; 1.152 + header.frameHeight = mHeight; 1.153 + header.aspectNumerator = 1; 1.154 + header.aspectDenominator = 1; 1.155 + 1.156 + header.framerateNumerator = impl->GetFps(); 1.157 + header.framerateDenominator = 1; 1.158 + 1.159 + rv = aWriter(this, aClosure, (const char*)&header, 0, sizeof(RawVideoHeader), aRead); 1.160 + 1.161 + if (NS_FAILED(rv)) 1.162 + return NS_OK; 1.163 + 1.164 + mHeaderSent = true; 1.165 + aCount -= sizeof(RawVideoHeader); 1.166 + mAvailable -= sizeof(RawVideoHeader); 1.167 + } 1.168 + 1.169 + { 1.170 + mozilla::ReentrantMonitorAutoEnter autoMonitor(mMonitor); 1.171 + while ((mAvailable > 0) && (aCount >= mFrameSize)) { 1.172 + uint32_t readThisTime = 0; 1.173 + 1.174 + char* frame = (char*)mFrameQueue->PopFront(); 1.175 + rv = aWriter(this, aClosure, (const char*)frame, *aRead, mFrameSize, &readThisTime); 1.176 + 1.177 + if (readThisTime != mFrameSize) { 1.178 + mFrameQueue->PushFront((void*)frame); 1.179 + return NS_OK; 1.180 + } 1.181 + 1.182 + // RawReader does a copy when calling VideoData::Create() 1.183 + nsMemory::Free(frame); 1.184 + 1.185 + if (NS_FAILED(rv)) 1.186 + return NS_OK; 1.187 + 1.188 + aCount -= readThisTime; 1.189 + mAvailable -= readThisTime; 1.190 + *aRead += readThisTime; 1.191 + } 1.192 + } 1.193 + return NS_OK; 1.194 +} 1.195 + 1.196 +NS_IMETHODIMP AndroidCameraInputStream::Close() { 1.197 + return CloseWithStatus(NS_OK); 1.198 +} 1.199 + 1.200 + 1.201 +/** 1.202 + * must be called on the main (java) thread 1.203 + */ 1.204 +void AndroidCameraInputStream::doClose() { 1.205 + NS_ASSERTION(!mClosed, "Camera is already closed"); 1.206 + 1.207 + CameraStreamImpl *impl = CameraStreamImpl::GetInstance(0); 1.208 + impl->Close(); 1.209 + mClosed = true; 1.210 +} 1.211 + 1.212 + 1.213 +void AndroidCameraInputStream::NotifyListeners() { 1.214 + mozilla::ReentrantMonitorAutoEnter autoMonitor(mMonitor); 1.215 + 1.216 + if (mCallback && (mAvailable > sizeof(RawVideoHeader))) { 1.217 + nsCOMPtr<nsIInputStreamCallback> callback; 1.218 + if (mCallbackTarget) { 1.219 + callback = NS_NewInputStreamReadyEvent(mCallback, mCallbackTarget); 1.220 + } else { 1.221 + callback = mCallback; 1.222 + } 1.223 + 1.224 + NS_ASSERTION(callback, "Shouldn't fail to make the callback!"); 1.225 + 1.226 + // Null the callback first because OnInputStreamReady may reenter AsyncWait 1.227 + mCallback = nullptr; 1.228 + mCallbackTarget = nullptr; 1.229 + 1.230 + callback->OnInputStreamReady(this); 1.231 + } 1.232 +} 1.233 + 1.234 +NS_IMETHODIMP AndroidCameraInputStream::AsyncWait(nsIInputStreamCallback *aCallback, uint32_t aFlags, uint32_t aRequestedCount, nsIEventTarget *aTarget) 1.235 +{ 1.236 + if (aFlags != 0) 1.237 + return NS_ERROR_NOT_IMPLEMENTED; 1.238 + 1.239 + if (mCallback || mCallbackTarget) 1.240 + return NS_ERROR_UNEXPECTED; 1.241 + 1.242 + mCallbackTarget = aTarget; 1.243 + mCallback = aCallback; 1.244 + 1.245 + // What we are being asked for may be present already 1.246 + NotifyListeners(); 1.247 + return NS_OK; 1.248 +} 1.249 + 1.250 + 1.251 +NS_IMETHODIMP AndroidCameraInputStream::CloseWithStatus(nsresult status) 1.252 +{ 1.253 + AndroidCameraInputStream::doClose(); 1.254 + return NS_OK; 1.255 +} 1.256 + 1.257 +/** 1.258 + * AndroidCaptureProvider implementation 1.259 + */ 1.260 + 1.261 +NS_IMPL_ISUPPORTS0(AndroidCaptureProvider) 1.262 + 1.263 +AndroidCaptureProvider* AndroidCaptureProvider::sInstance = nullptr; 1.264 + 1.265 +AndroidCaptureProvider::AndroidCaptureProvider() { 1.266 +} 1.267 + 1.268 +AndroidCaptureProvider::~AndroidCaptureProvider() { 1.269 + AndroidCaptureProvider::sInstance = nullptr; 1.270 +} 1.271 + 1.272 +nsresult AndroidCaptureProvider::Init(nsACString& aContentType, 1.273 + nsCaptureParams* aParams, 1.274 + nsIInputStream** aStream) { 1.275 + 1.276 + NS_ENSURE_ARG_POINTER(aParams); 1.277 + 1.278 + NS_ASSERTION(aParams->frameLimit == 0 || aParams->timeLimit == 0, 1.279 + "Cannot set both a frame limit and a time limit!"); 1.280 + 1.281 + nsRefPtr<AndroidCameraInputStream> stream; 1.282 + 1.283 + if (aContentType.EqualsLiteral("video/x-raw-yuv")) { 1.284 + stream = new AndroidCameraInputStream(); 1.285 + if (stream) { 1.286 + nsresult rv = stream->Init(aContentType, aParams); 1.287 + if (NS_FAILED(rv)) 1.288 + return rv; 1.289 + } 1.290 + else 1.291 + return NS_ERROR_OUT_OF_MEMORY; 1.292 + } else { 1.293 + NS_NOTREACHED("Should not have asked Android for this type!"); 1.294 + } 1.295 + return CallQueryInterface(stream, aStream); 1.296 +} 1.297 + 1.298 +already_AddRefed<AndroidCaptureProvider> GetAndroidCaptureProvider() { 1.299 + if (!AndroidCaptureProvider::sInstance) { 1.300 + AndroidCaptureProvider::sInstance = new AndroidCaptureProvider(); 1.301 + } 1.302 + nsRefPtr<AndroidCaptureProvider> ret = AndroidCaptureProvider::sInstance; 1.303 + return ret.forget(); 1.304 +}