|
|
|
@ -194,8 +194,14 @@ status_t Camera3Device::initializeCommonLocked() {
|
|
|
|
|
|
|
|
|
|
mTagMonitor.initialize(mVendorTagId);
|
|
|
|
|
|
|
|
|
|
Vector<int32_t> sessionParamKeys;
|
|
|
|
|
camera_metadata_entry_t sessionKeysEntry = mDeviceInfo.find(
|
|
|
|
|
ANDROID_REQUEST_AVAILABLE_SESSION_KEYS);
|
|
|
|
|
if (sessionKeysEntry.count > 0) {
|
|
|
|
|
sessionParamKeys.insertArrayAt(sessionKeysEntry.data.i32, 0, sessionKeysEntry.count);
|
|
|
|
|
}
|
|
|
|
|
/** Start up request queue thread */
|
|
|
|
|
mRequestThread = new RequestThread(this, mStatusTracker, mInterface);
|
|
|
|
|
mRequestThread = new RequestThread(this, mStatusTracker, mInterface, sessionParamKeys);
|
|
|
|
|
res = mRequestThread->run(String8::format("C3Dev-%s-ReqQueue", mId.string()).string());
|
|
|
|
|
if (res != OK) {
|
|
|
|
|
SET_ERR_L("Unable to start request queue thread: %s (%d)",
|
|
|
|
@ -1104,7 +1110,7 @@ sp<Camera3Device::CaptureRequest> Camera3Device::setUpRequestLocked(
|
|
|
|
|
if (mStatus == STATUS_UNCONFIGURED || mNeedConfig) {
|
|
|
|
|
// This point should only be reached via API1 (API2 must explicitly call configureStreams)
|
|
|
|
|
// so unilaterally select normal operating mode.
|
|
|
|
|
res = configureStreamsLocked(CAMERA3_STREAM_CONFIGURATION_NORMAL_MODE, mSessionParams);
|
|
|
|
|
res = filterParamsAndConfigureLocked(request, CAMERA3_STREAM_CONFIGURATION_NORMAL_MODE);
|
|
|
|
|
// Stream configuration failed. Client might try other configuraitons.
|
|
|
|
|
if (res != OK) {
|
|
|
|
|
CLOGE("Can't set up streams: %s (%d)", strerror(-res), res);
|
|
|
|
@ -1508,11 +1514,20 @@ status_t Camera3Device::configureStreams(const CameraMetadata& sessionParams, in
|
|
|
|
|
Mutex::Autolock il(mInterfaceLock);
|
|
|
|
|
Mutex::Autolock l(mLock);
|
|
|
|
|
|
|
|
|
|
return filterParamsAndConfigureLocked(sessionParams, operatingMode);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
status_t Camera3Device::filterParamsAndConfigureLocked(const CameraMetadata& sessionParams,
|
|
|
|
|
int operatingMode) {
|
|
|
|
|
//Filter out any incoming session parameters
|
|
|
|
|
const CameraMetadata params(sessionParams);
|
|
|
|
|
CameraMetadata filteredParams;
|
|
|
|
|
camera_metadata_entry_t availableSessionKeys = mDeviceInfo.find(
|
|
|
|
|
ANDROID_REQUEST_AVAILABLE_SESSION_KEYS);
|
|
|
|
|
CameraMetadata filteredParams(availableSessionKeys.count);
|
|
|
|
|
camera_metadata_t *meta = const_cast<camera_metadata_t *>(
|
|
|
|
|
filteredParams.getAndLock());
|
|
|
|
|
set_camera_metadata_vendor_id(meta, mVendorTagId);
|
|
|
|
|
filteredParams.unlock(meta);
|
|
|
|
|
if (availableSessionKeys.count > 0) {
|
|
|
|
|
for (size_t i = 0; i < availableSessionKeys.count; i++) {
|
|
|
|
|
camera_metadata_ro_entry entry = params.find(
|
|
|
|
@ -2203,10 +2218,47 @@ void Camera3Device::cancelStreamsConfigurationLocked() {
|
|
|
|
|
// properly clean things up
|
|
|
|
|
internalUpdateStatusLocked(STATUS_UNCONFIGURED);
|
|
|
|
|
mNeedConfig = true;
|
|
|
|
|
|
|
|
|
|
res = mPreparerThread->resume();
|
|
|
|
|
if (res != OK) {
|
|
|
|
|
ALOGE("%s: Camera %s: Preparer thread failed to resume!", __FUNCTION__, mId.string());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool Camera3Device::reconfigureCamera(const CameraMetadata& sessionParams) {
|
|
|
|
|
ATRACE_CALL();
|
|
|
|
|
bool ret = false;
|
|
|
|
|
|
|
|
|
|
Mutex::Autolock il(mInterfaceLock);
|
|
|
|
|
nsecs_t maxExpectedDuration = getExpectedInFlightDuration();
|
|
|
|
|
|
|
|
|
|
Mutex::Autolock l(mLock);
|
|
|
|
|
auto rc = internalPauseAndWaitLocked(maxExpectedDuration);
|
|
|
|
|
if (rc == NO_ERROR) {
|
|
|
|
|
mNeedConfig = true;
|
|
|
|
|
rc = configureStreamsLocked(mOperatingMode, sessionParams, /*notifyRequestThread*/ false);
|
|
|
|
|
if (rc == NO_ERROR) {
|
|
|
|
|
ret = true;
|
|
|
|
|
mPauseStateNotify = false;
|
|
|
|
|
//Moving to active state while holding 'mLock' is important.
|
|
|
|
|
//There could be pending calls to 'create-/deleteStream' which
|
|
|
|
|
//will trigger another stream configuration while the already
|
|
|
|
|
//present streams end up with outstanding buffers that will
|
|
|
|
|
//not get drained.
|
|
|
|
|
internalUpdateStatusLocked(STATUS_ACTIVE);
|
|
|
|
|
} else {
|
|
|
|
|
setErrorStateLocked("%s: Failed to re-configure camera: %d",
|
|
|
|
|
__FUNCTION__, rc);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
ALOGE("%s: Failed to pause streaming: %d", __FUNCTION__, rc);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
status_t Camera3Device::configureStreamsLocked(int operatingMode,
|
|
|
|
|
const CameraMetadata& sessionParams) {
|
|
|
|
|
const CameraMetadata& sessionParams, bool notifyRequestThread) {
|
|
|
|
|
ATRACE_CALL();
|
|
|
|
|
status_t res;
|
|
|
|
|
|
|
|
|
@ -2247,6 +2299,8 @@ status_t Camera3Device::configureStreamsLocked(int operatingMode,
|
|
|
|
|
// Start configuring the streams
|
|
|
|
|
ALOGV("%s: Camera %s: Starting stream configuration", __FUNCTION__, mId.string());
|
|
|
|
|
|
|
|
|
|
mPreparerThread->pause();
|
|
|
|
|
|
|
|
|
|
camera3_stream_configuration config;
|
|
|
|
|
config.operation_mode = mOperatingMode;
|
|
|
|
|
config.num_streams = (mInputStream != NULL) + mOutputStreams.size();
|
|
|
|
@ -2338,7 +2392,9 @@ status_t Camera3Device::configureStreamsLocked(int operatingMode,
|
|
|
|
|
|
|
|
|
|
// Request thread needs to know to avoid using repeat-last-settings protocol
|
|
|
|
|
// across configure_streams() calls
|
|
|
|
|
mRequestThread->configurationComplete(mIsConstrainedHighSpeedConfiguration);
|
|
|
|
|
if (notifyRequestThread) {
|
|
|
|
|
mRequestThread->configurationComplete(mIsConstrainedHighSpeedConfiguration, sessionParams);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
char value[PROPERTY_VALUE_MAX];
|
|
|
|
|
property_get("camera.fifo.disable", value, "0");
|
|
|
|
@ -2376,6 +2432,12 @@ status_t Camera3Device::configureStreamsLocked(int operatingMode,
|
|
|
|
|
// tear down the deleted streams after configure streams.
|
|
|
|
|
mDeletedStreams.clear();
|
|
|
|
|
|
|
|
|
|
auto rc = mPreparerThread->resume();
|
|
|
|
|
if (rc != OK) {
|
|
|
|
|
SET_ERR_L("%s: Camera %s: Preparer thread failed to resume!", __FUNCTION__, mId.string());
|
|
|
|
|
return rc;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return OK;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -3747,7 +3809,7 @@ void Camera3Device::HalInterface::onBufferFreed(
|
|
|
|
|
|
|
|
|
|
Camera3Device::RequestThread::RequestThread(wp<Camera3Device> parent,
|
|
|
|
|
sp<StatusTracker> statusTracker,
|
|
|
|
|
sp<HalInterface> interface) :
|
|
|
|
|
sp<HalInterface> interface, const Vector<int32_t>& sessionParamKeys) :
|
|
|
|
|
Thread(/*canCallJava*/false),
|
|
|
|
|
mParent(parent),
|
|
|
|
|
mStatusTracker(statusTracker),
|
|
|
|
@ -3764,7 +3826,9 @@ Camera3Device::RequestThread::RequestThread(wp<Camera3Device> parent,
|
|
|
|
|
mRepeatingLastFrameNumber(
|
|
|
|
|
hardware::camera2::ICameraDeviceUser::NO_IN_FLIGHT_REPEATING_FRAMES),
|
|
|
|
|
mPrepareVideoStream(false),
|
|
|
|
|
mRequestLatency(kRequestLatencyBinSize) {
|
|
|
|
|
mRequestLatency(kRequestLatencyBinSize),
|
|
|
|
|
mSessionParamKeys(sessionParamKeys),
|
|
|
|
|
mLatestSessionParams(sessionParamKeys.size()) {
|
|
|
|
|
mStatusId = statusTracker->addComponent();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -3777,10 +3841,12 @@ void Camera3Device::RequestThread::setNotificationListener(
|
|
|
|
|
mListener = listener;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Camera3Device::RequestThread::configurationComplete(bool isConstrainedHighSpeed) {
|
|
|
|
|
void Camera3Device::RequestThread::configurationComplete(bool isConstrainedHighSpeed,
|
|
|
|
|
const CameraMetadata& sessionParams) {
|
|
|
|
|
ATRACE_CALL();
|
|
|
|
|
Mutex::Autolock l(mRequestLock);
|
|
|
|
|
mReconfigured = true;
|
|
|
|
|
mLatestSessionParams = sessionParams;
|
|
|
|
|
// Prepare video stream for high speed recording.
|
|
|
|
|
mPrepareVideoStream = isConstrainedHighSpeed;
|
|
|
|
|
}
|
|
|
|
@ -4191,6 +4257,52 @@ nsecs_t Camera3Device::RequestThread::calculateMaxExpectedDuration(const camera_
|
|
|
|
|
return maxExpectedDuration;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool Camera3Device::RequestThread::updateSessionParameters(const CameraMetadata& settings) {
|
|
|
|
|
ATRACE_CALL();
|
|
|
|
|
bool updatesDetected = false;
|
|
|
|
|
|
|
|
|
|
for (auto tag : mSessionParamKeys) {
|
|
|
|
|
camera_metadata_ro_entry entry = settings.find(tag);
|
|
|
|
|
camera_metadata_entry lastEntry = mLatestSessionParams.find(tag);
|
|
|
|
|
|
|
|
|
|
if (entry.count > 0) {
|
|
|
|
|
bool isDifferent = false;
|
|
|
|
|
if (lastEntry.count > 0) {
|
|
|
|
|
// Have a last value, compare to see if changed
|
|
|
|
|
if (lastEntry.type == entry.type &&
|
|
|
|
|
lastEntry.count == entry.count) {
|
|
|
|
|
// Same type and count, compare values
|
|
|
|
|
size_t bytesPerValue = camera_metadata_type_size[lastEntry.type];
|
|
|
|
|
size_t entryBytes = bytesPerValue * lastEntry.count;
|
|
|
|
|
int cmp = memcmp(entry.data.u8, lastEntry.data.u8, entryBytes);
|
|
|
|
|
if (cmp != 0) {
|
|
|
|
|
isDifferent = true;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
// Count or type has changed
|
|
|
|
|
isDifferent = true;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
// No last entry, so always consider to be different
|
|
|
|
|
isDifferent = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (isDifferent) {
|
|
|
|
|
ALOGV("%s: Session parameter tag id %d changed", __FUNCTION__, tag);
|
|
|
|
|
mLatestSessionParams.update(entry);
|
|
|
|
|
updatesDetected = true;
|
|
|
|
|
}
|
|
|
|
|
} else if (lastEntry.count > 0) {
|
|
|
|
|
// Value has been removed
|
|
|
|
|
ALOGV("%s: Session parameter tag id %d removed", __FUNCTION__, tag);
|
|
|
|
|
mLatestSessionParams.erase(tag);
|
|
|
|
|
updatesDetected = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return updatesDetected;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool Camera3Device::RequestThread::threadLoop() {
|
|
|
|
|
ATRACE_CALL();
|
|
|
|
|
status_t res;
|
|
|
|
@ -4217,6 +4329,49 @@ bool Camera3Device::RequestThread::threadLoop() {
|
|
|
|
|
latestRequestId = NAME_NOT_FOUND;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 'mNextRequests' will at this point contain either a set of HFR batched requests
|
|
|
|
|
// or a single request from streaming or burst. In either case the first element
|
|
|
|
|
// should contain the latest camera settings that we need to check for any session
|
|
|
|
|
// parameter updates.
|
|
|
|
|
if (updateSessionParameters(mNextRequests[0].captureRequest->mSettings)) {
|
|
|
|
|
res = OK;
|
|
|
|
|
|
|
|
|
|
//Input stream buffers are already acquired at this point so an input stream
|
|
|
|
|
//will not be able to move to idle state unless we force it.
|
|
|
|
|
if (mNextRequests[0].captureRequest->mInputStream != nullptr) {
|
|
|
|
|
res = mNextRequests[0].captureRequest->mInputStream->forceToIdle();
|
|
|
|
|
if (res != OK) {
|
|
|
|
|
ALOGE("%s: Failed to force idle input stream: %d", __FUNCTION__, res);
|
|
|
|
|
cleanUpFailedRequests(/*sendRequestError*/ false);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (res == OK) {
|
|
|
|
|
sp<StatusTracker> statusTracker = mStatusTracker.promote();
|
|
|
|
|
if (statusTracker != 0) {
|
|
|
|
|
statusTracker->markComponentIdle(mStatusId, Fence::NO_FENCE);
|
|
|
|
|
|
|
|
|
|
sp<Camera3Device> parent = mParent.promote();
|
|
|
|
|
if (parent != nullptr) {
|
|
|
|
|
mReconfigured |= parent->reconfigureCamera(mLatestSessionParams);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
statusTracker->markComponentActive(mStatusId);
|
|
|
|
|
setPaused(false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (mNextRequests[0].captureRequest->mInputStream != nullptr) {
|
|
|
|
|
mNextRequests[0].captureRequest->mInputStream->restoreConfiguredState();
|
|
|
|
|
if (res != OK) {
|
|
|
|
|
ALOGE("%s: Failed to restore configured input stream: %d", __FUNCTION__, res);
|
|
|
|
|
cleanUpFailedRequests(/*sendRequestError*/ false);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Prepare a batch of HAL requests and output buffers.
|
|
|
|
|
res = prepareHalRequests();
|
|
|
|
|
if (res == TIMED_OUT) {
|
|
|
|
@ -4980,7 +5135,7 @@ status_t Camera3Device::RequestThread::addDummyTriggerIds(
|
|
|
|
|
|
|
|
|
|
Camera3Device::PreparerThread::PreparerThread() :
|
|
|
|
|
Thread(/*canCallJava*/false), mListener(nullptr),
|
|
|
|
|
mActive(false), mCancelNow(false) {
|
|
|
|
|
mActive(false), mCancelNow(false), mCurrentMaxCount(0), mCurrentPrepareComplete(false) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Camera3Device::PreparerThread::~PreparerThread() {
|
|
|
|
@ -5031,18 +5186,101 @@ status_t Camera3Device::PreparerThread::prepare(int maxCount, sp<Camera3StreamIn
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// queue up the work
|
|
|
|
|
mPendingStreams.push_back(stream);
|
|
|
|
|
mPendingStreams.emplace(maxCount, stream);
|
|
|
|
|
ALOGV("%s: Stream %d queued for preparing", __FUNCTION__, stream->getId());
|
|
|
|
|
|
|
|
|
|
return OK;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Camera3Device::PreparerThread::pause() {
|
|
|
|
|
ATRACE_CALL();
|
|
|
|
|
|
|
|
|
|
Mutex::Autolock l(mLock);
|
|
|
|
|
|
|
|
|
|
std::unordered_map<int, sp<camera3::Camera3StreamInterface> > pendingStreams;
|
|
|
|
|
pendingStreams.insert(mPendingStreams.begin(), mPendingStreams.end());
|
|
|
|
|
sp<camera3::Camera3StreamInterface> currentStream = mCurrentStream;
|
|
|
|
|
int currentMaxCount = mCurrentMaxCount;
|
|
|
|
|
mPendingStreams.clear();
|
|
|
|
|
mCancelNow = true;
|
|
|
|
|
while (mActive) {
|
|
|
|
|
auto res = mThreadActiveSignal.waitRelative(mLock, kActiveTimeout);
|
|
|
|
|
if (res == TIMED_OUT) {
|
|
|
|
|
ALOGE("%s: Timed out waiting on prepare thread!", __FUNCTION__);
|
|
|
|
|
return;
|
|
|
|
|
} else if (res != OK) {
|
|
|
|
|
ALOGE("%s: Encountered an error: %d waiting on prepare thread!", __FUNCTION__, res);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//Check whether the prepare thread was able to complete the current
|
|
|
|
|
//stream. In case work is still pending emplace it along with the rest
|
|
|
|
|
//of the streams in the pending list.
|
|
|
|
|
if (currentStream != nullptr) {
|
|
|
|
|
if (!mCurrentPrepareComplete) {
|
|
|
|
|
pendingStreams.emplace(currentMaxCount, currentStream);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
mPendingStreams.insert(pendingStreams.begin(), pendingStreams.end());
|
|
|
|
|
for (const auto& it : mPendingStreams) {
|
|
|
|
|
it.second->cancelPrepare();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
status_t Camera3Device::PreparerThread::resume() {
|
|
|
|
|
ATRACE_CALL();
|
|
|
|
|
status_t res;
|
|
|
|
|
|
|
|
|
|
Mutex::Autolock l(mLock);
|
|
|
|
|
sp<NotificationListener> listener = mListener.promote();
|
|
|
|
|
|
|
|
|
|
if (mActive) {
|
|
|
|
|
ALOGE("%s: Trying to resume an already active prepare thread!", __FUNCTION__);
|
|
|
|
|
return NO_INIT;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
auto it = mPendingStreams.begin();
|
|
|
|
|
for (; it != mPendingStreams.end();) {
|
|
|
|
|
res = it->second->startPrepare(it->first);
|
|
|
|
|
if (res == OK) {
|
|
|
|
|
if (listener != NULL) {
|
|
|
|
|
listener->notifyPrepared(it->second->getId());
|
|
|
|
|
}
|
|
|
|
|
it = mPendingStreams.erase(it);
|
|
|
|
|
} else if (res != NOT_ENOUGH_DATA) {
|
|
|
|
|
ALOGE("%s: Unable to start preparer stream: %d (%s)", __FUNCTION__,
|
|
|
|
|
res, strerror(-res));
|
|
|
|
|
it = mPendingStreams.erase(it);
|
|
|
|
|
} else {
|
|
|
|
|
it++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (mPendingStreams.empty()) {
|
|
|
|
|
return OK;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
res = Thread::run("C3PrepThread", PRIORITY_BACKGROUND);
|
|
|
|
|
if (res != OK) {
|
|
|
|
|
ALOGE("%s: Unable to start preparer stream: %d (%s)",
|
|
|
|
|
__FUNCTION__, res, strerror(-res));
|
|
|
|
|
return res;
|
|
|
|
|
}
|
|
|
|
|
mCancelNow = false;
|
|
|
|
|
mActive = true;
|
|
|
|
|
ALOGV("%s: Preparer stream started", __FUNCTION__);
|
|
|
|
|
|
|
|
|
|
return OK;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
status_t Camera3Device::PreparerThread::clear() {
|
|
|
|
|
ATRACE_CALL();
|
|
|
|
|
Mutex::Autolock l(mLock);
|
|
|
|
|
|
|
|
|
|
for (const auto& stream : mPendingStreams) {
|
|
|
|
|
stream->cancelPrepare();
|
|
|
|
|
for (const auto& it : mPendingStreams) {
|
|
|
|
|
it.second->cancelPrepare();
|
|
|
|
|
}
|
|
|
|
|
mPendingStreams.clear();
|
|
|
|
|
mCancelNow = true;
|
|
|
|
@ -5067,12 +5305,15 @@ bool Camera3Device::PreparerThread::threadLoop() {
|
|
|
|
|
// threadLoop _must not_ re-acquire mLock after it sets mActive to false; would
|
|
|
|
|
// cause deadlock with prepare()'s requestExitAndWait triggered by !mActive.
|
|
|
|
|
mActive = false;
|
|
|
|
|
mThreadActiveSignal.signal();
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Get next stream to prepare
|
|
|
|
|
auto it = mPendingStreams.begin();
|
|
|
|
|
mCurrentStream = *it;
|
|
|
|
|
mCurrentStream = it->second;
|
|
|
|
|
mCurrentMaxCount = it->first;
|
|
|
|
|
mCurrentPrepareComplete = false;
|
|
|
|
|
mPendingStreams.erase(it);
|
|
|
|
|
ATRACE_ASYNC_BEGIN("stream prepare", mCurrentStream->getId());
|
|
|
|
|
ALOGV("%s: Preparing stream %d", __FUNCTION__, mCurrentStream->getId());
|
|
|
|
@ -5107,6 +5348,7 @@ bool Camera3Device::PreparerThread::threadLoop() {
|
|
|
|
|
|
|
|
|
|
ATRACE_ASYNC_END("stream prepare", mCurrentStream->getId());
|
|
|
|
|
mCurrentStream.clear();
|
|
|
|
|
mCurrentPrepareComplete = true;
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|