Merge "AudioFlinger: fix RecordThread initial device"
diff --git a/drm/common/IDrmManagerService.cpp b/drm/common/IDrmManagerService.cpp
index 0282036..91fd91e 100644
--- a/drm/common/IDrmManagerService.cpp
+++ b/drm/common/IDrmManagerService.cpp
@@ -190,8 +190,9 @@
if (0 < bufferSize) {
data = new char[bufferSize];
reply.read(data, bufferSize);
+ drmConstraints->put(&key, data);
+ delete[] data;
}
- drmConstraints->put(&key, data);
}
}
return drmConstraints;
@@ -219,8 +220,9 @@
if (0 < bufferSize) {
data = new char[bufferSize];
reply.read(data, bufferSize);
+ drmMetadata->put(&key, data);
+ delete[] data;
}
- drmMetadata->put(&key, data);
}
}
return drmMetadata;
@@ -889,9 +891,11 @@
int bufferSize = 0;
if (NULL != value) {
bufferSize = strlen(value);
+ reply->writeInt32(bufferSize + 1);
+ reply->write(value, bufferSize + 1);
+ } else {
+ reply->writeInt32(0);
}
- reply->writeInt32(bufferSize + 1);
- reply->write(value, bufferSize + 1);
}
}
delete drmConstraints; drmConstraints = NULL;
diff --git a/drm/common/ReadWriteUtils.cpp b/drm/common/ReadWriteUtils.cpp
index fd17e98..d696f16 100644
--- a/drm/common/ReadWriteUtils.cpp
+++ b/drm/common/ReadWriteUtils.cpp
@@ -47,7 +47,7 @@
if (length == read(fd, (void*) bytes, length)) {
string.append(bytes, length);
}
- delete bytes;
+ delete[] bytes;
}
fclose(file);
}
diff --git a/drm/drmserver/DrmManager.cpp b/drm/drmserver/DrmManager.cpp
index e7b0e90..bfaf4bc 100644
--- a/drm/drmserver/DrmManager.cpp
+++ b/drm/drmserver/DrmManager.cpp
@@ -42,7 +42,8 @@
DrmManager::DrmManager() :
mDecryptSessionId(0),
mConvertId(0) {
-
+ srand(time(NULL));
+ memset(mUniqueIdArray, 0, sizeof(bool) * kMaxNumUniqueIds);
}
DrmManager::~DrmManager() {
@@ -52,48 +53,37 @@
int DrmManager::addUniqueId(bool isNative) {
Mutex::Autolock _l(mLock);
- int temp = 0;
- bool foundUniqueId = false;
- const int size = mUniqueIdVector.size();
- const int uniqueIdRange = 0xfff;
- int maxLoopTimes = (uniqueIdRange - 1) / 2;
- srand(time(NULL));
+ int uniqueId = -1;
+ int random = rand();
- while (!foundUniqueId) {
- temp = rand() & uniqueIdRange;
+ for (size_t index = 0; index < kMaxNumUniqueIds; ++index) {
+ int temp = (random + index) % kMaxNumUniqueIds;
+ if (!mUniqueIdArray[temp]) {
+ uniqueId = temp;
+ mUniqueIdArray[uniqueId] = true;
- if (isNative) {
- // set a flag to differentiate DrmManagerClient
- // created from native side and java side
- temp |= 0x1000;
- }
-
- int index = 0;
- for (; index < size; ++index) {
- if (mUniqueIdVector.itemAt(index) == temp) {
- foundUniqueId = false;
- break;
+ if (isNative) {
+ // set a flag to differentiate DrmManagerClient
+ // created from native side and java side
+ uniqueId |= 0x1000;
}
+ break;
}
- if (index == size) {
- foundUniqueId = true;
- }
-
- maxLoopTimes --;
- LOG_FATAL_IF(maxLoopTimes <= 0, "cannot find an unique ID for this session");
}
- mUniqueIdVector.push(temp);
- return temp;
+ // -1 indicates that no unique id can be allocated.
+ return uniqueId;
}
void DrmManager::removeUniqueId(int uniqueId) {
Mutex::Autolock _l(mLock);
- for (unsigned int i = 0; i < mUniqueIdVector.size(); i++) {
- if (uniqueId == mUniqueIdVector.itemAt(i)) {
- mUniqueIdVector.removeAt(i);
- break;
- }
+ if (uniqueId & 0x1000) {
+ // clear the flag for the native side.
+ uniqueId &= ~(0x1000);
+ }
+
+ if (uniqueId >= 0 && uniqueId < kMaxNumUniqueIds) {
+ mUniqueIdArray[uniqueId] = false;
}
}
diff --git a/drm/libdrmframework/include/DrmManager.h b/drm/libdrmframework/include/DrmManager.h
index 491e8f7..8ab693f 100644
--- a/drm/libdrmframework/include/DrmManager.h
+++ b/drm/libdrmframework/include/DrmManager.h
@@ -144,7 +144,11 @@
bool canHandle(int uniqueId, const String8& path);
private:
- Vector<int> mUniqueIdVector;
+ enum {
+ kMaxNumUniqueIds = 0x1000,
+ };
+
+ bool mUniqueIdArray[kMaxNumUniqueIds];
static const String8 EMPTY_STRING;
int mDecryptSessionId;
diff --git a/drm/libdrmframework/plugins/forward-lock/internal-format/converter/Android.mk b/drm/libdrmframework/plugins/forward-lock/internal-format/converter/Android.mk
index 37a3851..8f08c88 100644
--- a/drm/libdrmframework/plugins/forward-lock/internal-format/converter/Android.mk
+++ b/drm/libdrmframework/plugins/forward-lock/internal-format/converter/Android.mk
@@ -26,10 +26,6 @@
LOCAL_SHARED_LIBRARIES := libcrypto
-LOCAL_WHOLE_STATIC_LIBRARIES := libfwdlock-common
-
-LOCAL_STATIC_LIBRARIES := libfwdlock-common
-
LOCAL_MODULE := libfwdlock-converter
LOCAL_MODULE_TAGS := optional
diff --git a/drm/libdrmframework/plugins/forward-lock/internal-format/converter/FwdLockConv.c b/drm/libdrmframework/plugins/forward-lock/internal-format/converter/FwdLockConv.c
index bb97abc..9d15835 100644
--- a/drm/libdrmframework/plugins/forward-lock/internal-format/converter/FwdLockConv.c
+++ b/drm/libdrmframework/plugins/forward-lock/internal-format/converter/FwdLockConv.c
@@ -245,7 +245,9 @@
AES_KEY sessionRoundKeys;
unsigned char value[KEY_SIZE];
unsigned char key[KEY_SIZE];
- } *pData = malloc(sizeof *pData);
+ };
+ const size_t kSize = sizeof(struct FwdLockConv_DeriveKeys_Data);
+ struct FwdLockConv_DeriveKeys_Data *pData = malloc(kSize);
if (pData == NULL) {
status = FwdLockConv_Status_OutOfMemory;
} else {
@@ -268,7 +270,7 @@
status = FwdLockConv_Status_OK;
}
}
- memset(pData, 0, sizeof pData); // Zero out key data.
+ memset(pData, 0, kSize); // Zero out key data.
free(pData);
}
return status;
diff --git a/drm/libdrmframework/plugins/forward-lock/internal-format/decoder/Android.mk b/drm/libdrmframework/plugins/forward-lock/internal-format/decoder/Android.mk
index d9b5cfd..7b493c3 100644
--- a/drm/libdrmframework/plugins/forward-lock/internal-format/decoder/Android.mk
+++ b/drm/libdrmframework/plugins/forward-lock/internal-format/decoder/Android.mk
@@ -26,10 +26,6 @@
LOCAL_SHARED_LIBRARIES := libcrypto
-LOCAL_WHOLE_STATIC_LIBRARIES := libfwdlock-common
-
-LOCAL_STATIC_LIBRARIES := libfwdlock-common
-
LOCAL_MODULE := libfwdlock-decoder
LOCAL_MODULE_TAGS := optional
diff --git a/drm/libdrmframework/plugins/forward-lock/internal-format/decoder/FwdLockFile.c b/drm/libdrmframework/plugins/forward-lock/internal-format/decoder/FwdLockFile.c
index 7ff3c00..43b9e98 100644
--- a/drm/libdrmframework/plugins/forward-lock/internal-format/decoder/FwdLockFile.c
+++ b/drm/libdrmframework/plugins/forward-lock/internal-format/decoder/FwdLockFile.c
@@ -174,7 +174,10 @@
AES_KEY sessionRoundKeys;
unsigned char value[KEY_SIZE];
unsigned char key[KEY_SIZE];
- } *pData = malloc(sizeof *pData);
+ };
+
+ const size_t kSize = sizeof(struct FwdLockFile_DeriveKeys_Data);
+ struct FwdLockFile_DeriveKeys_Data *pData = malloc(kSize);
if (pData == NULL) {
result = FALSE;
} else {
@@ -202,7 +205,7 @@
if (!result) {
errno = ENOSYS;
}
- memset(pData, 0, sizeof pData); // Zero out key data.
+ memset(pData, 0, kSize); // Zero out key data.
free(pData);
}
return result;
diff --git a/drm/libdrmframework/plugins/passthru/src/DrmPassthruPlugIn.cpp b/drm/libdrmframework/plugins/passthru/src/DrmPassthruPlugIn.cpp
index fa659fd..084e323 100644
--- a/drm/libdrmframework/plugins/passthru/src/DrmPassthruPlugIn.cpp
+++ b/drm/libdrmframework/plugins/passthru/src/DrmPassthruPlugIn.cpp
@@ -65,10 +65,11 @@
char* charValue = NULL;
charValue = new char[value.length() + 1];
strncpy(charValue, value.string(), value.length());
+ charValue[value.length()] = '\0';
//Just add dummy available time for verification
drmConstraints->put(&(DrmConstraints::LICENSE_AVAILABLE_TIME), charValue);
-
+ delete[] charValue;
return drmConstraints;
}
diff --git a/include/media/IHDCP.h b/include/media/IHDCP.h
index a0613c7..6d27b18 100644
--- a/include/media/IHDCP.h
+++ b/include/media/IHDCP.h
@@ -45,18 +45,34 @@
// Request to shutdown the active HDCP session.
virtual status_t shutdownAsync() = 0;
- // Encrypt a data according to the HDCP spec. The data is to be
- // encrypted in-place, only size bytes of data should be read/write,
- // even if the size is not a multiple of 128 bit (16 bytes).
+ // ENCRYPTION only:
+ // Encrypt data according to the HDCP spec. "size" bytes of data are
+ // available at "inData" (virtual address), "size" may not be a multiple
+ // of 128 bits (16 bytes). An equal number of encrypted bytes should be
+ // written to the buffer at "outData" (virtual address).
// This operation is to be synchronous, i.e. this call does not return
// until outData contains size bytes of encrypted data.
// streamCTR will be assigned by the caller (to 0 for the first PES stream,
// 1 for the second and so on)
- // inputCTR will be maintained by the callee for each PES stream.
+ // inputCTR _will_be_maintained_by_the_callee_ for each PES stream.
virtual status_t encrypt(
const void *inData, size_t size, uint32_t streamCTR,
uint64_t *outInputCTR, void *outData) = 0;
+ // DECRYPTION only:
+ // Decrypt data according to the HDCP spec.
+ // "size" bytes of encrypted data are available at "inData"
+ // (virtual address), "size" may not be a multiple of 128 bits (16 bytes).
+ // An equal number of decrypted bytes should be written to the buffer
+ // at "outData" (virtual address).
+ // This operation is to be synchronous, i.e. this call does not return
+ // until outData contains size bytes of decrypted data.
+ // Both streamCTR and inputCTR will be provided by the caller.
+ virtual status_t decrypt(
+ const void *inData, size_t size,
+ uint32_t streamCTR, uint64_t inputCTR,
+ void *outData) = 0;
+
private:
DISALLOW_EVIL_CONSTRUCTORS(IHDCP);
};
diff --git a/include/media/IMediaLogService.h b/include/media/IMediaLogService.h
new file mode 100644
index 0000000..1f5777e
--- /dev/null
+++ b/include/media/IMediaLogService.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2013 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_IMEDIALOGSERVICE_H
+#define ANDROID_IMEDIALOGSERVICE_H
+
+#include <binder/IInterface.h>
+#include <binder/IMemory.h>
+#include <binder/Parcel.h>
+
+namespace android {
+
+class IMediaLogService: public IInterface
+{
+public:
+ DECLARE_META_INTERFACE(MediaLogService);
+
+ virtual void registerWriter(const sp<IMemory>& shared, size_t size, const char *name) = 0;
+ virtual void unregisterWriter(const sp<IMemory>& shared) = 0;
+
+};
+
+class BnMediaLogService: public BnInterface<IMediaLogService>
+{
+public:
+ virtual status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply,
+ uint32_t flags = 0);
+};
+
+} // namespace android
+
+#endif // ANDROID_IMEDIALOGSERVICE_H
diff --git a/include/media/IMediaPlayerService.h b/include/media/IMediaPlayerService.h
index 44db5bc..b29d3c7 100644
--- a/include/media/IMediaPlayerService.h
+++ b/include/media/IMediaPlayerService.h
@@ -52,7 +52,7 @@
virtual sp<IMemory> decode(int fd, int64_t offset, int64_t length, uint32_t *pSampleRate, int* pNumChannels, audio_format_t* pFormat) = 0;
virtual sp<IOMX> getOMX() = 0;
virtual sp<ICrypto> makeCrypto() = 0;
- virtual sp<IHDCP> makeHDCP() = 0;
+ virtual sp<IHDCP> makeHDCP(bool createEncryptionModule) = 0;
// Connects to a remote display.
// 'iface' specifies the address of the local interface on which to listen for
diff --git a/include/media/nbaio/NBLog.h b/include/media/nbaio/NBLog.h
new file mode 100644
index 0000000..8fc417f
--- /dev/null
+++ b/include/media/nbaio/NBLog.h
@@ -0,0 +1,188 @@
+/*
+ * Copyright (C) 2013 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.
+ */
+
+// Non-blocking event logger intended for safe communication between processes via shared memory
+
+#ifndef ANDROID_MEDIA_NBLOG_H
+#define ANDROID_MEDIA_NBLOG_H
+
+#include <binder/IMemory.h>
+#include <utils/Mutex.h>
+#include <media/nbaio/roundup.h>
+
+namespace android {
+
+class NBLog {
+
+public:
+
+class Writer;
+class Reader;
+
+private:
+
+enum Event {
+ EVENT_RESERVED,
+ EVENT_STRING, // ASCII string, not NUL-terminated
+ EVENT_TIMESTAMP, // clock_gettime(CLOCK_MONOTONIC)
+};
+
+// ---------------------------------------------------------------------------
+
+// representation of a single log entry in private memory
+struct Entry {
+ Entry(Event event, const void *data, size_t length)
+ : mEvent(event), mLength(length), mData(data) { }
+ /*virtual*/ ~Entry() { }
+
+ int readAt(size_t offset) const;
+
+private:
+ friend class Writer;
+ Event mEvent; // event type
+ size_t mLength; // length of additional data, 0 <= mLength <= 255
+ const void *mData; // event type-specific data
+};
+
+// representation of a single log entry in shared memory
+// byte[0] mEvent
+// byte[1] mLength
+// byte[2] mData[0]
+// ...
+// byte[2+i] mData[i]
+// ...
+// byte[2+mLength-1] mData[mLength-1]
+// byte[2+mLength] duplicate copy of mLength to permit reverse scan
+// byte[3+mLength] start of next log entry
+
+// located in shared memory
+struct Shared {
+ Shared() : mRear(0) { }
+ /*virtual*/ ~Shared() { }
+
+ volatile int32_t mRear; // index one byte past the end of most recent Entry
+ char mBuffer[0]; // circular buffer for entries
+};
+
+public:
+
+// ---------------------------------------------------------------------------
+
+// FIXME Timeline was intended to wrap Writer and Reader, but isn't actually used yet.
+// For now it is just a namespace for sharedSize().
+class Timeline : public RefBase {
+public:
+#if 0
+ Timeline(size_t size, void *shared = NULL);
+ virtual ~Timeline();
+#endif
+
+ static size_t sharedSize(size_t size);
+
+#if 0
+private:
+ friend class Writer;
+ friend class Reader;
+
+ const size_t mSize; // circular buffer size in bytes, must be a power of 2
+ bool mOwn; // whether I own the memory at mShared
+ Shared* const mShared; // pointer to shared memory
+#endif
+};
+
+// ---------------------------------------------------------------------------
+
+// Writer is thread-safe with respect to Reader, but not with respect to multiple threads
+// calling Writer methods. If you need multi-thread safety for writing, use LockedWriter.
+class Writer : public RefBase {
+public:
+ Writer(); // dummy nop implementation without shared memory
+ Writer(size_t size, void *shared);
+ Writer(size_t size, const sp<IMemory>& iMemory);
+ virtual ~Writer() { }
+
+ virtual void log(const char *string);
+ virtual void logf(const char *fmt, ...);
+ virtual void logvf(const char *fmt, va_list ap);
+ virtual void logTimestamp();
+ virtual void logTimestamp(const struct timespec& ts);
+
+ virtual bool isEnabled() const;
+
+ // return value for all of these is the previous isEnabled()
+ virtual bool setEnabled(bool enabled); // but won't enable if no shared memory
+ bool enable() { return setEnabled(true); }
+ bool disable() { return setEnabled(false); }
+
+ sp<IMemory> getIMemory() const { return mIMemory; }
+
+private:
+ void log(Event event, const void *data, size_t length);
+ void log(const Entry *entry, bool trusted = false);
+
+ const size_t mSize; // circular buffer size in bytes, must be a power of 2
+ Shared* const mShared; // raw pointer to shared memory
+ const sp<IMemory> mIMemory; // ref-counted version
+ int32_t mRear; // my private copy of mShared->mRear
+ bool mEnabled; // whether to actually log
+};
+
+// ---------------------------------------------------------------------------
+
+// Similar to Writer, but safe for multiple threads to call concurrently
+class LockedWriter : public Writer {
+public:
+ LockedWriter();
+ LockedWriter(size_t size, void *shared);
+
+ virtual void log(const char *string);
+ virtual void logf(const char *fmt, ...);
+ virtual void logvf(const char *fmt, va_list ap);
+ virtual void logTimestamp();
+ virtual void logTimestamp(const struct timespec& ts);
+
+ virtual bool isEnabled() const;
+ virtual bool setEnabled(bool enabled);
+
+private:
+ mutable Mutex mLock;
+};
+
+// ---------------------------------------------------------------------------
+
+class Reader : public RefBase {
+public:
+ Reader(size_t size, const void *shared);
+ Reader(size_t size, const sp<IMemory>& iMemory);
+ virtual ~Reader() { }
+
+ void dump(int fd, size_t indent = 0);
+ bool isIMemory(const sp<IMemory>& iMemory) const;
+
+private:
+ const size_t mSize; // circular buffer size in bytes, must be a power of 2
+ const Shared* const mShared; // raw pointer to shared memory
+ const sp<IMemory> mIMemory; // ref-counted version
+ int32_t mFront; // index of oldest acknowledged Entry
+
+ static const size_t kSquashTimestamp = 5; // squash this many or more adjacent timestamps
+};
+
+}; // class NBLog
+
+} // namespace android
+
+#endif // ANDROID_MEDIA_NBLOG_H
diff --git a/media/libmedia/Android.mk b/media/libmedia/Android.mk
index a35d562..52fa3e1 100644
--- a/media/libmedia/Android.mk
+++ b/media/libmedia/Android.mk
@@ -23,6 +23,7 @@
AudioRecord.cpp \
AudioSystem.cpp \
mediaplayer.cpp \
+ IMediaLogService.cpp \
IMediaPlayerService.cpp \
IMediaPlayerClient.cpp \
IMediaRecorderClient.cpp \
diff --git a/media/libmedia/IHDCP.cpp b/media/libmedia/IHDCP.cpp
index 493f5a4..f13addc 100644
--- a/media/libmedia/IHDCP.cpp
+++ b/media/libmedia/IHDCP.cpp
@@ -31,6 +31,7 @@
HDCP_INIT_ASYNC,
HDCP_SHUTDOWN_ASYNC,
HDCP_ENCRYPT,
+ HDCP_DECRYPT,
};
struct BpHDCPObserver : public BpInterface<IHDCPObserver> {
@@ -106,6 +107,29 @@
return err;
}
+
+ virtual status_t decrypt(
+ const void *inData, size_t size,
+ uint32_t streamCTR, uint64_t inputCTR,
+ void *outData) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IHDCP::getInterfaceDescriptor());
+ data.writeInt32(size);
+ data.write(inData, size);
+ data.writeInt32(streamCTR);
+ data.writeInt64(inputCTR);
+ remote()->transact(HDCP_DECRYPT, data, &reply);
+
+ status_t err = reply.readInt32();
+
+ if (err != OK) {
+ return err;
+ }
+
+ reply.read(outData, size);
+
+ return err;
+ }
};
IMPLEMENT_META_INTERFACE(HDCP, "android.hardware.IHDCP");
@@ -198,6 +222,31 @@
return OK;
}
+ case HDCP_DECRYPT:
+ {
+ size_t size = data.readInt32();
+
+ void *inData = malloc(2 * size);
+ void *outData = (uint8_t *)inData + size;
+
+ data.read(inData, size);
+
+ uint32_t streamCTR = data.readInt32();
+ uint64_t inputCTR = data.readInt64();
+ status_t err = decrypt(inData, size, streamCTR, inputCTR, outData);
+
+ reply->writeInt32(err);
+
+ if (err == OK) {
+ reply->write(outData, size);
+ }
+
+ free(inData);
+ inData = outData = NULL;
+
+ return OK;
+ }
+
default:
return BBinder::onTransact(code, data, reply, flags);
}
diff --git a/media/libmedia/IMediaLogService.cpp b/media/libmedia/IMediaLogService.cpp
new file mode 100644
index 0000000..33239a7
--- /dev/null
+++ b/media/libmedia/IMediaLogService.cpp
@@ -0,0 +1,94 @@
+/*
+**
+** Copyright 2007, 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 "IMediaLogService"
+//#define LOG_NDEBUG 0
+
+#include <utils/Log.h>
+#include <stdint.h>
+#include <sys/types.h>
+#include <binder/Parcel.h>
+#include <media/IMediaLogService.h>
+
+namespace android {
+
+enum {
+ REGISTER_WRITER = IBinder::FIRST_CALL_TRANSACTION,
+ UNREGISTER_WRITER,
+};
+
+class BpMediaLogService : public BpInterface<IMediaLogService>
+{
+public:
+ BpMediaLogService(const sp<IBinder>& impl)
+ : BpInterface<IMediaLogService>(impl)
+ {
+ }
+
+ virtual void registerWriter(const sp<IMemory>& shared, size_t size, const char *name) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IMediaLogService::getInterfaceDescriptor());
+ data.writeStrongBinder(shared->asBinder());
+ data.writeInt32((int32_t) size);
+ data.writeCString(name);
+ status_t status = remote()->transact(REGISTER_WRITER, data, &reply);
+ // FIXME ignores status
+ }
+
+ virtual void unregisterWriter(const sp<IMemory>& shared) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IMediaLogService::getInterfaceDescriptor());
+ data.writeStrongBinder(shared->asBinder());
+ status_t status = remote()->transact(UNREGISTER_WRITER, data, &reply);
+ // FIXME ignores status
+ }
+
+};
+
+IMPLEMENT_META_INTERFACE(MediaLogService, "android.media.IMediaLogService");
+
+// ----------------------------------------------------------------------
+
+status_t BnMediaLogService::onTransact(
+ uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
+{
+ switch (code) {
+
+ case REGISTER_WRITER: {
+ CHECK_INTERFACE(IMediaLogService, data, reply);
+ sp<IMemory> shared = interface_cast<IMemory>(data.readStrongBinder());
+ size_t size = (size_t) data.readInt32();
+ const char *name = data.readCString();
+ registerWriter(shared, size, name);
+ return NO_ERROR;
+ }
+
+ case UNREGISTER_WRITER: {
+ CHECK_INTERFACE(IMediaLogService, data, reply);
+ sp<IMemory> shared = interface_cast<IMemory>(data.readStrongBinder());
+ unregisterWriter(shared);
+ return NO_ERROR;
+ }
+
+ default:
+ return BBinder::onTransact(code, data, reply, flags);
+ }
+}
+
+// ----------------------------------------------------------------------------
+
+}; // namespace android
diff --git a/media/libmedia/IMediaPlayerService.cpp b/media/libmedia/IMediaPlayerService.cpp
index ae76c10..a95f4c9 100644
--- a/media/libmedia/IMediaPlayerService.cpp
+++ b/media/libmedia/IMediaPlayerService.cpp
@@ -123,9 +123,10 @@
return interface_cast<ICrypto>(reply.readStrongBinder());
}
- virtual sp<IHDCP> makeHDCP() {
+ virtual sp<IHDCP> makeHDCP(bool createEncryptionModule) {
Parcel data, reply;
data.writeInterfaceToken(IMediaPlayerService::getInterfaceDescriptor());
+ data.writeInt32(createEncryptionModule);
remote()->transact(MAKE_HDCP, data, &reply);
return interface_cast<IHDCP>(reply.readStrongBinder());
}
@@ -226,7 +227,8 @@
} break;
case MAKE_HDCP: {
CHECK_INTERFACE(IMediaPlayerService, data, reply);
- sp<IHDCP> hdcp = makeHDCP();
+ bool createEncryptionModule = data.readInt32();
+ sp<IHDCP> hdcp = makeHDCP(createEncryptionModule);
reply->writeStrongBinder(hdcp->asBinder());
return NO_ERROR;
} break;
diff --git a/media/libmediaplayerservice/HDCP.cpp b/media/libmediaplayerservice/HDCP.cpp
index 09b9719..469a02e 100644
--- a/media/libmediaplayerservice/HDCP.cpp
+++ b/media/libmediaplayerservice/HDCP.cpp
@@ -26,8 +26,9 @@
namespace android {
-HDCP::HDCP()
- : mLibHandle(NULL),
+HDCP::HDCP(bool createEncryptionModule)
+ : mIsEncryptionModule(createEncryptionModule),
+ mLibHandle(NULL),
mHDCPModule(NULL) {
mLibHandle = dlopen("libstagefright_hdcp.so", RTLD_NOW);
@@ -40,7 +41,10 @@
void *, HDCPModule::ObserverFunc);
CreateHDCPModuleFunc createHDCPModule =
- (CreateHDCPModuleFunc)dlsym(mLibHandle, "createHDCPModule");
+ mIsEncryptionModule
+ ? (CreateHDCPModuleFunc)dlsym(mLibHandle, "createHDCPModule")
+ : (CreateHDCPModuleFunc)dlsym(
+ mLibHandle, "createHDCPModuleForDecryption");
if (createHDCPModule == NULL) {
ALOGE("Unable to find symbol 'createHDCPModule'.");
@@ -101,6 +105,8 @@
uint64_t *outInputCTR, void *outData) {
Mutex::Autolock autoLock(mLock);
+ CHECK(mIsEncryptionModule);
+
if (mHDCPModule == NULL) {
*outInputCTR = 0;
@@ -110,6 +116,20 @@
return mHDCPModule->encrypt(inData, size, streamCTR, outInputCTR, outData);
}
+status_t HDCP::decrypt(
+ const void *inData, size_t size,
+ uint32_t streamCTR, uint64_t outInputCTR, void *outData) {
+ Mutex::Autolock autoLock(mLock);
+
+ CHECK(!mIsEncryptionModule);
+
+ if (mHDCPModule == NULL) {
+ return NO_INIT;
+ }
+
+ return mHDCPModule->decrypt(inData, size, streamCTR, outInputCTR, outData);
+}
+
// static
void HDCP::ObserveWrapper(void *me, int msg, int ext1, int ext2) {
static_cast<HDCP *>(me)->observe(msg, ext1, ext2);
diff --git a/media/libmediaplayerservice/HDCP.h b/media/libmediaplayerservice/HDCP.h
index b2fc457..42e6467 100644
--- a/media/libmediaplayerservice/HDCP.h
+++ b/media/libmediaplayerservice/HDCP.h
@@ -24,7 +24,7 @@
namespace android {
struct HDCP : public BnHDCP {
- HDCP();
+ HDCP(bool createEncryptionModule);
virtual ~HDCP();
virtual status_t setObserver(const sp<IHDCPObserver> &observer);
@@ -35,9 +35,15 @@
const void *inData, size_t size, uint32_t streamCTR,
uint64_t *outInputCTR, void *outData);
+ virtual status_t decrypt(
+ const void *inData, size_t size,
+ uint32_t streamCTR, uint64_t outInputCTR, void *outData);
+
private:
Mutex mLock;
+ bool mIsEncryptionModule;
+
void *mLibHandle;
HDCPModule *mHDCPModule;
sp<IHDCPObserver> mObserver;
diff --git a/media/libmediaplayerservice/MediaPlayerService.cpp b/media/libmediaplayerservice/MediaPlayerService.cpp
index 749f48c..f932131 100644
--- a/media/libmediaplayerservice/MediaPlayerService.cpp
+++ b/media/libmediaplayerservice/MediaPlayerService.cpp
@@ -285,8 +285,8 @@
return new Crypto;
}
-sp<IHDCP> MediaPlayerService::makeHDCP() {
- return new HDCP;
+sp<IHDCP> MediaPlayerService::makeHDCP(bool createEncryptionModule) {
+ return new HDCP(createEncryptionModule);
}
sp<IRemoteDisplay> MediaPlayerService::listenForRemoteDisplay(
diff --git a/media/libmediaplayerservice/MediaPlayerService.h b/media/libmediaplayerservice/MediaPlayerService.h
index d2d8939..2d2a09d 100644
--- a/media/libmediaplayerservice/MediaPlayerService.h
+++ b/media/libmediaplayerservice/MediaPlayerService.h
@@ -249,7 +249,7 @@
virtual sp<IMemory> decode(int fd, int64_t offset, int64_t length, uint32_t *pSampleRate, int* pNumChannels, audio_format_t* pFormat);
virtual sp<IOMX> getOMX();
virtual sp<ICrypto> makeCrypto();
- virtual sp<IHDCP> makeHDCP();
+ virtual sp<IHDCP> makeHDCP(bool createEncryptionModule);
virtual sp<IRemoteDisplay> listenForRemoteDisplay(const sp<IRemoteDisplayClient>& client,
const String8& iface);
diff --git a/media/libnbaio/Android.mk b/media/libnbaio/Android.mk
index 757272f..d372d20 100644
--- a/media/libnbaio/Android.mk
+++ b/media/libnbaio/Android.mk
@@ -14,6 +14,8 @@
roundup.c \
SourceAudioBufferProvider.cpp
+LOCAL_SRC_FILES += NBLog.cpp
+
# libsndfile license is incompatible; uncomment to use for local debug only
#LOCAL_SRC_FILES += LibsndfileSink.cpp LibsndfileSource.cpp
#LOCAL_C_INCLUDES += path/to/libsndfile/src
@@ -25,6 +27,7 @@
LOCAL_MODULE := libnbaio
LOCAL_SHARED_LIBRARIES := \
+ libbinder \
libcommon_time_client \
libcutils \
libutils
diff --git a/media/libnbaio/NBLog.cpp b/media/libnbaio/NBLog.cpp
new file mode 100644
index 0000000..045bf64
--- /dev/null
+++ b/media/libnbaio/NBLog.cpp
@@ -0,0 +1,447 @@
+/*
+ * Copyright (C) 2013 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 "NBLog"
+//#define LOG_NDEBUG 0
+
+#include <stdarg.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+#include <new>
+#include <cutils/atomic.h>
+#include <media/nbaio/NBLog.h>
+#include <utils/Log.h>
+
+namespace android {
+
+int NBLog::Entry::readAt(size_t offset) const
+{
+ // FIXME This is too slow, despite the name it is used during writing
+ if (offset == 0)
+ return mEvent;
+ else if (offset == 1)
+ return mLength;
+ else if (offset < (size_t) (mLength + 2))
+ return ((char *) mData)[offset - 2];
+ else if (offset == (size_t) (mLength + 2))
+ return mLength;
+ else
+ return 0;
+}
+
+// ---------------------------------------------------------------------------
+
+#if 0 // FIXME see note in NBLog.h
+NBLog::Timeline::Timeline(size_t size, void *shared)
+ : mSize(roundup(size)), mOwn(shared == NULL),
+ mShared((Shared *) (mOwn ? new char[sharedSize(size)] : shared))
+{
+ new (mShared) Shared;
+}
+
+NBLog::Timeline::~Timeline()
+{
+ mShared->~Shared();
+ if (mOwn) {
+ delete[] (char *) mShared;
+ }
+}
+#endif
+
+/*static*/
+size_t NBLog::Timeline::sharedSize(size_t size)
+{
+ return sizeof(Shared) + roundup(size);
+}
+
+// ---------------------------------------------------------------------------
+
+NBLog::Writer::Writer()
+ : mSize(0), mShared(NULL), mRear(0), mEnabled(false)
+{
+}
+
+NBLog::Writer::Writer(size_t size, void *shared)
+ : mSize(roundup(size)), mShared((Shared *) shared), mRear(0), mEnabled(mShared != NULL)
+{
+}
+
+NBLog::Writer::Writer(size_t size, const sp<IMemory>& iMemory)
+ : mSize(roundup(size)), mShared(iMemory != 0 ? (Shared *) iMemory->pointer() : NULL),
+ mIMemory(iMemory), mRear(0), mEnabled(mShared != NULL)
+{
+}
+
+void NBLog::Writer::log(const char *string)
+{
+ if (!mEnabled) {
+ return;
+ }
+ size_t length = strlen(string);
+ if (length > 255) {
+ length = 255;
+ }
+ log(EVENT_STRING, string, length);
+}
+
+void NBLog::Writer::logf(const char *fmt, ...)
+{
+ if (!mEnabled) {
+ return;
+ }
+ va_list ap;
+ va_start(ap, fmt);
+ Writer::logvf(fmt, ap); // the Writer:: is needed to avoid virtual dispatch for LockedWriter
+ va_end(ap);
+}
+
+void NBLog::Writer::logvf(const char *fmt, va_list ap)
+{
+ if (!mEnabled) {
+ return;
+ }
+ char buffer[256];
+ int length = vsnprintf(buffer, sizeof(buffer), fmt, ap);
+ if (length >= (int) sizeof(buffer)) {
+ length = sizeof(buffer) - 1;
+ // NUL termination is not required
+ // buffer[length] = '\0';
+ }
+ if (length >= 0) {
+ log(EVENT_STRING, buffer, length);
+ }
+}
+
+void NBLog::Writer::logTimestamp()
+{
+ if (!mEnabled) {
+ return;
+ }
+ struct timespec ts;
+ if (!clock_gettime(CLOCK_MONOTONIC, &ts)) {
+ log(EVENT_TIMESTAMP, &ts, sizeof(struct timespec));
+ }
+}
+
+void NBLog::Writer::logTimestamp(const struct timespec& ts)
+{
+ if (!mEnabled) {
+ return;
+ }
+ log(EVENT_TIMESTAMP, &ts, sizeof(struct timespec));
+}
+
+void NBLog::Writer::log(Event event, const void *data, size_t length)
+{
+ if (!mEnabled) {
+ return;
+ }
+ if (data == NULL || length > 255) {
+ return;
+ }
+ switch (event) {
+ case EVENT_STRING:
+ case EVENT_TIMESTAMP:
+ break;
+ case EVENT_RESERVED:
+ default:
+ return;
+ }
+ Entry entry(event, data, length);
+ log(&entry, true /*trusted*/);
+}
+
+void NBLog::Writer::log(const NBLog::Entry *entry, bool trusted)
+{
+ if (!mEnabled) {
+ return;
+ }
+ if (!trusted) {
+ log(entry->mEvent, entry->mData, entry->mLength);
+ return;
+ }
+ size_t rear = mRear & (mSize - 1);
+ size_t written = mSize - rear; // written = number of bytes that have been written so far
+ size_t need = entry->mLength + 3; // mEvent, mLength, data[length], mLength
+ // need = number of bytes remaining to write
+ if (written > need) {
+ written = need;
+ }
+ size_t i;
+ // FIXME optimize this using memcpy for the data part of the Entry.
+ // The Entry could have a method copyTo(ptr, offset, size) to optimize the copy.
+ for (i = 0; i < written; ++i) {
+ mShared->mBuffer[rear + i] = entry->readAt(i);
+ }
+ if (rear + written == mSize && (need -= written) > 0) {
+ for (i = 0; i < need; ++i) {
+ mShared->mBuffer[i] = entry->readAt(written + i);
+ }
+ written += need;
+ }
+ android_atomic_release_store(mRear += written, &mShared->mRear);
+}
+
+bool NBLog::Writer::isEnabled() const
+{
+ return mEnabled;
+}
+
+bool NBLog::Writer::setEnabled(bool enabled)
+{
+ bool old = mEnabled;
+ mEnabled = enabled && mShared != NULL;
+ return old;
+}
+
+// ---------------------------------------------------------------------------
+
+NBLog::LockedWriter::LockedWriter()
+ : Writer()
+{
+}
+
+NBLog::LockedWriter::LockedWriter(size_t size, void *shared)
+ : Writer(size, shared)
+{
+}
+
+void NBLog::LockedWriter::log(const char *string)
+{
+ Mutex::Autolock _l(mLock);
+ Writer::log(string);
+}
+
+void NBLog::LockedWriter::logf(const char *fmt, ...)
+{
+ // FIXME should not take the lock until after formatting is done
+ Mutex::Autolock _l(mLock);
+ va_list ap;
+ va_start(ap, fmt);
+ Writer::logvf(fmt, ap);
+ va_end(ap);
+}
+
+void NBLog::LockedWriter::logvf(const char *fmt, va_list ap)
+{
+ // FIXME should not take the lock until after formatting is done
+ Mutex::Autolock _l(mLock);
+ Writer::logvf(fmt, ap);
+}
+
+void NBLog::LockedWriter::logTimestamp()
+{
+ // FIXME should not take the lock until after the clock_gettime() syscall
+ Mutex::Autolock _l(mLock);
+ Writer::logTimestamp();
+}
+
+void NBLog::LockedWriter::logTimestamp(const struct timespec& ts)
+{
+ Mutex::Autolock _l(mLock);
+ Writer::logTimestamp(ts);
+}
+
+bool NBLog::LockedWriter::isEnabled() const
+{
+ Mutex::Autolock _l(mLock);
+ return Writer::isEnabled();
+}
+
+bool NBLog::LockedWriter::setEnabled(bool enabled)
+{
+ Mutex::Autolock _l(mLock);
+ return Writer::setEnabled(enabled);
+}
+
+// ---------------------------------------------------------------------------
+
+NBLog::Reader::Reader(size_t size, const void *shared)
+ : mSize(roundup(size)), mShared((const Shared *) shared), mFront(0)
+{
+}
+
+NBLog::Reader::Reader(size_t size, const sp<IMemory>& iMemory)
+ : mSize(roundup(size)), mShared(iMemory != 0 ? (const Shared *) iMemory->pointer() : NULL),
+ mIMemory(iMemory), mFront(0)
+{
+}
+
+void NBLog::Reader::dump(int fd, size_t indent)
+{
+ int32_t rear = android_atomic_acquire_load(&mShared->mRear);
+ size_t avail = rear - mFront;
+ if (avail == 0) {
+ return;
+ }
+ size_t lost = 0;
+ if (avail > mSize) {
+ lost = avail - mSize;
+ mFront += lost;
+ avail = mSize;
+ }
+ size_t remaining = avail; // remaining = number of bytes left to read
+ size_t front = mFront & (mSize - 1);
+ size_t read = mSize - front; // read = number of bytes that have been read so far
+ if (read > remaining) {
+ read = remaining;
+ }
+ // make a copy to avoid race condition with writer
+ uint8_t *copy = new uint8_t[avail];
+ // copy first part of circular buffer up until the wraparound point
+ memcpy(copy, &mShared->mBuffer[front], read);
+ if (front + read == mSize) {
+ if ((remaining -= read) > 0) {
+ // copy second part of circular buffer starting at beginning
+ memcpy(©[read], mShared->mBuffer, remaining);
+ read += remaining;
+ // remaining = 0 but not necessary
+ }
+ }
+ mFront += read;
+ size_t i = avail;
+ Event event;
+ size_t length;
+ struct timespec ts;
+ time_t maxSec = -1;
+ while (i >= 3) {
+ length = copy[i - 1];
+ if (length + 3 > i || copy[i - length - 2] != length) {
+ break;
+ }
+ event = (Event) copy[i - length - 3];
+ if (event == EVENT_TIMESTAMP) {
+ if (length != sizeof(struct timespec)) {
+ // corrupt
+ break;
+ }
+ memcpy(&ts, ©[i - length - 1], sizeof(struct timespec));
+ if (ts.tv_sec > maxSec) {
+ maxSec = ts.tv_sec;
+ }
+ }
+ i -= length + 3;
+ }
+ if (i > 0) {
+ lost += i;
+ if (fd >= 0) {
+ fdprintf(fd, "%*swarning: lost %u bytes worth of events\n", indent, "", lost);
+ } else {
+ ALOGI("%*swarning: lost %u bytes worth of events\n", indent, "", lost);
+ }
+ }
+ size_t width = 1;
+ while (maxSec >= 10) {
+ ++width;
+ maxSec /= 10;
+ }
+ char prefix[32];
+ if (maxSec >= 0) {
+ snprintf(prefix, sizeof(prefix), "[%*s] ", width + 4, "");
+ } else {
+ prefix[0] = '\0';
+ }
+ while (i < avail) {
+ event = (Event) copy[i];
+ length = copy[i + 1];
+ const void *data = ©[i + 2];
+ size_t advance = length + 3;
+ switch (event) {
+ case EVENT_STRING:
+ if (fd >= 0) {
+ fdprintf(fd, "%*s%s%.*s\n", indent, "", prefix, length, (const char *) data);
+ } else {
+ ALOGI("%*s%s%.*s", indent, "", prefix, length, (const char *) data);
+ } break;
+ case EVENT_TIMESTAMP: {
+ // already checked that length == sizeof(struct timespec);
+ memcpy(&ts, data, sizeof(struct timespec));
+ long prevNsec = ts.tv_nsec;
+ long deltaMin = LONG_MAX;
+ long deltaMax = -1;
+ long deltaTotal = 0;
+ size_t j = i;
+ for (;;) {
+ j += sizeof(struct timespec) + 3;
+ if (j >= avail || (Event) copy[j] != EVENT_TIMESTAMP) {
+ break;
+ }
+ struct timespec tsNext;
+ memcpy(&tsNext, ©[j + 2], sizeof(struct timespec));
+ if (tsNext.tv_sec != ts.tv_sec) {
+ break;
+ }
+ long delta = tsNext.tv_nsec - prevNsec;
+ if (delta < 0) {
+ break;
+ }
+ if (delta < deltaMin) {
+ deltaMin = delta;
+ }
+ if (delta > deltaMax) {
+ deltaMax = delta;
+ }
+ deltaTotal += delta;
+ prevNsec = tsNext.tv_nsec;
+ }
+ size_t n = (j - i) / (sizeof(struct timespec) + 3);
+ if (n >= kSquashTimestamp) {
+ if (fd >= 0) {
+ fdprintf(fd, "%*s[%d.%03d to .%.03d by .%.03d to .%.03d]\n", indent, "",
+ (int) ts.tv_sec, (int) (ts.tv_nsec / 1000000),
+ (int) ((ts.tv_nsec + deltaTotal) / 1000000),
+ (int) (deltaMin / 1000000), (int) (deltaMax / 1000000));
+ } else {
+ ALOGI("%*s[%d.%03d to .%.03d by .%.03d to .%.03d]\n", indent, "",
+ (int) ts.tv_sec, (int) (ts.tv_nsec / 1000000),
+ (int) ((ts.tv_nsec + deltaTotal) / 1000000),
+ (int) (deltaMin / 1000000), (int) (deltaMax / 1000000));
+ }
+ i = j;
+ advance = 0;
+ break;
+ }
+ if (fd >= 0) {
+ fdprintf(fd, "%*s[%d.%03d]\n", indent, "", (int) ts.tv_sec,
+ (int) (ts.tv_nsec / 1000000));
+ } else {
+ ALOGI("%*s[%d.%03d]", indent, "", (int) ts.tv_sec,
+ (int) (ts.tv_nsec / 1000000));
+ }
+ } break;
+ case EVENT_RESERVED:
+ default:
+ if (fd >= 0) {
+ fdprintf(fd, "%*s%swarning: unknown event %d\n", indent, "", prefix, event);
+ } else {
+ ALOGI("%*s%swarning: unknown event %d", indent, "", prefix, event);
+ }
+ break;
+ }
+ i += advance;
+ }
+ // FIXME it would be more efficient to put a char mCopy[256] as a member variable of the dumper
+ delete[] copy;
+}
+
+bool NBLog::Reader::isIMemory(const sp<IMemory>& iMemory) const
+{
+ return iMemory.get() == mIMemory.get();
+}
+
+} // namespace android
diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp
index 7920d32..7b27843 100644
--- a/media/libstagefright/ACodec.cpp
+++ b/media/libstagefright/ACodec.cpp
@@ -1421,7 +1421,8 @@
|| format.eColorFormat == OMX_COLOR_FormatCbYCrY
|| format.eColorFormat == OMX_TI_COLOR_FormatYUV420PackedSemiPlanar
|| format.eColorFormat == OMX_QCOM_COLOR_FormatYVU420SemiPlanar
- || format.eColorFormat == OMX_QCOM_COLOR_FormatYUV420PackedSemiPlanar64x32Tile2m8ka);
+ || format.eColorFormat == OMX_QCOM_COLOR_FormatYUV420PackedSemiPlanar64x32Tile2m8ka
+ || format.eColorFormat == OMX_SEC_COLOR_FormatNV12Tiled);
return mOMX->setParameter(
mNode, OMX_IndexParamVideoPortFormat,
diff --git a/media/libstagefright/mpeg2ts/ATSParser.cpp b/media/libstagefright/mpeg2ts/ATSParser.cpp
index 4f6c4b2..a167b5a 100644
--- a/media/libstagefright/mpeg2ts/ATSParser.cpp
+++ b/media/libstagefright/mpeg2ts/ATSParser.cpp
@@ -534,6 +534,16 @@
mBuffer->setRange(0, 0);
mExpectedContinuityCounter = -1;
+#if 0
+ // Uncomment this if you'd rather see no corruption whatsoever on
+ // screen and suspend updates until we come across another IDR frame.
+
+ if (mStreamType == STREAMTYPE_H264) {
+ ALOGI("clearing video queue");
+ mQueue->clear(true /* clearFormat */);
+ }
+#endif
+
return OK;
}
diff --git a/media/libstagefright/wifi-display/Android.mk b/media/libstagefright/wifi-display/Android.mk
index 75098f1..5095e82 100644
--- a/media/libstagefright/wifi-display/Android.mk
+++ b/media/libstagefright/wifi-display/Android.mk
@@ -6,6 +6,7 @@
ANetworkSession.cpp \
Parameters.cpp \
ParsedMessage.cpp \
+ sink/DirectRenderer.cpp \
sink/LinearRegression.cpp \
sink/RTPSink.cpp \
sink/TunnelRenderer.cpp \
@@ -18,6 +19,7 @@
source/TSPacketizer.cpp \
source/WifiDisplaySource.cpp \
TimeSeries.cpp \
+ VideoFormats.cpp \
LOCAL_C_INCLUDES:= \
$(TOP)/frameworks/av/media/libstagefright \
diff --git a/media/libstagefright/wifi-display/VideoFormats.cpp b/media/libstagefright/wifi-display/VideoFormats.cpp
new file mode 100644
index 0000000..9ad8c3c
--- /dev/null
+++ b/media/libstagefright/wifi-display/VideoFormats.cpp
@@ -0,0 +1,370 @@
+/*
+ * Copyright 2013, 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 "VideoFormats"
+#include <utils/Log.h>
+
+#include "VideoFormats.h"
+
+#include <media/stagefright/foundation/ADebug.h>
+
+namespace android {
+
+VideoFormats::VideoFormats() {
+ for (size_t i = 0; i < kNumResolutionTypes; ++i) {
+ mResolutionEnabled[i] = 0;
+ }
+
+ setNativeResolution(RESOLUTION_CEA, 0); // default to 640x480 p60
+}
+
+void VideoFormats::setNativeResolution(ResolutionType type, size_t index) {
+ CHECK_LT(type, kNumResolutionTypes);
+ CHECK(GetConfiguration(type, index, NULL, NULL, NULL, NULL));
+
+ mNativeType = type;
+ mNativeIndex = index;
+
+ setResolutionEnabled(type, index);
+}
+
+void VideoFormats::getNativeResolution(
+ ResolutionType *type, size_t *index) const {
+ *type = mNativeType;
+ *index = mNativeIndex;
+}
+
+void VideoFormats::disableAll() {
+ for (size_t i = 0; i < kNumResolutionTypes; ++i) {
+ mResolutionEnabled[i] = 0;
+ }
+}
+
+void VideoFormats::enableAll() {
+ for (size_t i = 0; i < kNumResolutionTypes; ++i) {
+ mResolutionEnabled[i] = 0xffffffff;
+ }
+}
+
+void VideoFormats::setResolutionEnabled(
+ ResolutionType type, size_t index, bool enabled) {
+ CHECK_LT(type, kNumResolutionTypes);
+ CHECK(GetConfiguration(type, index, NULL, NULL, NULL, NULL));
+
+ if (enabled) {
+ mResolutionEnabled[type] |= (1ul << index);
+ } else {
+ mResolutionEnabled[type] &= ~(1ul << index);
+ }
+}
+
+bool VideoFormats::isResolutionEnabled(
+ ResolutionType type, size_t index) const {
+ CHECK_LT(type, kNumResolutionTypes);
+ CHECK(GetConfiguration(type, index, NULL, NULL, NULL, NULL));
+
+ return mResolutionEnabled[type] & (1ul << index);
+}
+
+// static
+bool VideoFormats::GetConfiguration(
+ ResolutionType type,
+ size_t index,
+ size_t *width, size_t *height, size_t *framesPerSecond,
+ bool *interlaced) {
+ CHECK_LT(type, kNumResolutionTypes);
+
+ if (index >= 32) {
+ return false;
+ }
+
+ static const struct config_t {
+ size_t width, height, framesPerSecond;
+ bool interlaced;
+ } kConfigs[kNumResolutionTypes][32] = {
+ {
+ // CEA Resolutions
+ { 640, 480, 60, false },
+ { 720, 480, 60, false },
+ { 720, 480, 60, true },
+ { 720, 576, 50, false },
+ { 720, 576, 50, true },
+ { 1280, 720, 30, false },
+ { 1280, 720, 60, false },
+ { 1920, 1080, 30, false },
+ { 1920, 1080, 60, false },
+ { 1920, 1080, 60, true },
+ { 1280, 720, 25, false },
+ { 1280, 720, 50, false },
+ { 1920, 1080, 25, false },
+ { 1920, 1080, 50, false },
+ { 1920, 1080, 50, true },
+ { 1280, 720, 24, false },
+ { 1920, 1080, 24, false },
+ { 0, 0, 0, false },
+ { 0, 0, 0, false },
+ { 0, 0, 0, false },
+ { 0, 0, 0, false },
+ { 0, 0, 0, false },
+ { 0, 0, 0, false },
+ { 0, 0, 0, false },
+ { 0, 0, 0, false },
+ { 0, 0, 0, false },
+ { 0, 0, 0, false },
+ { 0, 0, 0, false },
+ { 0, 0, 0, false },
+ { 0, 0, 0, false },
+ { 0, 0, 0, false },
+ { 0, 0, 0, false },
+ },
+ {
+ // VESA Resolutions
+ { 800, 600, 30, false },
+ { 800, 600, 60, false },
+ { 1024, 768, 30, false },
+ { 1024, 768, 60, false },
+ { 1152, 864, 30, false },
+ { 1152, 864, 60, false },
+ { 1280, 768, 30, false },
+ { 1280, 768, 60, false },
+ { 1280, 800, 30, false },
+ { 1280, 800, 60, false },
+ { 1360, 768, 30, false },
+ { 1360, 768, 60, false },
+ { 1366, 768, 30, false },
+ { 1366, 768, 60, false },
+ { 1280, 1024, 30, false },
+ { 1280, 1024, 60, false },
+ { 1400, 1050, 30, false },
+ { 1400, 1050, 60, false },
+ { 1440, 900, 30, false },
+ { 1440, 900, 60, false },
+ { 1600, 900, 30, false },
+ { 1600, 900, 60, false },
+ { 1600, 1200, 30, false },
+ { 1600, 1200, 60, false },
+ { 1680, 1024, 30, false },
+ { 1680, 1024, 60, false },
+ { 1680, 1050, 30, false },
+ { 1680, 1050, 60, false },
+ { 1920, 1200, 30, false },
+ { 1920, 1200, 60, false },
+ { 0, 0, 0, false },
+ { 0, 0, 0, false },
+ },
+ {
+ // HH Resolutions
+ { 800, 480, 30, false },
+ { 800, 480, 60, false },
+ { 854, 480, 30, false },
+ { 854, 480, 60, false },
+ { 864, 480, 30, false },
+ { 864, 480, 60, false },
+ { 640, 360, 30, false },
+ { 640, 360, 60, false },
+ { 960, 540, 30, false },
+ { 960, 540, 60, false },
+ { 848, 480, 30, false },
+ { 848, 480, 60, false },
+ { 0, 0, 0, false },
+ { 0, 0, 0, false },
+ { 0, 0, 0, false },
+ { 0, 0, 0, false },
+ { 0, 0, 0, false },
+ { 0, 0, 0, false },
+ { 0, 0, 0, false },
+ { 0, 0, 0, false },
+ { 0, 0, 0, false },
+ { 0, 0, 0, false },
+ { 0, 0, 0, false },
+ { 0, 0, 0, false },
+ { 0, 0, 0, false },
+ { 0, 0, 0, false },
+ { 0, 0, 0, false },
+ { 0, 0, 0, false },
+ { 0, 0, 0, false },
+ { 0, 0, 0, false },
+ { 0, 0, 0, false },
+ { 0, 0, 0, false },
+ }
+ };
+
+ const config_t *config = &kConfigs[type][index];
+
+ if (config->width == 0) {
+ return false;
+ }
+
+ if (width) {
+ *width = config->width;
+ }
+
+ if (height) {
+ *height = config->height;
+ }
+
+ if (framesPerSecond) {
+ *framesPerSecond = config->framesPerSecond;
+ }
+
+ if (interlaced) {
+ *interlaced = config->interlaced;
+ }
+
+ return true;
+}
+
+bool VideoFormats::parseFormatSpec(const char *spec) {
+ CHECK_EQ(kNumResolutionTypes, 3);
+
+ unsigned native, dummy;
+
+ if (sscanf(
+ spec,
+ "%02x %02x %02x %02x %08X %08X %08X",
+ &native,
+ &dummy,
+ &dummy,
+ &dummy,
+ &mResolutionEnabled[0],
+ &mResolutionEnabled[1],
+ &mResolutionEnabled[2]) != 7) {
+ return false;
+ }
+
+ mNativeIndex = native >> 3;
+ mNativeType = (ResolutionType)(native & 7);
+
+ if (mNativeType >= kNumResolutionTypes) {
+ return false;
+ }
+
+ return GetConfiguration(mNativeType, mNativeIndex, NULL, NULL, NULL, NULL);
+}
+
+AString VideoFormats::getFormatSpec() const {
+ CHECK_EQ(kNumResolutionTypes, 3);
+
+ // wfd_video_formats:
+ // 1 byte "native"
+ // 1 byte "preferred-display-mode-supported" 0 or 1
+ // one or more avc codec structures
+ // 1 byte profile
+ // 1 byte level
+ // 4 byte CEA mask
+ // 4 byte VESA mask
+ // 4 byte HH mask
+ // 1 byte latency
+ // 2 byte min-slice-slice
+ // 2 byte slice-enc-params
+ // 1 byte framerate-control-support
+ // max-hres (none or 2 byte)
+ // max-vres (none or 2 byte)
+
+ return StringPrintf(
+ "%02x 00 02 02 %08x %08x %08x 00 0000 0000 00 none none",
+ (mNativeIndex << 3) | mNativeType,
+ mResolutionEnabled[0],
+ mResolutionEnabled[1],
+ mResolutionEnabled[2]);
+}
+
+// static
+bool VideoFormats::PickBestFormat(
+ const VideoFormats &sinkSupported,
+ const VideoFormats &sourceSupported,
+ ResolutionType *chosenType,
+ size_t *chosenIndex) {
+ ResolutionType nativeType;
+ size_t nativeIndex;
+ sinkSupported.getNativeResolution(&nativeType, &nativeIndex);
+ if (sinkSupported.isResolutionEnabled(nativeType, nativeIndex)) {
+ if (sourceSupported.isResolutionEnabled(nativeType, nativeIndex)) {
+ ALOGI("Choosing sink's native resolution");
+ *chosenType = nativeType;
+ *chosenIndex = nativeIndex;
+ return true;
+ }
+ } else {
+ ALOGW("Sink advertised native resolution that it doesn't "
+ "actually support... ignoring");
+ }
+
+ sourceSupported.getNativeResolution(&nativeType, &nativeIndex);
+ if (sourceSupported.isResolutionEnabled(nativeType, nativeIndex)) {
+ if (sinkSupported.isResolutionEnabled(nativeType, nativeIndex)) {
+ ALOGI("Choosing source's native resolution");
+ *chosenType = nativeType;
+ *chosenIndex = nativeIndex;
+ return true;
+ }
+ } else {
+ ALOGW("Source advertised native resolution that it doesn't "
+ "actually support... ignoring");
+ }
+
+ bool first = true;
+ uint32_t bestScore = 0;
+ size_t bestType = 0;
+ size_t bestIndex = 0;
+ for (size_t i = 0; i < kNumResolutionTypes; ++i) {
+ for (size_t j = 0; j < 32; ++j) {
+ size_t width, height, framesPerSecond;
+ bool interlaced;
+ if (!GetConfiguration(
+ (ResolutionType)i,
+ j,
+ &width, &height, &framesPerSecond, &interlaced)) {
+ break;
+ }
+
+ if (!sinkSupported.isResolutionEnabled((ResolutionType)i, j)
+ || !sourceSupported.isResolutionEnabled(
+ (ResolutionType)i, j)) {
+ continue;
+ }
+
+ ALOGV("type %u, index %u, %u x %u %c%u supported",
+ i, j, width, height, interlaced ? 'i' : 'p', framesPerSecond);
+
+ uint32_t score = width * height * framesPerSecond;
+ if (!interlaced) {
+ score *= 2;
+ }
+
+ if (first || score > bestScore) {
+ bestScore = score;
+ bestType = i;
+ bestIndex = j;
+
+ first = false;
+ }
+ }
+ }
+
+ if (first) {
+ return false;
+ }
+
+ *chosenType = (ResolutionType)bestType;
+ *chosenIndex = bestIndex;
+
+ return true;
+}
+
+} // namespace android
+
diff --git a/media/libstagefright/wifi-display/VideoFormats.h b/media/libstagefright/wifi-display/VideoFormats.h
new file mode 100644
index 0000000..a84407a
--- /dev/null
+++ b/media/libstagefright/wifi-display/VideoFormats.h
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2013, 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 VIDEO_FORMATS_H_
+
+#define VIDEO_FORMATS_H_
+
+#include <media/stagefright/foundation/ABase.h>
+
+#include <stdint.h>
+
+namespace android {
+
+struct AString;
+
+// This class encapsulates that video resolution capabilities of a wfd source
+// or sink as outlined in the wfd specs. Currently three sets of resolutions
+// are specified, each of which supports up to 32 resolutions.
+// In addition to its capabilities each sink/source also publishes its
+// "native" resolution, presumably one that is preferred among all others
+// because it wouldn't require any scaling and directly corresponds to the
+// display capabilities/pixels.
+struct VideoFormats {
+ VideoFormats();
+
+ enum ResolutionType {
+ RESOLUTION_CEA,
+ RESOLUTION_VESA,
+ RESOLUTION_HH,
+ kNumResolutionTypes,
+ };
+
+ void setNativeResolution(ResolutionType type, size_t index);
+ void getNativeResolution(ResolutionType *type, size_t *index) const;
+
+ void disableAll();
+ void enableAll();
+
+ void setResolutionEnabled(
+ ResolutionType type, size_t index, bool enabled = true);
+
+ bool isResolutionEnabled(ResolutionType type, size_t index) const;
+
+ static bool GetConfiguration(
+ ResolutionType type, size_t index,
+ size_t *width, size_t *height, size_t *framesPerSecond,
+ bool *interlaced);
+
+ bool parseFormatSpec(const char *spec);
+ AString getFormatSpec() const;
+
+ static bool PickBestFormat(
+ const VideoFormats &sinkSupported,
+ const VideoFormats &sourceSupported,
+ ResolutionType *chosenType,
+ size_t *chosenIndex);
+
+private:
+ ResolutionType mNativeType;
+ size_t mNativeIndex;
+
+ uint32_t mResolutionEnabled[kNumResolutionTypes];
+
+ DISALLOW_EVIL_CONSTRUCTORS(VideoFormats);
+};
+
+} // namespace android
+
+#endif // VIDEO_FORMATS_H_
+
diff --git a/media/libstagefright/wifi-display/sink/RTPSink.cpp b/media/libstagefright/wifi-display/sink/RTPSink.cpp
index 640e055..7f4b66f 100644
--- a/media/libstagefright/wifi-display/sink/RTPSink.cpp
+++ b/media/libstagefright/wifi-display/sink/RTPSink.cpp
@@ -21,7 +21,14 @@
#include "RTPSink.h"
#include "ANetworkSession.h"
+
+#if USE_TUNNEL_RENDERER
#include "TunnelRenderer.h"
+#define RENDERER_CLASS TunnelRenderer
+#else
+#include "DirectRenderer.h"
+#define RENDERER_CLASS DirectRenderer
+#endif
#include <media/stagefright/foundation/ABuffer.h>
#include <media/stagefright/foundation/ADebug.h>
@@ -238,9 +245,11 @@
RTPSink::RTPSink(
const sp<ANetworkSession> &netSession,
- const sp<IGraphicBufferProducer> &bufferProducer)
+ const sp<IGraphicBufferProducer> &bufferProducer,
+ const sp<AMessage> ¬ify)
: mNetSession(netSession),
mSurfaceTex(bufferProducer),
+ mNotify(notify),
mRTPPort(0),
mRTPSessionID(0),
mRTCPSessionID(0),
@@ -470,6 +479,7 @@
uint32_t rtpTime = U32_AT(&data[4]);
uint16_t seqNo = U16_AT(&data[2]);
+#if 0
int64_t arrivalTimeUs;
CHECK(buffer->meta()->findInt64("arrivalTimeUs", &arrivalTimeUs));
@@ -500,6 +510,7 @@
ALOGI("packet was %.2f ms late", latenessMs);
}
}
+#endif
sp<AMessage> meta = buffer->meta();
meta->setInt32("ssrc", srcId);
@@ -515,12 +526,12 @@
sp<AMessage> notifyLost = new AMessage(kWhatPacketLost, id());
notifyLost->setInt32("ssrc", srcId);
- mRenderer = new TunnelRenderer(notifyLost, mSurfaceTex);
+ mRenderer = new RENDERER_CLASS(notifyLost, mSurfaceTex);
looper()->registerHandler(mRenderer);
}
sp<AMessage> queueBufferMsg =
- new AMessage(TunnelRenderer::kWhatQueueBuffer, mRenderer->id());
+ new AMessage(RENDERER_CLASS::kWhatQueueBuffer, mRenderer->id());
sp<Source> source = new Source(seqNo, buffer, queueBufferMsg);
mSources.add(srcId, source);
@@ -776,12 +787,12 @@
int32_t blp = 0;
- sp<ABuffer> buf = new ABuffer(1500);
+ sp<ABuffer> buf = new ABuffer(16);
buf->setRange(0, 0);
uint8_t *ptr = buf->data();
ptr[0] = 0x80 | 1; // generic NACK
- ptr[1] = 205; // RTPFB
+ ptr[1] = 205; // TSFB
ptr[2] = 0;
ptr[3] = 3;
ptr[4] = 0xde; // sender SSRC
diff --git a/media/libstagefright/wifi-display/sink/RTPSink.h b/media/libstagefright/wifi-display/sink/RTPSink.h
index 2183fd6..6e40185 100644
--- a/media/libstagefright/wifi-display/sink/RTPSink.h
+++ b/media/libstagefright/wifi-display/sink/RTPSink.h
@@ -24,18 +24,26 @@
#include <gui/Surface.h>
+#define USE_TUNNEL_RENDERER 0
+
namespace android {
struct ABuffer;
struct ANetworkSession;
+
+#if USE_TUNNEL_RENDERER
struct TunnelRenderer;
+#else
+struct DirectRenderer;
+#endif
// Creates a pair of sockets for RTP/RTCP traffic, instantiates a renderer
// for incoming transport stream data and occasionally sends statistics over
// the RTCP channel.
struct RTPSink : public AHandler {
RTPSink(const sp<ANetworkSession> &netSession,
- const sp<IGraphicBufferProducer> &bufferProducer);
+ const sp<IGraphicBufferProducer> &bufferProducer,
+ const sp<AMessage> ¬ify);
// If TCP interleaving is used, no UDP sockets are created, instead
// incoming RTP/RTCP packets (arriving on the RTSP control connection)
@@ -67,6 +75,7 @@
sp<ANetworkSession> mNetSession;
sp<IGraphicBufferProducer> mSurfaceTex;
+ sp<AMessage> mNotify;
KeyedVector<uint32_t, sp<Source> > mSources;
int32_t mRTPPort;
@@ -78,7 +87,11 @@
LinearRegression mRegression;
int64_t mMaxDelayMs;
+#if USE_TUNNEL_RENDERER
sp<TunnelRenderer> mRenderer;
+#else
+ sp<DirectRenderer> mRenderer;
+#endif
status_t parseRTP(const sp<ABuffer> &buffer);
status_t parseRTCP(const sp<ABuffer> &buffer);
diff --git a/media/libstagefright/wifi-display/sink/TunnelRenderer.cpp b/media/libstagefright/wifi-display/sink/TunnelRenderer.cpp
index f3f4536..04dbd7b 100644
--- a/media/libstagefright/wifi-display/sink/TunnelRenderer.cpp
+++ b/media/libstagefright/wifi-display/sink/TunnelRenderer.cpp
@@ -264,16 +264,17 @@
if (mFirstFailedAttemptUs < 0ll) {
mFirstFailedAttemptUs = ALooper::GetNowUs();
- ALOGI("failed to get the correct packet the first time.");
+ ALOGV("failed to get the correct packet the first time.");
return NULL;
}
if (mFirstFailedAttemptUs + 50000ll > ALooper::GetNowUs()) {
// We're willing to wait a little while to get the right packet.
-#if 0
+#if 1
if (!mRequestedRetransmission) {
- ALOGI("requesting retransmission of seqNo %d",
+ ALOGI("requesting retransmission of extSeqNo %d (seqNo %d)",
+ mLastDequeuedExtSeqNo + 1,
(mLastDequeuedExtSeqNo + 1) & 0xffff);
sp<AMessage> notify = mNotifyLost->dup();
@@ -284,7 +285,7 @@
} else
#endif
{
- ALOGI("still waiting for the correct packet to arrive.");
+ ALOGV("still waiting for the correct packet to arrive.");
}
return NULL;
diff --git a/media/libstagefright/wifi-display/source/PlaybackSession.cpp b/media/libstagefright/wifi-display/source/PlaybackSession.cpp
index d6b87a7..91dc1fa 100644
--- a/media/libstagefright/wifi-display/source/PlaybackSession.cpp
+++ b/media/libstagefright/wifi-display/source/PlaybackSession.cpp
@@ -346,8 +346,17 @@
status_t WifiDisplaySource::PlaybackSession::init(
const char *clientIP, int32_t clientRtp, int32_t clientRtcp,
Sender::TransportMode transportMode,
- bool usePCMAudio) {
- status_t err = setupPacketizer(usePCMAudio);
+ bool enableAudio,
+ bool usePCMAudio,
+ bool enableVideo,
+ VideoFormats::ResolutionType videoResolutionType,
+ size_t videoResolutionIndex) {
+ status_t err = setupPacketizer(
+ enableAudio,
+ usePCMAudio,
+ enableVideo,
+ videoResolutionType,
+ videoResolutionIndex);
if (err != OK) {
return err;
@@ -639,13 +648,27 @@
}
}
-status_t WifiDisplaySource::PlaybackSession::setupPacketizer(bool usePCMAudio) {
+status_t WifiDisplaySource::PlaybackSession::setupPacketizer(
+ bool enableAudio,
+ bool usePCMAudio,
+ bool enableVideo,
+ VideoFormats::ResolutionType videoResolutionType,
+ size_t videoResolutionIndex) {
+ CHECK(enableAudio || enableVideo);
+
mPacketizer = new TSPacketizer;
- status_t err = addVideoSource();
+ if (enableVideo) {
+ status_t err = addVideoSource(
+ videoResolutionType, videoResolutionIndex);
- if (err != OK) {
- return err;
+ if (err != OK) {
+ return err;
+ }
+ }
+
+ if (!enableAudio) {
+ return OK;
}
return addAudioSource(usePCMAudio);
@@ -735,27 +758,30 @@
return OK;
}
-status_t WifiDisplaySource::PlaybackSession::addVideoSource() {
- sp<SurfaceMediaSource> source = new SurfaceMediaSource(width(), height());
+status_t WifiDisplaySource::PlaybackSession::addVideoSource(
+ VideoFormats::ResolutionType videoResolutionType,
+ size_t videoResolutionIndex) {
+ size_t width, height, framesPerSecond;
+ bool interlaced;
+ CHECK(VideoFormats::GetConfiguration(
+ videoResolutionType,
+ videoResolutionIndex,
+ &width,
+ &height,
+ &framesPerSecond,
+ &interlaced));
+
+ sp<SurfaceMediaSource> source = new SurfaceMediaSource(width, height);
source->setUseAbsoluteTimestamps();
-#if 1
sp<RepeaterSource> videoSource =
- new RepeaterSource(source, 30.0 /* rateHz */);
-#endif
+ new RepeaterSource(source, framesPerSecond);
-#if 1
size_t numInputBuffers;
status_t err = addSource(
true /* isVideo */, videoSource, true /* isRepeaterSource */,
false /* usePCMAudio */, &numInputBuffers);
-#else
- size_t numInputBuffers;
- status_t err = addSource(
- true /* isVideo */, source, false /* isRepeaterSource */,
- false /* usePCMAudio */, &numInputBuffers);
-#endif
if (err != OK) {
return err;
@@ -790,22 +816,6 @@
return mBufferQueue;
}
-int32_t WifiDisplaySource::PlaybackSession::width() const {
-#if USE_1080P
- return 1920;
-#else
- return 1280;
-#endif
-}
-
-int32_t WifiDisplaySource::PlaybackSession::height() const {
-#if USE_1080P
- return 1080;
-#else
- return 720;
-#endif
-}
-
void WifiDisplaySource::PlaybackSession::requestIDRFrame() {
for (size_t i = 0; i < mTracks.size(); ++i) {
const sp<Track> &track = mTracks.valueAt(i);
diff --git a/media/libstagefright/wifi-display/source/PlaybackSession.h b/media/libstagefright/wifi-display/source/PlaybackSession.h
index 281548d..7365c78 100644
--- a/media/libstagefright/wifi-display/source/PlaybackSession.h
+++ b/media/libstagefright/wifi-display/source/PlaybackSession.h
@@ -19,6 +19,7 @@
#define PLAYBACK_SESSION_H_
#include "Sender.h"
+#include "VideoFormats.h"
#include "WifiDisplaySource.h"
namespace android {
@@ -43,7 +44,11 @@
status_t init(
const char *clientIP, int32_t clientRtp, int32_t clientRtcp,
Sender::TransportMode transportMode,
- bool usePCMAudio);
+ bool enableAudio,
+ bool usePCMAudio,
+ bool enableVideo,
+ VideoFormats::ResolutionType videoResolutionType,
+ size_t videoResolutionIndex);
void destroyAsync();
@@ -57,8 +62,6 @@
status_t pause();
sp<IGraphicBufferProducer> getSurfaceTexture();
- int32_t width() const;
- int32_t height() const;
void requestIDRFrame();
@@ -109,7 +112,12 @@
bool mAllTracksHavePacketizerIndex;
- status_t setupPacketizer(bool usePCMAudio);
+ status_t setupPacketizer(
+ bool enableAudio,
+ bool usePCMAudio,
+ bool enableVideo,
+ VideoFormats::ResolutionType videoResolutionType,
+ size_t videoResolutionIndex);
status_t addSource(
bool isVideo,
@@ -118,7 +126,10 @@
bool usePCMAudio,
size_t *numInputBuffers);
- status_t addVideoSource();
+ status_t addVideoSource(
+ VideoFormats::ResolutionType videoResolutionType,
+ size_t videoResolutionIndex);
+
status_t addAudioSource(bool usePCMAudio);
ssize_t appendTSData(
diff --git a/media/libstagefright/wifi-display/source/Sender.cpp b/media/libstagefright/wifi-display/source/Sender.cpp
index 9048691..8b7d93f 100644
--- a/media/libstagefright/wifi-display/source/Sender.cpp
+++ b/media/libstagefright/wifi-display/source/Sender.cpp
@@ -685,7 +685,15 @@
if (!foundSeqNo || blp != 0) {
ALOGI("Some sequence numbers were no longer available for "
- "retransmission");
+ "retransmission (seqNo = %d, foundSeqNo = %d, blp = 0x%04x)",
+ seqNo, foundSeqNo, blp);
+
+ if (!mHistory.empty()) {
+ int32_t earliest = (*mHistory.begin())->int32Data() & 0xffff;
+ int32_t latest = (*--mHistory.end())->int32Data() & 0xffff;
+
+ ALOGI("have seq numbers from %d - %d", earliest, latest);
+ }
}
}
diff --git a/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp b/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp
index 9ec1064..981d5f9 100644
--- a/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp
+++ b/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp
@@ -58,8 +58,19 @@
mIsHDCP2_0(false),
mHDCPPort(0),
mHDCPInitializationComplete(false),
- mSetupTriggerDeferred(false)
-{
+ mSetupTriggerDeferred(false) {
+ mSupportedSourceVideoFormats.enableAll();
+
+ mSupportedSourceVideoFormats.setNativeResolution(
+ VideoFormats::RESOLUTION_CEA, 5); // 1280x720 p30
+
+ // Disable resolutions above 1080p since the encoder won't be able to
+ // handle them.
+ mSupportedSourceVideoFormats.setResolutionEnabled(
+ VideoFormats::RESOLUTION_VESA, 28, false); // 1920x1200 p30
+
+ mSupportedSourceVideoFormats.setResolutionEnabled(
+ VideoFormats::RESOLUTION_VESA, 29, false); // 1920x1200 p60
}
WifiDisplaySource::~WifiDisplaySource() {
@@ -375,13 +386,33 @@
IRemoteDisplayClient::kDisplayErrorUnknown);
} else if (what == PlaybackSession::kWhatSessionEstablished) {
if (mClient != NULL) {
- mClient->onDisplayConnected(
- mClientInfo.mPlaybackSession->getSurfaceTexture(),
- mClientInfo.mPlaybackSession->width(),
- mClientInfo.mPlaybackSession->height(),
- mUsingHDCP
- ? IRemoteDisplayClient::kDisplayFlagSecure
- : 0);
+ if (!mSinkSupportsVideo) {
+ mClient->onDisplayConnected(
+ NULL, // SurfaceTexture
+ 0, // width,
+ 0, // height,
+ mUsingHDCP
+ ? IRemoteDisplayClient::kDisplayFlagSecure
+ : 0);
+ } else {
+ size_t width, height;
+
+ CHECK(VideoFormats::GetConfiguration(
+ mChosenVideoResolutionType,
+ mChosenVideoResolutionIndex,
+ &width,
+ &height,
+ NULL /* framesPerSecond */,
+ NULL /* interlaced */));
+
+ mClient->onDisplayConnected(
+ mClientInfo.mPlaybackSession->getSurfaceTexture(),
+ width,
+ height,
+ mUsingHDCP
+ ? IRemoteDisplayClient::kDisplayFlagSecure
+ : 0);
+ }
}
if (mState == ABOUT_TO_PLAY) {
@@ -564,22 +595,6 @@
}
status_t WifiDisplaySource::sendM4(int32_t sessionID) {
- // wfd_video_formats:
- // 1 byte "native"
- // 1 byte "preferred-display-mode-supported" 0 or 1
- // one or more avc codec structures
- // 1 byte profile
- // 1 byte level
- // 4 byte CEA mask
- // 4 byte VESA mask
- // 4 byte HH mask
- // 1 byte latency
- // 2 byte min-slice-slice
- // 2 byte slice-enc-params
- // 1 byte framerate-control-support
- // max-hres (none or 2 byte)
- // max-vres (none or 2 byte)
-
CHECK_EQ(sessionID, mClientSessionID);
AString transportString = "UDP";
@@ -591,28 +606,35 @@
transportString = "TCP";
}
- // For 720p60:
- // use "30 00 02 02 00000040 00000000 00000000 00 0000 0000 00 none none\r\n"
- // For 720p30:
- // use "28 00 02 02 00000020 00000000 00000000 00 0000 0000 00 none none\r\n"
- // For 720p24:
- // use "78 00 02 02 00008000 00000000 00000000 00 0000 0000 00 none none\r\n"
- // For 1080p30:
- // use "38 00 02 02 00000080 00000000 00000000 00 0000 0000 00 none none\r\n"
- AString body = StringPrintf(
- "wfd_video_formats: "
-#if USE_1080P
- "38 00 02 02 00000080 00000000 00000000 00 0000 0000 00 none none\r\n"
-#else
- "28 00 02 02 00000020 00000000 00000000 00 0000 0000 00 none none\r\n"
-#endif
- "wfd_audio_codecs: %s\r\n"
- "wfd_presentation_URL: rtsp://%s/wfd1.0/streamid=0 none\r\n"
- "wfd_client_rtp_ports: RTP/AVP/%s;unicast %d 0 mode=play\r\n",
- (mUsingPCMAudio
- ? "LPCM 00000002 00" // 2 ch PCM 48kHz
- : "AAC 00000001 00"), // 2 ch AAC 48kHz
- mClientInfo.mLocalIP.c_str(), transportString.c_str(), mChosenRTPPort);
+ AString body;
+
+ if (mSinkSupportsVideo) {
+ body.append("wfd_video_formats: ");
+
+ VideoFormats chosenVideoFormat;
+ chosenVideoFormat.disableAll();
+ chosenVideoFormat.setNativeResolution(
+ mChosenVideoResolutionType, mChosenVideoResolutionIndex);
+
+ body.append(chosenVideoFormat.getFormatSpec());
+ body.append("\r\n");
+ }
+
+ if (mSinkSupportsAudio) {
+ body.append(
+ StringPrintf("wfd_audio_codecs: %s\r\n",
+ (mUsingPCMAudio
+ ? "LPCM 00000002 00" // 2 ch PCM 48kHz
+ : "AAC 00000001 00"))); // 2 ch AAC 48kHz
+ }
+
+ body.append(
+ StringPrintf(
+ "wfd_presentation_URL: rtsp://%s/wfd1.0/streamid=0 none\r\n"
+ "wfd_client_rtp_ports: RTP/AVP/%s;unicast %d 0 mode=play\r\n",
+ mClientInfo.mLocalIP.c_str(),
+ transportString.c_str(),
+ mChosenRTPPort));
AString request = "SET_PARAMETER rtsp://localhost/wfd1.0 RTSP/1.0\r\n";
AppendCommonResponse(&request, mNextCSeq);
@@ -789,39 +811,90 @@
mChosenRTPPort = port0;
+ if (!params->findParameter("wfd_video_formats", &value)) {
+ ALOGE("Sink doesn't report its choice of wfd_video_formats.");
+ return ERROR_MALFORMED;
+ }
+
+ mSinkSupportsVideo = false;
+
+ if (!(value == "none")) {
+ mSinkSupportsVideo = true;
+ if (!mSupportedSinkVideoFormats.parseFormatSpec(value.c_str())) {
+ ALOGE("Failed to parse sink provided wfd_video_formats (%s)",
+ value.c_str());
+
+ return ERROR_MALFORMED;
+ }
+
+ if (!VideoFormats::PickBestFormat(
+ mSupportedSinkVideoFormats,
+ mSupportedSourceVideoFormats,
+ &mChosenVideoResolutionType,
+ &mChosenVideoResolutionIndex)) {
+ ALOGE("Sink and source share no commonly supported video "
+ "formats.");
+
+ return ERROR_UNSUPPORTED;
+ }
+
+ size_t width, height, framesPerSecond;
+ bool interlaced;
+ CHECK(VideoFormats::GetConfiguration(
+ mChosenVideoResolutionType,
+ mChosenVideoResolutionIndex,
+ &width,
+ &height,
+ &framesPerSecond,
+ &interlaced));
+
+ ALOGI("Picked video resolution %u x %u %c%u",
+ width, height, interlaced ? 'i' : 'p', framesPerSecond);
+ } else {
+ ALOGI("Sink doesn't support video at all.");
+ }
+
if (!params->findParameter("wfd_audio_codecs", &value)) {
ALOGE("Sink doesn't report its choice of wfd_audio_codecs.");
return ERROR_MALFORMED;
}
- if (value == "none") {
- ALOGE("Sink doesn't support audio at all.");
- return ERROR_UNSUPPORTED;
+ mSinkSupportsAudio = false;
+
+ if (!(value == "none")) {
+ mSinkSupportsAudio = true;
+
+ uint32_t modes;
+ GetAudioModes(value.c_str(), "AAC", &modes);
+
+ bool supportsAAC = (modes & 1) != 0; // AAC 2ch 48kHz
+
+ GetAudioModes(value.c_str(), "LPCM", &modes);
+
+ bool supportsPCM = (modes & 2) != 0; // LPCM 2ch 48kHz
+
+ char val[PROPERTY_VALUE_MAX];
+ if (supportsPCM
+ && property_get("media.wfd.use-pcm-audio", val, NULL)
+ && (!strcasecmp("true", val) || !strcmp("1", val))) {
+ ALOGI("Using PCM audio.");
+ mUsingPCMAudio = true;
+ } else if (supportsAAC) {
+ ALOGI("Using AAC audio.");
+ mUsingPCMAudio = false;
+ } else if (supportsPCM) {
+ ALOGI("Using PCM audio.");
+ mUsingPCMAudio = true;
+ } else {
+ ALOGI("Sink doesn't support an audio format we do.");
+ return ERROR_UNSUPPORTED;
+ }
+ } else {
+ ALOGI("Sink doesn't support audio at all.");
}
- uint32_t modes;
- GetAudioModes(value.c_str(), "AAC", &modes);
-
- bool supportsAAC = (modes & 1) != 0; // AAC 2ch 48kHz
-
- GetAudioModes(value.c_str(), "LPCM", &modes);
-
- bool supportsPCM = (modes & 2) != 0; // LPCM 2ch 48kHz
-
- char val[PROPERTY_VALUE_MAX];
- if (supportsPCM
- && property_get("media.wfd.use-pcm-audio", val, NULL)
- && (!strcasecmp("true", val) || !strcmp("1", val))) {
- ALOGI("Using PCM audio.");
- mUsingPCMAudio = true;
- } else if (supportsAAC) {
- ALOGI("Using AAC audio.");
- mUsingPCMAudio = false;
- } else if (supportsPCM) {
- ALOGI("Using PCM audio.");
- mUsingPCMAudio = true;
- } else {
- ALOGI("Sink doesn't support an audio format we do.");
+ if (!mSinkSupportsVideo && !mSinkSupportsAudio) {
+ ALOGE("Sink supports neither video nor audio...");
return ERROR_UNSUPPORTED;
}
@@ -1160,7 +1233,11 @@
clientRtp,
clientRtcp,
transportMode,
- mUsingPCMAudio);
+ mSinkSupportsAudio,
+ mUsingPCMAudio,
+ mSinkSupportsVideo,
+ mChosenVideoResolutionType,
+ mChosenVideoResolutionIndex);
if (err != OK) {
looper()->unregisterHandler(playbackSession->id());
@@ -1560,7 +1637,7 @@
sp<IMediaPlayerService> service = interface_cast<IMediaPlayerService>(binder);
CHECK(service != NULL);
- mHDCP = service->makeHDCP();
+ mHDCP = service->makeHDCP(true /* createEncryptionModule */);
if (mHDCP == NULL) {
return ERROR_UNSUPPORTED;
diff --git a/media/libstagefright/wifi-display/source/WifiDisplaySource.h b/media/libstagefright/wifi-display/source/WifiDisplaySource.h
index 974e070..fec2c6d 100644
--- a/media/libstagefright/wifi-display/source/WifiDisplaySource.h
+++ b/media/libstagefright/wifi-display/source/WifiDisplaySource.h
@@ -19,6 +19,7 @@
#define WIFI_DISPLAY_SOURCE_H_
#include "ANetworkSession.h"
+#include "VideoFormats.h"
#include <media/stagefright/foundation/AHandler.h>
@@ -26,8 +27,6 @@
namespace android {
-#define USE_1080P 0
-
struct IHDCP;
struct IRemoteDisplayClient;
struct ParsedMessage;
@@ -112,6 +111,7 @@
kPlaybackSessionTimeoutSecs * 1000000ll;
State mState;
+ VideoFormats mSupportedSourceVideoFormats;
sp<ANetworkSession> mNetSession;
sp<IRemoteDisplayClient> mClient;
struct in_addr mInterfaceAddr;
@@ -121,6 +121,14 @@
int32_t mChosenRTPPort; // extracted from "wfd_client_rtp_ports"
+ bool mSinkSupportsVideo;
+ VideoFormats mSupportedSinkVideoFormats;
+
+ VideoFormats::ResolutionType mChosenVideoResolutionType;
+ size_t mChosenVideoResolutionIndex;
+
+ bool mSinkSupportsAudio;
+
bool mUsingPCMAudio;
int32_t mClientSessionID;
diff --git a/media/libstagefright/wifi-display/wfd.cpp b/media/libstagefright/wifi-display/wfd.cpp
index 2ec9b4f..21d661e 100644
--- a/media/libstagefright/wifi-display/wfd.cpp
+++ b/media/libstagefright/wifi-display/wfd.cpp
@@ -23,6 +23,7 @@
#include <binder/ProcessState.h>
#include <binder/IServiceManager.h>
+#include <gui/ISurfaceComposer.h>
#include <gui/SurfaceComposerClient.h>
#include <media/AudioSystem.h>
#include <media/IMediaPlayerService.h>
@@ -30,6 +31,8 @@
#include <media/IRemoteDisplayClient.h>
#include <media/stagefright/DataSource.h>
#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/foundation/AMessage.h>
+#include <ui/DisplayInfo.h>
namespace android {
@@ -281,12 +284,44 @@
exit(1);
}
+ sp<SurfaceComposerClient> composerClient = new SurfaceComposerClient;
+ CHECK_EQ(composerClient->initCheck(), (status_t)OK);
+
+ sp<IBinder> display(SurfaceComposerClient::getBuiltInDisplay(
+ ISurfaceComposer::eDisplayIdMain));
+ DisplayInfo info;
+ SurfaceComposerClient::getDisplayInfo(display, &info);
+ ssize_t displayWidth = info.w;
+ ssize_t displayHeight = info.h;
+
+ ALOGV("display is %d x %d\n", displayWidth, displayHeight);
+
+ sp<SurfaceControl> control =
+ composerClient->createSurface(
+ String8("A Surface"),
+ displayWidth,
+ displayHeight,
+ PIXEL_FORMAT_RGB_565,
+ 0);
+
+ CHECK(control != NULL);
+ CHECK(control->isValid());
+
+ SurfaceComposerClient::openGlobalTransaction();
+ CHECK_EQ(control->setLayer(INT_MAX), (status_t)OK);
+ CHECK_EQ(control->show(), (status_t)OK);
+ SurfaceComposerClient::closeGlobalTransaction();
+
+ sp<Surface> surface = control->getSurface();
+ CHECK(surface != NULL);
+
sp<ANetworkSession> session = new ANetworkSession;
session->start();
sp<ALooper> looper = new ALooper;
- sp<WifiDisplaySink> sink = new WifiDisplaySink(session);
+ sp<WifiDisplaySink> sink = new WifiDisplaySink(
+ session, surface->getSurfaceTexture());
looper->registerHandler(sink);
if (connectToPort >= 0) {
@@ -297,5 +332,7 @@
looper->start(true /* runOnCallingThread */);
+ composerClient->dispose();
+
return 0;
}
diff --git a/media/mediaserver/Android.mk b/media/mediaserver/Android.mk
index 8c3cc5e..0a0f4db 100644
--- a/media/mediaserver/Android.mk
+++ b/media/mediaserver/Android.mk
@@ -7,12 +7,17 @@
LOCAL_SHARED_LIBRARIES := \
libaudioflinger \
libcameraservice \
+ libmedialogservice \
+ libcutils \
+ libnbaio \
+ libmedia \
libmediaplayerservice \
libutils \
libbinder
LOCAL_C_INCLUDES := \
frameworks/av/media/libmediaplayerservice \
+ frameworks/av/services/medialog \
frameworks/av/services/audioflinger \
frameworks/av/services/camera/libcameraservice
diff --git a/media/mediaserver/main_mediaserver.cpp b/media/mediaserver/main_mediaserver.cpp
index ddd5b84..0862952 100644
--- a/media/mediaserver/main_mediaserver.cpp
+++ b/media/mediaserver/main_mediaserver.cpp
@@ -18,14 +18,19 @@
#define LOG_TAG "mediaserver"
//#define LOG_NDEBUG 0
+#include <fcntl.h>
+#include <sys/prctl.h>
+#include <sys/wait.h>
#include <binder/IPCThreadState.h>
#include <binder/ProcessState.h>
#include <binder/IServiceManager.h>
+#include <cutils/properties.h>
#include <utils/Log.h>
// from LOCAL_C_INCLUDES
#include "AudioFlinger.h"
#include "CameraService.h"
+#include "MediaLogService.h"
#include "MediaPlayerService.h"
#include "AudioPolicyService.h"
@@ -34,13 +39,95 @@
int main(int argc, char** argv)
{
signal(SIGPIPE, SIG_IGN);
- sp<ProcessState> proc(ProcessState::self());
- sp<IServiceManager> sm = defaultServiceManager();
- ALOGI("ServiceManager: %p", sm.get());
- AudioFlinger::instantiate();
- MediaPlayerService::instantiate();
- CameraService::instantiate();
- AudioPolicyService::instantiate();
- ProcessState::self()->startThreadPool();
- IPCThreadState::self()->joinThreadPool();
+ char value[PROPERTY_VALUE_MAX];
+ bool doLog = (property_get("ro.test_harness", value, "0") > 0) && (atoi(value) == 1);
+ pid_t childPid;
+ // FIXME The advantage of making the process containing media.log service the parent process of
+ // the process that contains all the other real services, is that it allows us to collect more
+ // detailed information such as signal numbers, stop and continue, resource usage, etc.
+ // But it is also more complex. Consider replacing this by independent processes, and using
+ // binder on death notification instead.
+ if (doLog && (childPid = fork()) != 0) {
+ // media.log service
+ //prctl(PR_SET_NAME, (unsigned long) "media.log", 0, 0, 0);
+ // unfortunately ps ignores PR_SET_NAME for the main thread, so use this ugly hack
+ strcpy(argv[0], "media.log");
+ sp<ProcessState> proc(ProcessState::self());
+ MediaLogService::instantiate();
+ ProcessState::self()->startThreadPool();
+ for (;;) {
+ siginfo_t info;
+ int ret = waitid(P_PID, childPid, &info, WEXITED | WSTOPPED | WCONTINUED);
+ if (ret == EINTR) {
+ continue;
+ }
+ if (ret < 0) {
+ break;
+ }
+ char buffer[32];
+ const char *code;
+ switch (info.si_code) {
+ case CLD_EXITED:
+ code = "CLD_EXITED";
+ break;
+ case CLD_KILLED:
+ code = "CLD_KILLED";
+ break;
+ case CLD_DUMPED:
+ code = "CLD_DUMPED";
+ break;
+ case CLD_STOPPED:
+ code = "CLD_STOPPED";
+ break;
+ case CLD_TRAPPED:
+ code = "CLD_TRAPPED";
+ break;
+ case CLD_CONTINUED:
+ code = "CLD_CONTINUED";
+ break;
+ default:
+ snprintf(buffer, sizeof(buffer), "unknown (%d)", info.si_code);
+ code = buffer;
+ break;
+ }
+ struct rusage usage;
+ getrusage(RUSAGE_CHILDREN, &usage);
+ ALOG(LOG_ERROR, "media.log", "pid %d status %d code %s user %ld.%03lds sys %ld.%03lds",
+ info.si_pid, info.si_status, code,
+ usage.ru_utime.tv_sec, usage.ru_utime.tv_usec / 1000,
+ usage.ru_stime.tv_sec, usage.ru_stime.tv_usec / 1000);
+ sp<IServiceManager> sm = defaultServiceManager();
+ sp<IBinder> binder = sm->getService(String16("media.log"));
+ if (binder != 0) {
+ Vector<String16> args;
+ binder->dump(-1, args);
+ }
+ switch (info.si_code) {
+ case CLD_EXITED:
+ case CLD_KILLED:
+ case CLD_DUMPED: {
+ ALOG(LOG_INFO, "media.log", "exiting");
+ _exit(0);
+ // not reached
+ }
+ default:
+ break;
+ }
+ }
+ } else {
+ // all other services
+ if (doLog) {
+ prctl(PR_SET_PDEATHSIG, SIGKILL); // if parent media.log dies before me, kill me also
+ setpgid(0, 0); // but if I die first, don't kill my parent
+ }
+ sp<ProcessState> proc(ProcessState::self());
+ sp<IServiceManager> sm = defaultServiceManager();
+ ALOGI("ServiceManager: %p", sm.get());
+ AudioFlinger::instantiate();
+ MediaPlayerService::instantiate();
+ CameraService::instantiate();
+ AudioPolicyService::instantiate();
+ ProcessState::self()->startThreadPool();
+ IPCThreadState::self()->joinThreadPool();
+ }
}
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index 89e9b52..47c2772 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -59,6 +59,8 @@
#include <common_time/cc_helper.h>
//#include <common_time/local_clock.h>
+#include <media/IMediaLogService.h>
+
// ----------------------------------------------------------------------------
// Note: the following macro is used for extremely verbose logging message. In
@@ -127,6 +129,11 @@
mMode(AUDIO_MODE_INVALID),
mBtNrecIsOff(false)
{
+ char value[PROPERTY_VALUE_MAX];
+ bool doLog = (property_get("ro.test_harness", value, "0") > 0) && (atoi(value) == 1);
+ if (doLog) {
+ mLogMemoryDealer = new MemoryDealer(kLogMemorySize, "LogWriters");
+ }
}
void AudioFlinger::onFirstRef()
@@ -323,6 +330,17 @@
if (locked) {
mLock.unlock();
}
+
+ // append a copy of media.log here by forwarding fd to it, but don't attempt
+ // to lookup the service if it's not running, as it will block for a second
+ if (mLogMemoryDealer != 0) {
+ sp<IBinder> binder = defaultServiceManager()->getService(String16("media.log"));
+ if (binder != 0) {
+ fdprintf(fd, "\nmedia.log:\n");
+ Vector<String16> args;
+ binder->dump(fd, args);
+ }
+ }
}
return NO_ERROR;
}
@@ -340,6 +358,38 @@
return client;
}
+sp<NBLog::Writer> AudioFlinger::newWriter_l(size_t size, const char *name)
+{
+ if (mLogMemoryDealer == 0) {
+ return new NBLog::Writer();
+ }
+ sp<IMemory> shared = mLogMemoryDealer->allocate(NBLog::Timeline::sharedSize(size));
+ sp<NBLog::Writer> writer = new NBLog::Writer(size, shared);
+ sp<IBinder> binder = defaultServiceManager()->getService(String16("media.log"));
+ if (binder != 0) {
+ interface_cast<IMediaLogService>(binder)->registerWriter(shared, size, name);
+ }
+ return writer;
+}
+
+void AudioFlinger::unregisterWriter(const sp<NBLog::Writer>& writer)
+{
+ if (writer == 0) {
+ return;
+ }
+ sp<IMemory> iMemory(writer->getIMemory());
+ if (iMemory == 0) {
+ return;
+ }
+ sp<IBinder> binder = defaultServiceManager()->getService(String16("media.log"));
+ if (binder != 0) {
+ interface_cast<IMediaLogService>(binder)->unregisterWriter(iMemory);
+ // Now the media.log remote reference to IMemory is gone.
+ // When our last local reference to IMemory also drops to zero,
+ // the IMemory destructor will deallocate the region from mMemoryDealer.
+ }
+}
+
// IAudioFlinger interface
diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h
index a7f5b9e..c3f08f6 100644
--- a/services/audioflinger/AudioFlinger.h
+++ b/services/audioflinger/AudioFlinger.h
@@ -53,6 +53,8 @@
#include <powermanager/IPowerManager.h>
+#include <media/nbaio/NBLog.h>
+
namespace android {
class audio_track_cblk_t;
@@ -222,6 +224,13 @@
// end of IAudioFlinger interface
+ sp<NBLog::Writer> newWriter_l(size_t size, const char *name);
+ void unregisterWriter(const sp<NBLog::Writer>& writer);
+private:
+ static const size_t kLogMemorySize = 10 * 1024;
+ sp<MemoryDealer> mLogMemoryDealer; // == 0 when NBLog is disabled
+public:
+
class SyncEvent;
typedef void (*sync_event_callback_t)(const wp<SyncEvent>& event) ;
diff --git a/services/audioflinger/FastMixer.cpp b/services/audioflinger/FastMixer.cpp
index 9283f53..80e37ca 100644
--- a/services/audioflinger/FastMixer.cpp
+++ b/services/audioflinger/FastMixer.cpp
@@ -92,6 +92,7 @@
struct timespec measuredWarmupTs = {0, 0}; // how long did it take for warmup to complete
uint32_t warmupCycles = 0; // counter of number of loop cycles required to warmup
NBAIO_Sink* teeSink = NULL; // if non-NULL, then duplicate write() to this non-blocking sink
+ NBLog::Writer dummyLogWriter, *logWriter = &dummyLogWriter;
for (;;) {
@@ -119,9 +120,12 @@
FastMixerState::Command command = next->mCommand;
if (next != current) {
+ logWriter->log("next != current");
+
// As soon as possible of learning of a new dump area, start using it
dumpState = next->mDumpState != NULL ? next->mDumpState : &dummyDumpState;
teeSink = next->mTeeSink;
+ logWriter = next->mNBLogWriter != NULL ? next->mNBLogWriter : &dummyLogWriter;
// We want to always have a valid reference to the previous (non-idle) state.
// However, the state queue only guarantees access to current and previous states.
@@ -163,6 +167,7 @@
ALOG_ASSERT(coldFutexAddr != NULL);
int32_t old = android_atomic_dec(coldFutexAddr);
if (old <= 0) {
+ logWriter->log("wait");
__futex_syscall4(coldFutexAddr, FUTEX_WAIT_PRIVATE, old - 1, NULL);
}
// This may be overly conservative; there could be times that the normal mixer
@@ -181,6 +186,7 @@
}
continue;
case FastMixerState::EXIT:
+ logWriter->log("exit");
delete mixer;
delete[] mixBuffer;
return false;
@@ -258,11 +264,15 @@
unsigned currentTrackMask = current->mTrackMask;
dumpState->mTrackMask = currentTrackMask;
if (current->mFastTracksGen != fastTracksGen) {
+ logWriter->logf("gen %d", current->mFastTracksGen);
ALOG_ASSERT(mixBuffer != NULL);
int name;
// process removed tracks first to avoid running out of track names
unsigned removedTracks = previousTrackMask & ~currentTrackMask;
+ if (removedTracks) {
+ logWriter->logf("removed %#x", removedTracks);
+ }
while (removedTracks != 0) {
i = __builtin_ctz(removedTracks);
removedTracks &= ~(1 << i);
@@ -282,6 +292,9 @@
// now process added tracks
unsigned addedTracks = currentTrackMask & ~previousTrackMask;
+ if (addedTracks) {
+ logWriter->logf("added %#x", addedTracks);
+ }
while (addedTracks != 0) {
i = __builtin_ctz(addedTracks);
addedTracks &= ~(1 << i);
@@ -312,6 +325,9 @@
// finally process modified tracks; these use the same slot
// but may have a different buffer provider or volume provider
unsigned modifiedTracks = currentTrackMask & previousTrackMask;
+ if (modifiedTracks) {
+ logWriter->logf("modified %#x", modifiedTracks);
+ }
while (modifiedTracks != 0) {
i = __builtin_ctz(modifiedTracks);
modifiedTracks &= ~(1 << i);
@@ -455,6 +471,7 @@
struct timespec newTs;
int rc = clock_gettime(CLOCK_MONOTONIC, &newTs);
if (rc == 0) {
+ logWriter->logTimestamp(newTs);
if (oldTsValid) {
time_t sec = newTs.tv_sec - oldTs.tv_sec;
long nsec = newTs.tv_nsec - oldTs.tv_nsec;
diff --git a/services/audioflinger/FastMixerState.cpp b/services/audioflinger/FastMixerState.cpp
index 6305a83..c45c81b 100644
--- a/services/audioflinger/FastMixerState.cpp
+++ b/services/audioflinger/FastMixerState.cpp
@@ -31,7 +31,7 @@
FastMixerState::FastMixerState() :
mFastTracksGen(0), mTrackMask(0), mOutputSink(NULL), mOutputSinkGen(0),
mFrameCount(0), mCommand(INITIAL), mColdFutexAddr(NULL), mColdGen(0),
- mDumpState(NULL), mTeeSink(NULL)
+ mDumpState(NULL), mTeeSink(NULL), mNBLogWriter(NULL)
{
}
diff --git a/services/audioflinger/FastMixerState.h b/services/audioflinger/FastMixerState.h
index 6e53f21..f6e7903 100644
--- a/services/audioflinger/FastMixerState.h
+++ b/services/audioflinger/FastMixerState.h
@@ -20,6 +20,7 @@
#include <system/audio.h>
#include <media/ExtendedAudioBufferProvider.h>
#include <media/nbaio/NBAIO.h>
+#include <media/nbaio/NBLog.h>
namespace android {
@@ -77,6 +78,7 @@
// This might be a one-time configuration rather than per-state
FastMixerDumpState* mDumpState; // if non-NULL, then update dump state periodically
NBAIO_Sink* mTeeSink; // if non-NULL, then duplicate write()s to this non-blocking sink
+ NBLog::Writer* mNBLogWriter; // non-blocking logger
}; // struct FastMixerState
} // namespace android
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index af0dccc..ba848d7 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -936,6 +936,7 @@
mFastTrackAvailMask(((1 << FastMixerState::kMaxFastTracks) - 1) & ~1)
{
snprintf(mName, kNameLength, "AudioOut_%X", id);
+ mNBLogWriter = audioFlinger->newWriter_l(kLogSize, mName);
// Assumes constructor is called by AudioFlinger with it's mLock held, but
// it would be safer to explicitly pass initial masterVolume/masterMute as
@@ -971,6 +972,7 @@
AudioFlinger::PlaybackThread::~PlaybackThread()
{
+ mAudioFlinger->unregisterWriter(mNBLogWriter);
delete [] mMixBuffer;
}
@@ -1247,6 +1249,7 @@
if (status) {
*status = lStatus;
}
+ mNBLogWriter->logf("createTrack_l");
return track;
}
@@ -1314,6 +1317,7 @@
// addTrack_l() must be called with ThreadBase::mLock held
status_t AudioFlinger::PlaybackThread::addTrack_l(const sp<Track>& track)
{
+ mNBLogWriter->logf("addTrack_l mName=%d", track->mName);
status_t status = ALREADY_EXISTS;
// set retry count for buffer fill
@@ -1347,6 +1351,7 @@
// destroyTrack_l() must be called with ThreadBase::mLock held
void AudioFlinger::PlaybackThread::destroyTrack_l(const sp<Track>& track)
{
+ mNBLogWriter->logf("destroyTrack_l mName=%d", track->mName);
track->mState = TrackBase::TERMINATED;
// active tracks are removed by threadLoop()
if (mActiveTracks.indexOf(track) < 0) {
@@ -1356,6 +1361,7 @@
void AudioFlinger::PlaybackThread::removeTrack_l(const sp<Track>& track)
{
+ mNBLogWriter->logf("removeTrack_l mName=%d", track->mName);
track->triggerEvents(AudioSystem::SYNC_EVENT_PRESENTATION_COMPLETE);
mTracks.remove(track);
deleteTrackName_l(track->name());
@@ -1892,6 +1898,11 @@
acquireWakeLock();
+ // mNBLogWriter->log can only be called while thread mutex mLock is held.
+ // So if you need to log when mutex is unlocked, set logString to a non-NULL string,
+ // and then that string will be logged at the next convenient opportunity.
+ const char *logString = NULL;
+
while (!exitPending())
{
cpuStats.sample(myName);
@@ -1904,6 +1915,12 @@
Mutex::Autolock _l(mLock);
+ if (logString != NULL) {
+ mNBLogWriter->logTimestamp();
+ mNBLogWriter->log(logString);
+ logString = NULL;
+ }
+
if (checkForNewParameters_l()) {
cacheParameters_l();
}
@@ -1917,6 +1934,7 @@
threadLoop_standby();
+ mNBLogWriter->log("standby");
mStandby = true;
}
@@ -2012,6 +2030,9 @@
// since we can't guarantee the destructors won't acquire that
// same lock. This will also mutate and push a new fast mixer state.
threadLoop_removeTracks(tracksToRemove);
+ if (tracksToRemove.size() > 0) {
+ logString = "remove";
+ }
tracksToRemove.clear();
// FIXME I don't understand the need for this here;
@@ -2143,6 +2164,8 @@
state->mColdGen++;
state->mDumpState = &mFastMixerDumpState;
state->mTeeSink = mTeeSink.get();
+ mFastMixerNBLogWriter = audioFlinger->newWriter_l(kFastMixerLogSize, "FastMixer");
+ state->mNBLogWriter = mFastMixerNBLogWriter.get();
sq->end();
sq->push(FastMixerStateQueue::BLOCK_UNTIL_PUSHED);
@@ -2219,6 +2242,7 @@
}
#endif
}
+ mAudioFlinger->unregisterWriter(mFastMixerNBLogWriter);
delete mAudioMixer;
}
@@ -2846,6 +2870,7 @@
if (CC_UNLIKELY(count)) {
for (size_t i=0 ; i<count ; i++) {
const sp<Track>& track = tracksToRemove->itemAt(i);
+ mNBLogWriter->logf("prepareTracks_l remove name=%u", track->name());
mActiveTracks.remove(track);
if (track->mainBuffer() != mMixBuffer) {
chain = getEffectChain_l(track->sessionId());
@@ -3222,6 +3247,9 @@
// remove all the tracks that need to be...
if (CC_UNLIKELY(trackToRemove != 0)) {
tracksToRemove->add(trackToRemove);
+#if 0
+ mNBLogWriter->logf("prepareTracks_l remove name=%u", trackToRemove->name());
+#endif
mActiveTracks.remove(trackToRemove);
if (!mEffectChains.isEmpty()) {
ALOGV("stopping track on chain %p for session Id: %d", mEffectChains[0].get(),
diff --git a/services/audioflinger/Threads.h b/services/audioflinger/Threads.h
index a1abcde..fa1e336 100644
--- a/services/audioflinger/Threads.h
+++ b/services/audioflinger/Threads.h
@@ -315,6 +315,8 @@
// keyed by session ID, the second by type UUID timeLow field
KeyedVector< int, KeyedVector< int, sp<SuspendedSessionDesc> > >
mSuspendedSessions;
+ static const size_t kLogSize = 512;
+ sp<NBLog::Writer> mNBLogWriter;
};
// --- PlaybackThread ---
@@ -544,6 +546,8 @@
sp<NBAIO_Sink> mTeeSink;
sp<NBAIO_Source> mTeeSource;
uint32_t mScreenState; // cached copy of gScreenState
+ static const size_t kFastMixerLogSize = 8 * 1024;
+ sp<NBLog::Writer> mFastMixerNBLogWriter;
public:
virtual bool hasFastMixer() const = 0;
virtual FastTrackUnderruns getFastTrackUnderruns(size_t fastIndex) const
diff --git a/services/audioflinger/Tracks.cpp b/services/audioflinger/Tracks.cpp
index c5f0ed7..315cbbc 100644
--- a/services/audioflinger/Tracks.cpp
+++ b/services/audioflinger/Tracks.cpp
@@ -569,6 +569,7 @@
sp<ThreadBase> thread = mThread.promote();
if (thread != 0) {
Mutex::Autolock _l(thread->mLock);
+ thread->mNBLogWriter->logf("start mName=%d", mName);
track_state state = mState;
// here the track could be either new, or restarted
// in both cases "unstop" the track
@@ -611,6 +612,7 @@
sp<ThreadBase> thread = mThread.promote();
if (thread != 0) {
Mutex::Autolock _l(thread->mLock);
+ thread->mNBLogWriter->logf("stop mName=%d", mName);
track_state state = mState;
if (state == RESUMING || state == ACTIVE || state == PAUSING || state == PAUSED) {
// If the track is not active (PAUSED and buffers full), flush buffers
@@ -647,6 +649,7 @@
sp<ThreadBase> thread = mThread.promote();
if (thread != 0) {
Mutex::Autolock _l(thread->mLock);
+ thread->mNBLogWriter->logf("pause mName=%d", mName);
if (mState == ACTIVE || mState == RESUMING) {
mState = PAUSING;
ALOGV("ACTIVE/RESUMING => PAUSING (%d) on thread %p", mName, thread.get());
@@ -670,6 +673,7 @@
sp<ThreadBase> thread = mThread.promote();
if (thread != 0) {
Mutex::Autolock _l(thread->mLock);
+ thread->mNBLogWriter->logf("flush mName=%d", mName);
if (mState != STOPPING_1 && mState != STOPPING_2 && mState != STOPPED && mState != PAUSED &&
mState != PAUSING && mState != IDLE && mState != FLUSHED) {
return;
diff --git a/services/medialog/Android.mk b/services/medialog/Android.mk
new file mode 100644
index 0000000..559b1ed
--- /dev/null
+++ b/services/medialog/Android.mk
@@ -0,0 +1,11 @@
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := MediaLogService.cpp
+
+LOCAL_SHARED_LIBRARIES := libmedia libbinder libutils libnbaio
+
+LOCAL_MODULE:= libmedialogservice
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/services/medialog/MediaLogService.cpp b/services/medialog/MediaLogService.cpp
new file mode 100644
index 0000000..2332b3e
--- /dev/null
+++ b/services/medialog/MediaLogService.cpp
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2013 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 "MediaLog"
+//#define LOG_NDEBUG 0
+
+#include <sys/mman.h>
+#include <utils/Log.h>
+#include <media/nbaio/NBLog.h>
+#include <private/android_filesystem_config.h>
+#include "MediaLogService.h"
+
+namespace android {
+
+void MediaLogService::registerWriter(const sp<IMemory>& shared, size_t size, const char *name)
+{
+ if (IPCThreadState::self()->getCallingUid() != AID_MEDIA || shared == 0 ||
+ size < kMinSize || size > kMaxSize || name == NULL ||
+ shared->size() < NBLog::Timeline::sharedSize(size)) {
+ return;
+ }
+ sp<NBLog::Reader> reader(new NBLog::Reader(size, shared));
+ NamedReader namedReader(reader, name);
+ Mutex::Autolock _l(mLock);
+ mNamedReaders.add(namedReader);
+}
+
+void MediaLogService::unregisterWriter(const sp<IMemory>& shared)
+{
+ if (IPCThreadState::self()->getCallingUid() != AID_MEDIA || shared == 0) {
+ return;
+ }
+ Mutex::Autolock _l(mLock);
+ for (size_t i = 0; i < mNamedReaders.size(); ) {
+ if (mNamedReaders[i].reader()->isIMemory(shared)) {
+ mNamedReaders.removeAt(i);
+ } else {
+ i++;
+ }
+ }
+}
+
+status_t MediaLogService::dump(int fd, const Vector<String16>& args)
+{
+ Vector<NamedReader> namedReaders;
+ {
+ Mutex::Autolock _l(mLock);
+ namedReaders = mNamedReaders;
+ }
+ for (size_t i = 0; i < namedReaders.size(); i++) {
+ const NamedReader& namedReader = namedReaders[i];
+ if (fd >= 0) {
+ fdprintf(fd, "\n%s:\n", namedReader.name());
+ } else {
+ ALOGI("%s:", namedReader.name());
+ }
+ namedReader.reader()->dump(fd, 0 /*indent*/);
+ }
+ return NO_ERROR;
+}
+
+status_t MediaLogService::onTransact(uint32_t code, const Parcel& data, Parcel* reply,
+ uint32_t flags)
+{
+ return BnMediaLogService::onTransact(code, data, reply, flags);
+}
+
+} // namespace android
diff --git a/services/medialog/MediaLogService.h b/services/medialog/MediaLogService.h
new file mode 100644
index 0000000..2d89a41
--- /dev/null
+++ b/services/medialog/MediaLogService.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2013 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_MEDIA_LOG_SERVICE_H
+#define ANDROID_MEDIA_LOG_SERVICE_H
+
+#include <binder/BinderService.h>
+#include <media/IMediaLogService.h>
+#include <media/nbaio/NBLog.h>
+
+namespace android {
+
+class MediaLogService : public BinderService<MediaLogService>, public BnMediaLogService
+{
+ friend class BinderService<MediaLogService>; // for MediaLogService()
+public:
+ MediaLogService() : BnMediaLogService() { }
+ virtual ~MediaLogService() { }
+ virtual void onFirstRef() { }
+
+ static const char* getServiceName() { return "media.log"; }
+
+ static const size_t kMinSize = 0x100;
+ static const size_t kMaxSize = 0x10000;
+ virtual void registerWriter(const sp<IMemory>& shared, size_t size, const char *name);
+ virtual void unregisterWriter(const sp<IMemory>& shared);
+
+ virtual status_t dump(int fd, const Vector<String16>& args);
+ virtual status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply,
+ uint32_t flags);
+
+private:
+ Mutex mLock;
+ class NamedReader {
+ public:
+ NamedReader() : mReader(0) { mName[0] = '\0'; } // for Vector
+ NamedReader(const sp<NBLog::Reader>& reader, const char *name) : mReader(reader)
+ { strlcpy(mName, name, sizeof(mName)); }
+ ~NamedReader() { }
+ const sp<NBLog::Reader>& reader() const { return mReader; }
+ const char* name() const { return mName; }
+ private:
+ sp<NBLog::Reader> mReader;
+ static const size_t kMaxName = 32;
+ char mName[kMaxName];
+ };
+ Vector<NamedReader> mNamedReaders;
+};
+
+} // namespace android
+
+#endif // ANDROID_MEDIA_LOG_SERVICE_H