diff options
50 files changed, 2073 insertions, 201 deletions
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 @@ DrmConstraints* BpDrmManagerService::getConstraints( 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 @@ DrmMetadata* BpDrmManagerService::getMetadata(int uniqueId, const String8* path) 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 @@ status_t BnDrmManagerService::onTransact( 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 @@ String8 ReadWriteUtils::readBytes(const String8& filePath) { 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 @@ const String8 DrmManager::EMPTY_STRING(""); DrmManager::DrmManager() : mDecryptSessionId(0), mConvertId(0) { - + srand(time(NULL)); + memset(mUniqueIdArray, 0, sizeof(bool) * kMaxNumUniqueIds); } DrmManager::~DrmManager() { @@ -52,48 +53,37 @@ DrmManager::~DrmManager() { 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 @@ private: 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_C_INCLUDES := \ 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 @@ static int FwdLockConv_DeriveKeys(FwdLockConv_Session_t *pSession) { 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 @@ static int FwdLockConv_DeriveKeys(FwdLockConv_Session_t *pSession) { 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_C_INCLUDES := \ 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 @@ static int FwdLockFile_DeriveKeys(FwdLockFile_Session_t * pSession) { 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 @@ static int FwdLockFile_DeriveKeys(FwdLockFile_Session_t * pSession) { 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 @@ DrmConstraints* DrmPassthruPlugIn::onGetConstraints( 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 @@ struct IHDCP : public IInterface { // 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 @@ public: 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 @@ LOCAL_SRC_FILES:= \ 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 @@ enum { HDCP_INIT_ASYNC, HDCP_SHUTDOWN_ASYNC, HDCP_ENCRYPT, + HDCP_DECRYPT, }; struct BpHDCPObserver : public BpInterface<IHDCPObserver> { @@ -106,6 +107,29 @@ struct BpHDCP : public BpInterface<IHDCP> { 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 @@ status_t BnHDCP::onTransact( 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 @@ public: 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 @@ status_t BnMediaPlayerService::onTransact( } 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 @@ HDCP::HDCP() 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 @@ status_t HDCP::encrypt( uint64_t *outInputCTR, void *outData) { Mutex::Autolock autoLock(mLock); + CHECK(mIsEncryptionModule); + if (mHDCPModule == NULL) { *outInputCTR = 0; @@ -110,6 +116,20 @@ status_t HDCP::encrypt( 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 @@ struct HDCP : public BnHDCP { 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 @@ sp<ICrypto> MediaPlayerService::makeCrypto() { 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 @@ public: 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 @@ LOCAL_SRC_FILES := \ 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_SRC_FILES := \ 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 @@ status_t ACodec::setSupportedOutputFormat() { || 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 @@ status_t ATSParser::Stream::parse( 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 @@ LOCAL_SRC_FILES:= \ ANetworkSession.cpp \ Parameters.cpp \ ParsedMessage.cpp \ + sink/DirectRenderer.cpp \ sink/LinearRegression.cpp \ sink/RTPSink.cpp \ sink/TunnelRenderer.cpp \ @@ -18,6 +19,7 @@ LOCAL_SRC_FILES:= \ 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 @@ void RTPSink::Source::addReportBlock( 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 @@ status_t RTPSink::parseRTP(const sp<ABuffer> &buffer) { 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 @@ status_t RTPSink::parseRTP(const sp<ABuffer> &buffer) { ALOGI("packet was %.2f ms late", latenessMs); } } +#endif sp<AMessage> meta = buffer->meta(); meta->setInt32("ssrc", srcId); @@ -515,12 +526,12 @@ status_t RTPSink::parseRTP(const sp<ABuffer> &buffer) { 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 @@ void RTPSink::onPacketLost(const sp<AMessage> &msg) { 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 @@ private: sp<ANetworkSession> mNetSession; sp<IGraphicBufferProducer> mSurfaceTex; + sp<AMessage> mNotify; KeyedVector<uint32_t, sp<Source> > mSources; int32_t mRTPPort; @@ -78,7 +87,11 @@ private: 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 @@ sp<ABuffer> TunnelRenderer::dequeueBuffer() { 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 @@ sp<ABuffer> TunnelRenderer::dequeueBuffer() { } 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 @@ WifiDisplaySource::PlaybackSession::PlaybackSession( 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 @@ void WifiDisplaySource::PlaybackSession::onMessageReceived( } } -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 @@ status_t WifiDisplaySource::PlaybackSession::addSource( 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 @@ sp<IGraphicBufferProducer> WifiDisplaySource::PlaybackSession::getSurfaceTexture 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 @@ struct WifiDisplaySource::PlaybackSession : public AHandler { 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 @@ struct WifiDisplaySource::PlaybackSession : public AHandler { status_t pause(); sp<IGraphicBufferProducer> getSurfaceTexture(); - int32_t width() const; - int32_t height() const; void requestIDRFrame(); @@ -109,7 +112,12 @@ private: 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 @@ private: 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 @@ status_t Sender::parseTSFB( 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 @@ WifiDisplaySource::WifiDisplaySource( 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 @@ void WifiDisplaySource::onMessageReceived(const sp<AMessage> &msg) { 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::sendM3(int32_t sessionID) { } 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 @@ status_t WifiDisplaySource::sendM4(int32_t sessionID) { 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 @@ status_t WifiDisplaySource::onReceiveM3Response( 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; - uint32_t modes; - GetAudioModes(value.c_str(), "AAC", &modes); + if (!(value == "none")) { + mSinkSupportsAudio = true; - bool supportsAAC = (modes & 1) != 0; // AAC 2ch 48kHz + uint32_t modes; + GetAudioModes(value.c_str(), "AAC", &modes); - GetAudioModes(value.c_str(), "LPCM", &modes); + bool supportsAAC = (modes & 1) != 0; // AAC 2ch 48kHz - bool supportsPCM = (modes & 2) != 0; // LPCM 2ch 48kHz + GetAudioModes(value.c_str(), "LPCM", &modes); - 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; + 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 an audio format we do."); + ALOGI("Sink doesn't support audio at all."); + } + + if (!mSinkSupportsVideo && !mSinkSupportsAudio) { + ALOGE("Sink supports neither video nor audio..."); return ERROR_UNSUPPORTED; } @@ -1160,7 +1233,11 @@ status_t WifiDisplaySource::onSetupRequest( clientRtp, clientRtcp, transportMode, - mUsingPCMAudio); + mSinkSupportsAudio, + mUsingPCMAudio, + mSinkSupportsVideo, + mChosenVideoResolutionType, + mChosenVideoResolutionIndex); if (err != OK) { looper()->unregisterHandler(playbackSession->id()); @@ -1560,7 +1637,7 @@ status_t WifiDisplaySource::makeHDCP() { 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 @@ private: kPlaybackSessionTimeoutSecs * 1000000ll; State mState; + VideoFormats mSupportedSourceVideoFormats; sp<ANetworkSession> mNetSession; sp<IRemoteDisplayClient> mClient; struct in_addr mInterfaceAddr; @@ -121,6 +121,14 @@ private: 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 @@ int main(int argc, char **argv) { 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 @@ int main(int argc, char **argv) { 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_SRC_FILES:= \ 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 @@ using namespace android; 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 @@ AudioFlinger::AudioFlinger() 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 @@ status_t AudioFlinger::dump(int fd, const Vector<String16>& args) 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 @@ sp<AudioFlinger::Client> AudioFlinger::registerPid_l(pid_t pid) 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 @@ public: // 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 @@ bool FastMixer::threadLoop() 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 @@ bool FastMixer::threadLoop() 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 @@ bool FastMixer::threadLoop() 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 @@ bool FastMixer::threadLoop() } continue; case FastMixerState::EXIT: + logWriter->log("exit"); delete mixer; delete[] mixBuffer; return false; @@ -258,11 +264,15 @@ bool FastMixer::threadLoop() 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 @@ bool FastMixer::threadLoop() // 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 @@ bool FastMixer::threadLoop() // 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 @@ bool FastMixer::threadLoop() 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 @@ FastTrack::~FastTrack() 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 @@ struct FastMixerState { // 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 @@ AudioFlinger::PlaybackThread::PlaybackThread(const sp<AudioFlinger>& audioFlinge 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(const sp<AudioFlinger>& audioFlinge AudioFlinger::PlaybackThread::~PlaybackThread() { + mAudioFlinger->unregisterWriter(mNBLogWriter); delete [] mMixBuffer; } @@ -1247,6 +1249,7 @@ Exit: if (status) { *status = lStatus; } + mNBLogWriter->logf("createTrack_l"); return track; } @@ -1314,6 +1317,7 @@ float AudioFlinger::PlaybackThread::streamVolume(audio_stream_type_t stream) con // 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 @@ status_t AudioFlinger::PlaybackThread::addTrack_l(const sp<Track>& track) // 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::destroyTrack_l(const sp<Track>& track) 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 @@ bool AudioFlinger::PlaybackThread::threadLoop() 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 @@ bool AudioFlinger::PlaybackThread::threadLoop() Mutex::Autolock _l(mLock); + if (logString != NULL) { + mNBLogWriter->logTimestamp(); + mNBLogWriter->log(logString); + logString = NULL; + } + if (checkForNewParameters_l()) { cacheParameters_l(); } @@ -1917,6 +1934,7 @@ bool AudioFlinger::PlaybackThread::threadLoop() threadLoop_standby(); + mNBLogWriter->log("standby"); mStandby = true; } @@ -2012,6 +2030,9 @@ if (mType == MIXER) { // 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 @@ AudioFlinger::MixerThread::MixerThread(const sp<AudioFlinger>& audioFlinger, Aud 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 @@ AudioFlinger::MixerThread::~MixerThread() } #endif } + mAudioFlinger->unregisterWriter(mFastMixerNBLogWriter); delete mAudioMixer; } @@ -2846,6 +2870,7 @@ track_is_ready: ; 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 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::DirectOutputThread::prep // 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 @@ protected: // 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 @@ private: 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 @@ status_t AudioFlinger::PlaybackThread::Track::start(AudioSystem::sync_event_t ev 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 @@ void AudioFlinger::PlaybackThread::Track::stop() 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 @@ void AudioFlinger::PlaybackThread::Track::pause() 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 @@ void AudioFlinger::PlaybackThread::Track::flush() 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 |