Merge "mpeg2ts: MPEG2TSExtractor detects A/V streams until timed out" into mnc-dev
diff --git a/camera/Android.mk b/camera/Android.mk
index 4c4700b..471cb0d 100644
--- a/camera/Android.mk
+++ b/camera/Android.mk
@@ -28,6 +28,7 @@
ICameraClient.cpp \
ICameraService.cpp \
ICameraServiceListener.cpp \
+ ICameraServiceProxy.cpp \
ICameraRecordingProxy.cpp \
ICameraRecordingProxyListener.cpp \
camera2/ICameraDeviceUser.cpp \
diff --git a/camera/ICameraService.cpp b/camera/ICameraService.cpp
index a02dbe2..0071e70 100644
--- a/camera/ICameraService.cpp
+++ b/camera/ICameraService.cpp
@@ -494,7 +494,8 @@
__FUNCTION__, len);
return FAILED_TRANSACTION;
}
- int32_t events[len] = {};
+ int32_t events[len];
+ memset(events, 0, sizeof(int32_t) * len);
status_t status = data.read(events, sizeof(int32_t) * len);
if (status != NO_ERROR) {
ALOGE("%s: Received poorly formatted binder request: notifySystemEvent.",
diff --git a/camera/ICameraServiceProxy.cpp b/camera/ICameraServiceProxy.cpp
new file mode 100644
index 0000000..06a5afb
--- /dev/null
+++ b/camera/ICameraServiceProxy.cpp
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "BpCameraServiceProxy"
+
+#include <stdint.h>
+
+#include <binder/Parcel.h>
+
+#include <camera/ICameraServiceProxy.h>
+
+namespace android {
+
+class BpCameraServiceProxy: public BpInterface<ICameraServiceProxy> {
+public:
+ BpCameraServiceProxy(const sp<IBinder>& impl) : BpInterface<ICameraServiceProxy>(impl) {}
+
+ virtual void pingForUserUpdate() {
+ Parcel data, reply;
+ data.writeInterfaceToken(ICameraServiceProxy::getInterfaceDescriptor());
+ remote()->transact(BnCameraServiceProxy::PING_FOR_USER_UPDATE, data, &reply,
+ IBinder::FLAG_ONEWAY);
+ }
+};
+
+
+IMPLEMENT_META_INTERFACE(CameraServiceProxy, "android.hardware.ICameraServiceProxy");
+
+status_t BnCameraServiceProxy::onTransact(uint32_t code, const Parcel& data, Parcel* reply,
+ uint32_t flags) {
+ switch(code) {
+ case PING_FOR_USER_UPDATE: {
+ CHECK_INTERFACE(ICameraServiceProxy, data, reply);
+ pingForUserUpdate();
+ return NO_ERROR;
+ } break;
+ default:
+ return BBinder::onTransact(code, data, reply, flags);
+ }
+}
+}; // namespace android
+
diff --git a/include/camera/ICameraServiceProxy.h b/include/camera/ICameraServiceProxy.h
new file mode 100644
index 0000000..12a555f
--- /dev/null
+++ b/include/camera/ICameraServiceProxy.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HARDWARE_ICAMERASERVICEPROXY_H
+#define ANDROID_HARDWARE_ICAMERASERVICEPROXY_H
+
+#include <utils/RefBase.h>
+#include <binder/IInterface.h>
+#include <binder/Parcel.h>
+
+namespace android {
+
+class ICameraServiceProxy : public IInterface {
+public:
+ enum {
+ PING_FOR_USER_UPDATE = IBinder::FIRST_CALL_TRANSACTION,
+ };
+
+ DECLARE_META_INTERFACE(CameraServiceProxy);
+
+ virtual void pingForUserUpdate() = 0;
+};
+
+class BnCameraServiceProxy: public BnInterface<ICameraServiceProxy>
+{
+public:
+ virtual status_t onTransact( uint32_t code,
+ const Parcel& data,
+ Parcel* reply,
+ uint32_t flags = 0);
+};
+
+
+
+}; // namespace android
+
+#endif // ANDROID_HARDWARE_ICAMERASERVICEPROXY_H
+
+
diff --git a/include/media/AudioSystem.h b/include/media/AudioSystem.h
index 3241e9c..26cffa6 100644
--- a/include/media/AudioSystem.h
+++ b/include/media/AudioSystem.h
@@ -158,6 +158,9 @@
// or no HW sync source is used.
static audio_hw_sync_t getAudioHwSyncForSession(audio_session_t sessionId);
+ // Indicate JAVA services are ready (scheduling, power management ...)
+ static status_t systemReady();
+
// Events used to synchronize actions between audio sessions.
// For instance SYNC_EVENT_PRESENTATION_COMPLETE can be used to delay recording start until
// playback is complete on another audio session.
diff --git a/include/media/IAudioFlinger.h b/include/media/IAudioFlinger.h
index 3f7fd09..5051aff 100644
--- a/include/media/IAudioFlinger.h
+++ b/include/media/IAudioFlinger.h
@@ -243,6 +243,9 @@
/* Get the HW synchronization source used for an audio session */
virtual audio_hw_sync_t getAudioHwSyncForSession(audio_session_t sessionId) = 0;
+
+ /* Indicate JAVA services are ready (scheduling, power management ...) */
+ virtual status_t systemReady() = 0;
};
diff --git a/include/media/stagefright/MediaCodecList.h b/include/media/stagefright/MediaCodecList.h
index ce34338..df5e519 100644
--- a/include/media/stagefright/MediaCodecList.h
+++ b/include/media/stagefright/MediaCodecList.h
@@ -32,6 +32,8 @@
namespace android {
+extern const char *kMaxEncoderInputBuffers;
+
struct AMessage;
struct MediaCodecList : public BnMediaCodecList {
diff --git a/media/libmedia/AudioSystem.cpp b/media/libmedia/AudioSystem.cpp
index 4c2e77b..6c2c226 100644
--- a/media/libmedia/AudioSystem.cpp
+++ b/media/libmedia/AudioSystem.cpp
@@ -423,6 +423,13 @@
return af->getAudioHwSyncForSession(sessionId);
}
+status_t AudioSystem::systemReady()
+{
+ const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger();
+ if (af == 0) return NO_INIT;
+ return af->systemReady();
+}
+
// ---------------------------------------------------------------------------
diff --git a/media/libmedia/IAudioFlinger.cpp b/media/libmedia/IAudioFlinger.cpp
index d722fe9..a3f014b 100644
--- a/media/libmedia/IAudioFlinger.cpp
+++ b/media/libmedia/IAudioFlinger.cpp
@@ -80,7 +80,8 @@
RELEASE_AUDIO_PATCH,
LIST_AUDIO_PATCHES,
SET_AUDIO_PORT_CONFIG,
- GET_AUDIO_HW_SYNC
+ GET_AUDIO_HW_SYNC,
+ SYSTEM_READY
};
#define MAX_ITEMS_PER_LIST 1024
@@ -903,6 +904,12 @@
}
return (audio_hw_sync_t)reply.readInt32();
}
+ virtual status_t systemReady()
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor());
+ return remote()->transact(SYSTEM_READY, data, &reply, IBinder::FLAG_ONEWAY);
+ }
};
IMPLEMENT_META_INTERFACE(AudioFlinger, "android.media.IAudioFlinger");
@@ -1396,6 +1403,11 @@
reply->writeInt32(getAudioHwSyncForSession((audio_session_t)data.readInt32()));
return NO_ERROR;
} break;
+ case SYSTEM_READY: {
+ CHECK_INTERFACE(IAudioFlinger, data, reply);
+ systemReady();
+ return NO_ERROR;
+ } break;
default:
return BBinder::onTransact(code, data, reply, flags);
}
diff --git a/media/libmedia/JetPlayer.cpp b/media/libmedia/JetPlayer.cpp
index 271be0c..34deb59 100644
--- a/media/libmedia/JetPlayer.cpp
+++ b/media/libmedia/JetPlayer.cpp
@@ -85,12 +85,18 @@
// create the output AudioTrack
mAudioTrack = new AudioTrack();
- mAudioTrack->set(AUDIO_STREAM_MUSIC, //TODO parameterize this
+ status_t status = mAudioTrack->set(AUDIO_STREAM_MUSIC, //TODO parameterize this
pLibConfig->sampleRate,
AUDIO_FORMAT_PCM_16_BIT,
audio_channel_out_mask_from_count(pLibConfig->numChannels),
(size_t) mTrackBufferSize,
AUDIO_OUTPUT_FLAG_NONE);
+ if (status != OK) {
+ ALOGE("JetPlayer::init(): Error initializing JET library; AudioTrack error %d", status);
+ mAudioTrack.clear();
+ mState = EAS_STATE_ERROR;
+ return EAS_FAILURE;
+ }
// create render and playback thread
{
diff --git a/media/libmediaplayerservice/MediaPlayerService.cpp b/media/libmediaplayerservice/MediaPlayerService.cpp
index 891a9e9..efbc0d6 100644
--- a/media/libmediaplayerservice/MediaPlayerService.cpp
+++ b/media/libmediaplayerservice/MediaPlayerService.cpp
@@ -1628,6 +1628,7 @@
if ((t == 0) || (t->initCheck() != NO_ERROR)) {
ALOGE("Unable to create audio track");
delete newcbd;
+ // t goes out of scope, so reference count drops to zero
return NO_INIT;
} else {
// successful AudioTrack initialization implies a legacy stream type was generated
diff --git a/media/libmediaplayerservice/nuplayer/GenericSource.cpp b/media/libmediaplayerservice/nuplayer/GenericSource.cpp
index 5e7b644..88a7745 100644
--- a/media/libmediaplayerservice/nuplayer/GenericSource.cpp
+++ b/media/libmediaplayerservice/nuplayer/GenericSource.cpp
@@ -1510,17 +1510,7 @@
mVideoTimeUs = timeUs;
}
- // formatChange && seeking: track whose source is changed during selection
- // formatChange && !seeking: track whose source is not changed during selection
- // !formatChange: normal seek
- if ((seeking || formatChange)
- && (trackType == MEDIA_TRACK_TYPE_AUDIO
- || trackType == MEDIA_TRACK_TYPE_VIDEO)) {
- ATSParser::DiscontinuityType type = (formatChange && seeking)
- ? ATSParser::DISCONTINUITY_FORMATCHANGE
- : ATSParser::DISCONTINUITY_NONE;
- track->mPackets->queueDiscontinuity( type, NULL, true /* discard */);
- }
+ queueDiscontinuityIfNeeded(seeking, formatChange, trackType, track);
sp<ABuffer> buffer = mediaBufferToABuffer(
mbuf, trackType, seekTimeUs, actualTimeUs);
@@ -1538,10 +1528,26 @@
false /* discard */);
#endif
} else {
+ queueDiscontinuityIfNeeded(seeking, formatChange, trackType, track);
track->mPackets->signalEOS(err);
break;
}
}
}
+void NuPlayer::GenericSource::queueDiscontinuityIfNeeded(
+ bool seeking, bool formatChange, media_track_type trackType, Track *track) {
+ // formatChange && seeking: track whose source is changed during selection
+ // formatChange && !seeking: track whose source is not changed during selection
+ // !formatChange: normal seek
+ if ((seeking || formatChange)
+ && (trackType == MEDIA_TRACK_TYPE_AUDIO
+ || trackType == MEDIA_TRACK_TYPE_VIDEO)) {
+ ATSParser::DiscontinuityType type = (formatChange && seeking)
+ ? ATSParser::DISCONTINUITY_FORMATCHANGE
+ : ATSParser::DISCONTINUITY_NONE;
+ track->mPackets->queueDiscontinuity(type, NULL /* extra */, true /* discard */);
+ }
+}
+
} // namespace android
diff --git a/media/libmediaplayerservice/nuplayer/GenericSource.h b/media/libmediaplayerservice/nuplayer/GenericSource.h
index 7fab051..0a75e4c 100644
--- a/media/libmediaplayerservice/nuplayer/GenericSource.h
+++ b/media/libmediaplayerservice/nuplayer/GenericSource.h
@@ -200,6 +200,9 @@
media_track_type trackType,
int64_t seekTimeUs = -1ll, int64_t *actualTimeUs = NULL, bool formatChange = false);
+ void queueDiscontinuityIfNeeded(
+ bool seeking, bool formatChange, media_track_type trackType, Track *track);
+
void schedulePollBuffering();
void cancelPollBuffering();
void restartPollBuffering();
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
index 96a7adb..c8f289c 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
@@ -1140,6 +1140,22 @@
ALOGV("kWhatSeek seekTimeUs=%lld us, needNotify=%d",
(long long)seekTimeUs, needNotify);
+ if (!mStarted) {
+ // Seek before the player is started. In order to preview video,
+ // need to start the player and pause it. This branch is called
+ // only once if needed. After the player is started, any seek
+ // operation will go through normal path.
+ // All cases, including audio-only, are handled in the same way
+ // for the sake of simplicity.
+ onStart(seekTimeUs);
+ onPause();
+ mPausedByClient = true;
+ if (needNotify) {
+ notifyDriverSeekComplete();
+ }
+ break;
+ }
+
mDeferredActions.push_back(
new FlushDecoderAction(FLUSH_CMD_FLUSH /* audio */,
FLUSH_CMD_FLUSH /* video */));
@@ -1233,13 +1249,16 @@
return OK;
}
-void NuPlayer::onStart() {
+void NuPlayer::onStart(int64_t startPositionUs) {
mOffloadAudio = false;
mAudioEOS = false;
mVideoEOS = false;
mStarted = true;
mSource->start();
+ if (startPositionUs > 0) {
+ performSeek(startPositionUs);
+ }
uint32_t flags = 0;
@@ -1895,11 +1914,15 @@
void NuPlayer::finishResume() {
if (mResumePending) {
mResumePending = false;
- if (mDriver != NULL) {
- sp<NuPlayerDriver> driver = mDriver.promote();
- if (driver != NULL) {
- driver->notifySeekComplete();
- }
+ notifyDriverSeekComplete();
+ }
+}
+
+void NuPlayer::notifyDriverSeekComplete() {
+ if (mDriver != NULL) {
+ sp<NuPlayerDriver> driver = mDriver.promote();
+ if (driver != NULL) {
+ driver->notifySeekComplete();
}
}
}
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.h b/media/libmediaplayerservice/nuplayer/NuPlayer.h
index f417f48..d7aa830 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayer.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayer.h
@@ -233,7 +233,7 @@
void handleFlushComplete(bool audio, bool isDecoder);
void finishFlushIfPossible();
- void onStart();
+ void onStart(int64_t startPositionUs = -1);
void onResume();
void onPause();
@@ -242,6 +242,7 @@
void flushDecoder(bool audio, bool needShutdown);
void finishResume();
+ void notifyDriverSeekComplete();
void postScanSources();
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
index 376c93a..d169964 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
@@ -414,6 +414,11 @@
sp<ABuffer> buffer;
mCodec->getInputBuffer(index, &buffer);
+ if (buffer == NULL) {
+ handleError(UNKNOWN_ERROR);
+ return false;
+ }
+
if (index >= mInputBuffers.size()) {
for (size_t i = mInputBuffers.size(); i <= index; ++i) {
mInputBuffers.add();
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
index 231f2e1..84ae25e 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
@@ -397,23 +397,13 @@
switch (mState) {
case STATE_PREPARED:
case STATE_STOPPED_AND_PREPARED:
- {
- mStartupSeekTimeUs = seekTimeUs;
- // pretend that the seek completed. It will actually happen when starting playback.
- // TODO: actually perform the seek here, so the player is ready to go at the new
- // location
- notifySeekComplete_l();
- break;
- }
-
- case STATE_RUNNING:
case STATE_PAUSED:
+ mStartupSeekTimeUs = seekTimeUs;
+ // fall through.
+ case STATE_RUNNING:
{
mAtEOS = false;
mSeekInProgress = true;
- if (mState == STATE_PAUSED) {
- mStartupSeekTimeUs = seekTimeUs;
- }
// seeks can take a while, so we essentially paused
notifyListener_l(MEDIA_PAUSED);
mPlayer->seekToAsync(seekTimeUs, true /* needNotify */);
diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp
index 34bd4c7..70480a2 100644
--- a/media/libstagefright/ACodec.cpp
+++ b/media/libstagefright/ACodec.cpp
@@ -2857,7 +2857,9 @@
break;
}
- ALOGI("setupVideoEncoder succeeded");
+ if (err == OK) {
+ ALOGI("setupVideoEncoder succeeded");
+ }
return err;
}
@@ -5124,7 +5126,7 @@
sp<CodecObserver> observer = new CodecObserver;
IOMX::node_id node = 0;
- status_t err = OMX_ErrorComponentNotFound;
+ status_t err = NAME_NOT_FOUND;
for (size_t matchIndex = 0; matchIndex < matchingCodecs.size();
++matchIndex) {
componentName = matchingCodecs.itemAt(matchIndex).mName.string();
diff --git a/media/libstagefright/AudioSource.cpp b/media/libstagefright/AudioSource.cpp
index e5a6a9b..34f0148 100644
--- a/media/libstagefright/AudioSource.cpp
+++ b/media/libstagefright/AudioSource.cpp
@@ -85,6 +85,9 @@
this,
frameCount /*notificationFrames*/);
mInitCheck = mRecord->initCheck();
+ if (mInitCheck != OK) {
+ mRecord.clear();
+ }
} else {
mInitCheck = status;
}
diff --git a/media/libstagefright/MediaCodec.cpp b/media/libstagefright/MediaCodec.cpp
index 46c154d..6f22e26 100644
--- a/media/libstagefright/MediaCodec.cpp
+++ b/media/libstagefright/MediaCodec.cpp
@@ -26,6 +26,7 @@
#include <binder/IPCThreadState.h>
#include <binder/IServiceManager.h>
#include <binder/MemoryDealer.h>
+#include <gui/BufferQueue.h>
#include <gui/Surface.h>
#include <media/ICrypto.h>
#include <media/IOMX.h>
@@ -320,6 +321,27 @@
CHECK_EQ(client.connect(), (status_t)OK);
sp<IOMX> omx = client.interface();
+ const sp<IMediaCodecList> mediaCodecList = MediaCodecList::getInstance();
+ if (mediaCodecList == NULL) {
+ ALOGE("Failed to obtain MediaCodecList!");
+ return NULL; // if called from Java should raise IOException
+ }
+
+ AString tmp;
+ sp<AMessage> globalSettings = mediaCodecList->getGlobalSettings();
+ if (globalSettings == NULL || !globalSettings->findString(
+ kMaxEncoderInputBuffers, &tmp)) {
+ ALOGE("Failed to get encoder input buffer count!");
+ return NULL;
+ }
+
+ int32_t bufferCount = strtol(tmp.c_str(), NULL, 10);
+ if (bufferCount <= 0
+ || bufferCount > BufferQueue::MAX_MAX_ACQUIRED_BUFFERS) {
+ ALOGE("Encoder input buffer count is invalid!");
+ return NULL;
+ }
+
sp<IGraphicBufferProducer> bufferProducer;
sp<IGraphicBufferConsumer> bufferConsumer;
@@ -331,6 +353,14 @@
return NULL;
}
+ err = bufferConsumer->setMaxAcquiredBufferCount(bufferCount);
+
+ if (err != NO_ERROR) {
+ ALOGE("Unable to set BQ max acquired buffer count to %u: %d",
+ bufferCount, err);
+ return NULL;
+ }
+
return new PersistentSurface(bufferProducer, bufferConsumer);
}
diff --git a/media/libstagefright/MediaCodecList.cpp b/media/libstagefright/MediaCodecList.cpp
index d2352bc..6708828 100644
--- a/media/libstagefright/MediaCodecList.cpp
+++ b/media/libstagefright/MediaCodecList.cpp
@@ -42,6 +42,8 @@
namespace android {
+const char *kMaxEncoderInputBuffers = "max-video-encoder-input-buffers";
+
static Mutex sInitMutex;
static MediaCodecList *gCodecList = NULL;
diff --git a/media/libstagefright/MediaCodecListOverrides.cpp b/media/libstagefright/MediaCodecListOverrides.cpp
index 0d95676..006454d 100644
--- a/media/libstagefright/MediaCodecListOverrides.cpp
+++ b/media/libstagefright/MediaCodecListOverrides.cpp
@@ -25,8 +25,10 @@
#include <media/IMediaCodecList.h>
#include <media/MediaCodecInfo.h>
#include <media/MediaResourcePolicy.h>
+#include <media/openmax/OMX_IVCommon.h>
#include <media/stagefright/foundation/AMessage.h>
#include <media/stagefright/MediaCodec.h>
+#include <media/stagefright/MediaCodecList.h>
namespace android {
@@ -86,6 +88,7 @@
int32_t bitrate = 0;
getMeasureBitrate(caps, &bitrate);
format->setInt32("bitrate", bitrate);
+ format->setInt32("encoder", 1);
}
if (mime.startsWith("video/")) {
@@ -114,15 +117,67 @@
return format;
}
+static size_t doProfileEncoderInputBuffers(
+ AString name, AString mime, sp<MediaCodecInfo::Capabilities> caps) {
+ ALOGV("doProfileEncoderInputBuffers: name %s, mime %s", name.c_str(), mime.c_str());
+
+ sp<AMessage> format = getMeasureFormat(true /* isEncoder */, mime, caps);
+ if (format == NULL) {
+ return 0;
+ }
+
+ format->setInt32("color-format", OMX_COLOR_FormatAndroidOpaque);
+ ALOGV("doProfileEncoderInputBuffers: format %s", format->debugString().c_str());
+
+ status_t err = OK;
+ sp<ALooper> looper = new ALooper;
+ looper->setName("MediaCodec_looper");
+ looper->start(
+ false /* runOnCallingThread */, false /* canCallJava */, ANDROID_PRIORITY_AUDIO);
+
+ sp<MediaCodec> codec = MediaCodec::CreateByComponentName(looper, name.c_str(), &err);
+ if (err != OK) {
+ ALOGE("Failed to create codec: %s", name.c_str());
+ return 0;
+ }
+
+ err = codec->configure(format, NULL, NULL, MediaCodec::CONFIGURE_FLAG_ENCODE);
+ if (err != OK) {
+ ALOGE("Failed to configure codec: %s with mime: %s", name.c_str(), mime.c_str());
+ codec->release();
+ return 0;
+ }
+
+ sp<IGraphicBufferProducer> bufferProducer;
+ err = codec->createInputSurface(&bufferProducer);
+ if (err != OK) {
+ ALOGE("Failed to create surface: %s with mime: %s", name.c_str(), mime.c_str());
+ codec->release();
+ return 0;
+ }
+
+ int minUndequeued = 0;
+ err = bufferProducer->query(
+ NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, &minUndequeued);
+ if (err != OK) {
+ ALOGE("Failed to query NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS");
+ minUndequeued = 0;
+ }
+
+ err = codec->release();
+ if (err != OK) {
+ ALOGW("Failed to release codec: %s with mime: %s", name.c_str(), mime.c_str());
+ }
+
+ return minUndequeued;
+}
+
static size_t doProfileCodecs(
bool isEncoder, AString name, AString mime, sp<MediaCodecInfo::Capabilities> caps) {
sp<AMessage> format = getMeasureFormat(isEncoder, mime, caps);
if (format == NULL) {
return 0;
}
- if (isEncoder) {
- format->setInt32("encoder", 1);
- }
ALOGV("doProfileCodecs %s %s %s %s",
name.c_str(), mime.c_str(), isEncoder ? "encoder" : "decoder",
format->debugString().c_str());
@@ -144,7 +199,7 @@
}
const sp<Surface> nativeWindow;
const sp<ICrypto> crypto;
- uint32_t flags = 0;
+ uint32_t flags = isEncoder ? MediaCodec::CONFIGURE_FLAG_ENCODE : 0;
ALOGV("doProfileCodecs configure");
err = codec->configure(format, nativeWindow, crypto, flags);
if (err != OK) {
@@ -211,6 +266,7 @@
bool forceToMeasure) {
KeyedVector<AString, sp<MediaCodecInfo::Capabilities>> codecsNeedMeasure;
AString supportMultipleSecureCodecs = "true";
+ size_t maxEncoderInputBuffers = 0;
for (size_t i = 0; i < infos.size(); ++i) {
const sp<MediaCodecInfo> info = infos[i];
AString name = info->getCodecName();
@@ -251,9 +307,21 @@
supportMultipleSecureCodecs = "false";
}
}
+ if (info->isEncoder() && mimes[i].startsWith("video/")) {
+ size_t encoderInputBuffers =
+ doProfileEncoderInputBuffers(name, mimes[i], caps);
+ if (encoderInputBuffers > maxEncoderInputBuffers) {
+ maxEncoderInputBuffers = encoderInputBuffers;
+ }
+ }
}
}
}
+ if (maxEncoderInputBuffers > 0) {
+ char tmp[32];
+ sprintf(tmp, "%zu", maxEncoderInputBuffers);
+ global_results->add(kMaxEncoderInputBuffers, tmp);
+ }
global_results->add(kPolicySupportsMultipleSecureCodecs, supportMultipleSecureCodecs);
}
diff --git a/media/libstagefright/NuCachedSource2.cpp b/media/libstagefright/NuCachedSource2.cpp
index 1c53b40..f82636b 100644
--- a/media/libstagefright/NuCachedSource2.cpp
+++ b/media/libstagefright/NuCachedSource2.cpp
@@ -583,6 +583,13 @@
Mutex::Autolock autoLock(mLock);
+ // If we're disconnecting, return EOS and don't access *data pointer.
+ // data could be on the stack of the caller to NuCachedSource2::readAt(),
+ // which may have exited already.
+ if (mDisconnecting) {
+ return ERROR_END_OF_STREAM;
+ }
+
if (!mFetching) {
mLastAccessPos = offset;
restartPrefetcherIfNecessary_l(
diff --git a/media/libstagefright/include/MPEG2TSExtractor.h b/media/libstagefright/include/MPEG2TSExtractor.h
index 4dd340c..8eb8f6c 100644
--- a/media/libstagefright/include/MPEG2TSExtractor.h
+++ b/media/libstagefright/include/MPEG2TSExtractor.h
@@ -20,7 +20,9 @@
#include <media/stagefright/foundation/ABase.h>
#include <media/stagefright/MediaExtractor.h>
+#include <media/stagefright/MediaSource.h>
#include <utils/threads.h>
+#include <utils/KeyedVector.h>
#include <utils/Vector.h>
namespace android {
@@ -54,10 +56,21 @@
Vector<sp<AnotherPacketSource> > mSourceImpls;
+ Vector<KeyedVector<int64_t, off64_t> > mSyncPoints;
+ // Sync points used for seeking --- normally one for video track is used.
+ // If no video track is present, audio track will be used instead.
+ KeyedVector<int64_t, off64_t> *mSeekSyncPoints;
+
off64_t mOffset;
void init();
status_t feedMore();
+ status_t seek(int64_t seekTimeUs,
+ const MediaSource::ReadOptions::SeekMode& seekMode);
+ status_t queueDiscontinuityForSeek(int64_t actualSeekTimeUs);
+ status_t seekBeyond(int64_t seekTimeUs);
+
+ status_t feedUntilBufferAvailable(const sp<AnotherPacketSource> &impl);
DISALLOW_EVIL_CONSTRUCTORS(MPEG2TSExtractor);
};
diff --git a/media/libstagefright/mpeg2ts/ATSParser.cpp b/media/libstagefright/mpeg2ts/ATSParser.cpp
index e8b2219..98d2877 100644
--- a/media/libstagefright/mpeg2ts/ATSParser.cpp
+++ b/media/libstagefright/mpeg2ts/ATSParser.cpp
@@ -54,10 +54,13 @@
bool parsePSISection(
unsigned pid, ABitReader *br, status_t *err);
+ // Pass to appropriate stream according to pid, and set event if it's a PES
+ // with a sync frame.
+ // Note that the method itself does not touch event.
bool parsePID(
unsigned pid, unsigned continuity_counter,
unsigned payload_unit_start_indicator,
- ABitReader *br, status_t *err);
+ ABitReader *br, status_t *err, SyncEvent *event);
void signalDiscontinuity(
DiscontinuityType type, const sp<AMessage> &extra);
@@ -118,10 +121,14 @@
unsigned pid() const { return mElementaryPID; }
void setPID(unsigned pid) { mElementaryPID = pid; }
+ // Parse the payload and set event when PES with a sync frame is detected.
+ // This method knows when a PES starts; so record mPesStartOffset in that
+ // case.
status_t parse(
unsigned continuity_counter,
unsigned payload_unit_start_indicator,
- ABitReader *br);
+ ABitReader *br,
+ SyncEvent *event);
void signalDiscontinuity(
DiscontinuityType type, const sp<AMessage> &extra);
@@ -150,17 +157,24 @@
bool mEOSReached;
uint64_t mPrevPTS;
+ off64_t mPesStartOffset;
ElementaryStreamQueue *mQueue;
- status_t flush();
- status_t parsePES(ABitReader *br);
+ // Flush accumulated payload if necessary --- i.e. at EOS or at the start of
+ // another payload. event is set if the flushed payload is PES with a sync
+ // frame.
+ status_t flush(SyncEvent *event);
+ // Strip and parse PES headers and pass remaining payload into onPayload
+ // with parsed metadata. event is set if the PES contains a sync frame.
+ status_t parsePES(ABitReader *br, SyncEvent *event);
+ // Feed the payload into mQueue and if a packet is identified, queue it
+ // into mSource. If the packet is a sync frame. set event with start offset
+ // and timestamp of the packet.
void onPayloadData(
unsigned PTS_DTS_flags, uint64_t PTS, uint64_t DTS,
- const uint8_t *data, size_t size);
-
- void extractAACFrames(const sp<ABuffer> &buffer);
+ const uint8_t *data, size_t size, SyncEvent *event);
DISALLOW_EVIL_CONSTRUCTORS(Stream);
};
@@ -190,6 +204,17 @@
DISALLOW_EVIL_CONSTRUCTORS(PSISection);
};
+ATSParser::SyncEvent::SyncEvent(off64_t offset)
+ : mInit(false), mOffset(offset), mTimeUs(0) {}
+
+void ATSParser::SyncEvent::init(off64_t offset, const sp<MediaSource> &source,
+ int64_t timeUs) {
+ mInit = true;
+ mOffset = offset;
+ mMediaSource = source;
+ mTimeUs = timeUs;
+}
+
////////////////////////////////////////////////////////////////////////////////
ATSParser::Program::Program(
@@ -220,7 +245,7 @@
bool ATSParser::Program::parsePID(
unsigned pid, unsigned continuity_counter,
unsigned payload_unit_start_indicator,
- ABitReader *br, status_t *err) {
+ ABitReader *br, status_t *err, SyncEvent *event) {
*err = OK;
ssize_t index = mStreams.indexOfKey(pid);
@@ -229,7 +254,7 @@
}
*err = mStreams.editValueAt(index)->parse(
- continuity_counter, payload_unit_start_indicator, br);
+ continuity_counter, payload_unit_start_indicator, br, event);
return true;
}
@@ -628,7 +653,8 @@
status_t ATSParser::Stream::parse(
unsigned continuity_counter,
- unsigned payload_unit_start_indicator, ABitReader *br) {
+ unsigned payload_unit_start_indicator, ABitReader *br,
+ SyncEvent *event) {
if (mQueue == NULL) {
return OK;
}
@@ -659,12 +685,13 @@
mExpectedContinuityCounter = (continuity_counter + 1) & 0x0f;
if (payload_unit_start_indicator) {
+ off64_t offset = (event != NULL) ? event->getOffset() : 0;
if (mPayloadStarted) {
// Otherwise we run the danger of receiving the trailing bytes
// of a PES packet that we never saw the start of and assuming
// we have a a complete PES packet.
- status_t err = flush();
+ status_t err = flush(event);
if (err != OK) {
return err;
@@ -672,6 +699,7 @@
}
mPayloadStarted = true;
+ mPesStartOffset = offset;
}
if (!mPayloadStarted) {
@@ -785,10 +813,10 @@
mSource->signalEOS(finalResult);
}
mEOSReached = true;
- flush();
+ flush(NULL);
}
-status_t ATSParser::Stream::parsePES(ABitReader *br) {
+status_t ATSParser::Stream::parsePES(ABitReader *br, SyncEvent *event) {
unsigned packet_startcode_prefix = br->getBits(24);
ALOGV("packet_startcode_prefix = 0x%08x", packet_startcode_prefix);
@@ -973,13 +1001,13 @@
}
onPayloadData(
- PTS_DTS_flags, PTS, DTS, br->data(), dataLength);
+ PTS_DTS_flags, PTS, DTS, br->data(), dataLength, event);
br->skipBits(dataLength * 8);
} else {
onPayloadData(
PTS_DTS_flags, PTS, DTS,
- br->data(), br->numBitsLeft() / 8);
+ br->data(), br->numBitsLeft() / 8, event);
size_t payloadSizeBits = br->numBitsLeft();
if (payloadSizeBits % 8 != 0u) {
@@ -1003,7 +1031,7 @@
return OK;
}
-status_t ATSParser::Stream::flush() {
+status_t ATSParser::Stream::flush(SyncEvent *event) {
if (mBuffer->size() == 0) {
return OK;
}
@@ -1012,7 +1040,7 @@
ABitReader br(mBuffer->data(), mBuffer->size());
- status_t err = parsePES(&br);
+ status_t err = parsePES(&br, event);
mBuffer->setRange(0, 0);
@@ -1021,7 +1049,7 @@
void ATSParser::Stream::onPayloadData(
unsigned PTS_DTS_flags, uint64_t PTS, uint64_t /* DTS */,
- const uint8_t *data, size_t size) {
+ const uint8_t *data, size_t size, SyncEvent *event) {
#if 0
ALOGI("payload streamType 0x%02x, PTS = 0x%016llx, dPTS = %lld",
mStreamType,
@@ -1048,6 +1076,7 @@
}
sp<ABuffer> accessUnit;
+ bool found = false;
while ((accessUnit = mQueue->dequeueAccessUnit()) != NULL) {
if (mSource == NULL) {
sp<MetaData> meta = mQueue->getFormat();
@@ -1075,6 +1104,17 @@
}
mSource->queueAccessUnit(accessUnit);
}
+
+ if ((event != NULL) && !found && mQueue->getFormat() != NULL) {
+ int32_t sync = 0;
+ if (accessUnit->meta()->findInt32("isSync", &sync) && sync) {
+ int64_t timeUs;
+ if (accessUnit->meta()->findInt64("timeUs", &timeUs)) {
+ found = true;
+ event->init(mPesStartOffset, mSource, timeUs);
+ }
+ }
+ }
}
}
@@ -1127,14 +1167,15 @@
ATSParser::~ATSParser() {
}
-status_t ATSParser::feedTSPacket(const void *data, size_t size) {
+status_t ATSParser::feedTSPacket(const void *data, size_t size,
+ SyncEvent *event) {
if (size != kTSPacketSize) {
ALOGE("Wrong TS packet size");
return BAD_VALUE;
}
ABitReader br((const uint8_t *)data, kTSPacketSize);
- return parseTS(&br);
+ return parseTS(&br, event);
}
void ATSParser::signalDiscontinuity(
@@ -1262,7 +1303,8 @@
status_t ATSParser::parsePID(
ABitReader *br, unsigned PID,
unsigned continuity_counter,
- unsigned payload_unit_start_indicator) {
+ unsigned payload_unit_start_indicator,
+ SyncEvent *event) {
ssize_t sectionIndex = mPSISections.indexOfKey(PID);
if (sectionIndex >= 0) {
@@ -1334,7 +1376,7 @@
status_t err;
if (mPrograms.editItemAt(i)->parsePID(
PID, continuity_counter, payload_unit_start_indicator,
- br, &err)) {
+ br, &err, event)) {
if (err != OK) {
return err;
}
@@ -1405,7 +1447,7 @@
return OK;
}
-status_t ATSParser::parseTS(ABitReader *br) {
+status_t ATSParser::parseTS(ABitReader *br, SyncEvent *event) {
ALOGV("---");
unsigned sync_byte = br->getBits(8);
@@ -1444,8 +1486,8 @@
}
if (err == OK) {
if (adaptation_field_control == 1 || adaptation_field_control == 3) {
- err = parsePID(
- br, PID, continuity_counter, payload_unit_start_indicator);
+ err = parsePID(br, PID, continuity_counter,
+ payload_unit_start_indicator, event);
}
}
diff --git a/media/libstagefright/mpeg2ts/ATSParser.h b/media/libstagefright/mpeg2ts/ATSParser.h
index 4def333..430a8d5 100644
--- a/media/libstagefright/mpeg2ts/ATSParser.h
+++ b/media/libstagefright/mpeg2ts/ATSParser.h
@@ -22,6 +22,7 @@
#include <media/stagefright/foundation/ABase.h>
#include <media/stagefright/foundation/AMessage.h>
+#include <media/stagefright/MediaSource.h>
#include <utils/KeyedVector.h>
#include <utils/Vector.h>
#include <utils/RefBase.h>
@@ -30,7 +31,6 @@
class ABitReader;
struct ABuffer;
-struct MediaSource;
struct ATSParser : public RefBase {
enum DiscontinuityType {
@@ -62,9 +62,43 @@
ALIGNED_VIDEO_DATA = 2,
};
+ // Event is used to signal sync point event at feedTSPacket().
+ struct SyncEvent {
+ SyncEvent(off64_t offset);
+
+ void init(off64_t offset, const sp<MediaSource> &source,
+ int64_t timeUs);
+
+ bool isInit() { return mInit; }
+ off64_t getOffset() { return mOffset; }
+ const sp<MediaSource> &getMediaSource() { return mMediaSource; }
+ int64_t getTimeUs() { return mTimeUs; }
+
+ private:
+ bool mInit;
+ /*
+ * mInit == false: the current offset
+ * mInit == true: the start offset of sync payload
+ */
+ off64_t mOffset;
+ /* The media source object for this event. */
+ sp<MediaSource> mMediaSource;
+ /* The timestamp of the sync frame. */
+ int64_t mTimeUs;
+ };
+
ATSParser(uint32_t flags = 0);
- status_t feedTSPacket(const void *data, size_t size);
+ // Feed a TS packet into the parser. uninitialized event with the start
+ // offset of this TS packet goes in, and if the parser detects PES with
+ // a sync frame, the event will be initiailzed with the start offset of the
+ // PES. Note that the offset of the event can be different from what we fed,
+ // as a PES may consist of multiple TS packets.
+ //
+ // Even in the case feedTSPacket() returns non-OK value, event still may be
+ // initialized if the parsing failed after the detection.
+ status_t feedTSPacket(
+ const void *data, size_t size, SyncEvent *event = NULL);
void signalDiscontinuity(
DiscontinuityType type, const sp<AMessage> &extra);
@@ -126,15 +160,25 @@
void parseProgramAssociationTable(ABitReader *br);
void parseProgramMap(ABitReader *br);
- void parsePES(ABitReader *br);
+ // Parse PES packet where br is pointing to. If the PES contains a sync
+ // frame, set event with the time and the start offset of this PES.
+ // Note that the method itself does not touch event.
+ void parsePES(ABitReader *br, SyncEvent *event);
+ // Strip remaining packet headers and pass to appropriate program/stream
+ // to parse the payload. If the payload turns out to be PES and contains
+ // a sync frame, event shall be set with the time and start offset of the
+ // PES.
+ // Note that the method itself does not touch event.
status_t parsePID(
ABitReader *br, unsigned PID,
unsigned continuity_counter,
- unsigned payload_unit_start_indicator);
+ unsigned payload_unit_start_indicator,
+ SyncEvent *event);
status_t parseAdaptationField(ABitReader *br, unsigned PID);
- status_t parseTS(ABitReader *br);
+ // see feedTSPacket().
+ status_t parseTS(ABitReader *br, SyncEvent *event);
void updatePCR(unsigned PID, uint64_t PCR, size_t byteOffsetFromStart);
diff --git a/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp b/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp
index 3a8e2ab..1c06c78 100644
--- a/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp
+++ b/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp
@@ -21,6 +21,7 @@
#include "include/MPEG2TSExtractor.h"
#include "include/NuCachedSource2.h"
+#include <media/stagefright/foundation/ABuffer.h>
#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/foundation/ALooper.h>
#include <media/stagefright/DataSource.h>
@@ -28,6 +29,7 @@
#include <media/stagefright/MediaErrors.h>
#include <media/stagefright/MediaSource.h>
#include <media/stagefright/MetaData.h>
+#include <media/IStreamSource.h>
#include <utils/String8.h>
#include "AnotherPacketSource.h"
@@ -41,7 +43,7 @@
MPEG2TSSource(
const sp<MPEG2TSExtractor> &extractor,
const sp<AnotherPacketSource> &impl,
- bool seekable);
+ bool doesSeek);
virtual status_t start(MetaData *params = NULL);
virtual status_t stop();
@@ -55,8 +57,8 @@
sp<AnotherPacketSource> mImpl;
// If there are both audio and video streams, only the video stream
- // will be seekable, otherwise the single stream will be seekable.
- bool mSeekable;
+ // will signal seek on the extractor; otherwise the single stream will seek.
+ bool mDoesSeek;
DISALLOW_EVIL_CONSTRUCTORS(MPEG2TSSource);
};
@@ -64,10 +66,10 @@
MPEG2TSSource::MPEG2TSSource(
const sp<MPEG2TSExtractor> &extractor,
const sp<AnotherPacketSource> &impl,
- bool seekable)
+ bool doesSeek)
: mExtractor(extractor),
mImpl(impl),
- mSeekable(seekable) {
+ mDoesSeek(doesSeek) {
}
status_t MPEG2TSSource::start(MetaData *params) {
@@ -86,27 +88,18 @@
MediaBuffer **out, const ReadOptions *options) {
*out = NULL;
- status_t finalResult;
- while (!mImpl->hasBufferAvailable(&finalResult)) {
- if (finalResult != OK) {
- return ERROR_END_OF_STREAM;
- }
-
- status_t err = mExtractor->feedMore();
+ int64_t seekTimeUs;
+ ReadOptions::SeekMode seekMode;
+ if (mDoesSeek && options && options->getSeekTo(&seekTimeUs, &seekMode)) {
+ // seek is needed
+ status_t err = mExtractor->seek(seekTimeUs, seekMode);
if (err != OK) {
- mImpl->signalEOS(err);
+ return err;
}
}
- int64_t seekTimeUs;
- ReadOptions::SeekMode seekMode;
- if (mSeekable && options && options->getSeekTo(&seekTimeUs, &seekMode)) {
- // A seek was requested, but we don't actually support seeking and so can only "seek" to
- // the current position
- int64_t nextBufTimeUs;
- if (mImpl->nextBufferTime(&nextBufTimeUs) != OK || seekTimeUs != nextBufTimeUs) {
- return ERROR_UNSUPPORTED;
- }
+ if (mExtractor->feedUntilBufferAvailable(mImpl) != OK) {
+ return ERROR_END_OF_STREAM;
}
return mImpl->read(out, options);
@@ -130,23 +123,10 @@
return NULL;
}
- bool seekable = true;
- if (mSourceImpls.size() > 1) {
- if (mSourceImpls.size() != 2u) {
- ALOGE("Wrong size");
- return NULL;
- }
-
- sp<MetaData> meta = mSourceImpls.editItemAt(index)->getFormat();
- const char *mime;
- CHECK(meta->findCString(kKeyMIMEType, &mime));
-
- if (!strncasecmp("audio/", mime, 6)) {
- seekable = false;
- }
- }
-
- return new MPEG2TSSource(this, mSourceImpls.editItemAt(index), seekable);
+ // The seek reference track (video if present; audio otherwise) performs
+ // seek requests, while other tracks ignore requests.
+ return new MPEG2TSSource(this, mSourceImpls.editItemAt(index),
+ (mSeekSyncPoints == &mSyncPoints.editItemAt(index)));
}
sp<MetaData> MPEG2TSExtractor::getTrackMetaData(
@@ -179,6 +159,8 @@
if (impl != NULL) {
haveVideo = true;
mSourceImpls.push(impl);
+ mSyncPoints.push();
+ mSeekSyncPoints = &mSyncPoints.editTop();
}
}
@@ -190,6 +172,10 @@
if (impl != NULL) {
haveAudio = true;
mSourceImpls.push(impl);
+ mSyncPoints.push();
+ if (!haveVideo) {
+ mSeekSyncPoints = &mSyncPoints.editTop();
+ }
}
}
@@ -199,6 +185,55 @@
}
}
+ off64_t size;
+ if (mDataSource->getSize(&size) == OK && (haveAudio || haveVideo)) {
+ sp<AnotherPacketSource> impl = haveVideo
+ ? (AnotherPacketSource *)mParser->getSource(
+ ATSParser::VIDEO).get()
+ : (AnotherPacketSource *)mParser->getSource(
+ ATSParser::AUDIO).get();
+ int64_t prevBufferedDurationUs = 0;
+ int64_t durationUs = -1;
+ List<int64_t> durations;
+ // Estimate duration --- stabilize until you get <500ms deviation.
+ for (; feedMore() == OK && numPacketsParsed <= 10000;
+ ++numPacketsParsed) {
+ status_t err;
+ int64_t bufferedDurationUs = impl->getBufferedDurationUs(&err);
+ if (err != OK) {
+ break;
+ }
+ if (bufferedDurationUs != prevBufferedDurationUs) {
+ durationUs = size * bufferedDurationUs / mOffset;
+ durations.push_back(durationUs);
+ if (durations.size() > 5) {
+ durations.erase(durations.begin());
+ int64_t min = *durations.begin();
+ int64_t max = *durations.begin();
+ for (List<int64_t>::iterator i = durations.begin();
+ i != durations.end();
+ ++i) {
+ if (min > *i) {
+ min = *i;
+ }
+ if (max < *i) {
+ max = *i;
+ }
+ }
+ if (max - min < 500 * 1000) {
+ break;
+ }
+ }
+ prevBufferedDurationUs = bufferedDurationUs;
+ }
+ }
+ if (durationUs > 0) {
+ const sp<MetaData> meta = impl->getFormat();
+ meta->setInt64(kKeyDuration, durationUs);
+ impl->setFormat(meta);
+ }
+ }
+
ALOGI("haveAudio=%d, haveVideo=%d, elaspedTime=%lld",
haveAudio, haveVideo, ALooper::GetNowUs() - startTime);
}
@@ -216,12 +251,195 @@
return (n < 0) ? (status_t)n : ERROR_END_OF_STREAM;
}
+ ATSParser::SyncEvent event(mOffset);
mOffset += n;
- return mParser->feedTSPacket(packet, kTSPacketSize);
+ status_t err = mParser->feedTSPacket(packet, kTSPacketSize, &event);
+ if (event.isInit()) {
+ for (size_t i = 0; i < mSourceImpls.size(); ++i) {
+ if (mSourceImpls[i].get() == event.getMediaSource().get()) {
+ mSyncPoints.editItemAt(i).add(
+ event.getTimeUs(), event.getOffset());
+ break;
+ }
+ }
+ }
+ return err;
}
uint32_t MPEG2TSExtractor::flags() const {
- return CAN_PAUSE;
+ return CAN_PAUSE | CAN_SEEK_BACKWARD | CAN_SEEK_FORWARD;
+}
+
+status_t MPEG2TSExtractor::seek(int64_t seekTimeUs,
+ const MediaSource::ReadOptions::SeekMode &seekMode) {
+ if (mSeekSyncPoints == NULL || mSeekSyncPoints->isEmpty()) {
+ ALOGW("No sync point to seek to.");
+ // ... and therefore we have nothing useful to do here.
+ return OK;
+ }
+
+ // Determine whether we're seeking beyond the known area.
+ bool shouldSeekBeyond =
+ (seekTimeUs > mSeekSyncPoints->keyAt(mSeekSyncPoints->size() - 1));
+
+ // Determine the sync point to seek.
+ size_t index = 0;
+ for (; index < mSeekSyncPoints->size(); ++index) {
+ int64_t timeUs = mSeekSyncPoints->keyAt(index);
+ if (timeUs > seekTimeUs) {
+ break;
+ }
+ }
+
+ switch (seekMode) {
+ case MediaSource::ReadOptions::SEEK_NEXT_SYNC:
+ if (index == mSeekSyncPoints->size()) {
+ ALOGW("Next sync not found; starting from the latest sync.");
+ --index;
+ }
+ break;
+ case MediaSource::ReadOptions::SEEK_CLOSEST_SYNC:
+ case MediaSource::ReadOptions::SEEK_CLOSEST:
+ ALOGW("seekMode not supported: %d; falling back to PREVIOUS_SYNC",
+ seekMode);
+ // fall-through
+ case MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC:
+ if (index == 0) {
+ ALOGW("Previous sync not found; starting from the earliest "
+ "sync.");
+ } else {
+ --index;
+ }
+ break;
+ }
+ if (!shouldSeekBeyond || mOffset <= mSeekSyncPoints->valueAt(index)) {
+ int64_t actualSeekTimeUs = mSeekSyncPoints->keyAt(index);
+ mOffset = mSeekSyncPoints->valueAt(index);
+ status_t err = queueDiscontinuityForSeek(actualSeekTimeUs);
+ if (err != OK) {
+ return err;
+ }
+ }
+
+ if (shouldSeekBeyond) {
+ status_t err = seekBeyond(seekTimeUs);
+ if (err != OK) {
+ return err;
+ }
+ }
+
+ // Fast-forward to sync frame.
+ for (size_t i = 0; i < mSourceImpls.size(); ++i) {
+ const sp<AnotherPacketSource> &impl = mSourceImpls[i];
+ status_t err;
+ feedUntilBufferAvailable(impl);
+ while (impl->hasBufferAvailable(&err)) {
+ sp<AMessage> meta = impl->getMetaAfterLastDequeued(0);
+ sp<ABuffer> buffer;
+ if (meta == NULL) {
+ return UNKNOWN_ERROR;
+ }
+ int32_t sync;
+ if (meta->findInt32("isSync", &sync) && sync) {
+ break;
+ }
+ err = impl->dequeueAccessUnit(&buffer);
+ if (err != OK) {
+ return err;
+ }
+ feedUntilBufferAvailable(impl);
+ }
+ }
+
+ return OK;
+}
+
+status_t MPEG2TSExtractor::queueDiscontinuityForSeek(int64_t actualSeekTimeUs) {
+ // Signal discontinuity
+ sp<AMessage> extra(new AMessage);
+ extra->setInt64(IStreamListener::kKeyMediaTimeUs, actualSeekTimeUs);
+ mParser->signalDiscontinuity(ATSParser::DISCONTINUITY_TIME, extra);
+
+ // After discontinuity, impl should only have discontinuities
+ // with the last being what we queued. Dequeue them all here.
+ for (size_t i = 0; i < mSourceImpls.size(); ++i) {
+ const sp<AnotherPacketSource> &impl = mSourceImpls.itemAt(i);
+ sp<ABuffer> buffer;
+ status_t err;
+ while (impl->hasBufferAvailable(&err)) {
+ if (err != OK) {
+ return err;
+ }
+ err = impl->dequeueAccessUnit(&buffer);
+ // If the source contains anything but discontinuity, that's
+ // a programming mistake.
+ CHECK(err == INFO_DISCONTINUITY);
+ }
+ }
+
+ // Feed until we have a buffer for each source.
+ for (size_t i = 0; i < mSourceImpls.size(); ++i) {
+ const sp<AnotherPacketSource> &impl = mSourceImpls.itemAt(i);
+ sp<ABuffer> buffer;
+ status_t err = feedUntilBufferAvailable(impl);
+ if (err != OK) {
+ return err;
+ }
+ }
+
+ return OK;
+}
+
+status_t MPEG2TSExtractor::seekBeyond(int64_t seekTimeUs) {
+ // If we're seeking beyond where we know --- read until we reach there.
+ size_t syncPointsSize = mSeekSyncPoints->size();
+
+ while (seekTimeUs > mSeekSyncPoints->keyAt(
+ mSeekSyncPoints->size() - 1)) {
+ status_t err;
+ if (syncPointsSize < mSeekSyncPoints->size()) {
+ syncPointsSize = mSeekSyncPoints->size();
+ int64_t syncTimeUs = mSeekSyncPoints->keyAt(syncPointsSize - 1);
+ // Dequeue buffers before sync point in order to avoid too much
+ // cache building up.
+ sp<ABuffer> buffer;
+ for (size_t i = 0; i < mSourceImpls.size(); ++i) {
+ const sp<AnotherPacketSource> &impl = mSourceImpls[i];
+ int64_t timeUs;
+ while ((err = impl->nextBufferTime(&timeUs)) == OK) {
+ if (timeUs < syncTimeUs) {
+ impl->dequeueAccessUnit(&buffer);
+ } else {
+ break;
+ }
+ }
+ if (err != OK && err != -EWOULDBLOCK) {
+ return err;
+ }
+ }
+ }
+ if (feedMore() != OK) {
+ return ERROR_END_OF_STREAM;
+ }
+ }
+
+ return OK;
+}
+
+status_t MPEG2TSExtractor::feedUntilBufferAvailable(
+ const sp<AnotherPacketSource> &impl) {
+ status_t finalResult;
+ while (!impl->hasBufferAvailable(&finalResult)) {
+ if (finalResult != OK) {
+ return finalResult;
+ }
+
+ status_t err = feedMore();
+ if (err != OK) {
+ impl->signalEOS(err);
+ }
+ }
+ return OK;
}
////////////////////////////////////////////////////////////////////////////////
diff --git a/media/libstagefright/omx/OMXNodeInstance.cpp b/media/libstagefright/omx/OMXNodeInstance.cpp
index 91cee73..0540a82 100644
--- a/media/libstagefright/omx/OMXNodeInstance.cpp
+++ b/media/libstagefright/omx/OMXNodeInstance.cpp
@@ -868,17 +868,9 @@
consumer->setConsumerName(name);
consumer->setConsumerUsageBits(GRALLOC_USAGE_HW_VIDEO_ENCODER);
- status_t err = consumer->setMaxAcquiredBufferCount(
- BufferQueue::MAX_MAX_ACQUIRED_BUFFERS);
- if (err != NO_ERROR) {
- ALOGE("Unable to set BQ max acquired buffer count to %u: %d",
- BufferQueue::MAX_MAX_ACQUIRED_BUFFERS, err);
- return err;
- }
-
sp<BufferQueue::ProxyConsumerListener> proxy =
new BufferQueue::ProxyConsumerListener(NULL);
- err = consumer->consumerConnect(proxy, false);
+ status_t err = consumer->consumerConnect(proxy, false);
if (err != NO_ERROR) {
ALOGE("Error connecting to BufferQueue: %s (%d)",
strerror(-err), err);
diff --git a/media/libstagefright/rtsp/MyHandler.h b/media/libstagefright/rtsp/MyHandler.h
index ba17e90..e64a7a1 100644
--- a/media/libstagefright/rtsp/MyHandler.h
+++ b/media/libstagefright/rtsp/MyHandler.h
@@ -1737,7 +1737,7 @@
}
if (!mAllTracksHaveTime) {
- bool allTracksHaveTime = true;
+ bool allTracksHaveTime = (mTracks.size() > 0);
for (size_t i = 0; i < mTracks.size(); ++i) {
TrackInfo *track = &mTracks.editItemAt(i);
if (track->mNTPAnchorUs < 0) {
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index 93b1642..52fce34 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -181,7 +181,8 @@
mIsLowRamDevice(true),
mIsDeviceTypeKnown(false),
mGlobalEffectEnableTime(0),
- mPrimaryOutputSampleRate(0)
+ mPrimaryOutputSampleRate(0),
+ mSystemReady(false)
{
getpid_cached = getpid();
char value[PROPERTY_VALUE_MAX];
@@ -1722,6 +1723,26 @@
return (audio_hw_sync_t)value;
}
+status_t AudioFlinger::systemReady()
+{
+ Mutex::Autolock _l(mLock);
+ ALOGI("%s", __FUNCTION__);
+ if (mSystemReady) {
+ ALOGW("%s called twice", __FUNCTION__);
+ return NO_ERROR;
+ }
+ mSystemReady = true;
+ for (size_t i = 0; i < mPlaybackThreads.size(); i++) {
+ ThreadBase *thread = (ThreadBase *)mPlaybackThreads.valueAt(i).get();
+ thread->systemReady();
+ }
+ for (size_t i = 0; i < mRecordThreads.size(); i++) {
+ ThreadBase *thread = (ThreadBase *)mRecordThreads.valueAt(i).get();
+ thread->systemReady();
+ }
+ return NO_ERROR;
+}
+
// setAudioHwSyncForSession_l() must be called with AudioFlinger::mLock held
void AudioFlinger::setAudioHwSyncForSession_l(PlaybackThread *thread, audio_session_t sessionId)
{
@@ -1794,15 +1815,15 @@
PlaybackThread *thread;
if (flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) {
- thread = new OffloadThread(this, outputStream, *output, devices);
+ thread = new OffloadThread(this, outputStream, *output, devices, mSystemReady);
ALOGV("openOutput_l() created offload output: ID %d thread %p", *output, thread);
} else if ((flags & AUDIO_OUTPUT_FLAG_DIRECT)
|| !isValidPcmSinkFormat(config->format)
|| !isValidPcmSinkChannelMask(config->channel_mask)) {
- thread = new DirectOutputThread(this, outputStream, *output, devices);
+ thread = new DirectOutputThread(this, outputStream, *output, devices, mSystemReady);
ALOGV("openOutput_l() created direct output: ID %d thread %p", *output, thread);
} else {
- thread = new MixerThread(this, outputStream, *output, devices);
+ thread = new MixerThread(this, outputStream, *output, devices, mSystemReady);
ALOGV("openOutput_l() created mixer output: ID %d thread %p", *output, thread);
}
mPlaybackThreads.add(*output, thread);
@@ -1873,7 +1894,7 @@
}
audio_io_handle_t id = nextUniqueId();
- DuplicatingThread *thread = new DuplicatingThread(this, thread1, id);
+ DuplicatingThread *thread = new DuplicatingThread(this, thread1, id, mSystemReady);
thread->addOutputTrack(thread2);
mPlaybackThreads.add(id, thread);
// notify client processes of the new output creation
@@ -2120,7 +2141,8 @@
inputStream,
*input,
primaryOutputDevice_l(),
- devices
+ devices,
+ mSystemReady
#ifdef TEE_SINK
, teeSink
#endif
diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h
index 51b2610..d087ced 100644
--- a/services/audioflinger/AudioFlinger.h
+++ b/services/audioflinger/AudioFlinger.h
@@ -257,6 +257,9 @@
/* Get the HW synchronization source used for an audio session */
virtual audio_hw_sync_t getAudioHwSyncForSession(audio_session_t sessionId);
+ /* Indicate JAVA services are ready (scheduling, power management ...) */
+ virtual status_t systemReady();
+
virtual status_t onTransact(
uint32_t code,
const Parcel& data,
@@ -356,6 +359,15 @@
// check that channelMask is the "canonical" one we expect for the channelCount.
return channelMask == audio_channel_out_mask_from_count(channelCount);
}
+ case AUDIO_CHANNEL_REPRESENTATION_INDEX:
+ if (kEnableExtendedChannels) {
+ const uint32_t channelCount = audio_channel_count_from_out_mask(channelMask);
+ if (channelCount >= FCC_2 // mono is not supported at this time
+ && channelCount <= AudioMixer::MAX_NUM_CHANNELS) {
+ return true;
+ }
+ }
+ return false;
default:
return false;
}
@@ -752,6 +764,7 @@
uint32_t mPrimaryOutputSampleRate; // sample rate of the primary output, or zero if none
// protected by mHardwareLock
+ bool mSystemReady;
};
#undef INCLUDING_FROM_AUDIOFLINGER_H
diff --git a/services/audioflinger/AudioMixer.cpp b/services/audioflinger/AudioMixer.cpp
index 586c737..01efc53 100644
--- a/services/audioflinger/AudioMixer.cpp
+++ b/services/audioflinger/AudioMixer.cpp
@@ -66,6 +66,13 @@
#define ARRAY_SIZE(x) (sizeof(x)/sizeof((x)[0]))
#endif
+// TODO: Move these macro/inlines to a header file.
+template <typename T>
+static inline
+T max(const T& x, const T& y) {
+ return x > y ? x : y;
+}
+
// Set kUseNewMixer to true to use the new mixer engine always. Otherwise the
// original code will be used for stereo sinks, the new mixer for multichannel.
static const bool kUseNewMixer = true;
@@ -499,41 +506,99 @@
static inline bool setVolumeRampVariables(float newVolume, int32_t ramp,
int16_t *pIntSetVolume, int32_t *pIntPrevVolume, int32_t *pIntVolumeInc,
float *pSetVolume, float *pPrevVolume, float *pVolumeInc) {
+ // check floating point volume to see if it is identical to the previously
+ // set volume.
+ // We do not use a tolerance here (and reject changes too small)
+ // as it may be confusing to use a different value than the one set.
+ // If the resulting volume is too small to ramp, it is a direct set of the volume.
if (newVolume == *pSetVolume) {
return false;
}
- /* set the floating point volume variables */
- if (ramp != 0) {
- *pVolumeInc = (newVolume - *pSetVolume) / ramp;
- *pPrevVolume = *pSetVolume;
+ if (newVolume < 0) {
+ newVolume = 0; // should not have negative volumes
} else {
- *pVolumeInc = 0;
- *pPrevVolume = newVolume;
+ switch (fpclassify(newVolume)) {
+ case FP_SUBNORMAL:
+ case FP_NAN:
+ newVolume = 0;
+ break;
+ case FP_ZERO:
+ break; // zero volume is fine
+ case FP_INFINITE:
+ // Infinite volume could be handled consistently since
+ // floating point math saturates at infinities,
+ // but we limit volume to unity gain float.
+ // ramp = 0; break;
+ //
+ newVolume = AudioMixer::UNITY_GAIN_FLOAT;
+ break;
+ case FP_NORMAL:
+ default:
+ // Floating point does not have problems with overflow wrap
+ // that integer has. However, we limit the volume to
+ // unity gain here.
+ // TODO: Revisit the volume limitation and perhaps parameterize.
+ if (newVolume > AudioMixer::UNITY_GAIN_FLOAT) {
+ newVolume = AudioMixer::UNITY_GAIN_FLOAT;
+ }
+ break;
+ }
}
- *pSetVolume = newVolume;
- /* set the legacy integer volume variables */
- int32_t intVolume = newVolume * AudioMixer::UNITY_GAIN_INT;
- if (intVolume > AudioMixer::UNITY_GAIN_INT) {
- intVolume = AudioMixer::UNITY_GAIN_INT;
- } else if (intVolume < 0) {
- ALOGE("negative volume %.7g", newVolume);
- intVolume = 0; // should never happen, but for safety check.
+ // set floating point volume ramp
+ if (ramp != 0) {
+ // when the ramp completes, *pPrevVolume is set to *pSetVolume, so there
+ // is no computational mismatch; hence equality is checked here.
+ ALOGD_IF(*pPrevVolume != *pSetVolume, "previous float ramp hasn't finished,"
+ " prev:%f set_to:%f", *pPrevVolume, *pSetVolume);
+ const float inc = (newVolume - *pPrevVolume) / ramp; // could be inf, nan, subnormal
+ const float maxv = max(newVolume, *pPrevVolume); // could be inf, cannot be nan, subnormal
+
+ if (isnormal(inc) // inc must be a normal number (no subnormals, infinite, nan)
+ && maxv + inc != maxv) { // inc must make forward progress
+ *pVolumeInc = inc;
+ // ramp is set now.
+ // Note: if newVolume is 0, then near the end of the ramp,
+ // it may be possible that the ramped volume may be subnormal or
+ // temporarily negative by a small amount or subnormal due to floating
+ // point inaccuracies.
+ } else {
+ ramp = 0; // ramp not allowed
+ }
}
- if (intVolume == *pIntSetVolume) {
- *pIntVolumeInc = 0;
- /* TODO: integer/float workaround: ignore floating volume ramp */
+
+ // compute and check integer volume, no need to check negative values
+ // The integer volume is limited to "unity_gain" to avoid wrapping and other
+ // audio artifacts, so it never reaches the range limit of U4.28.
+ // We safely use signed 16 and 32 bit integers here.
+ const float scaledVolume = newVolume * AudioMixer::UNITY_GAIN_INT; // not neg, subnormal, nan
+ const int32_t intVolume = (scaledVolume >= (float)AudioMixer::UNITY_GAIN_INT) ?
+ AudioMixer::UNITY_GAIN_INT : (int32_t)scaledVolume;
+
+ // set integer volume ramp
+ if (ramp != 0) {
+ // integer volume is U4.12 (to use 16 bit multiplies), but ramping uses U4.28.
+ // when the ramp completes, *pIntPrevVolume is set to *pIntSetVolume << 16, so there
+ // is no computational mismatch; hence equality is checked here.
+ ALOGD_IF(*pIntPrevVolume != *pIntSetVolume << 16, "previous int ramp hasn't finished,"
+ " prev:%d set_to:%d", *pIntPrevVolume, *pIntSetVolume << 16);
+ const int32_t inc = ((intVolume << 16) - *pIntPrevVolume) / ramp;
+
+ if (inc != 0) { // inc must make forward progress
+ *pIntVolumeInc = inc;
+ } else {
+ ramp = 0; // ramp not allowed
+ }
+ }
+
+ // if no ramp, or ramp not allowed, then clear float and integer increments
+ if (ramp == 0) {
*pVolumeInc = 0;
*pPrevVolume = newVolume;
- return true;
- }
- if (ramp != 0) {
- *pIntVolumeInc = ((intVolume - *pIntSetVolume) << 16) / ramp;
- *pIntPrevVolume = (*pIntVolumeInc == 0 ? intVolume : *pIntSetVolume) << 16;
- } else {
*pIntVolumeInc = 0;
*pIntPrevVolume = intVolume << 16;
}
+ *pSetVolume = newVolume;
*pIntSetVolume = intVolume;
return true;
}
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index 594ed05..9f08851 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -487,7 +487,7 @@
}
AudioFlinger::ThreadBase::ThreadBase(const sp<AudioFlinger>& audioFlinger, audio_io_handle_t id,
- audio_devices_t outDevice, audio_devices_t inDevice, type_t type)
+ audio_devices_t outDevice, audio_devices_t inDevice, type_t type, bool systemReady)
: Thread(false /*canCallJava*/),
mType(type),
mAudioFlinger(audioFlinger),
@@ -498,7 +498,8 @@
mStandby(false), mOutDevice(outDevice), mInDevice(inDevice),
mAudioSource(AUDIO_SOURCE_DEFAULT), mId(id),
// mName will be set by concrete (non-virtual) subclass
- mDeathRecipient(new PMDeathRecipient(this))
+ mDeathRecipient(new PMDeathRecipient(this)),
+ mSystemReady(systemReady)
{
memset(&mPatch, 0, sizeof(struct audio_patch));
}
@@ -567,6 +568,11 @@
{
status_t status = NO_ERROR;
+ if (event->mRequiresSystemReady && !mSystemReady) {
+ event->mWaitStatus = false;
+ mPendingConfigEvents.add(event);
+ return status;
+ }
mConfigEvents.add(event);
ALOGV("sendConfigEvent_l() num events %d event %d", mConfigEvents.size(), event->mType);
mWaitWorkCV.signal();
@@ -598,6 +604,12 @@
sendConfigEvent_l(configEvent);
}
+void AudioFlinger::ThreadBase::sendPrioConfigEvent(pid_t pid, pid_t tid, int32_t prio)
+{
+ Mutex::Autolock _l(mLock);
+ sendPrioConfigEvent_l(pid, tid, prio);
+}
+
// sendPrioConfigEvent_l() must be called with ThreadBase::mLock held
void AudioFlinger::ThreadBase::sendPrioConfigEvent_l(pid_t pid, pid_t tid, int32_t prio)
{
@@ -697,49 +709,62 @@
String8 channelMaskToString(audio_channel_mask_t mask, bool output) {
String8 s;
- if (output) {
- if (mask & AUDIO_CHANNEL_OUT_FRONT_LEFT) s.append("front-left, ");
- if (mask & AUDIO_CHANNEL_OUT_FRONT_RIGHT) s.append("front-right, ");
- if (mask & AUDIO_CHANNEL_OUT_FRONT_CENTER) s.append("front-center, ");
- if (mask & AUDIO_CHANNEL_OUT_LOW_FREQUENCY) s.append("low freq, ");
- if (mask & AUDIO_CHANNEL_OUT_BACK_LEFT) s.append("back-left, ");
- if (mask & AUDIO_CHANNEL_OUT_BACK_RIGHT) s.append("back-right, ");
- if (mask & AUDIO_CHANNEL_OUT_FRONT_LEFT_OF_CENTER) s.append("front-left-of-center, ");
- if (mask & AUDIO_CHANNEL_OUT_FRONT_RIGHT_OF_CENTER) s.append("front-right-of-center, ");
- if (mask & AUDIO_CHANNEL_OUT_BACK_CENTER) s.append("back-center, ");
- if (mask & AUDIO_CHANNEL_OUT_SIDE_LEFT) s.append("side-left, ");
- if (mask & AUDIO_CHANNEL_OUT_SIDE_RIGHT) s.append("side-right, ");
- if (mask & AUDIO_CHANNEL_OUT_TOP_CENTER) s.append("top-center ,");
- if (mask & AUDIO_CHANNEL_OUT_TOP_FRONT_LEFT) s.append("top-front-left, ");
- if (mask & AUDIO_CHANNEL_OUT_TOP_FRONT_CENTER) s.append("top-front-center, ");
- if (mask & AUDIO_CHANNEL_OUT_TOP_FRONT_RIGHT) s.append("top-front-right, ");
- if (mask & AUDIO_CHANNEL_OUT_TOP_BACK_LEFT) s.append("top-back-left, ");
- if (mask & AUDIO_CHANNEL_OUT_TOP_BACK_CENTER) s.append("top-back-center, " );
- if (mask & AUDIO_CHANNEL_OUT_TOP_BACK_RIGHT) s.append("top-back-right, " );
- if (mask & ~AUDIO_CHANNEL_OUT_ALL) s.append("unknown, ");
- } else {
- if (mask & AUDIO_CHANNEL_IN_LEFT) s.append("left, ");
- if (mask & AUDIO_CHANNEL_IN_RIGHT) s.append("right, ");
- if (mask & AUDIO_CHANNEL_IN_FRONT) s.append("front, ");
- if (mask & AUDIO_CHANNEL_IN_BACK) s.append("back, ");
- if (mask & AUDIO_CHANNEL_IN_LEFT_PROCESSED) s.append("left-processed, ");
- if (mask & AUDIO_CHANNEL_IN_RIGHT_PROCESSED) s.append("right-processed, ");
- if (mask & AUDIO_CHANNEL_IN_FRONT_PROCESSED) s.append("front-processed, ");
- if (mask & AUDIO_CHANNEL_IN_BACK_PROCESSED) s.append("back-processed, ");
- if (mask & AUDIO_CHANNEL_IN_PRESSURE) s.append("pressure, ");
- if (mask & AUDIO_CHANNEL_IN_X_AXIS) s.append("X, ");
- if (mask & AUDIO_CHANNEL_IN_Y_AXIS) s.append("Y, ");
- if (mask & AUDIO_CHANNEL_IN_Z_AXIS) s.append("Z, ");
- if (mask & AUDIO_CHANNEL_IN_VOICE_UPLINK) s.append("voice-uplink, ");
- if (mask & AUDIO_CHANNEL_IN_VOICE_DNLINK) s.append("voice-dnlink, ");
- if (mask & ~AUDIO_CHANNEL_IN_ALL) s.append("unknown, ");
+ const audio_channel_representation_t representation = audio_channel_mask_get_representation(mask);
+
+ switch (representation) {
+ case AUDIO_CHANNEL_REPRESENTATION_POSITION: {
+ if (output) {
+ if (mask & AUDIO_CHANNEL_OUT_FRONT_LEFT) s.append("front-left, ");
+ if (mask & AUDIO_CHANNEL_OUT_FRONT_RIGHT) s.append("front-right, ");
+ if (mask & AUDIO_CHANNEL_OUT_FRONT_CENTER) s.append("front-center, ");
+ if (mask & AUDIO_CHANNEL_OUT_LOW_FREQUENCY) s.append("low freq, ");
+ if (mask & AUDIO_CHANNEL_OUT_BACK_LEFT) s.append("back-left, ");
+ if (mask & AUDIO_CHANNEL_OUT_BACK_RIGHT) s.append("back-right, ");
+ if (mask & AUDIO_CHANNEL_OUT_FRONT_LEFT_OF_CENTER) s.append("front-left-of-center, ");
+ if (mask & AUDIO_CHANNEL_OUT_FRONT_RIGHT_OF_CENTER) s.append("front-right-of-center, ");
+ if (mask & AUDIO_CHANNEL_OUT_BACK_CENTER) s.append("back-center, ");
+ if (mask & AUDIO_CHANNEL_OUT_SIDE_LEFT) s.append("side-left, ");
+ if (mask & AUDIO_CHANNEL_OUT_SIDE_RIGHT) s.append("side-right, ");
+ if (mask & AUDIO_CHANNEL_OUT_TOP_CENTER) s.append("top-center ,");
+ if (mask & AUDIO_CHANNEL_OUT_TOP_FRONT_LEFT) s.append("top-front-left, ");
+ if (mask & AUDIO_CHANNEL_OUT_TOP_FRONT_CENTER) s.append("top-front-center, ");
+ if (mask & AUDIO_CHANNEL_OUT_TOP_FRONT_RIGHT) s.append("top-front-right, ");
+ if (mask & AUDIO_CHANNEL_OUT_TOP_BACK_LEFT) s.append("top-back-left, ");
+ if (mask & AUDIO_CHANNEL_OUT_TOP_BACK_CENTER) s.append("top-back-center, " );
+ if (mask & AUDIO_CHANNEL_OUT_TOP_BACK_RIGHT) s.append("top-back-right, " );
+ if (mask & ~AUDIO_CHANNEL_OUT_ALL) s.append("unknown, ");
+ } else {
+ if (mask & AUDIO_CHANNEL_IN_LEFT) s.append("left, ");
+ if (mask & AUDIO_CHANNEL_IN_RIGHT) s.append("right, ");
+ if (mask & AUDIO_CHANNEL_IN_FRONT) s.append("front, ");
+ if (mask & AUDIO_CHANNEL_IN_BACK) s.append("back, ");
+ if (mask & AUDIO_CHANNEL_IN_LEFT_PROCESSED) s.append("left-processed, ");
+ if (mask & AUDIO_CHANNEL_IN_RIGHT_PROCESSED) s.append("right-processed, ");
+ if (mask & AUDIO_CHANNEL_IN_FRONT_PROCESSED) s.append("front-processed, ");
+ if (mask & AUDIO_CHANNEL_IN_BACK_PROCESSED) s.append("back-processed, ");
+ if (mask & AUDIO_CHANNEL_IN_PRESSURE) s.append("pressure, ");
+ if (mask & AUDIO_CHANNEL_IN_X_AXIS) s.append("X, ");
+ if (mask & AUDIO_CHANNEL_IN_Y_AXIS) s.append("Y, ");
+ if (mask & AUDIO_CHANNEL_IN_Z_AXIS) s.append("Z, ");
+ if (mask & AUDIO_CHANNEL_IN_VOICE_UPLINK) s.append("voice-uplink, ");
+ if (mask & AUDIO_CHANNEL_IN_VOICE_DNLINK) s.append("voice-dnlink, ");
+ if (mask & ~AUDIO_CHANNEL_IN_ALL) s.append("unknown, ");
+ }
+ const int len = s.length();
+ if (len > 2) {
+ char *str = s.lockBuffer(len); // needed?
+ s.unlockBuffer(len - 2); // remove trailing ", "
+ }
+ return s;
}
- int len = s.length();
- if (s.length() > 2) {
- char *str = s.lockBuffer(len);
- s.unlockBuffer(len - 2);
+ case AUDIO_CHANNEL_REPRESENTATION_INDEX:
+ s.appendFormat("index mask, bits:%#x", audio_channel_mask_get_bits(mask));
+ return s;
+ default:
+ s.appendFormat("unknown mask, representation:%d bits:%#x",
+ representation, audio_channel_mask_get_bits(mask));
+ return s;
}
- return s;
}
void AudioFlinger::ThreadBase::dumpBase(int fd, const Vector<String16>& args __unused)
@@ -880,8 +905,7 @@
}
void AudioFlinger::ThreadBase::getPowerManager_l() {
-
- if (mPowerManager == 0) {
+ if (mSystemReady && mPowerManager == 0) {
// use checkService() to avoid blocking if power service is not up yet
sp<IBinder> binder =
defaultServiceManager()->checkService(String16("power"));
@@ -895,7 +919,6 @@
}
void AudioFlinger::ThreadBase::updateWakeLockUids_l(const SortedVector<int> &uids) {
-
getPowerManager_l();
if (mWakeLockToken == NULL) {
ALOGE("no wake lock to update!");
@@ -1337,6 +1360,20 @@
AUDIO_PORT_CONFIG_FORMAT;
}
+void AudioFlinger::ThreadBase::systemReady()
+{
+ Mutex::Autolock _l(mLock);
+ if (mSystemReady) {
+ return;
+ }
+ mSystemReady = true;
+
+ for (size_t i = 0; i < mPendingConfigEvents.size(); i++) {
+ sendConfigEvent_l(mPendingConfigEvents.editItemAt(i));
+ }
+ mPendingConfigEvents.clear();
+}
+
// ----------------------------------------------------------------------------
// Playback
@@ -1346,8 +1383,9 @@
AudioStreamOut* output,
audio_io_handle_t id,
audio_devices_t device,
- type_t type)
- : ThreadBase(audioFlinger, id, device, AUDIO_DEVICE_NONE, type),
+ type_t type,
+ bool systemReady)
+ : ThreadBase(audioFlinger, id, device, AUDIO_DEVICE_NONE, type, systemReady),
mNormalFrameCount(0), mSinkBuffer(NULL),
mMixerBufferEnabled(AudioFlinger::kEnableExtendedPrecision),
mMixerBuffer(NULL),
@@ -1572,10 +1610,12 @@
) &&
// PCM data
audio_is_linear_pcm(format) &&
- // identical channel mask to sink, or mono in and stereo sink
+ // TODO: extract as a data library function that checks that a computationally
+ // expensive downmixer is not required: isFastOutputChannelConversion()
(channelMask == mChannelMask ||
- (channelMask == AUDIO_CHANNEL_OUT_MONO &&
- mChannelMask == AUDIO_CHANNEL_OUT_STEREO)) &&
+ mChannelMask != AUDIO_CHANNEL_OUT_STEREO ||
+ (channelMask == AUDIO_CHANNEL_OUT_MONO
+ /* && mChannelMask == AUDIO_CHANNEL_OUT_STEREO */)) &&
// hardware sample rate
(sampleRate == mSampleRate) &&
// normal mixer has an associated fast mixer
@@ -3118,8 +3158,8 @@
// ----------------------------------------------------------------------------
AudioFlinger::MixerThread::MixerThread(const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output,
- audio_io_handle_t id, audio_devices_t device, type_t type)
- : PlaybackThread(audioFlinger, output, id, device, type),
+ audio_io_handle_t id, audio_devices_t device, bool systemReady, type_t type)
+ : PlaybackThread(audioFlinger, output, id, device, type, systemReady),
// mAudioMixer below
// mFastMixer below
mFastMixerFutex(0)
@@ -3252,11 +3292,7 @@
// start the fast mixer
mFastMixer->run("FastMixer", PRIORITY_URGENT_AUDIO);
pid_t tid = mFastMixer->getTid();
- int err = requestPriority(getpid_cached, tid, kPriorityFastMixer);
- if (err != 0) {
- ALOGW("Policy SCHED_FIFO priority %d is unavailable for pid %d tid %d; error %d",
- kPriorityFastMixer, getpid_cached, tid, err);
- }
+ sendPrioConfigEvent(getpid_cached, tid, kPriorityFastMixer);
#ifdef AUDIO_WATCHDOG
// create and start the watchdog
@@ -3264,11 +3300,7 @@
mAudioWatchdog->setDump(&mAudioWatchdogDump);
mAudioWatchdog->run("AudioWatchdog", PRIORITY_URGENT_AUDIO);
tid = mAudioWatchdog->getTid();
- err = requestPriority(getpid_cached, tid, kPriorityFastMixer);
- if (err != 0) {
- ALOGW("Policy SCHED_FIFO priority %d is unavailable for pid %d tid %d; error %d",
- kPriorityFastMixer, getpid_cached, tid, err);
- }
+ sendPrioConfigEvent(getpid_cached, tid, kPriorityFastMixer);
#endif
}
@@ -4294,16 +4326,16 @@
// ----------------------------------------------------------------------------
AudioFlinger::DirectOutputThread::DirectOutputThread(const sp<AudioFlinger>& audioFlinger,
- AudioStreamOut* output, audio_io_handle_t id, audio_devices_t device)
- : PlaybackThread(audioFlinger, output, id, device, DIRECT)
+ AudioStreamOut* output, audio_io_handle_t id, audio_devices_t device, bool systemReady)
+ : PlaybackThread(audioFlinger, output, id, device, DIRECT, systemReady)
// mLeftVolFloat, mRightVolFloat
{
}
AudioFlinger::DirectOutputThread::DirectOutputThread(const sp<AudioFlinger>& audioFlinger,
AudioStreamOut* output, audio_io_handle_t id, uint32_t device,
- ThreadBase::type_t type)
- : PlaybackThread(audioFlinger, output, id, device, type)
+ ThreadBase::type_t type, bool systemReady)
+ : PlaybackThread(audioFlinger, output, id, device, type, systemReady)
// mLeftVolFloat, mRightVolFloat
{
}
@@ -4832,8 +4864,8 @@
// ----------------------------------------------------------------------------
AudioFlinger::OffloadThread::OffloadThread(const sp<AudioFlinger>& audioFlinger,
- AudioStreamOut* output, audio_io_handle_t id, uint32_t device)
- : DirectOutputThread(audioFlinger, output, id, device, OFFLOAD),
+ AudioStreamOut* output, audio_io_handle_t id, uint32_t device, bool systemReady)
+ : DirectOutputThread(audioFlinger, output, id, device, OFFLOAD, systemReady),
mPausedBytesRemaining(0)
{
//FIXME: mStandby should be set to true by ThreadBase constructor
@@ -5110,9 +5142,9 @@
// ----------------------------------------------------------------------------
AudioFlinger::DuplicatingThread::DuplicatingThread(const sp<AudioFlinger>& audioFlinger,
- AudioFlinger::MixerThread* mainThread, audio_io_handle_t id)
+ AudioFlinger::MixerThread* mainThread, audio_io_handle_t id, bool systemReady)
: MixerThread(audioFlinger, mainThread->getOutput(), id, mainThread->outDevice(),
- DUPLICATING),
+ systemReady, DUPLICATING),
mWaitTimeMs(UINT_MAX)
{
addOutputTrack(mainThread);
@@ -5292,12 +5324,13 @@
AudioStreamIn *input,
audio_io_handle_t id,
audio_devices_t outDevice,
- audio_devices_t inDevice
+ audio_devices_t inDevice,
+ bool systemReady
#ifdef TEE_SINK
, const sp<NBAIO_Sink>& teeSink
#endif
) :
- ThreadBase(audioFlinger, id, outDevice, inDevice, RECORD),
+ ThreadBase(audioFlinger, id, outDevice, inDevice, RECORD, systemReady),
mInput(input), mActiveTracksGen(0), mRsmpInBuffer(NULL),
// mRsmpInFrames and mRsmpInFramesP2 are set by readInputParameters_l()
mRsmpInRear(0)
@@ -5416,12 +5449,7 @@
// start the fast capture
mFastCapture->run("FastCapture", ANDROID_PRIORITY_URGENT_AUDIO);
pid_t tid = mFastCapture->getTid();
- int err = requestPriority(getpid_cached, tid, kPriorityFastMixer);
- if (err != 0) {
- ALOGW("Policy SCHED_FIFO priority %d is unavailable for pid %d tid %d; error %d",
- kPriorityFastCapture, getpid_cached, tid, err);
- }
-
+ sendPrioConfigEvent(getpid_cached, tid, kPriorityFastMixer);
#ifdef AUDIO_WATCHDOG
// FIXME
#endif
diff --git a/services/audioflinger/Threads.h b/services/audioflinger/Threads.h
index 37bacae..aa20525 100644
--- a/services/audioflinger/Threads.h
+++ b/services/audioflinger/Threads.h
@@ -35,7 +35,8 @@
static const char *threadTypeToString(type_t type);
ThreadBase(const sp<AudioFlinger>& audioFlinger, audio_io_handle_t id,
- audio_devices_t outDevice, audio_devices_t inDevice, type_t type);
+ audio_devices_t outDevice, audio_devices_t inDevice, type_t type,
+ bool systemReady);
virtual ~ThreadBase();
virtual status_t readyToRun();
@@ -92,10 +93,13 @@
Condition mCond; // condition for status return
status_t mStatus; // status communicated to sender
bool mWaitStatus; // true if sender is waiting for status
+ bool mRequiresSystemReady; // true if must wait for system ready to enter event queue
sp<ConfigEventData> mData; // event specific parameter data
protected:
- ConfigEvent(int type) : mType(type), mStatus(NO_ERROR), mWaitStatus(false), mData(NULL) {}
+ ConfigEvent(int type, bool requiresSystemReady = false) :
+ mType(type), mStatus(NO_ERROR), mWaitStatus(false),
+ mRequiresSystemReady(requiresSystemReady), mData(NULL) {}
};
class IoConfigEventData : public ConfigEventData {
@@ -136,7 +140,7 @@
class PrioConfigEvent : public ConfigEvent {
public:
PrioConfigEvent(pid_t pid, pid_t tid, int32_t prio) :
- ConfigEvent(CFG_EVENT_PRIO) {
+ ConfigEvent(CFG_EVENT_PRIO, true) {
mData = new PrioConfigEventData(pid, tid, prio);
}
virtual ~PrioConfigEvent() {}
@@ -258,6 +262,7 @@
status_t sendConfigEvent_l(sp<ConfigEvent>& event);
void sendIoConfigEvent(audio_io_config_event event);
void sendIoConfigEvent_l(audio_io_config_event event);
+ void sendPrioConfigEvent(pid_t pid, pid_t tid, int32_t prio);
void sendPrioConfigEvent_l(pid_t pid, pid_t tid, int32_t prio);
status_t sendSetParameterConfigEvent_l(const String8& keyValuePair);
status_t sendCreateAudioPatchConfigEvent(const struct audio_patch *patch,
@@ -359,6 +364,8 @@
virtual sp<IMemory> pipeMemory() const { return 0; }
+ void systemReady();
+
mutable Mutex mLock;
protected:
@@ -418,6 +425,7 @@
size_t mBufferSize; // HAL buffer size for read() or write()
Vector< sp<ConfigEvent> > mConfigEvents;
+ Vector< sp<ConfigEvent> > mPendingConfigEvents; // events awaiting system ready
// These fields are written and read by thread itself without lock or barrier,
// and read by other threads without lock or barrier via standby(), outDevice()
@@ -445,6 +453,7 @@
mSuspendedSessions;
static const size_t kLogSize = 4 * 1024;
sp<NBLog::Writer> mNBLogWriter;
+ bool mSystemReady;
};
// --- PlaybackThread ---
@@ -470,7 +479,7 @@
static const int8_t kMaxTrackRetriesOffload = 20;
PlaybackThread(const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output,
- audio_io_handle_t id, audio_devices_t device, type_t type);
+ audio_io_handle_t id, audio_devices_t device, type_t type, bool systemReady);
virtual ~PlaybackThread();
void dump(int fd, const Vector<String16>& args);
@@ -842,6 +851,7 @@
AudioStreamOut* output,
audio_io_handle_t id,
audio_devices_t device,
+ bool systemReady,
type_t type = MIXER);
virtual ~MixerThread();
@@ -903,7 +913,7 @@
public:
DirectOutputThread(const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output,
- audio_io_handle_t id, audio_devices_t device);
+ audio_io_handle_t id, audio_devices_t device, bool systemReady);
virtual ~DirectOutputThread();
// Thread virtuals
@@ -933,7 +943,8 @@
float mRightVolFloat;
DirectOutputThread(const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output,
- audio_io_handle_t id, uint32_t device, ThreadBase::type_t type);
+ audio_io_handle_t id, uint32_t device, ThreadBase::type_t type,
+ bool systemReady);
void processVolume_l(Track *track, bool lastTrack);
// prepareTracks_l() tells threadLoop_mix() the name of the single active track
@@ -946,7 +957,7 @@
public:
OffloadThread(const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output,
- audio_io_handle_t id, uint32_t device);
+ audio_io_handle_t id, uint32_t device, bool systemReady);
virtual ~OffloadThread() {};
virtual void flushHw_l();
@@ -1001,7 +1012,7 @@
class DuplicatingThread : public MixerThread {
public:
DuplicatingThread(const sp<AudioFlinger>& audioFlinger, MixerThread* mainThread,
- audio_io_handle_t id);
+ audio_io_handle_t id, bool systemReady);
virtual ~DuplicatingThread();
// Thread virtuals
@@ -1177,7 +1188,8 @@
AudioStreamIn *input,
audio_io_handle_t id,
audio_devices_t outDevice,
- audio_devices_t inDevice
+ audio_devices_t inDevice,
+ bool systemReady
#ifdef TEE_SINK
, const sp<NBAIO_Sink>& teeSink
#endif
@@ -1294,6 +1306,7 @@
// one-time initialization, no locks required
sp<FastCapture> mFastCapture; // non-0 if there is also
// a fast capture
+
// FIXME audio watchdog thread
// contents are not guaranteed to be consistent, no locks required
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
index d1ee400..17060e9 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
@@ -579,24 +579,43 @@
audio_channel_mask_t channelMask,
audio_output_flags_t flags)
{
+ // only retain flags that will drive the direct output profile selection
+ // if explicitly requested
+ static const uint32_t kRelevantFlags =
+ (AUDIO_OUTPUT_FLAG_HW_AV_SYNC | AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD);
+ flags =
+ (audio_output_flags_t)((flags & kRelevantFlags) | AUDIO_OUTPUT_FLAG_DIRECT);
+
+ sp<IOProfile> profile;
+
for (size_t i = 0; i < mHwModules.size(); i++) {
if (mHwModules[i]->mHandle == 0) {
continue;
}
for (size_t j = 0; j < mHwModules[i]->mOutputProfiles.size(); j++) {
- sp<IOProfile> profile = mHwModules[i]->mOutputProfiles[j];
- bool found = profile->isCompatibleProfile(device, String8(""),
+ sp<IOProfile> curProfile = mHwModules[i]->mOutputProfiles[j];
+ if (!curProfile->isCompatibleProfile(device, String8(""),
samplingRate, NULL /*updatedSamplingRate*/,
format, NULL /*updatedFormat*/,
channelMask, NULL /*updatedChannelMask*/,
- flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD ?
- AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD : AUDIO_OUTPUT_FLAG_DIRECT);
- if (found && (mAvailableOutputDevices.types() & profile->mSupportedDevices.types())) {
- return profile;
+ flags)) {
+ continue;
+ }
+ // reject profiles not corresponding to a device currently available
+ if ((mAvailableOutputDevices.types() & curProfile->mSupportedDevices.types()) == 0) {
+ continue;
+ }
+ // if several profiles are compatible, give priority to one with offload capability
+ if (profile != 0 && ((curProfile->mFlags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) == 0)) {
+ continue;
+ }
+ profile = curProfile;
+ if ((profile->mFlags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) != 0) {
+ break;
}
}
}
- return 0;
+ return profile;
}
audio_io_handle_t AudioPolicyManager::getOutput(audio_stream_type_t stream,
@@ -819,10 +838,27 @@
if (outputDesc != NULL) {
closeOutput(outputDesc->mIoHandle);
}
+
+ // if the selected profile is offloaded and no offload info was specified,
+ // create a default one
+ audio_offload_info_t defaultOffloadInfo = AUDIO_INFO_INITIALIZER;
+ if ((profile->mFlags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) && !offloadInfo) {
+ flags = (audio_output_flags_t)(flags | AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD);
+ defaultOffloadInfo.sample_rate = samplingRate;
+ defaultOffloadInfo.channel_mask = channelMask;
+ defaultOffloadInfo.format = format;
+ defaultOffloadInfo.stream_type = stream;
+ defaultOffloadInfo.bit_rate = 0;
+ defaultOffloadInfo.duration_us = -1;
+ defaultOffloadInfo.has_video = true; // conservative
+ defaultOffloadInfo.is_streaming = true; // likely
+ offloadInfo = &defaultOffloadInfo;
+ }
+
outputDesc = new SwAudioOutputDescriptor(profile, mpClientInterface);
outputDesc->mDevice = device;
outputDesc->mLatency = 0;
- outputDesc->mFlags =(audio_output_flags_t) (outputDesc->mFlags | flags);
+ outputDesc->mFlags = (audio_output_flags_t)(outputDesc->mFlags | flags);
audio_config_t config = AUDIO_CONFIG_INITIALIZER;
config.sample_rate = samplingRate;
config.channel_mask = channelMask;
@@ -854,10 +890,6 @@
if (audio_is_linear_pcm(format) && samplingRate <= MAX_MIXER_SAMPLING_RATE) {
goto non_direct_output;
}
- // fall back to mixer output if possible when the direct output could not be open
- if (audio_is_linear_pcm(format) && samplingRate <= MAX_MIXER_SAMPLING_RATE) {
- goto non_direct_output;
- }
return AUDIO_IO_HANDLE_NONE;
}
outputDesc->mSamplingRate = config.sample_rate;
diff --git a/services/camera/libcameraservice/CameraService.cpp b/services/camera/libcameraservice/CameraService.cpp
index fc9a332..79e73f9 100644
--- a/services/camera/libcameraservice/CameraService.cpp
+++ b/services/camera/libcameraservice/CameraService.cpp
@@ -33,6 +33,7 @@
#include <binder/MemoryBase.h>
#include <binder/MemoryHeapBase.h>
#include <binder/ProcessInfoService.h>
+#include <camera/ICameraServiceProxy.h>
#include <cutils/atomic.h>
#include <cutils/properties.h>
#include <gui/Surface.h>
@@ -224,6 +225,18 @@
}
CameraDeviceFactory::registerService(this);
+
+ CameraService::pingCameraServiceProxy();
+}
+
+void CameraService::pingCameraServiceProxy() {
+ sp<IServiceManager> sm = defaultServiceManager();
+ sp<IBinder> binder = sm->getService(String16("media.camera.proxy"));
+ if (binder == nullptr) {
+ return;
+ }
+ sp<ICameraServiceProxy> proxyBinder = interface_cast<ICameraServiceProxy>(binder);
+ proxyBinder->pingForUserUpdate();
}
CameraService::~CameraService() {
@@ -959,6 +972,10 @@
"(PID %" PRId32 ", priority %" PRId32 ")", i->getKey().string(),
String8{i->getValue()->getPackageName()}.string(), i->getOwnerId(),
i->getPriority());
+ ALOGE(" Conflicts with: Device %s, client package %s (PID %"
+ PRId32 ", priority %" PRId32 ")", i->getKey().string(),
+ String8{i->getValue()->getPackageName()}.string(), i->getOwnerId(),
+ i->getPriority());
}
// Log the client's attempt
@@ -1975,7 +1992,7 @@
auto conflicting = i->getConflicting();
auto clientSp = i->getValue();
String8 packageName;
- userid_t clientUserId;
+ userid_t clientUserId = 0;
if (clientSp.get() != nullptr) {
packageName = String8{clientSp->getPackageName()};
uid_t clientUid = clientSp->getClientUid();
diff --git a/services/camera/libcameraservice/CameraService.h b/services/camera/libcameraservice/CameraService.h
index 9b7163a..ce3cb44 100644
--- a/services/camera/libcameraservice/CameraService.h
+++ b/services/camera/libcameraservice/CameraService.h
@@ -702,6 +702,8 @@
static String8 toString(std::set<userid_t> intSet);
+ static void pingCameraServiceProxy();
+
};
template<class Func>
diff --git a/services/camera/libcameraservice/api1/Camera2Client.cpp b/services/camera/libcameraservice/api1/Camera2Client.cpp
index 05ede92..f2d6ab2 100644
--- a/services/camera/libcameraservice/api1/Camera2Client.cpp
+++ b/services/camera/libcameraservice/api1/Camera2Client.cpp
@@ -1881,6 +1881,16 @@
mCaptureSequencer->notifyAutoExposure(newState, triggerId);
}
+void Camera2Client::notifyShutter(const CaptureResultExtras& resultExtras,
+ nsecs_t timestamp) {
+ (void)resultExtras;
+ (void)timestamp;
+
+ ALOGV("%s: Shutter notification for request id %" PRId32 " at time %" PRId64,
+ __FUNCTION__, resultExtras.requestId, timestamp);
+ mCaptureSequencer->notifyShutter(resultExtras, timestamp);
+}
+
camera2::SharedParameters& Camera2Client::getParameters() {
return mParameters;
}
diff --git a/services/camera/libcameraservice/api1/Camera2Client.h b/services/camera/libcameraservice/api1/Camera2Client.h
index a988037..3784aab 100644
--- a/services/camera/libcameraservice/api1/Camera2Client.h
+++ b/services/camera/libcameraservice/api1/Camera2Client.h
@@ -106,6 +106,8 @@
virtual void notifyAutoFocus(uint8_t newState, int triggerId);
virtual void notifyAutoExposure(uint8_t newState, int triggerId);
+ virtual void notifyShutter(const CaptureResultExtras& resultExtras,
+ nsecs_t timestamp);
/**
* Interface used by independent components of Camera2Client.
diff --git a/services/camera/libcameraservice/api1/client2/CaptureSequencer.cpp b/services/camera/libcameraservice/api1/client2/CaptureSequencer.cpp
index 9849f4d..d847e0f 100644
--- a/services/camera/libcameraservice/api1/client2/CaptureSequencer.cpp
+++ b/services/camera/libcameraservice/api1/client2/CaptureSequencer.cpp
@@ -43,6 +43,8 @@
mNewFrameReceived(false),
mNewCaptureReceived(false),
mShutterNotified(false),
+ mHalNotifiedShutter(false),
+ mShutterCaptureId(-1),
mClient(client),
mCaptureState(IDLE),
mStateTransitionCount(0),
@@ -106,6 +108,16 @@
}
}
+void CaptureSequencer::notifyShutter(const CaptureResultExtras& resultExtras,
+ nsecs_t timestamp) {
+ ATRACE_CALL();
+ Mutex::Autolock l(mInputMutex);
+ if (!mHalNotifiedShutter && resultExtras.requestId == mShutterCaptureId) {
+ mHalNotifiedShutter = true;
+ mShutterNotifySignal.signal();
+ }
+}
+
void CaptureSequencer::onResultAvailable(const CaptureResult &result) {
ATRACE_CALL();
ALOGV("%s: New result available.", __FUNCTION__);
@@ -335,6 +347,11 @@
} else {
nextState = STANDARD_START;
}
+ {
+ Mutex::Autolock l(mInputMutex);
+ mShutterCaptureId = mCaptureId;
+ mHalNotifiedShutter = false;
+ }
mShutterNotified = false;
return nextState;
@@ -541,6 +558,7 @@
return DONE;
}
}
+
// TODO: Capture should be atomic with setStreamingRequest here
res = client->getCameraDevice()->capture(captureCopy);
if (res != OK) {
@@ -560,6 +578,31 @@
ATRACE_CALL();
Mutex::Autolock l(mInputMutex);
+
+ // Wait for shutter callback
+ while (!mHalNotifiedShutter) {
+ if (mTimeoutCount <= 0) {
+ break;
+ }
+ res = mShutterNotifySignal.waitRelative(mInputMutex, kWaitDuration);
+ if (res == TIMED_OUT) {
+ mTimeoutCount--;
+ return STANDARD_CAPTURE_WAIT;
+ }
+ }
+
+ if (mHalNotifiedShutter) {
+ if (!mShutterNotified) {
+ SharedParameters::Lock l(client->getParameters());
+ /* warning: this also locks a SharedCameraCallbacks */
+ shutterNotifyLocked(l.mParameters, client, mMsgType);
+ mShutterNotified = true;
+ }
+ } else if (mTimeoutCount <= 0) {
+ ALOGW("Timed out waiting for shutter notification");
+ return DONE;
+ }
+
// Wait for new metadata result (mNewFrame)
while (!mNewFrameReceived) {
res = mNewFrameSignal.waitRelative(mInputMutex, kWaitDuration);
@@ -569,15 +612,6 @@
}
}
- // Approximation of the shutter being closed
- // - TODO: use the hal3 exposure callback in Camera3Device instead
- if (mNewFrameReceived && !mShutterNotified) {
- SharedParameters::Lock l(client->getParameters());
- /* warning: this also locks a SharedCameraCallbacks */
- shutterNotifyLocked(l.mParameters, client, mMsgType);
- mShutterNotified = true;
- }
-
// Wait until jpeg was captured by JpegProcessor
while (mNewFrameReceived && !mNewCaptureReceived) {
res = mNewCaptureSignal.waitRelative(mInputMutex, kWaitDuration);
@@ -591,6 +625,7 @@
return DONE;
}
if (mNewFrameReceived && mNewCaptureReceived) {
+
if (mNewFrameId != mCaptureId) {
ALOGW("Mismatched capture frame IDs: Expected %d, got %d",
mCaptureId, mNewFrameId);
@@ -667,7 +702,6 @@
sp<Camera2Client> &/*client*/) {
status_t res;
ATRACE_CALL();
-
while (!mNewCaptureReceived) {
res = mNewCaptureSignal.waitRelative(mInputMutex, kWaitDuration);
if (res == TIMED_OUT) {
diff --git a/services/camera/libcameraservice/api1/client2/CaptureSequencer.h b/services/camera/libcameraservice/api1/client2/CaptureSequencer.h
index d42ab13..10252fb 100644
--- a/services/camera/libcameraservice/api1/client2/CaptureSequencer.h
+++ b/services/camera/libcameraservice/api1/client2/CaptureSequencer.h
@@ -62,6 +62,10 @@
// Notifications about AE state changes
void notifyAutoExposure(uint8_t newState, int triggerId);
+ // Notifications about shutter (capture start)
+ void notifyShutter(const CaptureResultExtras& resultExtras,
+ nsecs_t timestamp);
+
// Notification from the frame processor
virtual void onResultAvailable(const CaptureResult &result);
@@ -95,7 +99,10 @@
sp<MemoryBase> mCaptureBuffer;
Condition mNewCaptureSignal;
- bool mShutterNotified;
+ bool mShutterNotified; // Has CaptureSequencer sent shutter to Client
+ bool mHalNotifiedShutter; // Has HAL sent shutter to CaptureSequencer
+ int32_t mShutterCaptureId; // The captureId which is waiting for shutter notification
+ Condition mShutterNotifySignal;
/**
* Internal to CaptureSequencer