summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/media/IResourceManagerClient.h2
-rw-r--r--include/media/stagefright/MediaSync.h3
-rw-r--r--include/media/stagefright/MetaData.h3
-rw-r--r--media/libmedia/IResourceManagerClient.cpp20
-rw-r--r--media/libmediaplayerservice/StagefrightRecorder.cpp6
-rw-r--r--media/libmediaplayerservice/nuplayer/GenericSource.cpp8
-rw-r--r--media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp2
-rw-r--r--media/libstagefright/MediaCodec.cpp28
-rw-r--r--media/libstagefright/MediaSync.cpp78
-rw-r--r--media/libstagefright/httplive/Android.mk1
-rw-r--r--media/libstagefright/httplive/HTTPDownloader.cpp273
-rw-r--r--media/libstagefright/httplive/HTTPDownloader.h86
-rw-r--r--media/libstagefright/httplive/LiveSession.cpp302
-rw-r--r--media/libstagefright/httplive/LiveSession.h33
-rw-r--r--media/libstagefright/httplive/PlaylistFetcher.cpp129
-rw-r--r--media/libstagefright/httplive/PlaylistFetcher.h14
-rw-r--r--media/libstagefright/matroska/MatroskaExtractor.cpp5
-rw-r--r--media/libstagefright/mpeg2ts/AnotherPacketSource.cpp5
-rw-r--r--services/audioflinger/AudioFlinger.h12
-rw-r--r--services/audioflinger/AudioMixer.cpp5
-rw-r--r--services/audioflinger/FastCapture.cpp2
-rw-r--r--services/audioflinger/Threads.cpp227
-rw-r--r--services/audioflinger/Threads.h19
-rw-r--r--services/audiopolicy/common/managerdefinitions/include/AudioPort.h6
-rw-r--r--services/audiopolicy/common/managerdefinitions/include/ConfigParsingUtils.h14
-rw-r--r--services/audiopolicy/common/managerdefinitions/src/AudioPort.cpp113
-rw-r--r--services/audiopolicy/managerdefault/AudioPolicyManager.cpp13
-rw-r--r--services/mediaresourcemanager/ResourceManagerService.cpp45
-rw-r--r--services/mediaresourcemanager/ResourceManagerService.h2
-rw-r--r--services/mediaresourcemanager/test/ResourceManagerService_test.cpp4
-rw-r--r--services/radio/RadioService.cpp7
31 files changed, 996 insertions, 471 deletions
diff --git a/include/media/IResourceManagerClient.h b/include/media/IResourceManagerClient.h
index 3587aea..aa0cd88 100644
--- a/include/media/IResourceManagerClient.h
+++ b/include/media/IResourceManagerClient.h
@@ -18,6 +18,7 @@
#define ANDROID_IRESOURCEMANAGERCLIENT_H
#include <utils/RefBase.h>
+#include <utils/String8.h>
#include <binder/IInterface.h>
#include <binder/Parcel.h>
@@ -29,6 +30,7 @@ public:
DECLARE_META_INTERFACE(ResourceManagerClient);
virtual bool reclaimResource() = 0;
+ virtual String8 getName() = 0;
};
// ----------------------------------------------------------------------------
diff --git a/include/media/stagefright/MediaSync.h b/include/media/stagefright/MediaSync.h
index 8bb8c7f..8ad74a4 100644
--- a/include/media/stagefright/MediaSync.h
+++ b/include/media/stagefright/MediaSync.h
@@ -113,6 +113,9 @@ public:
// MediaClock::getMediaTime() and MediaClock::getRealTimeFor().
sp<const MediaClock> getMediaClock();
+ // Get the play time for pending audio frames in audio sink.
+ status_t getPlayTimeForPendingAudioFrames(int64_t *outTimeUs);
+
protected:
virtual void onMessageReceived(const sp<AMessage> &msg);
diff --git a/include/media/stagefright/MetaData.h b/include/media/stagefright/MetaData.h
index 8bdebf6..ca80123 100644
--- a/include/media/stagefright/MetaData.h
+++ b/include/media/stagefright/MetaData.h
@@ -176,6 +176,9 @@ enum {
kKeyTrackIsDefault = 'dflt', // bool (int32_t)
// Similar to MediaFormat.KEY_IS_FORCED_SUBTITLE but pertains to av tracks as well.
kKeyTrackIsForced = 'frcd', // bool (int32_t)
+
+ // H264 supplemental enhancement information offsets/sizes
+ kKeySEI = 'sei ', // raw data
};
enum {
diff --git a/media/libmedia/IResourceManagerClient.cpp b/media/libmedia/IResourceManagerClient.cpp
index 6fa56fc..b3f56e8 100644
--- a/media/libmedia/IResourceManagerClient.cpp
+++ b/media/libmedia/IResourceManagerClient.cpp
@@ -25,6 +25,7 @@ namespace android {
enum {
RECLAIM_RESOURCE = IBinder::FIRST_CALL_TRANSACTION,
+ GET_NAME,
};
class BpResourceManagerClient: public BpInterface<IResourceManagerClient>
@@ -46,6 +47,19 @@ public:
}
return ret;
}
+
+ virtual String8 getName() {
+ Parcel data, reply;
+ data.writeInterfaceToken(IResourceManagerClient::getInterfaceDescriptor());
+
+ String8 ret;
+ status_t status = remote()->transact(GET_NAME, data, &reply);
+ if (status == NO_ERROR) {
+ ret = reply.readString8();
+ }
+ return ret;
+ }
+
};
IMPLEMENT_META_INTERFACE(ResourceManagerClient, "android.media.IResourceManagerClient");
@@ -62,6 +76,12 @@ status_t BnResourceManagerClient::onTransact(
reply->writeInt32(ret);
return NO_ERROR;
} break;
+ case GET_NAME: {
+ CHECK_INTERFACE(IResourceManagerClient, data, reply);
+ String8 ret = getName();
+ reply->writeString8(ret);
+ return NO_ERROR;
+ } break;
default:
return BBinder::onTransact(code, data, reply, flags);
}
diff --git a/media/libmediaplayerservice/StagefrightRecorder.cpp b/media/libmediaplayerservice/StagefrightRecorder.cpp
index fb21c73..8a0b060 100644
--- a/media/libmediaplayerservice/StagefrightRecorder.cpp
+++ b/media/libmediaplayerservice/StagefrightRecorder.cpp
@@ -953,6 +953,7 @@ sp<MediaSource> StagefrightRecorder::createAudioSource() {
if (mAudioTimeScale > 0) {
format->setInt32("time-scale", mAudioTimeScale);
}
+ format->setInt32("priority", 0 /* realtime */);
sp<MediaSource> audioEncoder =
MediaCodecSource::Create(mLooper, format, audioSource);
@@ -1543,6 +1544,11 @@ status_t StagefrightRecorder::setupVideoEncoder(
format->setInt32("level", mVideoEncoderLevel);
}
+ format->setInt32("priority", 0 /* realtime */);
+ if (mCaptureTimeLapse) {
+ format->setFloat("operating-rate", mCaptureFps);
+ }
+
uint32_t flags = 0;
if (mIsMetaDataStoredInVideoBuffers) {
flags |= MediaCodecSource::FLAG_USE_METADATA_INPUT;
diff --git a/media/libmediaplayerservice/nuplayer/GenericSource.cpp b/media/libmediaplayerservice/nuplayer/GenericSource.cpp
index b7a88e7..7eaa0e0 100644
--- a/media/libmediaplayerservice/nuplayer/GenericSource.cpp
+++ b/media/libmediaplayerservice/nuplayer/GenericSource.cpp
@@ -1400,6 +1400,14 @@ sp<ABuffer> NuPlayer::GenericSource::mediaBufferToABuffer(
meta->setInt32("trackIndex", mSubtitleTrack.mIndex);
}
+ uint32_t dataType; // unused
+ const void *seiData;
+ size_t seiLength;
+ if (mb->meta_data()->findData(kKeySEI, &dataType, &seiData, &seiLength)) {
+ sp<ABuffer> sei = ABuffer::CreateAsCopy(seiData, seiLength);;
+ meta->setBuffer("sei", sei);
+ }
+
if (actualTimeUs) {
*actualTimeUs = timeUs;
}
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
index 65e80c3..acc9ef5 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
@@ -658,7 +658,7 @@ status_t NuPlayer::Decoder::fetchInputData(sp<AMessage> &reply) {
#if 0
int64_t mediaTimeUs;
CHECK(accessUnit->meta()->findInt64("timeUs", &mediaTimeUs));
- ALOGV("[%s] feeding input buffer at media time %" PRId64,
+ ALOGV("[%s] feeding input buffer at media time %.3f",
mIsAudio ? "audio" : "video",
mediaTimeUs / 1E6);
#endif
diff --git a/media/libstagefright/MediaCodec.cpp b/media/libstagefright/MediaCodec.cpp
index 93864e4..7065a6e 100644
--- a/media/libstagefright/MediaCodec.cpp
+++ b/media/libstagefright/MediaCodec.cpp
@@ -79,6 +79,21 @@ struct ResourceManagerClient : public BnResourceManagerClient {
return (err == OK);
}
+ virtual String8 getName() {
+ String8 ret;
+ sp<MediaCodec> codec = mMediaCodec.promote();
+ if (codec == NULL) {
+ // codec is already gone.
+ return ret;
+ }
+
+ AString name;
+ if (codec->getName(&name) == OK) {
+ ret.setTo(name.c_str());
+ }
+ return ret;
+ }
+
protected:
virtual ~ResourceManagerClient() {}
@@ -139,12 +154,7 @@ sp<IBatteryStats> MediaCodec::BatteryNotifier::getBatteryService_l() {
return NULL;
}
mDeathNotifier = new DeathNotifier();
- if (IInterface::asBinder(mBatteryStatService)->
- linkToDeath(mDeathNotifier) != OK) {
- mBatteryStatService.clear();
- mDeathNotifier.clear();
- return NULL;
- }
+ IInterface::asBinder(mBatteryStatService)->linkToDeath(mDeathNotifier);
// notify start now if media already started
if (mVideoRefCount > 0) {
mBatteryStatService->noteStartVideo(AID_MEDIA);
@@ -179,8 +189,9 @@ void MediaCodec::BatteryNotifier::noteStopVideo() {
return;
}
- mVideoRefCount--;
sp<IBatteryStats> batteryService = getBatteryService_l();
+
+ mVideoRefCount--;
if (mVideoRefCount == 0 && batteryService != NULL) {
batteryService->noteStopVideo(AID_MEDIA);
}
@@ -202,8 +213,9 @@ void MediaCodec::BatteryNotifier::noteStopAudio() {
return;
}
- mAudioRefCount--;
sp<IBatteryStats> batteryService = getBatteryService_l();
+
+ mAudioRefCount--;
if (mAudioRefCount == 0 && batteryService != NULL) {
batteryService->noteStopAudio(AID_MEDIA);
}
diff --git a/media/libstagefright/MediaSync.cpp b/media/libstagefright/MediaSync.cpp
index ec956c4..8030a36 100644
--- a/media/libstagefright/MediaSync.cpp
+++ b/media/libstagefright/MediaSync.cpp
@@ -179,6 +179,42 @@ sp<const MediaClock> MediaSync::getMediaClock() {
return mMediaClock;
}
+status_t MediaSync::getPlayTimeForPendingAudioFrames(int64_t *outTimeUs) {
+ Mutex::Autolock lock(mMutex);
+ // User should check the playback rate if it doesn't want to receive a
+ // huge number for play time.
+ if (mPlaybackRate == 0.0f) {
+ *outTimeUs = INT64_MAX;
+ return OK;
+ }
+
+ uint32_t numFramesPlayed = 0;
+ if (mAudioTrack != NULL) {
+ status_t res = mAudioTrack->getPosition(&numFramesPlayed);
+ if (res != OK) {
+ return res;
+ }
+ }
+
+ int64_t numPendingFrames = mNumFramesWritten - numFramesPlayed;
+ if (numPendingFrames < 0) {
+ numPendingFrames = 0;
+ ALOGW("getPlayTimeForPendingAudioFrames: pending frame count is negative.");
+ }
+ double timeUs = numPendingFrames * 1000000.0
+ / (mNativeSampleRateInHz * (double)mPlaybackRate);
+ if (timeUs > (double)INT64_MAX) {
+ // Overflow.
+ *outTimeUs = INT64_MAX;
+ ALOGW("getPlayTimeForPendingAudioFrames: play time for pending audio frames "
+ "is too high, possibly due to super low playback rate(%f)", mPlaybackRate);
+ } else {
+ *outTimeUs = (int64_t)timeUs;
+ }
+
+ return OK;
+}
+
status_t MediaSync::updateQueuedAudioData(
size_t sizeInBytes, int64_t presentationTimeUs) {
if (sizeInBytes == 0) {
@@ -195,13 +231,14 @@ status_t MediaSync::updateQueuedAudioData(
int64_t numFrames = sizeInBytes / mAudioTrack->frameSize();
int64_t maxMediaTimeUs = presentationTimeUs
+ getDurationIfPlayedAtNativeSampleRate_l(numFrames);
- mNumFramesWritten += numFrames;
int64_t nowUs = ALooper::GetNowUs();
- int64_t nowMediaUs = maxMediaTimeUs
+ int64_t nowMediaUs = presentationTimeUs
- getDurationIfPlayedAtNativeSampleRate_l(mNumFramesWritten)
+ getPlayedOutAudioDurationMedia_l(nowUs);
+ mNumFramesWritten += numFrames;
+
int64_t oldRealTime = -1;
if (mNextBufferItemMediaUs != -1) {
oldRealTime = getRealTime(mNextBufferItemMediaUs, nowUs);
@@ -212,12 +249,13 @@ status_t MediaSync::updateQueuedAudioData(
if (oldRealTime != -1) {
int64_t newRealTime = getRealTime(mNextBufferItemMediaUs, nowUs);
- if (newRealTime < oldRealTime) {
- mNextBufferItemMediaUs = -1;
- onDrainVideo_l();
+ if (newRealTime >= oldRealTime) {
+ return OK;
}
}
+ mNextBufferItemMediaUs = -1;
+ onDrainVideo_l();
return OK;
}
@@ -316,12 +354,12 @@ void MediaSync::onDrainVideo_l() {
return;
}
- int64_t nowUs = ALooper::GetNowUs();
-
while (!mBufferItems.empty()) {
+ int64_t nowUs = ALooper::GetNowUs();
BufferItem *bufferItem = &*mBufferItems.begin();
int64_t itemMediaUs = bufferItem->mTimestamp / 1000;
int64_t itemRealUs = getRealTime(itemMediaUs, nowUs);
+
if (itemRealUs <= nowUs) {
if (mHasAudio) {
if (nowUs - itemRealUs <= kMaxAllowedVideoLateTimeUs) {
@@ -341,15 +379,13 @@ void MediaSync::onDrainVideo_l() {
}
mBufferItems.erase(mBufferItems.begin());
-
- if (mBufferItems.empty()) {
- mNextBufferItemMediaUs = -1;
- }
+ mNextBufferItemMediaUs = -1;
} else {
if (mNextBufferItemMediaUs == -1
- || mNextBufferItemMediaUs != itemMediaUs) {
+ || mNextBufferItemMediaUs > itemMediaUs) {
sp<AMessage> msg = new AMessage(kWhatDrainVideo, this);
msg->post(itemRealUs - nowUs);
+ mNextBufferItemMediaUs = itemMediaUs;
}
break;
}
@@ -395,7 +431,9 @@ void MediaSync::onFrameAvailableFromInput() {
}
mBufferItems.push_back(bufferItem);
- onDrainVideo_l();
+ if (mBufferItems.size() == 1) {
+ onDrainVideo_l();
+ }
}
void MediaSync::renderOneBufferItem_l( const BufferItem &bufferItem) {
@@ -499,6 +537,20 @@ void MediaSync::onMessageReceived(const sp<AMessage> &msg) {
case kWhatDrainVideo:
{
Mutex::Autolock lock(mMutex);
+ if (mNextBufferItemMediaUs != -1) {
+ int64_t nowUs = ALooper::GetNowUs();
+ int64_t itemRealUs = getRealTime(mNextBufferItemMediaUs, nowUs);
+
+ // The message could arrive earlier than expected due to
+ // various reasons, e.g., media clock has been changed because
+ // of new anchor time or playback rate. In such cases, the
+ // message needs to be re-posted.
+ if (itemRealUs > nowUs) {
+ msg->post(itemRealUs - nowUs);
+ break;
+ }
+ }
+
onDrainVideo_l();
break;
}
diff --git a/media/libstagefright/httplive/Android.mk b/media/libstagefright/httplive/Android.mk
index 2639deb..fc85835 100644
--- a/media/libstagefright/httplive/Android.mk
+++ b/media/libstagefright/httplive/Android.mk
@@ -3,6 +3,7 @@ LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_SRC_FILES:= \
+ HTTPDownloader.cpp \
LiveDataSource.cpp \
LiveSession.cpp \
M3UParser.cpp \
diff --git a/media/libstagefright/httplive/HTTPDownloader.cpp b/media/libstagefright/httplive/HTTPDownloader.cpp
new file mode 100644
index 0000000..3b44bae
--- /dev/null
+++ b/media/libstagefright/httplive/HTTPDownloader.cpp
@@ -0,0 +1,273 @@
+/*
+ * Copyright 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_NDEBUG 0
+#define LOG_TAG "HTTPDownloader"
+#include <utils/Log.h>
+
+#include "HTTPDownloader.h"
+#include "M3UParser.h"
+
+#include <media/IMediaHTTPConnection.h>
+#include <media/IMediaHTTPService.h>
+#include <media/stagefright/foundation/ABuffer.h>
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/MediaHTTP.h>
+#include <media/stagefright/DataSource.h>
+#include <media/stagefright/FileSource.h>
+#include <openssl/aes.h>
+#include <openssl/md5.h>
+#include <utils/Mutex.h>
+
+namespace android {
+
+HTTPDownloader::HTTPDownloader(
+ const sp<IMediaHTTPService> &httpService,
+ const KeyedVector<String8, String8> &headers) :
+ mHTTPDataSource(new MediaHTTP(httpService->makeHTTPConnection())),
+ mExtraHeaders(headers),
+ mDisconnecting(false) {
+}
+
+void HTTPDownloader::reconnect() {
+ AutoMutex _l(mLock);
+ mDisconnecting = false;
+}
+
+void HTTPDownloader::disconnect() {
+ {
+ AutoMutex _l(mLock);
+ mDisconnecting = true;
+ }
+ mHTTPDataSource->disconnect();
+}
+
+bool HTTPDownloader::isDisconnecting() {
+ AutoMutex _l(mLock);
+ return mDisconnecting;
+}
+
+/*
+ * Illustration of parameters:
+ *
+ * 0 `range_offset`
+ * +------------+-------------------------------------------------------+--+--+
+ * | | | next block to fetch | | |
+ * | | `source` handle => `out` buffer | | | |
+ * | `url` file |<--------- buffer size --------->|<--- `block_size` -->| | |
+ * | |<----------- `range_length` / buffer capacity ----------->| |
+ * |<------------------------------ file_size ------------------------------->|
+ *
+ * Special parameter values:
+ * - range_length == -1 means entire file
+ * - block_size == 0 means entire range
+ *
+ */
+ssize_t HTTPDownloader::fetchBlock(
+ const char *url, sp<ABuffer> *out,
+ int64_t range_offset, int64_t range_length,
+ uint32_t block_size, /* download block size */
+ String8 *actualUrl,
+ bool reconnect /* force connect HTTP when resuing source */) {
+ if (isDisconnecting()) {
+ return ERROR_NOT_CONNECTED;
+ }
+
+ off64_t size;
+
+ if (reconnect) {
+ if (!strncasecmp(url, "file://", 7)) {
+ mDataSource = new FileSource(url + 7);
+ } else if (strncasecmp(url, "http://", 7)
+ && strncasecmp(url, "https://", 8)) {
+ return ERROR_UNSUPPORTED;
+ } else {
+ KeyedVector<String8, String8> headers = mExtraHeaders;
+ if (range_offset > 0 || range_length >= 0) {
+ headers.add(
+ String8("Range"),
+ String8(
+ AStringPrintf(
+ "bytes=%lld-%s",
+ range_offset,
+ range_length < 0
+ ? "" : AStringPrintf("%lld",
+ range_offset + range_length - 1).c_str()).c_str()));
+ }
+
+ status_t err = mHTTPDataSource->connect(url, &headers);
+
+ if (isDisconnecting()) {
+ return ERROR_NOT_CONNECTED;
+ }
+
+ if (err != OK) {
+ return err;
+ }
+
+ mDataSource = mHTTPDataSource;
+ }
+ }
+
+ status_t getSizeErr = mDataSource->getSize(&size);
+
+ if (isDisconnecting()) {
+ return ERROR_NOT_CONNECTED;
+ }
+
+ if (getSizeErr != OK) {
+ size = 65536;
+ }
+
+ sp<ABuffer> buffer = *out != NULL ? *out : new ABuffer(size);
+ if (*out == NULL) {
+ buffer->setRange(0, 0);
+ }
+
+ ssize_t bytesRead = 0;
+ // adjust range_length if only reading partial block
+ if (block_size > 0 && (range_length == -1 || (int64_t)(buffer->size() + block_size) < range_length)) {
+ range_length = buffer->size() + block_size;
+ }
+ for (;;) {
+ // Only resize when we don't know the size.
+ size_t bufferRemaining = buffer->capacity() - buffer->size();
+ if (bufferRemaining == 0 && getSizeErr != OK) {
+ size_t bufferIncrement = buffer->size() / 2;
+ if (bufferIncrement < 32768) {
+ bufferIncrement = 32768;
+ }
+ bufferRemaining = bufferIncrement;
+
+ ALOGV("increasing download buffer to %zu bytes",
+ buffer->size() + bufferRemaining);
+
+ sp<ABuffer> copy = new ABuffer(buffer->size() + bufferRemaining);
+ memcpy(copy->data(), buffer->data(), buffer->size());
+ copy->setRange(0, buffer->size());
+
+ buffer = copy;
+ }
+
+ size_t maxBytesToRead = bufferRemaining;
+ if (range_length >= 0) {
+ int64_t bytesLeftInRange = range_length - buffer->size();
+ if (bytesLeftInRange < (int64_t)maxBytesToRead) {
+ maxBytesToRead = bytesLeftInRange;
+
+ if (bytesLeftInRange == 0) {
+ break;
+ }
+ }
+ }
+
+ // The DataSource is responsible for informing us of error (n < 0) or eof (n == 0)
+ // to help us break out of the loop.
+ ssize_t n = mDataSource->readAt(
+ buffer->size(), buffer->data() + buffer->size(),
+ maxBytesToRead);
+
+ if (isDisconnecting()) {
+ return ERROR_NOT_CONNECTED;
+ }
+
+ if (n < 0) {
+ return n;
+ }
+
+ if (n == 0) {
+ break;
+ }
+
+ buffer->setRange(0, buffer->size() + (size_t)n);
+ bytesRead += n;
+ }
+
+ *out = buffer;
+ if (actualUrl != NULL) {
+ *actualUrl = mDataSource->getUri();
+ if (actualUrl->isEmpty()) {
+ *actualUrl = url;
+ }
+ }
+
+ return bytesRead;
+}
+
+ssize_t HTTPDownloader::fetchFile(
+ const char *url, sp<ABuffer> *out, String8 *actualUrl) {
+ ssize_t err = fetchBlock(url, out, 0, -1, 0, actualUrl, true /* reconnect */);
+
+ // close off the connection after use
+ mHTTPDataSource->disconnect();
+
+ return err;
+}
+
+sp<M3UParser> HTTPDownloader::fetchPlaylist(
+ const char *url, uint8_t *curPlaylistHash, bool *unchanged) {
+ ALOGV("fetchPlaylist '%s'", url);
+
+ *unchanged = false;
+
+ sp<ABuffer> buffer;
+ String8 actualUrl;
+ ssize_t err = fetchFile(url, &buffer, &actualUrl);
+
+ // close off the connection after use
+ mHTTPDataSource->disconnect();
+
+ if (err <= 0) {
+ return NULL;
+ }
+
+ // MD5 functionality is not available on the simulator, treat all
+ // playlists as changed.
+
+#if defined(HAVE_ANDROID_OS)
+ uint8_t hash[16];
+
+ MD5_CTX m;
+ MD5_Init(&m);
+ MD5_Update(&m, buffer->data(), buffer->size());
+
+ MD5_Final(hash, &m);
+
+ if (curPlaylistHash != NULL && !memcmp(hash, curPlaylistHash, 16)) {
+ // playlist unchanged
+ *unchanged = true;
+
+ return NULL;
+ }
+
+ if (curPlaylistHash != NULL) {
+ memcpy(curPlaylistHash, hash, sizeof(hash));
+ }
+#endif
+
+ sp<M3UParser> playlist =
+ new M3UParser(actualUrl.string(), buffer->data(), buffer->size());
+
+ if (playlist->initCheck() != OK) {
+ ALOGE("failed to parse .m3u8 playlist");
+
+ return NULL;
+ }
+
+ return playlist;
+}
+
+} // namespace android
diff --git a/media/libstagefright/httplive/HTTPDownloader.h b/media/libstagefright/httplive/HTTPDownloader.h
new file mode 100644
index 0000000..1db4a48
--- /dev/null
+++ b/media/libstagefright/httplive/HTTPDownloader.h
@@ -0,0 +1,86 @@
+/*
+ * Copyright 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 HTTP_DOWNLOADER_H_
+
+#define HTTP_DOWNLOADER_H_
+
+#include <media/stagefright/foundation/ADebug.h>
+#include <utils/KeyedVector.h>
+#include <utils/Mutex.h>
+#include <utils/RefBase.h>
+
+namespace android {
+
+struct ABuffer;
+class DataSource;
+struct HTTPBase;
+struct IMediaHTTPService;
+struct M3UParser;
+
+struct HTTPDownloader : public RefBase {
+ HTTPDownloader(
+ const sp<IMediaHTTPService> &httpService,
+ const KeyedVector<String8, String8> &headers);
+
+ void reconnect();
+ void disconnect();
+ bool isDisconnecting();
+ // If given a non-zero block_size (default 0), it is used to cap the number of
+ // bytes read in from the DataSource. If given a non-NULL buffer, new content
+ // is read into the end.
+ //
+ // The DataSource we read from is responsible for signaling error or EOF to help us
+ // break out of the read loop. The DataSource can be returned to the caller, so
+ // that the caller can reuse it for subsequent fetches (within the initially
+ // requested range).
+ //
+ // For reused HTTP sources, the caller must download a file sequentially without
+ // any overlaps or gaps to prevent reconnection.
+ ssize_t fetchBlock(
+ const char *url,
+ sp<ABuffer> *out,
+ int64_t range_offset, /* open file at range_offset */
+ int64_t range_length, /* open file for range_length (-1: entire file) */
+ uint32_t block_size, /* download block size (0: entire range) */
+ String8 *actualUrl, /* returns actual URL */
+ bool reconnect /* force connect http */
+ );
+
+ // simplified version to fetch a single file
+ ssize_t fetchFile(
+ const char *url,
+ sp<ABuffer> *out,
+ String8 *actualUrl = NULL);
+
+ // fetch a playlist file
+ sp<M3UParser> fetchPlaylist(
+ const char *url, uint8_t *curPlaylistHash, bool *unchanged);
+
+private:
+ sp<HTTPBase> mHTTPDataSource;
+ sp<DataSource> mDataSource;
+ KeyedVector<String8, String8> mExtraHeaders;
+
+ Mutex mLock;
+ bool mDisconnecting;
+
+ DISALLOW_EVIL_CONSTRUCTORS(HTTPDownloader);
+};
+
+} // namespace android
+
+#endif // HTTP_DOWNLOADER_H_
diff --git a/media/libstagefright/httplive/LiveSession.cpp b/media/libstagefright/httplive/LiveSession.cpp
index 9ac764c..d8c38e7 100644
--- a/media/libstagefright/httplive/LiveSession.cpp
+++ b/media/libstagefright/httplive/LiveSession.cpp
@@ -19,25 +19,18 @@
#include <utils/Log.h>
#include "LiveSession.h"
-
+#include "HTTPDownloader.h"
#include "M3UParser.h"
#include "PlaylistFetcher.h"
-#include "include/HTTPBase.h"
#include "mpeg2ts/AnotherPacketSource.h"
#include <cutils/properties.h>
-#include <media/IMediaHTTPConnection.h>
#include <media/IMediaHTTPService.h>
-#include <media/stagefright/foundation/hexdump.h>
#include <media/stagefright/foundation/ABuffer.h>
#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/foundation/AMessage.h>
#include <media/stagefright/foundation/AUtils.h>
-#include <media/stagefright/DataSource.h>
-#include <media/stagefright/FileSource.h>
-#include <media/stagefright/MediaErrors.h>
-#include <media/stagefright/MediaHTTP.h>
#include <media/stagefright/MediaDefs.h>
#include <media/stagefright/MetaData.h>
#include <media/stagefright/Utils.h>
@@ -46,8 +39,6 @@
#include <ctype.h>
#include <inttypes.h>
-#include <openssl/aes.h>
-#include <openssl/md5.h>
namespace android {
@@ -257,7 +248,6 @@ LiveSession::LiveSession(
mInPreparationPhase(true),
mPollBufferingGeneration(0),
mPrevBufferPercentage(-1),
- mHTTPDataSource(new MediaHTTP(mHTTPService->makeHTTPConnection())),
mCurBandwidthIndex(-1),
mOrigBandwidthIndex(-1),
mLastBandwidthBps(-1ll),
@@ -317,7 +307,7 @@ status_t LiveSession::dequeueAccessUnit(
ssize_t streamIdx = typeToIndex(stream);
if (streamIdx < 0) {
- return INVALID_VALUE;
+ return BAD_VALUE;
}
const char *streamStr = getNameForStream(stream);
// Do not let client pull data if we don't have data packets yet.
@@ -465,8 +455,8 @@ status_t LiveSession::getStreamFormat(StreamType stream, sp<AMessage> *format) {
return convertMetaDataToMessage(meta, format);
}
-sp<HTTPBase> LiveSession::getHTTPDataSource() {
- return new MediaHTTP(mHTTPService->makeHTTPConnection());
+sp<HTTPDownloader> LiveSession::getHTTPDownloader() {
+ return new HTTPDownloader(mHTTPService, mExtraHeaders);
}
void LiveSession::connectAsync(
@@ -838,6 +828,12 @@ void LiveSession::onMessageReceived(const sp<AMessage> &msg) {
break;
}
+ case PlaylistFetcher::kWhatPlaylistFetched:
+ {
+ onMasterPlaylistFetched(msg);
+ break;
+ }
+
case PlaylistFetcher::kWhatMetadataDetected:
{
if (!mHasMetadata) {
@@ -874,12 +870,6 @@ void LiveSession::onMessageReceived(const sp<AMessage> &msg) {
break;
}
- case kWhatFinishDisconnect2:
- {
- onFinishDisconnect2();
- break;
- }
-
case kWhatPollBuffering:
{
int32_t generation;
@@ -931,8 +921,10 @@ ssize_t LiveSession::typeToIndex(int32_t type) {
}
void LiveSession::onConnect(const sp<AMessage> &msg) {
- AString url;
- CHECK(msg->findString("url", &url));
+ CHECK(msg->findString("url", &mMasterURL));
+
+ // TODO currently we don't know if we are coming here from incognito mode
+ ALOGI("onConnect %s", uriDebugString(mMasterURL).c_str());
KeyedVector<String8, String8> *headers = NULL;
if (!msg->findPointer("headers", (void **)&headers)) {
@@ -944,21 +936,6 @@ void LiveSession::onConnect(const sp<AMessage> &msg) {
headers = NULL;
}
- // TODO currently we don't know if we are coming here from incognito mode
- ALOGI("onConnect %s", uriDebugString(url).c_str());
-
- mMasterURL = url;
-
- bool dummy;
- mPlaylist = fetchPlaylist(url.c_str(), NULL /* curPlaylistHash */, &dummy);
-
- if (mPlaylist == NULL) {
- ALOGE("unable to fetch master playlist %s.", uriDebugString(url).c_str());
-
- postPrepared(ERROR_IO);
- return;
- }
-
// create looper for fetchers
if (mFetcherLooper == NULL) {
mFetcherLooper = new ALooper();
@@ -967,6 +944,31 @@ void LiveSession::onConnect(const sp<AMessage> &msg) {
mFetcherLooper->start(false, false);
}
+ // create fetcher to fetch the master playlist
+ addFetcher(mMasterURL.c_str())->fetchPlaylistAsync();
+}
+
+void LiveSession::onMasterPlaylistFetched(const sp<AMessage> &msg) {
+ AString uri;
+ CHECK(msg->findString("uri", &uri));
+ ssize_t index = mFetcherInfos.indexOfKey(uri);
+ if (index < 0) {
+ ALOGW("fetcher for master playlist is gone.");
+ return;
+ }
+
+ // no longer useful, remove
+ mFetcherLooper->unregisterHandler(mFetcherInfos[index].mFetcher->id());
+ mFetcherInfos.removeItemsAt(index);
+
+ CHECK(msg->findObject("playlist", (sp<RefBase> *)&mPlaylist));
+ if (mPlaylist == NULL) {
+ ALOGE("unable to fetch master playlist %s.",
+ uriDebugString(mMasterURL).c_str());
+
+ postPrepared(ERROR_IO);
+ return;
+ }
// We trust the content provider to make a reasonable choice of preferred
// initial bandwidth by listing it first in the variant playlist.
// At startup we really don't have a good estimate on the available
@@ -1050,22 +1052,26 @@ void LiveSession::finishDisconnect() {
// cancel buffer polling
cancelPollBuffering();
+ // TRICKY: don't wait for all fetcher to be stopped when disconnecting
+ //
+ // Some fetchers might be stuck in connect/getSize at this point. These
+ // operations will eventually timeout (as we have a timeout set in
+ // MediaHTTPConnection), but we don't want to block the main UI thread
+ // until then. Here we just need to make sure we clear all references
+ // to the fetchers, so that when they finally exit from the blocking
+ // operation, they can be destructed.
+ //
+ // There is one very tricky point though. For this scheme to work, the
+ // fecther must hold a reference to LiveSession, so that LiveSession is
+ // destroyed after fetcher. Otherwise LiveSession would get stuck in its
+ // own destructor when it waits for mFetcherLooper to stop, which still
+ // blocks main UI thread.
for (size_t i = 0; i < mFetcherInfos.size(); ++i) {
mFetcherInfos.valueAt(i).mFetcher->stopAsync();
+ mFetcherLooper->unregisterHandler(
+ mFetcherInfos.valueAt(i).mFetcher->id());
}
-
- sp<AMessage> msg = new AMessage(kWhatFinishDisconnect2, this);
-
- mContinuationCounter = mFetcherInfos.size();
- mContinuation = msg;
-
- if (mContinuationCounter == 0) {
- msg->post();
- }
-}
-
-void LiveSession::onFinishDisconnect2() {
- mContinuation.clear();
+ mFetcherInfos.clear();
mPacketSources.valueFor(STREAMTYPE_AUDIO)->signalEOS(ERROR_END_OF_STREAM);
mPacketSources.valueFor(STREAMTYPE_VIDEO)->signalEOS(ERROR_END_OF_STREAM);
@@ -1104,198 +1110,6 @@ sp<PlaylistFetcher> LiveSession::addFetcher(const char *uri) {
return info.mFetcher;
}
-/*
- * Illustration of parameters:
- *
- * 0 `range_offset`
- * +------------+-------------------------------------------------------+--+--+
- * | | | next block to fetch | | |
- * | | `source` handle => `out` buffer | | | |
- * | `url` file |<--------- buffer size --------->|<--- `block_size` -->| | |
- * | |<----------- `range_length` / buffer capacity ----------->| |
- * |<------------------------------ file_size ------------------------------->|
- *
- * Special parameter values:
- * - range_length == -1 means entire file
- * - block_size == 0 means entire range
- *
- */
-ssize_t LiveSession::fetchFile(
- const char *url, sp<ABuffer> *out,
- int64_t range_offset, int64_t range_length,
- uint32_t block_size, /* download block size */
- sp<DataSource> *source, /* to return and reuse source */
- String8 *actualUrl,
- bool forceConnectHTTP /* force connect HTTP when resuing source */) {
- off64_t size;
- sp<DataSource> temp_source;
- if (source == NULL) {
- source = &temp_source;
- }
-
- if (*source == NULL || forceConnectHTTP) {
- if (!strncasecmp(url, "file://", 7)) {
- *source = new FileSource(url + 7);
- } else if (strncasecmp(url, "http://", 7)
- && strncasecmp(url, "https://", 8)) {
- return ERROR_UNSUPPORTED;
- } else {
- KeyedVector<String8, String8> headers = mExtraHeaders;
- if (range_offset > 0 || range_length >= 0) {
- headers.add(
- String8("Range"),
- String8(
- AStringPrintf(
- "bytes=%lld-%s",
- range_offset,
- range_length < 0
- ? "" : AStringPrintf("%lld",
- range_offset + range_length - 1).c_str()).c_str()));
- }
-
- HTTPBase* httpDataSource =
- (*source == NULL) ? mHTTPDataSource.get() : (HTTPBase*)source->get();
- status_t err = httpDataSource->connect(url, &headers);
-
- if (err != OK) {
- return err;
- }
-
- if (*source == NULL) {
- *source = mHTTPDataSource;
- }
- }
- }
-
- status_t getSizeErr = (*source)->getSize(&size);
- if (getSizeErr != OK) {
- size = 65536;
- }
-
- sp<ABuffer> buffer = *out != NULL ? *out : new ABuffer(size);
- if (*out == NULL) {
- buffer->setRange(0, 0);
- }
-
- ssize_t bytesRead = 0;
- // adjust range_length if only reading partial block
- if (block_size > 0 && (range_length == -1 || (int64_t)(buffer->size() + block_size) < range_length)) {
- range_length = buffer->size() + block_size;
- }
- for (;;) {
- // Only resize when we don't know the size.
- size_t bufferRemaining = buffer->capacity() - buffer->size();
- if (bufferRemaining == 0 && getSizeErr != OK) {
- size_t bufferIncrement = buffer->size() / 2;
- if (bufferIncrement < 32768) {
- bufferIncrement = 32768;
- }
- bufferRemaining = bufferIncrement;
-
- ALOGV("increasing download buffer to %zu bytes",
- buffer->size() + bufferRemaining);
-
- sp<ABuffer> copy = new ABuffer(buffer->size() + bufferRemaining);
- memcpy(copy->data(), buffer->data(), buffer->size());
- copy->setRange(0, buffer->size());
-
- buffer = copy;
- }
-
- size_t maxBytesToRead = bufferRemaining;
- if (range_length >= 0) {
- int64_t bytesLeftInRange = range_length - buffer->size();
- if (bytesLeftInRange < (int64_t)maxBytesToRead) {
- maxBytesToRead = bytesLeftInRange;
-
- if (bytesLeftInRange == 0) {
- break;
- }
- }
- }
-
- // The DataSource is responsible for informing us of error (n < 0) or eof (n == 0)
- // to help us break out of the loop.
- ssize_t n = (*source)->readAt(
- buffer->size(), buffer->data() + buffer->size(),
- maxBytesToRead);
-
- if (n < 0) {
- return n;
- }
-
- if (n == 0) {
- break;
- }
-
- buffer->setRange(0, buffer->size() + (size_t)n);
- bytesRead += n;
- }
-
- *out = buffer;
- if (actualUrl != NULL) {
- *actualUrl = (*source)->getUri();
- if (actualUrl->isEmpty()) {
- *actualUrl = url;
- }
- }
-
- return bytesRead;
-}
-
-sp<M3UParser> LiveSession::fetchPlaylist(
- const char *url, uint8_t *curPlaylistHash, bool *unchanged) {
- ALOGV("fetchPlaylist '%s'", url);
-
- *unchanged = false;
-
- sp<ABuffer> buffer;
- String8 actualUrl;
- ssize_t err = fetchFile(url, &buffer, 0, -1, 0, NULL, &actualUrl);
-
- // close off the connection after use
- mHTTPDataSource->disconnect();
-
- if (err <= 0) {
- return NULL;
- }
-
- // MD5 functionality is not available on the simulator, treat all
- // playlists as changed.
-
-#if defined(HAVE_ANDROID_OS)
- uint8_t hash[16];
-
- MD5_CTX m;
- MD5_Init(&m);
- MD5_Update(&m, buffer->data(), buffer->size());
-
- MD5_Final(hash, &m);
-
- if (curPlaylistHash != NULL && !memcmp(hash, curPlaylistHash, 16)) {
- // playlist unchanged
- *unchanged = true;
-
- return NULL;
- }
-
- if (curPlaylistHash != NULL) {
- memcpy(curPlaylistHash, hash, sizeof(hash));
- }
-#endif
-
- sp<M3UParser> playlist =
- new M3UParser(actualUrl.string(), buffer->data(), buffer->size());
-
- if (playlist->initCheck() != OK) {
- ALOGE("failed to parse .m3u8 playlist");
-
- return NULL;
- }
-
- return playlist;
-}
-
#if 0
static double uniformRand() {
return (double)rand() / RAND_MAX;
@@ -1690,9 +1504,11 @@ void LiveSession::changeConfiguration(
fetcher->stopAsync();
} else {
float threshold = -1.0f; // always finish fetching by default
+ bool disconnect = false;
if (timeUs >= 0ll) {
// seeking, no need to finish fetching
threshold = 0.0f;
+ disconnect = true;
} else if (delayRemoval) {
// adapting, abort if remaining of current segment is over threshold
threshold = getAbortThreshold(
@@ -1701,7 +1517,7 @@ void LiveSession::changeConfiguration(
ALOGV("pausing fetcher-%d, threshold=%.2f",
fetcher->getFetcherID(), threshold);
- fetcher->pauseAsync(threshold);
+ fetcher->pauseAsync(threshold, disconnect);
}
}
diff --git a/media/libstagefright/httplive/LiveSession.h b/media/libstagefright/httplive/LiveSession.h
index 4e7ccac..21be413 100644
--- a/media/libstagefright/httplive/LiveSession.h
+++ b/media/libstagefright/httplive/LiveSession.h
@@ -37,6 +37,7 @@ struct LiveDataSource;
struct M3UParser;
struct PlaylistFetcher;
struct HLSTime;
+struct HTTPDownloader;
struct LiveSession : public AHandler {
enum Flags {
@@ -76,7 +77,7 @@ struct LiveSession : public AHandler {
status_t getStreamFormat(StreamType stream, sp<AMessage> *format);
- sp<HTTPBase> getHTTPDataSource();
+ sp<HTTPDownloader> getHTTPDownloader();
void connectAsync(
const char *url,
@@ -127,7 +128,6 @@ private:
kWhatChangeConfiguration = 'chC0',
kWhatChangeConfiguration2 = 'chC2',
kWhatChangeConfiguration3 = 'chC3',
- kWhatFinishDisconnect2 = 'fin2',
kWhatPollBuffering = 'poll',
};
@@ -191,7 +191,6 @@ private:
int32_t mPollBufferingGeneration;
int32_t mPrevBufferPercentage;
- sp<HTTPBase> mHTTPDataSource;
KeyedVector<String8, String8> mExtraHeaders;
AString mMasterURL;
@@ -253,34 +252,8 @@ private:
sp<PlaylistFetcher> addFetcher(const char *uri);
void onConnect(const sp<AMessage> &msg);
+ void onMasterPlaylistFetched(const sp<AMessage> &msg);
void onSeek(const sp<AMessage> &msg);
- void onFinishDisconnect2();
-
- // If given a non-zero block_size (default 0), it is used to cap the number of
- // bytes read in from the DataSource. If given a non-NULL buffer, new content
- // is read into the end.
- //
- // The DataSource we read from is responsible for signaling error or EOF to help us
- // break out of the read loop. The DataSource can be returned to the caller, so
- // that the caller can reuse it for subsequent fetches (within the initially
- // requested range).
- //
- // For reused HTTP sources, the caller must download a file sequentially without
- // any overlaps or gaps to prevent reconnection.
- ssize_t fetchFile(
- const char *url, sp<ABuffer> *out,
- /* request/open a file starting at range_offset for range_length bytes */
- int64_t range_offset = 0, int64_t range_length = -1,
- /* download block size */
- uint32_t block_size = 0,
- /* reuse DataSource if doing partial fetch */
- sp<DataSource> *source = NULL,
- String8 *actualUrl = NULL,
- /* force connect http even when resuing DataSource */
- bool forceConnectHTTP = false);
-
- sp<M3UParser> fetchPlaylist(
- const char *url, uint8_t *curPlaylistHash, bool *unchanged);
bool UriIsSameAsIndex( const AString &uri, int32_t index, bool newUri);
sp<AnotherPacketSource> getPacketSourceForStreamIndex(size_t trackIndex, bool newUri);
diff --git a/media/libstagefright/httplive/PlaylistFetcher.cpp b/media/libstagefright/httplive/PlaylistFetcher.cpp
index 8350c1b..53087b6 100644
--- a/media/libstagefright/httplive/PlaylistFetcher.cpp
+++ b/media/libstagefright/httplive/PlaylistFetcher.cpp
@@ -20,23 +20,16 @@
#include <utils/misc.h>
#include "PlaylistFetcher.h"
-
-#include "LiveDataSource.h"
+#include "HTTPDownloader.h"
#include "LiveSession.h"
#include "M3UParser.h"
-
#include "include/avc_utils.h"
-#include "include/HTTPBase.h"
#include "include/ID3.h"
#include "mpeg2ts/AnotherPacketSource.h"
-#include <media/IStreamSource.h>
#include <media/stagefright/foundation/ABitReader.h>
#include <media/stagefright/foundation/ABuffer.h>
#include <media/stagefright/foundation/ADebug.h>
-#include <media/stagefright/foundation/AUtils.h>
-#include <media/stagefright/foundation/hexdump.h>
-#include <media/stagefright/FileSource.h>
#include <media/stagefright/MediaDefs.h>
#include <media/stagefright/MetaData.h>
#include <media/stagefright/Utils.h>
@@ -44,7 +37,6 @@
#include <ctype.h>
#include <inttypes.h>
#include <openssl/aes.h>
-#include <openssl/md5.h>
#define FLOGV(fmt, ...) ALOGV("[fetcher-%d] " fmt, mFetcherID, ##__VA_ARGS__)
#define FSLOGV(stream, fmt, ...) ALOGV("[fetcher-%d] [%s] " fmt, mFetcherID, \
@@ -179,7 +171,7 @@ PlaylistFetcher::PlaylistFetcher(
mDownloadState(new DownloadState()),
mHasMetadata(false) {
memset(mPlaylistHash, 0, sizeof(mPlaylistHash));
- mHTTPDataSource = mSession->getHTTPDataSource();
+ mHTTPDownloader = mSession->getHTTPDownloader();
}
PlaylistFetcher::~PlaylistFetcher() {
@@ -338,9 +330,11 @@ status_t PlaylistFetcher::decryptBuffer(
if (index >= 0) {
key = mAESKeyForURI.valueAt(index);
} else {
- ssize_t err = mSession->fetchFile(keyURI.c_str(), &key);
+ ssize_t err = mHTTPDownloader->fetchFile(keyURI.c_str(), &key);
- if (err < 0) {
+ if (err == ERROR_NOT_CONNECTED) {
+ return ERROR_NOT_CONNECTED;
+ } else if (err < 0) {
ALOGE("failed to fetch cipher key from '%s'.", keyURI.c_str());
return ERROR_IO;
} else if (key->size() != 16) {
@@ -448,12 +442,32 @@ void PlaylistFetcher::cancelMonitorQueue() {
++mMonitorQueueGeneration;
}
-void PlaylistFetcher::setStoppingThreshold(float thresholdRatio) {
- AutoMutex _l(mThresholdLock);
- if (mStreamTypeMask == LiveSession::STREAMTYPE_SUBTITLES) {
- return;
+void PlaylistFetcher::setStoppingThreshold(float thresholdRatio, bool disconnect) {
+ {
+ AutoMutex _l(mThresholdLock);
+ mThresholdRatio = thresholdRatio;
+ }
+ if (disconnect) {
+ mHTTPDownloader->disconnect();
+ }
+}
+
+void PlaylistFetcher::resetStoppingThreshold(bool disconnect) {
+ {
+ AutoMutex _l(mThresholdLock);
+ mThresholdRatio = -1.0f;
+ }
+ if (disconnect) {
+ mHTTPDownloader->disconnect();
+ } else {
+ // allow reconnect
+ mHTTPDownloader->reconnect();
}
- mThresholdRatio = thresholdRatio;
+}
+
+float PlaylistFetcher::getStoppingThreshold() {
+ AutoMutex _l(mThresholdLock);
+ return mThresholdRatio;
}
void PlaylistFetcher::startAsync(
@@ -497,15 +511,15 @@ void PlaylistFetcher::startAsync(
msg->post();
}
-void PlaylistFetcher::pauseAsync(float thresholdRatio) {
- if (thresholdRatio >= 0.0f) {
- setStoppingThreshold(thresholdRatio);
- }
+void PlaylistFetcher::pauseAsync(
+ float thresholdRatio, bool disconnect) {
+ setStoppingThreshold(thresholdRatio, disconnect);
+
(new AMessage(kWhatPause, this))->post();
}
void PlaylistFetcher::stopAsync(bool clear) {
- setStoppingThreshold(0.0f);
+ setStoppingThreshold(0.0f, true /* disconncect */);
sp<AMessage> msg = new AMessage(kWhatStop, this);
msg->setInt32("clear", clear);
@@ -520,6 +534,10 @@ void PlaylistFetcher::resumeUntilAsync(const sp<AMessage> &params) {
msg->post();
}
+void PlaylistFetcher::fetchPlaylistAsync() {
+ (new AMessage(kWhatFetchPlaylist, this))->post();
+}
+
void PlaylistFetcher::onMessageReceived(const sp<AMessage> &msg) {
switch (msg->what()) {
case kWhatStart:
@@ -557,6 +575,19 @@ void PlaylistFetcher::onMessageReceived(const sp<AMessage> &msg) {
break;
}
+ case kWhatFetchPlaylist:
+ {
+ bool unchanged;
+ sp<M3UParser> playlist = mHTTPDownloader->fetchPlaylist(
+ mURI.c_str(), NULL /* curPlaylistHash */, &unchanged);
+
+ sp<AMessage> notify = mNotify->dup();
+ notify->setInt32("what", kWhatPlaylistFetched);
+ notify->setObject("playlist", playlist);
+ notify->post();
+ break;
+ }
+
case kWhatMonitorQueue:
case kWhatDownloadNext:
{
@@ -676,7 +707,7 @@ void PlaylistFetcher::onPause() {
cancelMonitorQueue();
mLastDiscontinuitySeq = mDiscontinuitySeq;
- setStoppingThreshold(-1.0f);
+ resetStoppingThreshold(false /* disconnect */);
}
void PlaylistFetcher::onStop(const sp<AMessage> &msg) {
@@ -691,14 +722,11 @@ void PlaylistFetcher::onStop(const sp<AMessage> &msg) {
}
}
- // close off the connection after use
- mHTTPDataSource->disconnect();
-
mDownloadState->resetState();
mPacketSources.clear();
mStreamTypeMask = 0;
- setStoppingThreshold(-1.0f);
+ resetStoppingThreshold(true /* disconnect */);
}
// Resume until we have reached the boundary timestamps listed in `msg`; when
@@ -815,7 +843,7 @@ void PlaylistFetcher::onMonitorQueue() {
status_t PlaylistFetcher::refreshPlaylist() {
if (delayUsToRefreshPlaylist() <= 0) {
bool unchanged;
- sp<M3UParser> playlist = mSession->fetchPlaylist(
+ sp<M3UParser> playlist = mHTTPDownloader->fetchPlaylist(
mURI.c_str(), mPlaylistHash, &unchanged);
if (playlist == NULL) {
@@ -864,18 +892,12 @@ bool PlaylistFetcher::shouldPauseDownload() {
}
// Calculate threshold to abort current download
- int64_t targetDurationUs = mPlaylist->getTargetDuration();
- int64_t thresholdUs = -1;
- {
- AutoMutex _l(mThresholdLock);
- thresholdUs = (mThresholdRatio < 0.0f) ?
- -1ll : mThresholdRatio * targetDurationUs;
- }
+ float thresholdRatio = getStoppingThreshold();
- if (thresholdUs < 0) {
+ if (thresholdRatio < 0.0f) {
// never abort
return false;
- } else if (thresholdUs == 0) {
+ } else if (thresholdRatio == 0.0f) {
// immediately abort
return true;
}
@@ -905,6 +927,9 @@ bool PlaylistFetcher::shouldPauseDownload() {
}
lastEnqueueUs -= mSegmentFirstPTS;
+ int64_t targetDurationUs = mPlaylist->getTargetDuration();
+ int64_t thresholdUs = thresholdRatio * targetDurationUs;
+
FLOGV("%spausing now, thresholdUs %lld, remaining %lld",
targetDurationUs - lastEnqueueUs > thresholdUs ? "" : "not ",
(long long)thresholdUs,
@@ -1101,7 +1126,9 @@ bool PlaylistFetcher::initDownloadState(
junk->setRange(0, 16);
status_t err = decryptBuffer(mSeqNumber - firstSeqNumberInPlaylist, junk,
true /* first */);
- if (err != OK) {
+ if (err == ERROR_NOT_CONNECTED) {
+ return false;
+ } else if (err != OK) {
notifyError(err);
return false;
}
@@ -1202,12 +1229,21 @@ void PlaylistFetcher::onDownloadNext() {
bool shouldPause = false;
ssize_t bytesRead;
do {
- sp<DataSource> source = mHTTPDataSource;
-
int64_t startUs = ALooper::GetNowUs();
- bytesRead = mSession->fetchFile(
+ bytesRead = mHTTPDownloader->fetchBlock(
uri.c_str(), &buffer, range_offset, range_length, kDownloadBlockSize,
- &source, NULL, connectHTTP);
+ NULL /* actualURL */, connectHTTP);
+ int64_t delayUs = ALooper::GetNowUs() - startUs;
+
+ if (bytesRead == ERROR_NOT_CONNECTED) {
+ return;
+ }
+ if (bytesRead < 0) {
+ status_t err = bytesRead;
+ ALOGE("failed to fetch .ts segment at url '%s'", uri.c_str());
+ notifyError(err);
+ return;
+ }
// add sample for bandwidth estimation, excluding samples from subtitles (as
// its too small), or during startup/resumeUntil (when we could have more than
@@ -1216,9 +1252,7 @@ void PlaylistFetcher::onDownloadNext() {
&& (mStreamTypeMask
& (LiveSession::STREAMTYPE_AUDIO
| LiveSession::STREAMTYPE_VIDEO))) {
- int64_t delayUs = ALooper::GetNowUs() - startUs;
mSession->addBandwidthMeasurement(bytesRead, delayUs);
-
if (delayUs > 2000000ll) {
FLOGV("bytesRead %zd took %.2f seconds - abnormal bandwidth dip",
bytesRead, (double)delayUs / 1.0e6);
@@ -1227,13 +1261,6 @@ void PlaylistFetcher::onDownloadNext() {
connectHTTP = false;
- if (bytesRead < 0) {
- status_t err = bytesRead;
- ALOGE("failed to fetch .ts segment at url '%s'", uri.c_str());
- notifyError(err);
- return;
- }
-
CHECK(buffer != NULL);
size_t size = buffer->size();
diff --git a/media/libstagefright/httplive/PlaylistFetcher.h b/media/libstagefright/httplive/PlaylistFetcher.h
index 1f5e9b0..c8ca457 100644
--- a/media/libstagefright/httplive/PlaylistFetcher.h
+++ b/media/libstagefright/httplive/PlaylistFetcher.h
@@ -49,6 +49,7 @@ struct PlaylistFetcher : public AHandler {
kWhatPreparationFailed,
kWhatStartedAt,
kWhatStopReached,
+ kWhatPlaylistFetched,
kWhatMetadataDetected,
};
@@ -61,8 +62,6 @@ struct PlaylistFetcher : public AHandler {
int32_t getFetcherID() const;
- sp<DataSource> getDataSource();
-
void startAsync(
const sp<AnotherPacketSource> &audioSource,
const sp<AnotherPacketSource> &videoSource,
@@ -74,12 +73,14 @@ struct PlaylistFetcher : public AHandler {
int32_t startDiscontinuitySeq = -1,
LiveSession::SeekMode seekMode = LiveSession::kSeekModeExactPosition);
- void pauseAsync(float thresholdRatio);
+ void pauseAsync(float thresholdRatio, bool disconnect);
void stopAsync(bool clear = true);
void resumeUntilAsync(const sp<AMessage> &params);
+ void fetchPlaylistAsync();
+
uint32_t getStreamTypeMask() const {
return mStreamTypeMask;
}
@@ -100,6 +101,7 @@ private:
kWhatMonitorQueue = 'moni',
kWhatResumeUntil = 'rsme',
kWhatDownloadNext = 'dlnx',
+ kWhatFetchPlaylist = 'flst'
};
struct DownloadState;
@@ -114,7 +116,7 @@ private:
sp<AMessage> mNotify;
sp<AMessage> mStartTimeUsNotify;
- sp<HTTPBase> mHTTPDataSource;
+ sp<HTTPDownloader> mHTTPDownloader;
sp<LiveSession> mSession;
AString mURI;
@@ -197,7 +199,9 @@ private:
void postMonitorQueue(int64_t delayUs = 0, int64_t minDelayUs = 0);
void cancelMonitorQueue();
- void setStoppingThreshold(float thresholdRatio);
+ void setStoppingThreshold(float thresholdRatio, bool disconnect);
+ void resetStoppingThreshold(bool disconnect);
+ float getStoppingThreshold();
bool shouldPauseDownload();
int64_t delayUsToRefreshPlaylist() const;
diff --git a/media/libstagefright/matroska/MatroskaExtractor.cpp b/media/libstagefright/matroska/MatroskaExtractor.cpp
index 0712bf0..ddca437 100644
--- a/media/libstagefright/matroska/MatroskaExtractor.cpp
+++ b/media/libstagefright/matroska/MatroskaExtractor.cpp
@@ -941,7 +941,10 @@ void MatroskaExtractor::addTracks() {
const mkvparser::VideoTrack *vtrack =
static_cast<const mkvparser::VideoTrack *>(track);
- if (!strcmp("V_MPEG4/ISO/AVC", codecID)) {
+ if (codecID == NULL) {
+ ALOGW("unknown codecID is not supported.");
+ continue;
+ } else if (!strcmp("V_MPEG4/ISO/AVC", codecID)) {
meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_AVC);
meta->setData(kKeyAVCC, 0, codecPrivate, codecPrivateSize);
} else if (!strcmp("V_MPEG4/ISO/ASP", codecID)) {
diff --git a/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp b/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp
index f74b859..0878a1b 100644
--- a/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp
+++ b/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp
@@ -211,6 +211,11 @@ status_t AnotherPacketSource::read(
mediaBuffer->meta_data()->setInt32(kKeyIsSyncFrame, isSync);
}
+ sp<ABuffer> sei;
+ if (buffer->meta()->findBuffer("sei", &sei) && sei != NULL) {
+ mediaBuffer->meta_data()->setData(kKeySEI, 0, sei->data(), sei->size());
+ }
+
*out = mediaBuffer;
return OK;
}
diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h
index c7d9161..e1ddcbc 100644
--- a/services/audioflinger/AudioFlinger.h
+++ b/services/audioflinger/AudioFlinger.h
@@ -73,18 +73,18 @@ class AudioMixer;
class AudioBuffer;
class AudioResampler;
class FastMixer;
+class PassthruBufferProvider;
class ServerProxy;
// ----------------------------------------------------------------------------
-// AudioFlinger has a hard-coded upper limit of 2 channels for capture and playback.
-// There is support for > 2 channel tracks down-mixed to 2 channel output via a down-mix effect.
-// Adding full support for > 2 channel capture or playback would require more than simply changing
-// this #define. There is an independent hard-coded upper limit in AudioMixer;
-// removing that AudioMixer limit would be necessary but insufficient to support > 2 channels.
-// The macro FCC_2 highlights some (but not all) places where there is are 2-channel assumptions.
+// The macro FCC_2 highlights some (but not all) places where there are are 2-channel assumptions.
+// This is typically due to legacy implementation of stereo input or output.
// Search also for "2", "left", "right", "[0]", "[1]", ">> 16", "<< 16", etc.
#define FCC_2 2 // FCC_2 = Fixed Channel Count 2
+// The macro FCC_8 highlights places where there are 8-channel assumptions.
+// This is typically due to audio mixer and resampler limitations.
+#define FCC_8 8 // FCC_8 = Fixed Channel Count 8
static const nsecs_t kDefaultStandbyTimeInNsecs = seconds(3);
diff --git a/services/audioflinger/AudioMixer.cpp b/services/audioflinger/AudioMixer.cpp
index 18ce1d0..7040af4 100644
--- a/services/audioflinger/AudioMixer.cpp
+++ b/services/audioflinger/AudioMixer.cpp
@@ -317,7 +317,7 @@ status_t AudioMixer::track_t::prepareForDownmix()
// discard the previous downmixer if there was one
unprepareForDownmix();
- // Only remix (upmix or downmix) if the track and mixer/device channel masks
+ // MONO_HACK Only remix (upmix or downmix) if the track and mixer/device channel masks
// are not the same and not handled internally, as mono -> stereo currently is.
if (channelMask == mMixerChannelMask
|| (channelMask == AUDIO_CHANNEL_OUT_MONO
@@ -920,7 +920,8 @@ void AudioMixer::process__validate(state_t* state, int64_t pts)
} else {
if ((n & NEEDS_CHANNEL_COUNT__MASK) == NEEDS_CHANNEL_1){
t.hook = getTrackHook(
- t.mMixerChannelCount == 2 // TODO: MONO_HACK.
+ (t.mMixerChannelMask == AUDIO_CHANNEL_OUT_STEREO // TODO: MONO_HACK
+ && t.channelMask == AUDIO_CHANNEL_OUT_MONO)
? TRACKTYPE_NORESAMPLEMONO : TRACKTYPE_NORESAMPLE,
t.mMixerChannelCount,
t.mMixerInFormat, t.mMixerFormat);
diff --git a/services/audioflinger/FastCapture.cpp b/services/audioflinger/FastCapture.cpp
index 9e7e8a4..79ac12b 100644
--- a/services/audioflinger/FastCapture.cpp
+++ b/services/audioflinger/FastCapture.cpp
@@ -105,7 +105,7 @@ void FastCapture::onStateChange()
mFormat = mInputSource->format();
mSampleRate = Format_sampleRate(mFormat);
unsigned channelCount = Format_channelCount(mFormat);
- ALOG_ASSERT(channelCount == 1 || channelCount == 2);
+ ALOG_ASSERT(channelCount >= 1 && channelCount <= FCC_8);
}
dumpState->mSampleRate = mSampleRate;
eitherChanged = true;
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index 4eaeda3..4039564 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -56,6 +56,7 @@
#include "AudioFlinger.h"
#include "AudioMixer.h"
+#include "BufferProviders.h"
#include "FastMixer.h"
#include "FastCapture.h"
#include "ServiceUtilities.h"
@@ -94,6 +95,10 @@ static inline T min(const T& a, const T& b)
return a < b ? a : b;
}
+#ifndef ARRAY_SIZE
+#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))
+#endif
+
namespace android {
// retry counts for buffer fill timeout
@@ -5324,7 +5329,7 @@ AudioFlinger::RecordThread::~RecordThread()
}
mAudioFlinger->unregisterWriter(mFastCaptureNBLogWriter);
mAudioFlinger->unregisterWriter(mNBLogWriter);
- delete[] mRsmpInBuffer;
+ free(mRsmpInBuffer);
}
void AudioFlinger::RecordThread::onFirstRef()
@@ -5557,7 +5562,7 @@ reacquire_wakelock:
// If an NBAIO source is present, use it to read the normal capture's data
if (mPipeSource != 0) {
size_t framesToRead = mBufferSize / mFrameSize;
- framesRead = mPipeSource->read(&mRsmpInBuffer[rear * mChannelCount],
+ framesRead = mPipeSource->read((uint8_t*)mRsmpInBuffer + rear * mFrameSize,
framesToRead, AudioBufferProvider::kInvalidPTS);
if (framesRead == 0) {
// since pipe is non-blocking, simulate blocking input
@@ -5566,7 +5571,7 @@ reacquire_wakelock:
// otherwise use the HAL / AudioStreamIn directly
} else {
ssize_t bytesRead = mInput->stream->read(mInput->stream,
- &mRsmpInBuffer[rear * mChannelCount], mBufferSize);
+ (uint8_t*)mRsmpInBuffer + rear * mFrameSize, mBufferSize);
if (bytesRead < 0) {
framesRead = bytesRead;
} else {
@@ -5586,13 +5591,13 @@ reacquire_wakelock:
ALOG_ASSERT(framesRead > 0);
if (mTeeSink != 0) {
- (void) mTeeSink->write(&mRsmpInBuffer[rear * mChannelCount], framesRead);
+ (void) mTeeSink->write((uint8_t*)mRsmpInBuffer + rear * mFrameSize, framesRead);
}
// If destination is non-contiguous, we now correct for reading past end of buffer.
{
size_t part1 = mRsmpInFramesP2 - rear;
if ((size_t) framesRead > part1) {
- memcpy(mRsmpInBuffer, &mRsmpInBuffer[mRsmpInFramesP2 * mChannelCount],
+ memcpy(mRsmpInBuffer, (uint8_t*)mRsmpInBuffer + mRsmpInFramesP2 * mFrameSize,
(framesRead - part1) * mFrameSize);
}
}
@@ -6210,7 +6215,7 @@ status_t AudioFlinger::RecordThread::ResamplerBufferProvider::getNextBuffer(
return NOT_ENOUGH_DATA;
}
- buffer->raw = recordThread->mRsmpInBuffer + front * recordThread->mChannelCount;
+ buffer->raw = (uint8_t*)recordThread->mRsmpInBuffer + front * recordThread->mFrameSize;
buffer->frameCount = part1;
mRsmpInUnrel = part1;
return NO_ERROR;
@@ -6246,7 +6251,11 @@ AudioFlinger::RecordThread::RecordBufferConverter::RecordBufferConverter(
// mDstChannelCount
// mDstFrameSize
mBuf(NULL), mBufFrames(0), mBufFrameSize(0),
- mResampler(NULL), mRsmpOutBuffer(NULL), mRsmpOutFrameCount(0)
+ mResampler(NULL),
+ mIsLegacyDownmix(false),
+ mIsLegacyUpmix(false),
+ mRequiresFloat(false),
+ mInputConverterProvider(NULL)
{
(void)updateParameters(srcChannelMask, srcFormat, srcSampleRate,
dstChannelMask, dstFormat, dstSampleRate);
@@ -6255,13 +6264,18 @@ AudioFlinger::RecordThread::RecordBufferConverter::RecordBufferConverter(
AudioFlinger::RecordThread::RecordBufferConverter::~RecordBufferConverter() {
free(mBuf);
delete mResampler;
- free(mRsmpOutBuffer);
+ delete mInputConverterProvider;
}
size_t AudioFlinger::RecordThread::RecordBufferConverter::convert(void *dst,
AudioBufferProvider *provider, size_t frames)
{
- if (mSrcSampleRate == mDstSampleRate) {
+ if (mInputConverterProvider != NULL) {
+ mInputConverterProvider->setBufferProvider(provider);
+ provider = mInputConverterProvider;
+ }
+
+ if (mResampler == NULL) {
ALOGVV("NO RESAMPLING sampleRate:%u mSrcFormat:%#x mDstFormat:%#x",
mSrcSampleRate, mSrcFormat, mDstFormat);
@@ -6273,8 +6287,8 @@ size_t AudioFlinger::RecordThread::RecordBufferConverter::convert(void *dst,
frames -= i; // cannot fill request.
break;
}
- // convert to destination buffer
- convert(dst, buffer.raw, buffer.frameCount);
+ // format convert to destination buffer
+ convertNoResampler(dst, buffer.raw, buffer.frameCount);
dst = (int8_t*)dst + buffer.frameCount * mDstFrameSize;
i -= buffer.frameCount;
@@ -6284,20 +6298,17 @@ size_t AudioFlinger::RecordThread::RecordBufferConverter::convert(void *dst,
ALOGVV("RESAMPLING mSrcSampleRate:%u mDstSampleRate:%u mSrcFormat:%#x mDstFormat:%#x",
mSrcSampleRate, mDstSampleRate, mSrcFormat, mDstFormat);
- // reallocate mRsmpOutBuffer as needed; we will grow but never shrink
- if (mRsmpOutFrameCount < frames) {
- // FIXME why does each track need it's own mRsmpOutBuffer? can't they share?
- free(mRsmpOutBuffer);
- // resampler always outputs stereo (FOR NOW)
- (void)posix_memalign(&mRsmpOutBuffer, 32, frames * FCC_2 * sizeof(int32_t) /*Q4.27*/);
- mRsmpOutFrameCount = frames;
- }
+ // reallocate buffer if needed
+ if (mBufFrameSize != 0 && mBufFrames < frames) {
+ free(mBuf);
+ mBufFrames = frames;
+ (void)posix_memalign(&mBuf, 32, mBufFrames * mBufFrameSize);
+ }
// resampler accumulates, but we only have one source track
- memset(mRsmpOutBuffer, 0, frames * FCC_2 * sizeof(int32_t));
- frames = mResampler->resample((int32_t*)mRsmpOutBuffer, frames, provider);
-
- // convert to destination buffer
- convert(dst, mRsmpOutBuffer, frames);
+ memset(mBuf, 0, frames * mBufFrameSize);
+ frames = mResampler->resample((int32_t*)mBuf, frames, provider);
+ // format convert to destination buffer
+ convertResampler(dst, mBuf, frames);
}
return frames;
}
@@ -6341,74 +6352,132 @@ status_t AudioFlinger::RecordThread::RecordBufferConverter::updateParameters(
mDstChannelCount = audio_channel_count_from_in_mask(dstChannelMask);
mDstFrameSize = mDstChannelCount * audio_bytes_per_sample(mDstFormat);
- // do we need a format buffer?
- if (mSrcFormat != mDstFormat && mDstChannelCount != mSrcChannelCount) {
+ // do we need to resample?
+ delete mResampler;
+ mResampler = NULL;
+ if (mSrcSampleRate != mDstSampleRate) {
+ mResampler = AudioResampler::create(AUDIO_FORMAT_PCM_FLOAT,
+ mSrcChannelCount, mDstSampleRate);
+ mResampler->setSampleRate(mSrcSampleRate);
+ mResampler->setVolume(AudioMixer::UNITY_GAIN_FLOAT, AudioMixer::UNITY_GAIN_FLOAT);
+ }
+
+ // are we running legacy channel conversion modes?
+ mIsLegacyDownmix = (mSrcChannelMask == AUDIO_CHANNEL_IN_STEREO
+ || mSrcChannelMask == AUDIO_CHANNEL_IN_FRONT_BACK)
+ && mDstChannelMask == AUDIO_CHANNEL_IN_MONO;
+ mIsLegacyUpmix = mSrcChannelMask == AUDIO_CHANNEL_IN_MONO
+ && (mDstChannelMask == AUDIO_CHANNEL_IN_STEREO
+ || mDstChannelMask == AUDIO_CHANNEL_IN_FRONT_BACK);
+
+ // do we need to process in float?
+ mRequiresFloat = mResampler != NULL || mIsLegacyDownmix || mIsLegacyUpmix;
+
+ // do we need a staging buffer to convert for destination (we can still optimize this)?
+ // we use mBufFrameSize > 0 to indicate both frame size as well as buffer necessity
+ if (mResampler != NULL) {
+ mBufFrameSize = max(mSrcChannelCount, FCC_2)
+ * audio_bytes_per_sample(AUDIO_FORMAT_PCM_FLOAT);
+ } else if ((mIsLegacyUpmix || mIsLegacyDownmix) && mDstFormat != AUDIO_FORMAT_PCM_FLOAT) {
+ mBufFrameSize = mDstChannelCount * audio_bytes_per_sample(AUDIO_FORMAT_PCM_FLOAT);
+ } else if (mSrcChannelMask != mDstChannelMask && mDstFormat != mSrcFormat) {
mBufFrameSize = mDstChannelCount * audio_bytes_per_sample(mSrcFormat);
} else {
mBufFrameSize = 0;
}
mBufFrames = 0; // force the buffer to be resized.
- // do we need to resample?
- if (mSrcSampleRate != mDstSampleRate) {
- if (mResampler != NULL) {
- delete mResampler;
- }
- mResampler = AudioResampler::create(AUDIO_FORMAT_PCM_16_BIT,
- mSrcChannelCount, mDstSampleRate); // may seem confusing...
- mResampler->setSampleRate(mSrcSampleRate);
- mResampler->setVolume(AudioMixer::UNITY_GAIN_FLOAT, AudioMixer::UNITY_GAIN_FLOAT);
+ // do we need an input converter buffer provider to give us float?
+ delete mInputConverterProvider;
+ mInputConverterProvider = NULL;
+ if (mRequiresFloat && mSrcFormat != AUDIO_FORMAT_PCM_FLOAT) {
+ mInputConverterProvider = new ReformatBufferProvider(
+ audio_channel_count_from_in_mask(mSrcChannelMask),
+ mSrcFormat,
+ AUDIO_FORMAT_PCM_FLOAT,
+ 256 /* provider buffer frame count */);
+ }
+
+ // do we need a remixer to do channel mask conversion
+ if (!mIsLegacyDownmix && !mIsLegacyUpmix && mSrcChannelMask != mDstChannelMask) {
+ (void) memcpy_by_index_array_initialization_from_channel_mask(
+ mIdxAry, ARRAY_SIZE(mIdxAry), mDstChannelMask, mSrcChannelMask);
}
return NO_ERROR;
}
-void AudioFlinger::RecordThread::RecordBufferConverter::convert(
- void *dst, /*const*/ void *src, size_t frames)
+void AudioFlinger::RecordThread::RecordBufferConverter::convertNoResampler(
+ void *dst, const void *src, size_t frames)
{
- // check if a memcpy will do
- if (mResampler == NULL
- && mSrcChannelCount == mDstChannelCount
- && mSrcFormat == mDstFormat) {
- memcpy(dst, src,
- frames * mDstChannelCount * audio_bytes_per_sample(mDstFormat));
- return;
- }
- // reallocate buffer if needed
+ // src is native type unless there is legacy upmix or downmix, whereupon it is float.
if (mBufFrameSize != 0 && mBufFrames < frames) {
free(mBuf);
mBufFrames = frames;
(void)posix_memalign(&mBuf, 32, mBufFrames * mBufFrameSize);
}
- // do processing
- if (mResampler != NULL) {
- // src channel count is always >= 2.
+ // do we need to do legacy upmix and downmix?
+ if (mIsLegacyUpmix || mIsLegacyDownmix) {
void *dstBuf = mBuf != NULL ? mBuf : dst;
- // ditherAndClamp() works as long as all buffers returned by
- // activeTrack->getNextBuffer() are 32 bit aligned which should be always true.
- if (mDstChannelCount == 1) {
- // the resampler always outputs stereo samples.
- // FIXME: this rewrites back into src
- ditherAndClamp((int32_t *)src, (const int32_t *)src, frames);
- downmix_to_mono_i16_from_stereo_i16((int16_t *)dstBuf,
- (const int16_t *)src, frames);
- } else {
- ditherAndClamp((int32_t *)dstBuf, (const int32_t *)src, frames);
+ if (mIsLegacyUpmix) {
+ upmix_to_stereo_float_from_mono_float((float *)dstBuf,
+ (const float *)src, frames);
+ } else /*mIsLegacyDownmix */ {
+ downmix_to_mono_float_from_stereo_float((float *)dstBuf,
+ (const float *)src, frames);
}
- } else if (mSrcChannelCount != mDstChannelCount) {
+ if (mBuf != NULL) {
+ memcpy_by_audio_format(dst, mDstFormat, mBuf, AUDIO_FORMAT_PCM_FLOAT,
+ frames * mDstChannelCount);
+ }
+ return;
+ }
+ // do we need to do channel mask conversion?
+ if (mSrcChannelMask != mDstChannelMask) {
void *dstBuf = mBuf != NULL ? mBuf : dst;
+ memcpy_by_index_array(dstBuf, mDstChannelCount,
+ src, mSrcChannelCount, mIdxAry, audio_bytes_per_sample(mSrcFormat), frames);
+ if (dstBuf == dst) {
+ return; // format is the same
+ }
+ }
+ // convert to destination buffer
+ const void *convertBuf = mBuf != NULL ? mBuf : src;
+ memcpy_by_audio_format(dst, mDstFormat, convertBuf, mSrcFormat,
+ frames * mDstChannelCount);
+}
+
+void AudioFlinger::RecordThread::RecordBufferConverter::convertResampler(
+ void *dst, /*not-a-const*/ void *src, size_t frames)
+{
+ // src buffer format is ALWAYS float when entering this routine
+ if (mIsLegacyUpmix) {
+ ; // mono to stereo already handled by resampler
+ } else if (mIsLegacyDownmix
+ || (mSrcChannelMask == mDstChannelMask && mSrcChannelCount == 1)) {
+ // the resampler outputs stereo for mono input channel (a feature?)
+ // must convert to mono
+ downmix_to_mono_float_from_stereo_float((float *)src,
+ (const float *)src, frames);
+ } else if (mSrcChannelMask != mDstChannelMask) {
+ // convert to mono channel again for channel mask conversion (could be skipped
+ // with further optimization).
if (mSrcChannelCount == 1) {
- upmix_to_stereo_i16_from_mono_i16((int16_t *)dstBuf, (const int16_t *)src,
- frames);
- } else {
- downmix_to_mono_i16_from_stereo_i16((int16_t *)dstBuf,
- (const int16_t *)src, frames);
+ downmix_to_mono_float_from_stereo_float((float *)src,
+ (const float *)src, frames);
}
+ // convert to destination format (in place, OK as float is larger than other types)
+ if (mDstFormat != AUDIO_FORMAT_PCM_FLOAT) {
+ memcpy_by_audio_format(src, mDstFormat, src, AUDIO_FORMAT_PCM_FLOAT,
+ frames * mSrcChannelCount);
+ }
+ // channel convert and save to dst
+ memcpy_by_index_array(dst, mDstChannelCount,
+ src, mSrcChannelCount, mIdxAry, audio_bytes_per_sample(mDstFormat), frames);
+ return;
}
- if (mSrcFormat != mDstFormat) {
- void *srcBuf = mBuf != NULL ? mBuf : src;
- memcpy_by_audio_format(dst, mDstFormat, srcBuf, mSrcFormat,
- frames * mDstChannelCount);
- }
+ // convert to destination format and save to dst
+ memcpy_by_audio_format(dst, mDstFormat, src, AUDIO_FORMAT_PCM_FLOAT,
+ frames * mDstChannelCount);
}
bool AudioFlinger::RecordThread::checkForNewParameter_l(const String8& keyValuePair,
@@ -6421,6 +6490,10 @@ bool AudioFlinger::RecordThread::checkForNewParameter_l(const String8& keyValueP
audio_format_t reqFormat = mFormat;
uint32_t samplingRate = mSampleRate;
audio_channel_mask_t channelMask = audio_channel_in_mask_from_count(mChannelCount);
+ // possible that we are > 2 channels, use channel index mask
+ if (channelMask == AUDIO_CHANNEL_INVALID && mChannelCount <= FCC_8) {
+ audio_channel_mask_for_index_assignment_from_count(mChannelCount);
+ }
AudioParameter param = AudioParameter(keyValuePair);
int value;
@@ -6441,7 +6514,8 @@ bool AudioFlinger::RecordThread::checkForNewParameter_l(const String8& keyValueP
}
if (param.getInt(String8(AudioParameter::keyChannels), value) == NO_ERROR) {
audio_channel_mask_t mask = (audio_channel_mask_t) value;
- if (mask != AUDIO_CHANNEL_IN_MONO && mask != AUDIO_CHANNEL_IN_STEREO) {
+ if (!audio_is_input_channel(mask) ||
+ audio_channel_count_from_in_mask(mask) > FCC_8) {
status = BAD_VALUE;
} else {
channelMask = mask;
@@ -6566,10 +6640,13 @@ void AudioFlinger::RecordThread::readInputParameters_l()
mSampleRate = mInput->stream->common.get_sample_rate(&mInput->stream->common);
mChannelMask = mInput->stream->common.get_channels(&mInput->stream->common);
mChannelCount = audio_channel_count_from_in_mask(mChannelMask);
+ if (mChannelCount > FCC_8) {
+ ALOGE("HAL channel count %d > %d", mChannelCount, FCC_8);
+ }
mHALFormat = mInput->stream->common.get_format(&mInput->stream->common);
mFormat = mHALFormat;
- if (mFormat != AUDIO_FORMAT_PCM_16_BIT) {
- ALOGE("HAL format %#x not supported; must be AUDIO_FORMAT_PCM_16_BIT", mFormat);
+ if (!audio_is_linear_pcm(mFormat)) {
+ ALOGE("HAL format %#x is not linear pcm", mFormat);
}
mFrameSize = audio_stream_in_frame_size(mInput->stream);
mBufferSize = mInput->stream->common.get_buffer_size(&mInput->stream->common);
@@ -6584,7 +6661,7 @@ void AudioFlinger::RecordThread::readInputParameters_l()
// Note this is independent of the maximum downsampling ratio permitted for capture.
mRsmpInFrames = mFrameCount * 7;
mRsmpInFramesP2 = roundup(mRsmpInFrames);
- delete[] mRsmpInBuffer;
+ free(mRsmpInBuffer);
// TODO optimize audio capture buffer sizes ...
// Here we calculate the size of the sliding buffer used as a source
@@ -6594,7 +6671,7 @@ void AudioFlinger::RecordThread::readInputParameters_l()
// The current value is higher than necessary. However it should not add to latency.
// Over-allocate beyond mRsmpInFramesP2 to permit a HAL read past end of buffer
- mRsmpInBuffer = new int16_t[(mRsmpInFramesP2 + mFrameCount - 1) * mChannelCount];
+ (void)posix_memalign(&mRsmpInBuffer, 32, (mRsmpInFramesP2 + mFrameCount - 1) * mFrameSize);
// AudioRecord mSampleRate and mChannelCount are constant due to AudioRecord API constraints.
// But if thread's mSampleRate or mChannelCount changes, how will that affect active tracks?
diff --git a/services/audioflinger/Threads.h b/services/audioflinger/Threads.h
index 27bc56b..b7c1ed1 100644
--- a/services/audioflinger/Threads.h
+++ b/services/audioflinger/Threads.h
@@ -1130,8 +1130,11 @@ public:
}
private:
- // internal convert function for format and channel mask.
- void convert(void *dst, /*const*/ void *src, size_t frames);
+ // format conversion when not using resampler
+ void convertNoResampler(void *dst, const void *src, size_t frames);
+
+ // format conversion when using resampler; modifies src in-place
+ void convertResampler(void *dst, /*not-a-const*/ void *src, size_t frames);
// user provided information
audio_channel_mask_t mSrcChannelMask;
@@ -1153,10 +1156,12 @@ public:
// resampler info
AudioResampler *mResampler;
- // interleaved stereo pairs of fixed-point Q4.27 or float depending on resampler
- void *mRsmpOutBuffer;
- // current allocated frame count for the above, which may be larger than needed
- size_t mRsmpOutFrameCount;
+
+ bool mIsLegacyDownmix; // legacy stereo to mono conversion needed
+ bool mIsLegacyUpmix; // legacy mono to stereo conversion needed
+ bool mRequiresFloat; // data processing requires float (e.g. resampler)
+ PassthruBufferProvider *mInputConverterProvider; // converts input to float
+ int8_t mIdxAry[sizeof(uint32_t) * 8]; // used for channel mask conversion
};
#include "RecordTracks.h"
@@ -1267,7 +1272,7 @@ private:
Condition mStartStopCond;
// resampler converts input at HAL Hz to output at AudioRecord client Hz
- int16_t *mRsmpInBuffer; // see new[] for details on the size
+ void *mRsmpInBuffer; //
size_t mRsmpInFrames; // size of resampler input in frames
size_t mRsmpInFramesP2;// size rounded up to a power-of-2
diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioPort.h b/services/audiopolicy/common/managerdefinitions/include/AudioPort.h
index 1c2c27e..82e2c43 100644
--- a/services/audiopolicy/common/managerdefinitions/include/AudioPort.h
+++ b/services/audiopolicy/common/managerdefinitions/include/AudioPort.h
@@ -75,10 +75,8 @@ public:
audio_format_t pickFormat() const;
static const audio_format_t sPcmFormatCompareTable[];
- static int compareFormatsGoodToBad(
- const audio_format_t *format1, const audio_format_t *format2) {
- // compareFormats sorts from bad to good, we reverse it here
- return compareFormats(*format2, *format1);
+ static int compareFormats(const audio_format_t *format1, const audio_format_t *format2) {
+ return compareFormats(*format1, *format2);
}
static int compareFormats(audio_format_t format1, audio_format_t format2);
diff --git a/services/audiopolicy/common/managerdefinitions/include/ConfigParsingUtils.h b/services/audiopolicy/common/managerdefinitions/include/ConfigParsingUtils.h
index f8c4d08..0b08430 100644
--- a/services/audiopolicy/common/managerdefinitions/include/ConfigParsingUtils.h
+++ b/services/audiopolicy/common/managerdefinitions/include/ConfigParsingUtils.h
@@ -38,7 +38,8 @@ struct StringToEnum {
uint32_t value;
};
-#define STRING_TO_ENUM(string) { #string, string }
+// TODO: move to a separate file. Should be in sync with audio.h.
+#define STRING_TO_ENUM(string) { #string, (uint32_t)string } // uint32_t cast removes warning
#define NAME_TO_ENUM(name, value) { name, value }
#ifndef ARRAY_SIZE
#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
@@ -204,6 +205,17 @@ const StringToEnum sInChannelsNameToEnumTable[] = {
STRING_TO_ENUM(AUDIO_CHANNEL_IN_FRONT_BACK),
};
+const StringToEnum sIndexChannelsNameToEnumTable[] = {
+ STRING_TO_ENUM(AUDIO_CHANNEL_INDEX_MASK_1),
+ STRING_TO_ENUM(AUDIO_CHANNEL_INDEX_MASK_2),
+ STRING_TO_ENUM(AUDIO_CHANNEL_INDEX_MASK_3),
+ STRING_TO_ENUM(AUDIO_CHANNEL_INDEX_MASK_4),
+ STRING_TO_ENUM(AUDIO_CHANNEL_INDEX_MASK_5),
+ STRING_TO_ENUM(AUDIO_CHANNEL_INDEX_MASK_6),
+ STRING_TO_ENUM(AUDIO_CHANNEL_INDEX_MASK_7),
+ STRING_TO_ENUM(AUDIO_CHANNEL_INDEX_MASK_8),
+};
+
const StringToEnum sGainModeNameToEnumTable[] = {
STRING_TO_ENUM(AUDIO_GAIN_MODE_JOINT),
STRING_TO_ENUM(AUDIO_GAIN_MODE_CHANNELS),
diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioPort.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioPort.cpp
index f3978ec..64f883a 100644
--- a/services/audiopolicy/common/managerdefinitions/src/AudioPort.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/AudioPort.cpp
@@ -146,7 +146,7 @@ void AudioPort::importAudioPort(const sp<AudioPort> port) {
break;
}
}
- if (!hasFormat) { // never import a channel mask twice
+ if (!hasFormat) { // never import a format twice
mFormats.add(format);
}
}
@@ -216,7 +216,12 @@ void AudioPort::loadFormats(char *name)
}
str = strtok(NULL, "|");
}
- mFormats.sort(compareFormatsGoodToBad);
+ // we sort from worst to best, so that AUDIO_FORMAT_DEFAULT is always the first entry.
+ // TODO: compareFormats could be a lambda to convert between pointer-to-format to format:
+ // [](const audio_format_t *format1, const audio_format_t *format2) {
+ // return compareFormats(*format1, *format2);
+ // }
+ mFormats.sort(compareFormats);
}
void AudioPort::loadInChannels(char *name)
@@ -235,8 +240,14 @@ void AudioPort::loadInChannels(char *name)
(audio_channel_mask_t)ConfigParsingUtils::stringToEnum(sInChannelsNameToEnumTable,
ARRAY_SIZE(sInChannelsNameToEnumTable),
str);
+ if (channelMask == 0) { // if not found, check the channel index table
+ channelMask = (audio_channel_mask_t)
+ ConfigParsingUtils::stringToEnum(sIndexChannelsNameToEnumTable,
+ ARRAY_SIZE(sIndexChannelsNameToEnumTable),
+ str);
+ }
if (channelMask != 0) {
- ALOGV("loadInChannels() adding channelMask %04x", channelMask);
+ ALOGV("loadInChannels() adding channelMask %#x", channelMask);
mChannelMasks.add(channelMask);
}
str = strtok(NULL, "|");
@@ -441,30 +452,87 @@ status_t AudioPort::checkCompatibleChannelMask(audio_channel_mask_t channelMask,
}
const bool isRecordThread = mType == AUDIO_PORT_TYPE_MIX && mRole == AUDIO_PORT_ROLE_SINK;
+ const bool isIndex = audio_channel_mask_get_representation(channelMask)
+ == AUDIO_CHANNEL_REPRESENTATION_INDEX;
+ int bestMatch = 0;
for (size_t i = 0; i < mChannelMasks.size(); i ++) {
- // FIXME Does not handle multi-channel automatic conversions yet
audio_channel_mask_t supported = mChannelMasks[i];
if (supported == channelMask) {
+ // Exact matches always taken.
if (updatedChannelMask != NULL) {
*updatedChannelMask = channelMask;
}
return NO_ERROR;
}
- if (isRecordThread) {
- // This uses hard-coded knowledge that AudioFlinger can silently down-mix and up-mix.
- // FIXME Abstract this out to a table.
- if (((supported == AUDIO_CHANNEL_IN_FRONT_BACK || supported == AUDIO_CHANNEL_IN_STEREO)
- && channelMask == AUDIO_CHANNEL_IN_MONO) ||
- (supported == AUDIO_CHANNEL_IN_MONO && (channelMask == AUDIO_CHANNEL_IN_FRONT_BACK
- || channelMask == AUDIO_CHANNEL_IN_STEREO))) {
+
+ // AUDIO_CHANNEL_NONE (value: 0) is used for dynamic channel support
+ if (isRecordThread && supported != AUDIO_CHANNEL_NONE) {
+ // Approximate (best) match:
+ // The match score measures how well the supported channel mask matches the
+ // desired mask, where increasing-is-better.
+ //
+ // TODO: Some tweaks may be needed.
+ // Should be a static function of the data processing library.
+ //
+ // In priority:
+ // match score = 1000 if legacy channel conversion equivalent (always prefer this)
+ // OR
+ // match score += 100 if the channel mask representations match
+ // match score += number of channels matched.
+ //
+ // If there are no matched channels, the mask may still be accepted
+ // but the playback or record will be silent.
+ const bool isSupportedIndex = (audio_channel_mask_get_representation(supported)
+ == AUDIO_CHANNEL_REPRESENTATION_INDEX);
+ int match;
+ if (isIndex && isSupportedIndex) {
+ // index equivalence
+ match = 100 + __builtin_popcount(
+ audio_channel_mask_get_bits(channelMask)
+ & audio_channel_mask_get_bits(supported));
+ } else if (isIndex && !isSupportedIndex) {
+ const uint32_t equivalentBits =
+ (1 << audio_channel_count_from_in_mask(supported)) - 1 ;
+ match = __builtin_popcount(
+ audio_channel_mask_get_bits(channelMask) & equivalentBits);
+ } else if (!isIndex && isSupportedIndex) {
+ const uint32_t equivalentBits =
+ (1 << audio_channel_count_from_in_mask(channelMask)) - 1;
+ match = __builtin_popcount(
+ equivalentBits & audio_channel_mask_get_bits(supported));
+ } else {
+ // positional equivalence
+ match = 100 + __builtin_popcount(
+ audio_channel_mask_get_bits(channelMask)
+ & audio_channel_mask_get_bits(supported));
+ switch (supported) {
+ case AUDIO_CHANNEL_IN_FRONT_BACK:
+ case AUDIO_CHANNEL_IN_STEREO:
+ if (channelMask == AUDIO_CHANNEL_IN_MONO) {
+ match = 1000;
+ }
+ break;
+ case AUDIO_CHANNEL_IN_MONO:
+ if (channelMask == AUDIO_CHANNEL_IN_FRONT_BACK
+ || channelMask == AUDIO_CHANNEL_IN_STEREO) {
+ match = 1000;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ if (match > bestMatch) {
+ bestMatch = match;
if (updatedChannelMask != NULL) {
*updatedChannelMask = supported;
+ } else {
+ return NO_ERROR; // any match will do in this case.
}
- return NO_ERROR;
}
}
}
- return BAD_VALUE;
+ return bestMatch > 0 ? NO_ERROR : BAD_VALUE;
}
status_t AudioPort::checkExactFormat(audio_format_t format) const
@@ -495,11 +563,13 @@ status_t AudioPort::checkCompatibleFormat(audio_format_t format, audio_format_t
mType == AUDIO_PORT_TYPE_MIX && mRole == AUDIO_PORT_ROLE_SINK
&& audio_is_linear_pcm(format);
- for (size_t i = 0; i < mFormats.size(); ++i) {
+ // iterate from best format to worst format (reverse order)
+ for (ssize_t i = mFormats.size() - 1; i >= 0 ; --i) {
if (mFormats[i] == format ||
- (checkInexact && audio_is_linear_pcm(mFormats[i]))) {
- // for inexact checks we take the first linear pcm format since
- // mFormats is sorted from best PCM format to worst PCM format.
+ (checkInexact
+ && mFormats[i] != AUDIO_FORMAT_DEFAULT
+ && audio_is_linear_pcm(mFormats[i]))) {
+ // for inexact checks we take the first linear pcm format due to sorting.
if (updatedFormat != NULL) {
*updatedFormat = mFormats[i];
}
@@ -726,10 +796,15 @@ void AudioPort::dump(int fd, int spaces) const
const char *formatStr = ConfigParsingUtils::enumToString(sFormatNameToEnumTable,
ARRAY_SIZE(sFormatNameToEnumTable),
mFormats[i]);
- if (i == 0 && strcmp(formatStr, "") == 0) {
+ const bool isEmptyStr = formatStr[0] == 0;
+ if (i == 0 && isEmptyStr) {
snprintf(buffer, SIZE, "Dynamic");
} else {
- snprintf(buffer, SIZE, "%s", formatStr);
+ if (isEmptyStr) {
+ snprintf(buffer, SIZE, "%#x", mFormats[i]);
+ } else {
+ snprintf(buffer, SIZE, "%s", formatStr);
+ }
}
result.append(buffer);
result.append(i == (mFormats.size() - 1) ? "" : ", ");
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
index 01f2b61..ba3fcaf 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
@@ -1420,6 +1420,13 @@ status_t AudioPolicyManager::startInput(audio_io_handle_t input,
}
if (inputDesc->mRefCount == 0) {
+ // if input maps to a dynamic policy with an activity listener, notify of state change
+ if ((inputDesc->mPolicyMix != NULL)
+ && ((inputDesc->mPolicyMix->mFlags & MIX_FLAG_NOTIFY_ACTIVITY) != 0)) {
+ mpClientInterface->onDynamicPolicyMixStateUpdate(inputDesc->mPolicyMix->mRegistrationId,
+ MIX_STATE_MIXING);
+ }
+
if (mInputs.activeInputsCount() == 0) {
SoundTrigger::setCaptureState(true);
}
@@ -1473,6 +1480,12 @@ status_t AudioPolicyManager::stopInput(audio_io_handle_t input,
inputDesc->mRefCount--;
if (inputDesc->mRefCount == 0) {
+ // if input maps to a dynamic policy with an activity listener, notify of state change
+ if ((inputDesc->mPolicyMix != NULL)
+ && ((inputDesc->mPolicyMix->mFlags & MIX_FLAG_NOTIFY_ACTIVITY) != 0)) {
+ mpClientInterface->onDynamicPolicyMixStateUpdate(inputDesc->mPolicyMix->mRegistrationId,
+ MIX_STATE_IDLE);
+ }
// automatically disable the remote submix output when input is stopped if not
// used by a policy mix of type MIX_TYPE_RECORDERS
diff --git a/services/mediaresourcemanager/ResourceManagerService.cpp b/services/mediaresourcemanager/ResourceManagerService.cpp
index 75a69ed..17aac4e 100644
--- a/services/mediaresourcemanager/ResourceManagerService.cpp
+++ b/services/mediaresourcemanager/ResourceManagerService.cpp
@@ -88,6 +88,51 @@ static ResourceInfo& getResourceInfoForEdit(
return infos.editItemAt(infos.size() - 1);
}
+status_t ResourceManagerService::dump(int fd, const Vector<String16>& args) {
+ Mutex::Autolock lock(mLock);
+
+ String8 result;
+ const size_t SIZE = 256;
+ char buffer[SIZE];
+
+ snprintf(buffer, SIZE, "ResourceManagerService: %p\n", this);
+ result.append(buffer);
+ result.append(" Policies:\n");
+ snprintf(buffer, SIZE, " SupportsMultipleSecureCodecs: %d\n", mSupportsMultipleSecureCodecs);
+ result.append(buffer);
+ snprintf(buffer, SIZE, " SupportsSecureWithNonSecureCodec: %d\n", mSupportsSecureWithNonSecureCodec);
+ result.append(buffer);
+
+ snprintf(buffer, SIZE, " Processes:\n");
+ result.append(buffer);
+ for (size_t i = 0; i < mMap.size(); ++i) {
+ snprintf(buffer, SIZE, " Pid: %d\n", mMap.keyAt(i));
+ result.append(buffer);
+
+ const ResourceInfos &infos = mMap.valueAt(i);
+ for (size_t j = 0; j < infos.size(); ++j) {
+ snprintf(buffer, SIZE, " Client:\n");
+ result.append(buffer);
+ snprintf(buffer, SIZE, " Id: %lld\n", (long long)infos[j].clientId);
+ result.append(buffer);
+
+ snprintf(buffer, SIZE, " Name: %s\n", infos[j].client->getName().string());
+ result.append(buffer);
+
+ Vector<MediaResource> resources = infos[j].resources;
+ snprintf(buffer, SIZE, " Resources:\n");
+ result.append(buffer);
+ for (size_t k = 0; k < resources.size(); ++k) {
+ snprintf(buffer, SIZE, " %s\n", resources[k].toString().string());
+ result.append(buffer);
+ }
+ }
+ }
+
+ write(fd, result.string(), result.size());
+ return OK;
+}
+
ResourceManagerService::ResourceManagerService()
: mProcessInfo(new ProcessInfo()),
mSupportsMultipleSecureCodecs(true),
diff --git a/services/mediaresourcemanager/ResourceManagerService.h b/services/mediaresourcemanager/ResourceManagerService.h
index 2ed9bf8..0c3d694 100644
--- a/services/mediaresourcemanager/ResourceManagerService.h
+++ b/services/mediaresourcemanager/ResourceManagerService.h
@@ -48,6 +48,8 @@ class ResourceManagerService
public:
static char const *getServiceName() { return "media.resource_manager"; }
+ virtual status_t dump(int fd, const Vector<String16>& args);
+
ResourceManagerService();
ResourceManagerService(sp<ProcessInfoInterface> processInfo);
diff --git a/services/mediaresourcemanager/test/ResourceManagerService_test.cpp b/services/mediaresourcemanager/test/ResourceManagerService_test.cpp
index 48d1395..bccc7fa 100644
--- a/services/mediaresourcemanager/test/ResourceManagerService_test.cpp
+++ b/services/mediaresourcemanager/test/ResourceManagerService_test.cpp
@@ -55,6 +55,10 @@ struct TestClient : public BnResourceManagerClient {
return true;
}
+ virtual String8 getName() {
+ return String8("test_client");
+ }
+
bool reclaimed() const {
return mReclaimed;
}
diff --git a/services/radio/RadioService.cpp b/services/radio/RadioService.cpp
index a6c2bdf..cd0f5f3 100644
--- a/services/radio/RadioService.cpp
+++ b/services/radio/RadioService.cpp
@@ -146,7 +146,7 @@ status_t RadioService::attach(radio_handle_t handle,
radio = module->addClient(client, config, withAudio);
if (radio == 0) {
- NO_INIT;
+ return NO_INIT;
}
return NO_ERROR;
}
@@ -500,13 +500,12 @@ sp<RadioService::ModuleClient> RadioService::Module::addClient(const sp<IRadioCl
if (audio) {
notifyDeviceConnection(true, "");
}
+ ALOGV("addClient() DONE moduleClient %p", moduleClient.get());
} else {
+ ALOGW("%s open_tuner failed with error %d", __FUNCTION__, ret);
moduleClient.clear();
}
-
- ALOGV("addClient() DONE moduleClient %p", moduleClient.get());
-
return moduleClient;
}