зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1152500 - Fix how stop recording may be handled out-of-order. r=dhylands
This commit is contained in:
Родитель
a7226ca634
Коммит
dca0017a8d
|
@ -201,6 +201,7 @@ nsDOMCameraControl::nsDOMCameraControl(uint32_t aCameraId,
|
|||
, mGetCameraPromise(aPromise)
|
||||
, mWindow(aWindow)
|
||||
, mPreviewState(CameraControlListener::kPreviewStopped)
|
||||
, mRecording(false)
|
||||
, mSetInitialConfig(false)
|
||||
{
|
||||
DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
|
||||
|
@ -742,31 +743,22 @@ nsDOMCameraControl::StartRecording(const CameraStartRecordingOptions& aOptions,
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
if (mStartRecordingPromise) {
|
||||
if (mStartRecordingPromise || mRecording) {
|
||||
promise->MaybeReject(NS_ERROR_IN_PROGRESS);
|
||||
return promise.forget();
|
||||
}
|
||||
|
||||
NotifyRecordingStatusChange(NS_LITERAL_STRING("starting"));
|
||||
|
||||
#ifdef MOZ_B2G
|
||||
if (!mAudioChannelAgent) {
|
||||
mAudioChannelAgent = do_CreateInstance("@mozilla.org/audiochannelagent;1");
|
||||
if (mAudioChannelAgent) {
|
||||
// Camera app will stop recording when it falls to the background, so no callback is necessary.
|
||||
mAudioChannelAgent->Init(mWindow, (int32_t)AudioChannel::Content, nullptr);
|
||||
// Video recording doesn't output any sound, so it's not necessary to check canPlay.
|
||||
int32_t canPlay;
|
||||
mAudioChannelAgent->StartPlaying(&canPlay);
|
||||
}
|
||||
aRv = NotifyRecordingStatusChange(NS_LITERAL_STRING("starting"));
|
||||
if (aRv.Failed()) {
|
||||
return nullptr;
|
||||
}
|
||||
#endif
|
||||
|
||||
mDSFileDescriptor = new DeviceStorageFileDescriptor();
|
||||
nsRefPtr<DOMRequest> request = aStorageArea.CreateFileDescriptor(aFilename,
|
||||
mDSFileDescriptor.get(),
|
||||
aRv);
|
||||
if (aRv.Failed()) {
|
||||
NotifyRecordingStatusChange(NS_LITERAL_STRING("shutdown"));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
@ -775,10 +767,12 @@ nsDOMCameraControl::StartRecording(const CameraStartRecordingOptions& aOptions,
|
|||
|
||||
EventListenerManager* elm = request->GetOrCreateListenerManager();
|
||||
if (!elm) {
|
||||
NotifyRecordingStatusChange(NS_LITERAL_STRING("shutdown"));
|
||||
aRv.Throw(NS_ERROR_UNEXPECTED);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
mRecording = true;
|
||||
nsCOMPtr<nsIDOMEventListener> listener = new StartRecordingHelper(this);
|
||||
elm->AddEventListener(NS_LITERAL_STRING("success"), listener, false, false);
|
||||
elm->AddEventListener(NS_LITERAL_STRING("error"), listener, false, false);
|
||||
|
@ -792,6 +786,10 @@ nsDOMCameraControl::OnCreatedFileDescriptor(bool aSucceeded)
|
|||
|
||||
if (!mCameraControl) {
|
||||
rv = NS_ERROR_NOT_AVAILABLE;
|
||||
} else if (!mRecording) {
|
||||
// Race condition where StopRecording comes in before we issue
|
||||
// the start recording request to Gonk
|
||||
rv = NS_ERROR_ABORT;
|
||||
} else if (aSucceeded && mDSFileDescriptor->mFileDescriptor.IsValid()) {
|
||||
ICameraControl::StartRecordingOptions o;
|
||||
|
||||
|
@ -823,13 +821,8 @@ nsDOMCameraControl::StopRecording(ErrorResult& aRv)
|
|||
DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
|
||||
THROW_IF_NO_CAMERACONTROL();
|
||||
|
||||
#ifdef MOZ_B2G
|
||||
if (mAudioChannelAgent) {
|
||||
mAudioChannelAgent->StopPlaying();
|
||||
mAudioChannelAgent = nullptr;
|
||||
}
|
||||
#endif
|
||||
|
||||
ReleaseAudioChannelAgent();
|
||||
mRecording = false;
|
||||
aRv = mCameraControl->StopRecording();
|
||||
}
|
||||
|
||||
|
@ -1039,15 +1032,50 @@ nsDOMCameraControl::Shutdown()
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsDOMCameraControl::ReleaseAudioChannelAgent()
|
||||
{
|
||||
#ifdef MOZ_B2G
|
||||
if (mAudioChannelAgent) {
|
||||
mAudioChannelAgent->StopPlaying();
|
||||
mAudioChannelAgent = nullptr;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsDOMCameraControl::NotifyRecordingStatusChange(const nsString& aMsg)
|
||||
{
|
||||
NS_ENSURE_TRUE(mWindow, NS_ERROR_FAILURE);
|
||||
|
||||
return MediaManager::NotifyRecordingStatusChange(mWindow,
|
||||
aMsg,
|
||||
true /* aIsAudio */,
|
||||
true /* aIsVideo */);
|
||||
if (aMsg.EqualsLiteral("shutdown")) {
|
||||
ReleaseAudioChannelAgent();
|
||||
}
|
||||
|
||||
nsresult rv = MediaManager::NotifyRecordingStatusChange(mWindow,
|
||||
aMsg,
|
||||
true /* aIsAudio */,
|
||||
true /* aIsVideo */);
|
||||
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
#ifdef MOZ_B2G
|
||||
if (aMsg.EqualsLiteral("starting") && !mAudioChannelAgent) {
|
||||
mAudioChannelAgent = do_CreateInstance("@mozilla.org/audiochannelagent;1");
|
||||
if (!mAudioChannelAgent) {
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
// Camera app will stop recording when it falls to the background, so no callback is necessary.
|
||||
mAudioChannelAgent->Init(mWindow, (int32_t)AudioChannel::Content, nullptr);
|
||||
// Video recording doesn't output any sound, so it's not necessary to check canPlay.
|
||||
int32_t canPlay;
|
||||
mAudioChannelAgent->StartPlaying(&canPlay);
|
||||
}
|
||||
#endif
|
||||
return rv;
|
||||
}
|
||||
|
||||
already_AddRefed<Promise>
|
||||
|
@ -1231,7 +1259,7 @@ nsDOMCameraControl::OnRecorderStateChange(CameraControlListener::RecorderState a
|
|||
int32_t aArg, int32_t aTrackNum)
|
||||
{
|
||||
// For now, we do nothing with 'aStatus' and 'aTrackNum'.
|
||||
DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
|
||||
DOM_CAMERA_LOGT("%s:%d : this=%p, state=%u\n", __func__, __LINE__, this, aState);
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
ErrorResult ignored;
|
||||
|
@ -1424,7 +1452,7 @@ nsDOMCameraControl::OnTakePictureComplete(nsIDOMBlob* aPicture)
|
|||
void
|
||||
nsDOMCameraControl::OnUserError(CameraControlListener::UserContext aContext, nsresult aError)
|
||||
{
|
||||
DOM_CAMERA_LOGI("DOM OnUserError : this=%paContext=%u, aError=0x%x\n",
|
||||
DOM_CAMERA_LOGI("DOM OnUserError : this=%p, aContext=%u, aError=0x%x\n",
|
||||
this, aContext, aError);
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
|
@ -1477,6 +1505,8 @@ nsDOMCameraControl::OnUserError(CameraControlListener::UserContext aContext, nsr
|
|||
|
||||
case CameraControlListener::kInStartRecording:
|
||||
promise = mStartRecordingPromise.forget();
|
||||
mRecording = false;
|
||||
NotifyRecordingStatusChange(NS_LITERAL_STRING("shutdown"));
|
||||
break;
|
||||
|
||||
case CameraControlListener::kInStartFaceDetection:
|
||||
|
|
|
@ -182,6 +182,7 @@ protected:
|
|||
bool IsWindowStillActive();
|
||||
nsresult SelectPreviewSize(const dom::CameraSize& aRequestedPreviewSize, ICameraControl::Size& aSelectedPreviewSize);
|
||||
|
||||
void ReleaseAudioChannelAgent();
|
||||
nsresult NotifyRecordingStatusChange(const nsString& aMsg);
|
||||
|
||||
already_AddRefed<dom::Promise> CreatePromise(ErrorResult& aRv);
|
||||
|
@ -223,7 +224,7 @@ protected:
|
|||
dom::CameraStartRecordingOptions mOptions;
|
||||
nsRefPtr<DeviceStorageFileDescriptor> mDSFileDescriptor;
|
||||
DOMCameraControlListener::PreviewState mPreviewState;
|
||||
|
||||
bool mRecording;
|
||||
bool mSetInitialConfig;
|
||||
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
|
|
|
@ -19,11 +19,18 @@ var baseConfig = {
|
|||
mode: 'video',
|
||||
};
|
||||
|
||||
var testFilePath = 'test.3gp';
|
||||
var storage = navigator.getDeviceStorage("videos");
|
||||
|
||||
suite.test('recording', function() {
|
||||
storage.delete("test.3gp");
|
||||
function cleanup()
|
||||
{
|
||||
return storage.delete(testFilePath).then(function(p) {
|
||||
}, function(e) {
|
||||
Promise.resolve();
|
||||
});
|
||||
}
|
||||
|
||||
suite.test('recording', function() {
|
||||
function startRecording(p) {
|
||||
var eventPromise = new Promise(function(resolve, reject) {
|
||||
function onEvent(evt) {
|
||||
|
@ -34,7 +41,7 @@ suite.test('recording', function() {
|
|||
suite.camera.addEventListener('recorderstatechange', onEvent);
|
||||
});
|
||||
|
||||
var domPromise = suite.camera.startRecording({}, storage, "test.3gp");
|
||||
var domPromise = suite.camera.startRecording({}, storage, testFilePath);
|
||||
return Promise.all([domPromise, eventPromise]);
|
||||
}
|
||||
|
||||
|
@ -61,11 +68,91 @@ suite.test('recording', function() {
|
|||
}
|
||||
|
||||
return suite.getCamera(undefined, baseConfig)
|
||||
.then(startRecording, suite.rejectGetCamera)
|
||||
.then(cleanup, suite.rejectGetCamera)
|
||||
.then(startRecording)
|
||||
.then(stopRecording, suite.rejectStartRecording)
|
||||
.catch(suite.rejectStopRecording);
|
||||
});
|
||||
|
||||
// bug 1152500
|
||||
suite.test('interrupt-record', function() {
|
||||
function startRecording(p) {
|
||||
var startPromise = suite.camera.startRecording({}, storage, testFilePath);
|
||||
suite.camera.stopRecording();
|
||||
return startPromise;
|
||||
}
|
||||
|
||||
function rejectStartRecording(e) {
|
||||
ok(e.name === 'NS_ERROR_ABORT', 'onError called correctly on startRecording interrupted: ' + e);
|
||||
}
|
||||
|
||||
return suite.getCamera(undefined, baseConfig)
|
||||
.then(cleanup, suite.rejectGetCamera)
|
||||
.then(startRecording)
|
||||
.then(suite.expectedRejectStartRecording, rejectStartRecording);
|
||||
});
|
||||
|
||||
// bug 1152500
|
||||
suite.test('already-initiated-recording', function() {
|
||||
function startRecording(p) {
|
||||
return new Promise(function(resolve, reject) {
|
||||
var firstCall = false;
|
||||
var secondCall = false;
|
||||
|
||||
function end() {
|
||||
if (firstCall && secondCall) {
|
||||
resolve();
|
||||
}
|
||||
}
|
||||
|
||||
suite.camera.startRecording({}, storage, testFilePath).then(function(p) {
|
||||
ok(true, "First call to startRecording() succeeded");
|
||||
firstCall = true;
|
||||
end();
|
||||
}, function(e) {
|
||||
ok(false, "First call to startRecording() failed unexpectedly with: " + e);
|
||||
firstCall = true;
|
||||
end();
|
||||
});
|
||||
|
||||
suite.camera.startRecording({}, storage, testFilePath).then(function(p) {
|
||||
ok(false, "Second call to startRecording() succeeded unexpectedly");
|
||||
secondCall = true;
|
||||
end();
|
||||
}, function(e) {
|
||||
ok(e.name === 'NS_ERROR_IN_PROGRESS', "Second call to startRecording() failed expectedly with: " + e);
|
||||
secondCall = true;
|
||||
end();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
return suite.getCamera(undefined, baseConfig)
|
||||
.then(cleanup, suite.rejectGetCamera)
|
||||
.then(startRecording);
|
||||
});
|
||||
|
||||
// bug 1152500
|
||||
suite.test('already-started-recording', function() {
|
||||
function startRecording(p) {
|
||||
return suite.camera.startRecording({}, storage, testFilePath);
|
||||
}
|
||||
|
||||
function startRecordingAgain(p) {
|
||||
return suite.camera.startRecording({}, storage, testFilePath);
|
||||
}
|
||||
|
||||
function rejectStartRecordingAgain(e) {
|
||||
ok(e.name === 'NS_ERROR_IN_PROGRESS', "Second call to startRecording() failed expectedly with: " + e);
|
||||
}
|
||||
|
||||
return suite.getCamera(undefined, baseConfig)
|
||||
.then(cleanup, suite.rejectGetCamera)
|
||||
.then(startRecording)
|
||||
.then(startRecordingAgain, suite.rejectStartRecording)
|
||||
.then(suite.expectedRejectStartRecording, rejectStartRecordingAgain)
|
||||
});
|
||||
|
||||
suite.setup()
|
||||
.then(suite.run);
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче