netwerk/protocol/device/AndroidCaptureProvider.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

michael@0 1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
michael@0 2 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 3 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 5
michael@0 6 #include "base/basictypes.h"
michael@0 7 #include "AndroidCaptureProvider.h"
michael@0 8 #include "nsXULAppAPI.h"
michael@0 9 #include "AndroidBridge.h"
michael@0 10 #include "nsStreamUtils.h"
michael@0 11 #include "nsThreadUtils.h"
michael@0 12 #include "nsMemory.h"
michael@0 13 #include "RawStructs.h"
michael@0 14
michael@0 15 // The maximum number of frames we keep in our queue. Don't live in the past.
michael@0 16 #define MAX_FRAMES_QUEUED 10
michael@0 17
michael@0 18 using namespace mozilla::net;
michael@0 19
michael@0 20 NS_IMPL_ISUPPORTS(AndroidCameraInputStream, nsIInputStream, nsIAsyncInputStream)
michael@0 21
michael@0 22 AndroidCameraInputStream::AndroidCameraInputStream() :
michael@0 23 mWidth(0), mHeight(0), mCamera(0), mHeaderSent(false), mClosed(true), mFrameSize(0),
michael@0 24 mMonitor("AndroidCamera.Monitor")
michael@0 25 {
michael@0 26 mAvailable = sizeof(RawVideoHeader);
michael@0 27 mFrameQueue = new nsDeque();
michael@0 28 }
michael@0 29
michael@0 30 AndroidCameraInputStream::~AndroidCameraInputStream() {
michael@0 31 // clear the frame queue
michael@0 32 while (mFrameQueue->GetSize() > 0) {
michael@0 33 nsMemory::Free(mFrameQueue->PopFront());
michael@0 34 }
michael@0 35 delete mFrameQueue;
michael@0 36 }
michael@0 37
michael@0 38 NS_IMETHODIMP
michael@0 39 AndroidCameraInputStream::Init(nsACString& aContentType, nsCaptureParams* aParams)
michael@0 40 {
michael@0 41 if (XRE_GetProcessType() != GeckoProcessType_Default)
michael@0 42 return NS_ERROR_NOT_IMPLEMENTED;
michael@0 43
michael@0 44 mContentType = aContentType;
michael@0 45 mWidth = aParams->width;
michael@0 46 mHeight = aParams->height;
michael@0 47 mCamera = aParams->camera;
michael@0 48
michael@0 49 CameraStreamImpl *impl = CameraStreamImpl::GetInstance(0);
michael@0 50 if (!impl)
michael@0 51 return NS_ERROR_OUT_OF_MEMORY;
michael@0 52 if (impl->Init(mContentType, mCamera, mWidth, mHeight, this)) {
michael@0 53 mWidth = impl->GetWidth();
michael@0 54 mHeight = impl->GetHeight();
michael@0 55 mClosed = false;
michael@0 56 }
michael@0 57 return NS_OK;
michael@0 58 }
michael@0 59
michael@0 60 void AndroidCameraInputStream::ReceiveFrame(char* frame, uint32_t length) {
michael@0 61 {
michael@0 62 mozilla::ReentrantMonitorAutoEnter autoMonitor(mMonitor);
michael@0 63 if (mFrameQueue->GetSize() > MAX_FRAMES_QUEUED) {
michael@0 64 nsMemory::Free(mFrameQueue->PopFront());
michael@0 65 mAvailable -= mFrameSize;
michael@0 66 }
michael@0 67 }
michael@0 68
michael@0 69 mFrameSize = sizeof(RawPacketHeader) + length;
michael@0 70
michael@0 71 char* fullFrame = (char*)nsMemory::Alloc(mFrameSize);
michael@0 72
michael@0 73 if (!fullFrame)
michael@0 74 return;
michael@0 75
michael@0 76 RawPacketHeader* header = (RawPacketHeader*) fullFrame;
michael@0 77 header->packetID = 0xFF;
michael@0 78 header->codecID = 0x595556; // "YUV"
michael@0 79
michael@0 80 // we copy the Y plane, and de-interlace the CrCb
michael@0 81
michael@0 82 uint32_t yFrameSize = mWidth * mHeight;
michael@0 83 uint32_t uvFrameSize = yFrameSize / 4;
michael@0 84
michael@0 85 memcpy(fullFrame + sizeof(RawPacketHeader), frame, yFrameSize);
michael@0 86
michael@0 87 char* uFrame = fullFrame + yFrameSize;
michael@0 88 char* vFrame = fullFrame + yFrameSize + uvFrameSize;
michael@0 89 char* yFrame = frame + yFrameSize;
michael@0 90 for (uint32_t i = 0; i < uvFrameSize; i++) {
michael@0 91 uFrame[i] = yFrame[2 * i + 1];
michael@0 92 vFrame[i] = yFrame[2 * i];
michael@0 93 }
michael@0 94
michael@0 95 {
michael@0 96 mozilla::ReentrantMonitorAutoEnter autoMonitor(mMonitor);
michael@0 97 mAvailable += mFrameSize;
michael@0 98 mFrameQueue->Push((void*)fullFrame);
michael@0 99 }
michael@0 100
michael@0 101 NotifyListeners();
michael@0 102 }
michael@0 103
michael@0 104 NS_IMETHODIMP
michael@0 105 AndroidCameraInputStream::Available(uint64_t *aAvailable)
michael@0 106 {
michael@0 107 mozilla::ReentrantMonitorAutoEnter autoMonitor(mMonitor);
michael@0 108
michael@0 109 *aAvailable = mAvailable;
michael@0 110
michael@0 111 return NS_OK;
michael@0 112 }
michael@0 113
michael@0 114 NS_IMETHODIMP AndroidCameraInputStream::IsNonBlocking(bool *aNonBlock) {
michael@0 115 *aNonBlock = true;
michael@0 116 return NS_OK;
michael@0 117 }
michael@0 118
michael@0 119 NS_IMETHODIMP AndroidCameraInputStream::Read(char *aBuffer, uint32_t aCount, uint32_t *aRead) {
michael@0 120 return ReadSegments(NS_CopySegmentToBuffer, aBuffer, aCount, aRead);
michael@0 121 }
michael@0 122
michael@0 123 NS_IMETHODIMP AndroidCameraInputStream::ReadSegments(nsWriteSegmentFun aWriter, void *aClosure, uint32_t aCount, uint32_t *aRead) {
michael@0 124 *aRead = 0;
michael@0 125
michael@0 126 nsresult rv;
michael@0 127
michael@0 128 if (mAvailable == 0)
michael@0 129 return NS_BASE_STREAM_WOULD_BLOCK;
michael@0 130
michael@0 131 if (aCount > mAvailable)
michael@0 132 aCount = mAvailable;
michael@0 133
michael@0 134 if (!mHeaderSent) {
michael@0 135 CameraStreamImpl *impl = CameraStreamImpl::GetInstance(0);
michael@0 136 RawVideoHeader header;
michael@0 137 header.headerPacketID = 0;
michael@0 138 header.codecID = 0x595556; // "YUV"
michael@0 139 header.majorVersion = 0;
michael@0 140 header.minorVersion = 1;
michael@0 141 header.options = 1 | 1 << 1; // color, 4:2:2
michael@0 142
michael@0 143 header.alphaChannelBpp = 0;
michael@0 144 header.lumaChannelBpp = 8;
michael@0 145 header.chromaChannelBpp = 4;
michael@0 146 header.colorspace = 1;
michael@0 147
michael@0 148 header.frameWidth = mWidth;
michael@0 149 header.frameHeight = mHeight;
michael@0 150 header.aspectNumerator = 1;
michael@0 151 header.aspectDenominator = 1;
michael@0 152
michael@0 153 header.framerateNumerator = impl->GetFps();
michael@0 154 header.framerateDenominator = 1;
michael@0 155
michael@0 156 rv = aWriter(this, aClosure, (const char*)&header, 0, sizeof(RawVideoHeader), aRead);
michael@0 157
michael@0 158 if (NS_FAILED(rv))
michael@0 159 return NS_OK;
michael@0 160
michael@0 161 mHeaderSent = true;
michael@0 162 aCount -= sizeof(RawVideoHeader);
michael@0 163 mAvailable -= sizeof(RawVideoHeader);
michael@0 164 }
michael@0 165
michael@0 166 {
michael@0 167 mozilla::ReentrantMonitorAutoEnter autoMonitor(mMonitor);
michael@0 168 while ((mAvailable > 0) && (aCount >= mFrameSize)) {
michael@0 169 uint32_t readThisTime = 0;
michael@0 170
michael@0 171 char* frame = (char*)mFrameQueue->PopFront();
michael@0 172 rv = aWriter(this, aClosure, (const char*)frame, *aRead, mFrameSize, &readThisTime);
michael@0 173
michael@0 174 if (readThisTime != mFrameSize) {
michael@0 175 mFrameQueue->PushFront((void*)frame);
michael@0 176 return NS_OK;
michael@0 177 }
michael@0 178
michael@0 179 // RawReader does a copy when calling VideoData::Create()
michael@0 180 nsMemory::Free(frame);
michael@0 181
michael@0 182 if (NS_FAILED(rv))
michael@0 183 return NS_OK;
michael@0 184
michael@0 185 aCount -= readThisTime;
michael@0 186 mAvailable -= readThisTime;
michael@0 187 *aRead += readThisTime;
michael@0 188 }
michael@0 189 }
michael@0 190 return NS_OK;
michael@0 191 }
michael@0 192
michael@0 193 NS_IMETHODIMP AndroidCameraInputStream::Close() {
michael@0 194 return CloseWithStatus(NS_OK);
michael@0 195 }
michael@0 196
michael@0 197
michael@0 198 /**
michael@0 199 * must be called on the main (java) thread
michael@0 200 */
michael@0 201 void AndroidCameraInputStream::doClose() {
michael@0 202 NS_ASSERTION(!mClosed, "Camera is already closed");
michael@0 203
michael@0 204 CameraStreamImpl *impl = CameraStreamImpl::GetInstance(0);
michael@0 205 impl->Close();
michael@0 206 mClosed = true;
michael@0 207 }
michael@0 208
michael@0 209
michael@0 210 void AndroidCameraInputStream::NotifyListeners() {
michael@0 211 mozilla::ReentrantMonitorAutoEnter autoMonitor(mMonitor);
michael@0 212
michael@0 213 if (mCallback && (mAvailable > sizeof(RawVideoHeader))) {
michael@0 214 nsCOMPtr<nsIInputStreamCallback> callback;
michael@0 215 if (mCallbackTarget) {
michael@0 216 callback = NS_NewInputStreamReadyEvent(mCallback, mCallbackTarget);
michael@0 217 } else {
michael@0 218 callback = mCallback;
michael@0 219 }
michael@0 220
michael@0 221 NS_ASSERTION(callback, "Shouldn't fail to make the callback!");
michael@0 222
michael@0 223 // Null the callback first because OnInputStreamReady may reenter AsyncWait
michael@0 224 mCallback = nullptr;
michael@0 225 mCallbackTarget = nullptr;
michael@0 226
michael@0 227 callback->OnInputStreamReady(this);
michael@0 228 }
michael@0 229 }
michael@0 230
michael@0 231 NS_IMETHODIMP AndroidCameraInputStream::AsyncWait(nsIInputStreamCallback *aCallback, uint32_t aFlags, uint32_t aRequestedCount, nsIEventTarget *aTarget)
michael@0 232 {
michael@0 233 if (aFlags != 0)
michael@0 234 return NS_ERROR_NOT_IMPLEMENTED;
michael@0 235
michael@0 236 if (mCallback || mCallbackTarget)
michael@0 237 return NS_ERROR_UNEXPECTED;
michael@0 238
michael@0 239 mCallbackTarget = aTarget;
michael@0 240 mCallback = aCallback;
michael@0 241
michael@0 242 // What we are being asked for may be present already
michael@0 243 NotifyListeners();
michael@0 244 return NS_OK;
michael@0 245 }
michael@0 246
michael@0 247
michael@0 248 NS_IMETHODIMP AndroidCameraInputStream::CloseWithStatus(nsresult status)
michael@0 249 {
michael@0 250 AndroidCameraInputStream::doClose();
michael@0 251 return NS_OK;
michael@0 252 }
michael@0 253
michael@0 254 /**
michael@0 255 * AndroidCaptureProvider implementation
michael@0 256 */
michael@0 257
michael@0 258 NS_IMPL_ISUPPORTS0(AndroidCaptureProvider)
michael@0 259
michael@0 260 AndroidCaptureProvider* AndroidCaptureProvider::sInstance = nullptr;
michael@0 261
michael@0 262 AndroidCaptureProvider::AndroidCaptureProvider() {
michael@0 263 }
michael@0 264
michael@0 265 AndroidCaptureProvider::~AndroidCaptureProvider() {
michael@0 266 AndroidCaptureProvider::sInstance = nullptr;
michael@0 267 }
michael@0 268
michael@0 269 nsresult AndroidCaptureProvider::Init(nsACString& aContentType,
michael@0 270 nsCaptureParams* aParams,
michael@0 271 nsIInputStream** aStream) {
michael@0 272
michael@0 273 NS_ENSURE_ARG_POINTER(aParams);
michael@0 274
michael@0 275 NS_ASSERTION(aParams->frameLimit == 0 || aParams->timeLimit == 0,
michael@0 276 "Cannot set both a frame limit and a time limit!");
michael@0 277
michael@0 278 nsRefPtr<AndroidCameraInputStream> stream;
michael@0 279
michael@0 280 if (aContentType.EqualsLiteral("video/x-raw-yuv")) {
michael@0 281 stream = new AndroidCameraInputStream();
michael@0 282 if (stream) {
michael@0 283 nsresult rv = stream->Init(aContentType, aParams);
michael@0 284 if (NS_FAILED(rv))
michael@0 285 return rv;
michael@0 286 }
michael@0 287 else
michael@0 288 return NS_ERROR_OUT_OF_MEMORY;
michael@0 289 } else {
michael@0 290 NS_NOTREACHED("Should not have asked Android for this type!");
michael@0 291 }
michael@0 292 return CallQueryInterface(stream, aStream);
michael@0 293 }
michael@0 294
michael@0 295 already_AddRefed<AndroidCaptureProvider> GetAndroidCaptureProvider() {
michael@0 296 if (!AndroidCaptureProvider::sInstance) {
michael@0 297 AndroidCaptureProvider::sInstance = new AndroidCaptureProvider();
michael@0 298 }
michael@0 299 nsRefPtr<AndroidCaptureProvider> ret = AndroidCaptureProvider::sInstance;
michael@0 300 return ret.forget();
michael@0 301 }

mercurial