Merge "framework/av: fixbug for mistake clear mainbuffer data, root cause by not offset to fout"
am: 25ef483a8c
Change-Id: I6c8ee1c28b51f1578a781fdc7dd1d51326046d2f
diff --git a/camera/CameraBase.cpp b/camera/CameraBase.cpp
index 15d7715..194e1d3 100644
--- a/camera/CameraBase.cpp
+++ b/camera/CameraBase.cpp
@@ -20,6 +20,7 @@
#include <utils/Log.h>
#include <utils/threads.h>
#include <utils/Mutex.h>
+#include <cutils/properties.h>
#include <android/hardware/ICameraService.h>
@@ -90,6 +91,12 @@
{
Mutex::Autolock _l(gLock);
if (gCameraService.get() == 0) {
+ char value[PROPERTY_VALUE_MAX];
+ property_get("config.disable_cameraservice", value, "0");
+ if (strncmp(value, "0", 2) != 0 && strncasecmp(value, "false", 6) != 0) {
+ return gCameraService;
+ }
+
sp<IServiceManager> sm = defaultServiceManager();
sp<IBinder> binder;
do {
diff --git a/camera/CameraMetadata.cpp b/camera/CameraMetadata.cpp
index c78fc5d..373b94e 100644
--- a/camera/CameraMetadata.cpp
+++ b/camera/CameraMetadata.cpp
@@ -20,8 +20,9 @@
#include <utils/Log.h>
#include <utils/Errors.h>
-#include <camera/CameraMetadata.h>
#include <binder/Parcel.h>
+#include <camera/CameraMetadata.h>
+#include <camera/VendorTagDescriptor.h>
namespace android {
@@ -277,6 +278,18 @@
return updateImpl(tag, (const void*)string.string(), string.size() + 1);
}
+status_t CameraMetadata::update(const camera_metadata_ro_entry &entry) {
+ status_t res;
+ if (mLocked) {
+ ALOGE("%s: CameraMetadata is locked", __FUNCTION__);
+ return INVALID_OPERATION;
+ }
+ if ( (res = checkType(entry.tag, entry.type)) != OK) {
+ return res;
+ }
+ return updateImpl(entry.tag, (const void*)entry.data.u8, entry.count);
+}
+
status_t CameraMetadata::updateImpl(uint32_t tag, const void *data,
size_t data_count) {
status_t res;
@@ -681,4 +694,99 @@
mBuffer = otherBuf;
}
+status_t CameraMetadata::getTagFromName(const char *name,
+ const VendorTagDescriptor* vTags, uint32_t *tag) {
+
+ if (name == nullptr || tag == nullptr) return BAD_VALUE;
+
+ size_t nameLength = strlen(name);
+
+ const SortedVector<String8> *vendorSections;
+ size_t vendorSectionCount = 0;
+
+ if (vTags != NULL) {
+ vendorSections = vTags->getAllSectionNames();
+ vendorSectionCount = vendorSections->size();
+ }
+
+ // First, find the section by the longest string match
+ const char *section = NULL;
+ size_t sectionIndex = 0;
+ size_t sectionLength = 0;
+ size_t totalSectionCount = ANDROID_SECTION_COUNT + vendorSectionCount;
+ for (size_t i = 0; i < totalSectionCount; ++i) {
+
+ const char *str = (i < ANDROID_SECTION_COUNT) ? camera_metadata_section_names[i] :
+ (*vendorSections)[i - ANDROID_SECTION_COUNT].string();
+
+ ALOGV("%s: Trying to match against section '%s'", __FUNCTION__, str);
+
+ if (strstr(name, str) == name) { // name begins with the section name
+ size_t strLength = strlen(str);
+
+ ALOGV("%s: Name begins with section name", __FUNCTION__);
+
+ // section name is the longest we've found so far
+ if (section == NULL || sectionLength < strLength) {
+ section = str;
+ sectionIndex = i;
+ sectionLength = strLength;
+
+ ALOGV("%s: Found new best section (%s)", __FUNCTION__, section);
+ }
+ }
+ }
+
+ // TODO: Make above get_camera_metadata_section_from_name ?
+
+ if (section == NULL) {
+ return NAME_NOT_FOUND;
+ } else {
+ ALOGV("%s: Found matched section '%s' (%zu)",
+ __FUNCTION__, section, sectionIndex);
+ }
+
+ // Get the tag name component of the name
+ const char *nameTagName = name + sectionLength + 1; // x.y.z -> z
+ if (sectionLength + 1 >= nameLength) {
+ return BAD_VALUE;
+ }
+
+ // Match rest of name against the tag names in that section only
+ uint32_t candidateTag = 0;
+ if (sectionIndex < ANDROID_SECTION_COUNT) {
+ // Match built-in tags (typically android.*)
+ uint32_t tagBegin, tagEnd; // [tagBegin, tagEnd)
+ tagBegin = camera_metadata_section_bounds[sectionIndex][0];
+ tagEnd = camera_metadata_section_bounds[sectionIndex][1];
+
+ for (candidateTag = tagBegin; candidateTag < tagEnd; ++candidateTag) {
+ const char *tagName = get_camera_metadata_tag_name(candidateTag);
+
+ if (strcmp(nameTagName, tagName) == 0) {
+ ALOGV("%s: Found matched tag '%s' (%d)",
+ __FUNCTION__, tagName, candidateTag);
+ break;
+ }
+ }
+
+ if (candidateTag == tagEnd) {
+ return NAME_NOT_FOUND;
+ }
+ } else if (vTags != NULL) {
+ // Match vendor tags (typically com.*)
+ const String8 sectionName(section);
+ const String8 tagName(nameTagName);
+
+ status_t res = OK;
+ if ((res = vTags->lookupTag(tagName, sectionName, &candidateTag)) != OK) {
+ return NAME_NOT_FOUND;
+ }
+ }
+
+ *tag = candidateTag;
+ return OK;
+}
+
+
}; // namespace android
diff --git a/camera/VendorTagDescriptor.cpp b/camera/VendorTagDescriptor.cpp
index ca9cfd5..f3b3dbb 100644
--- a/camera/VendorTagDescriptor.cpp
+++ b/camera/VendorTagDescriptor.cpp
@@ -280,8 +280,8 @@
return res;
}
-SortedVector<String8> VendorTagDescriptor::getAllSectionNames() const {
- return mSections;
+const SortedVector<String8>* VendorTagDescriptor::getAllSectionNames() const {
+ return &mSections;
}
status_t VendorTagDescriptor::lookupTag(const String8& name, const String8& section, /*out*/uint32_t* tag) const {
diff --git a/camera/aidl/android/hardware/camera2/ICameraDeviceUser.aidl b/camera/aidl/android/hardware/camera2/ICameraDeviceUser.aidl
index 1e8744b..96ecfa0 100644
--- a/camera/aidl/android/hardware/camera2/ICameraDeviceUser.aidl
+++ b/camera/aidl/android/hardware/camera2/ICameraDeviceUser.aidl
@@ -121,4 +121,6 @@
void tearDown(int streamId);
void prepare2(int maxCount, int streamId);
+
+ void setDeferredConfiguration(int streamId, in OutputConfiguration outputConfiguration);
}
diff --git a/camera/camera2/CaptureRequest.cpp b/camera/camera2/CaptureRequest.cpp
index fb43708..0d689a6 100644
--- a/camera/camera2/CaptureRequest.cpp
+++ b/camera/camera2/CaptureRequest.cpp
@@ -37,7 +37,7 @@
mMetadata.clear();
mSurfaceList.clear();
- status_t err;
+ status_t err = OK;
if ((err = mMetadata.readFromParcel(parcel)) != OK) {
ALOGE("%s: Failed to read metadata from parcel", __FUNCTION__);
@@ -65,19 +65,16 @@
}
// Surface.writeToParcel
- const char16_t* name = parcel->readString16Inplace(&len);
- ALOGV("%s: Read surface name = %s", __FUNCTION__,
- name != NULL ? String8(name).string() : "<null>");
- sp<IBinder> binder(parcel->readStrongBinder());
- ALOGV("%s: Read surface binder = %p",
- __FUNCTION__, binder.get());
+ view::Surface surfaceShim;
+ if ((err = surfaceShim.readFromParcel(parcel)) != OK) {
+ ALOGE("%s: Failed to read output target Surface %d from parcel: %s (%d)",
+ __FUNCTION__, i, strerror(-err), err);
+ return err;
+ }
sp<Surface> surface;
-
- if (binder != NULL) {
- sp<IGraphicBufferProducer> gbp =
- interface_cast<IGraphicBufferProducer>(binder);
- surface = new Surface(gbp);
+ if (surfaceShim.graphicBufferProducer != NULL) {
+ surface = new Surface(surfaceShim.graphicBufferProducer);
}
mSurfaceList.push_back(surface);
@@ -99,7 +96,7 @@
return BAD_VALUE;
}
- status_t err;
+ status_t err = OK;
if ((err = mMetadata.writeToParcel(parcel)) != OK) {
return err;
@@ -111,20 +108,18 @@
parcel->writeInt32(size);
for (int32_t i = 0; i < size; ++i) {
- sp<Surface> surface = mSurfaceList[i];
-
- sp<IBinder> binder;
- if (surface != 0) {
- binder = IInterface::asBinder(surface->getIGraphicBufferProducer());
- }
-
// not sure if readParcelableArray does this, hard to tell from source
parcel->writeString16(String16("android.view.Surface"));
// Surface.writeToParcel
- parcel->writeString16(String16("unknown_name"));
- // Surface.nativeWriteToParcel
- parcel->writeStrongBinder(binder);
+ view::Surface surfaceShim;
+ surfaceShim.name = String16("unknown_name");
+ surfaceShim.graphicBufferProducer = mSurfaceList[i]->getIGraphicBufferProducer();
+ if ((err = surfaceShim.writeToParcel(parcel)) != OK) {
+ ALOGE("%s: Failed to write output target Surface %d to parcel: %s (%d)",
+ __FUNCTION__, i, strerror(-err), err);
+ return err;
+ }
}
parcel->writeInt32(mIsReprocess ? 1 : 0);
diff --git a/camera/camera2/OutputConfiguration.cpp b/camera/camera2/OutputConfiguration.cpp
index 3247d0d..38e1c01 100644
--- a/camera/camera2/OutputConfiguration.cpp
+++ b/camera/camera2/OutputConfiguration.cpp
@@ -42,9 +42,24 @@
return mSurfaceSetID;
}
+int OutputConfiguration::getSurfaceType() const {
+ return mSurfaceType;
+}
+
+int OutputConfiguration::getWidth() const {
+ return mWidth;
+}
+
+int OutputConfiguration::getHeight() const {
+ return mHeight;
+}
+
OutputConfiguration::OutputConfiguration() :
mRotation(INVALID_ROTATION),
- mSurfaceSetID(INVALID_SET_ID) {
+ mSurfaceSetID(INVALID_SET_ID),
+ mSurfaceType(SURFACE_TYPE_UNKNOWN),
+ mWidth(0),
+ mHeight(0) {
}
OutputConfiguration::OutputConfiguration(const Parcel& parcel) :
@@ -70,18 +85,48 @@
return err;
}
+ int surfaceType = SURFACE_TYPE_UNKNOWN;
+ if ((err = parcel->readInt32(&surfaceType)) != OK) {
+ ALOGE("%s: Failed to read surface type from parcel", __FUNCTION__);
+ return err;
+ }
+
+ int width = 0;
+ if ((err = parcel->readInt32(&width)) != OK) {
+ ALOGE("%s: Failed to read surface width from parcel", __FUNCTION__);
+ return err;
+ }
+
+ int height = 0;
+ if ((err = parcel->readInt32(&height)) != OK) {
+ ALOGE("%s: Failed to read surface height from parcel", __FUNCTION__);
+ return err;
+ }
+
view::Surface surfaceShim;
if ((err = surfaceShim.readFromParcel(parcel)) != OK) {
- ALOGE("%s: Failed to read surface from parcel", __FUNCTION__);
- return err;
+ // Read surface failure for deferred surface configuration is expected.
+ if (surfaceType == SURFACE_TYPE_SURFACE_VIEW ||
+ surfaceType == SURFACE_TYPE_SURFACE_TEXTURE) {
+ ALOGV("%s: Get null surface from a deferred surface configuration (%dx%d)",
+ __FUNCTION__, width, height);
+ err = OK;
+ } else {
+ ALOGE("%s: Failed to read surface from parcel", __FUNCTION__);
+ return err;
+ }
}
mGbp = surfaceShim.graphicBufferProducer;
mRotation = rotation;
mSurfaceSetID = setID;
+ mSurfaceType = surfaceType;
+ mWidth = width;
+ mHeight = height;
- ALOGV("%s: OutputConfiguration: bp = %p, name = %s, rotation = %d, setId = %d", __FUNCTION__,
- mGbp.get(), String8(surfaceShim.name).string(), mRotation, mSurfaceSetID);
+ ALOGV("%s: OutputConfiguration: bp = %p, name = %s, rotation = %d, setId = %d,"
+ "surfaceType = %d", __FUNCTION__, mGbp.get(), String8(surfaceShim.name).string(),
+ mRotation, mSurfaceSetID, mSurfaceType);
return err;
}
@@ -104,6 +149,15 @@
err = parcel->writeInt32(mSurfaceSetID);
if (err != OK) return err;
+ err = parcel->writeInt32(mSurfaceType);
+ if (err != OK) return err;
+
+ err = parcel->writeInt32(mWidth);
+ if (err != OK) return err;
+
+ err = parcel->writeInt32(mHeight);
+ if (err != OK) return err;
+
view::Surface surfaceShim;
surfaceShim.name = String16("unknown_name"); // name of surface
surfaceShim.graphicBufferProducer = mGbp;
diff --git a/camera/cameraserver/cameraserver.rc b/camera/cameraserver/cameraserver.rc
index 16d9da8..fea5a1d 100644
--- a/camera/cameraserver/cameraserver.rc
+++ b/camera/cameraserver/cameraserver.rc
@@ -1,6 +1,6 @@
service cameraserver /system/bin/cameraserver
class main
user cameraserver
- group audio camera drmrpc inet media mediadrm net_bt net_bt_admin net_bw_acct
+ group audio camera input drmrpc
ioprio rt 4
- writepid /dev/cpuset/foreground/tasks
+ writepid /dev/cpuset/camera-daemon/tasks /dev/stune/top-app/tasks
diff --git a/camera/ndk/impl/ACameraManager.cpp b/camera/ndk/impl/ACameraManager.cpp
index 26d6679..35555ff 100644
--- a/camera/ndk/impl/ACameraManager.cpp
+++ b/camera/ndk/impl/ACameraManager.cpp
@@ -22,6 +22,7 @@
#include "ACameraMetadata.h"
#include "ACameraDevice.h"
#include <utils/Vector.h>
+#include <cutils/properties.h>
#include <stdlib.h>
#include <camera/VendorTagDescriptor.h>
@@ -71,9 +72,19 @@
mCameraService.clear();
}
+static bool isCameraServiceDisabled() {
+ char value[PROPERTY_VALUE_MAX];
+ property_get("config.disable_cameraservice", value, "0");
+ return (strncmp(value, "0", 2) != 0 && strncasecmp(value, "false", 6) != 0);
+}
+
sp<hardware::ICameraService> CameraManagerGlobal::getCameraService() {
Mutex::Autolock _l(mLock);
if (mCameraService.get() == nullptr) {
+ if (isCameraServiceDisabled()) {
+ return mCameraService;
+ }
+
sp<IServiceManager> sm = defaultServiceManager();
sp<IBinder> binder;
do {
@@ -302,6 +313,13 @@
camera_status_t
ACameraManager::getOrCreateCameraIdListLocked(ACameraIdList** cameraIdList) {
if (mCachedCameraIdList.numCameras == kCameraIdListNotInit) {
+ if (isCameraServiceDisabled()) {
+ mCachedCameraIdList.numCameras = 0;
+ mCachedCameraIdList.cameraIds = new const char*[0];
+ *cameraIdList = &mCachedCameraIdList;
+ return ACAMERA_OK;
+ }
+
int numCameras = 0;
Vector<char *> cameraIds;
sp<hardware::ICameraService> cs = CameraManagerGlobal::getInstance().getCameraService();
diff --git a/cmds/screenrecord/screenrecord.cpp b/cmds/screenrecord/screenrecord.cpp
index 36a7e73..59d5661 100644
--- a/cmds/screenrecord/screenrecord.cpp
+++ b/cmds/screenrecord/screenrecord.cpp
@@ -80,7 +80,7 @@
static uint32_t gTimeLimitSec = kMaxTimeLimitSec;
// Set by signal handler to stop recording.
-static volatile bool gStopRequested;
+static volatile bool gStopRequested = false;
// Previous signal handler state, restored after first hit.
static struct sigaction gOrigSigactionINT;
@@ -334,9 +334,6 @@
return err;
}
- // This is set by the signal handler.
- gStopRequested = false;
-
// Run until we're signaled.
while (!gStopRequested) {
size_t bufIndex, offset, size;
@@ -640,6 +637,11 @@
case FORMAT_MP4: {
// Configure muxer. We have to wait for the CSD blob from the encoder
// before we can start it.
+ err = unlink(fileName);
+ if (err != 0 && errno != ENOENT) {
+ fprintf(stderr, "ERROR: couldn't remove existing file\n");
+ abort();
+ }
int fd = open(fileName, O_CREAT | O_LARGEFILE | O_TRUNC | O_RDWR, S_IRUSR | S_IWUSR);
if (fd < 0) {
fprintf(stderr, "ERROR: couldn't open file\n");
@@ -718,7 +720,7 @@
if (muxer != NULL) {
// If we don't stop muxer explicitly, i.e. let the destructor run,
// it may hang (b/11050628).
- muxer->stop();
+ err = muxer->stop();
} else if (rawFp != stdout) {
fclose(rawFp);
}
diff --git a/drm/mediadrm/plugins/clearkey/DrmFactory.cpp b/drm/mediadrm/plugins/clearkey/DrmFactory.cpp
index 40275cf..d27956c 100644
--- a/drm/mediadrm/plugins/clearkey/DrmFactory.cpp
+++ b/drm/mediadrm/plugins/clearkey/DrmFactory.cpp
@@ -24,6 +24,7 @@
#include "DrmPlugin.h"
#include "ClearKeyUUID.h"
+#include "MimeType.h"
#include "SessionLibrary.h"
namespace clearkeydrm {
@@ -32,10 +33,14 @@
return isClearKeyUUID(uuid);
}
-bool DrmFactory::isContentTypeSupported(const android::String8 &initDataType) {
+bool DrmFactory::isContentTypeSupported(const android::String8 &type) {
// This should match the types handed by InitDataParser.
- return initDataType == "cenc" ||
- initDataType == "webm";
+ return type == kIsoBmffVideoMimeType ||
+ type == kIsoBmffAudioMimeType ||
+ type == kCencInitDataFormat ||
+ type == kWebmVideoMimeType ||
+ type == kWebmAudioMimeType ||
+ type == kWebmInitDataFormat;
}
android::status_t DrmFactory::createDrmPlugin(
diff --git a/drm/mediadrm/plugins/clearkey/DrmFactory.h b/drm/mediadrm/plugins/clearkey/DrmFactory.h
index 164d3d0..87db982 100644
--- a/drm/mediadrm/plugins/clearkey/DrmFactory.h
+++ b/drm/mediadrm/plugins/clearkey/DrmFactory.h
@@ -32,7 +32,7 @@
virtual bool isCryptoSchemeSupported(const uint8_t uuid[16]);
- virtual bool isContentTypeSupported(const android::String8 &initDataType);
+ virtual bool isContentTypeSupported(const android::String8 &mimeType);
virtual android::status_t createDrmPlugin(
const uint8_t uuid[16], android::DrmPlugin** plugin);
diff --git a/drm/mediadrm/plugins/clearkey/DrmPlugin.cpp b/drm/mediadrm/plugins/clearkey/DrmPlugin.cpp
index e5ee403..86bf047 100644
--- a/drm/mediadrm/plugins/clearkey/DrmPlugin.cpp
+++ b/drm/mediadrm/plugins/clearkey/DrmPlugin.cpp
@@ -46,7 +46,7 @@
status_t DrmPlugin::getKeyRequest(
const Vector<uint8_t>& scope,
const Vector<uint8_t>& initData,
- const String8& initDataType,
+ const String8& mimeType,
KeyType keyType,
const KeyedVector<String8, String8>& optionalParameters,
Vector<uint8_t>& request,
@@ -62,7 +62,7 @@
if (!session.get()) {
return android::ERROR_DRM_SESSION_NOT_OPENED;
}
- return session->getKeyRequest(initData, initDataType, &request);
+ return session->getKeyRequest(initData, mimeType, &request);
}
status_t DrmPlugin::provideKeyResponse(
diff --git a/drm/mediadrm/plugins/clearkey/DrmPlugin.h b/drm/mediadrm/plugins/clearkey/DrmPlugin.h
index 820bda2..c4d934e 100644
--- a/drm/mediadrm/plugins/clearkey/DrmPlugin.h
+++ b/drm/mediadrm/plugins/clearkey/DrmPlugin.h
@@ -49,7 +49,7 @@
virtual status_t getKeyRequest(
const Vector<uint8_t>& scope,
- const Vector<uint8_t>& initData,
+ const Vector<uint8_t>& mimeType,
const String8& initDataType,
KeyType keyType,
const KeyedVector<String8, String8>& optionalParameters,
diff --git a/drm/mediadrm/plugins/clearkey/InitDataParser.cpp b/drm/mediadrm/plugins/clearkey/InitDataParser.cpp
index c22d73a..0216b8d 100644
--- a/drm/mediadrm/plugins/clearkey/InitDataParser.cpp
+++ b/drm/mediadrm/plugins/clearkey/InitDataParser.cpp
@@ -27,6 +27,7 @@
#include "InitDataParser.h"
#include "ClearKeyUUID.h"
+#include "MimeType.h"
#include "Utils.h"
namespace clearkeydrm {
@@ -41,16 +42,20 @@
}
android::status_t InitDataParser::parse(const Vector<uint8_t>& initData,
- const String8& initDataType,
+ const String8& type,
Vector<uint8_t>* licenseRequest) {
// Build a list of the key IDs
Vector<const uint8_t*> keyIds;
- if (initDataType == "cenc") {
+ if (type == kIsoBmffVideoMimeType ||
+ type == kIsoBmffAudioMimeType ||
+ type == kCencInitDataFormat) {
android::status_t res = parsePssh(initData, &keyIds);
if (res != android::OK) {
return res;
}
- } else if (initDataType == "webm") {
+ } else if (type == kWebmVideoMimeType ||
+ type == kWebmAudioMimeType ||
+ type == kWebmInitDataFormat) {
// WebM "init data" is just a single key ID
if (initData.size() != kKeyIdSize) {
return android::ERROR_DRM_CANNOT_HANDLE;
diff --git a/drm/mediadrm/plugins/clearkey/InitDataParser.h b/drm/mediadrm/plugins/clearkey/InitDataParser.h
index 9505d2a..a9707bf 100644
--- a/drm/mediadrm/plugins/clearkey/InitDataParser.h
+++ b/drm/mediadrm/plugins/clearkey/InitDataParser.h
@@ -29,7 +29,7 @@
InitDataParser() {}
android::status_t parse(const android::Vector<uint8_t>& initData,
- const android::String8& initDataType,
+ const android::String8& type,
android::Vector<uint8_t>* licenseRequest);
private:
diff --git a/drm/mediadrm/plugins/clearkey/MimeType.h b/drm/mediadrm/plugins/clearkey/MimeType.h
new file mode 100644
index 0000000..085f17a
--- /dev/null
+++ b/drm/mediadrm/plugins/clearkey/MimeType.h
@@ -0,0 +1,15 @@
+#ifndef CLEARKEY_MIMETYPE_H_
+#define CLEARKEY_MIMETYPE_H_
+
+#include <utils/String8.h>
+
+namespace {
+ const android::String8 kCencInitDataFormat("cenc");
+ const android::String8 kIsoBmffAudioMimeType("audio/mp4");
+ const android::String8 kIsoBmffVideoMimeType("video/mp4");
+ const android::String8 kWebmInitDataFormat("webm");
+ const android::String8 kWebmAudioMimeType("audio/webm");
+ const android::String8 kWebmVideoMimeType("video/webm");
+}
+
+#endif // CLEARKEY_MIMETYPE_H_
diff --git a/drm/mediadrm/plugins/clearkey/Session.cpp b/drm/mediadrm/plugins/clearkey/Session.cpp
index 95016f5..d210f5e 100644
--- a/drm/mediadrm/plugins/clearkey/Session.cpp
+++ b/drm/mediadrm/plugins/clearkey/Session.cpp
@@ -36,10 +36,10 @@
status_t Session::getKeyRequest(
const Vector<uint8_t>& initData,
- const String8& initDataType,
+ const String8& mimeType,
Vector<uint8_t>* keyRequest) const {
InitDataParser parser;
- return parser.parse(initData, initDataType, keyRequest);
+ return parser.parse(initData, mimeType, keyRequest);
}
status_t Session::provideKeyResponse(const Vector<uint8_t>& response) {
diff --git a/drm/mediadrm/plugins/clearkey/Session.h b/drm/mediadrm/plugins/clearkey/Session.h
index cab0dc3..0933506 100644
--- a/drm/mediadrm/plugins/clearkey/Session.h
+++ b/drm/mediadrm/plugins/clearkey/Session.h
@@ -38,7 +38,7 @@
const android::Vector<uint8_t>& sessionId() const { return mSessionId; }
android::status_t getKeyRequest(
- const android::Vector<uint8_t>& initData,
+ const android::Vector<uint8_t>& mimeType,
const android::String8& initDataType,
android::Vector<uint8_t>* keyRequest) const;
diff --git a/drm/mediadrm/plugins/clearkey/tests/InitDataParserUnittest.cpp b/drm/mediadrm/plugins/clearkey/tests/InitDataParserUnittest.cpp
index 4ba65ed..e275108 100644
--- a/drm/mediadrm/plugins/clearkey/tests/InitDataParserUnittest.cpp
+++ b/drm/mediadrm/plugins/clearkey/tests/InitDataParserUnittest.cpp
@@ -30,27 +30,27 @@
namespace {
const size_t kKeyIdSize = 16;
- const String8 kCencType("cenc");
- const String8 kWebMType("webm");
+ const String8 kCencMimeType("video/mp4");
+ const String8 kWebmMimeType("video/webm");
const String8 kBase64Padding("=");
}
class InitDataParserTest : public ::testing::Test {
protected:
status_t attemptParse(const Vector<uint8_t>& initData,
- const String8& initDataType,
+ const String8& mimeType,
Vector<uint8_t>* licenseRequest) {
InitDataParser parser;
- return parser.parse(initData, initDataType, licenseRequest);
+ return parser.parse(initData, mimeType, licenseRequest);
}
void attemptParseExpectingSuccess(const Vector<uint8_t>& initData,
- const String8& initDataType,
+ const String8& mimeType,
const Vector<String8>& expectedKeys) {
const String8 kRequestPrefix("{\"kids\":[");
const String8 kRequestSuffix("],\"type\":\"temporary\"}");
Vector<uint8_t> request;
- ASSERT_EQ(android::OK, attemptParse(initData, initDataType, &request));
+ ASSERT_EQ(android::OK, attemptParse(initData, mimeType, &request));
String8 requestString(reinterpret_cast<const char*>(request.array()),
request.size());
@@ -68,9 +68,9 @@
}
void attemptParseExpectingFailure(const Vector<uint8_t>& initData,
- const String8& initDataType) {
+ const String8& mimeType) {
Vector<uint8_t> request;
- ASSERT_NE(android::OK, attemptParse(initData, initDataType, &request));
+ ASSERT_NE(android::OK, attemptParse(initData, mimeType, &request));
EXPECT_EQ(0, request.size());
}
};
@@ -93,7 +93,7 @@
Vector<String8> expectedKeys;
expectedKeys.push(String8("01234567890ABCDE"));
- attemptParseExpectingSuccess(initData, kCencType, expectedKeys);
+ attemptParseExpectingSuccess(initData, kCencMimeType, expectedKeys);
}
TEST_F(InitDataParserTest, ParsesMultipleKeyPssh) {
@@ -120,7 +120,7 @@
expectedKeys.push(String8("ClearKeyClearKey"));
expectedKeys.push(String8(" GOOGLE GOOGLE "));
- attemptParseExpectingSuccess(initData, kCencType, expectedKeys);
+ attemptParseExpectingSuccess(initData, kCencMimeType, expectedKeys);
}
TEST_F(InitDataParserTest, ParsesWebM) {
@@ -134,7 +134,7 @@
Vector<String8> expectedKeys;
expectedKeys.push(String8("01234567890ABCDE"));
- attemptParseExpectingSuccess(initData, kWebMType, expectedKeys);
+ attemptParseExpectingSuccess(initData, kWebmMimeType, expectedKeys);
}
TEST_F(InitDataParserTest, FailsForPsshTooSmall) {
@@ -147,7 +147,7 @@
Vector<uint8_t> initData;
initData.appendArray(pssh, 16);
- attemptParseExpectingFailure(initData, kCencType);
+ attemptParseExpectingFailure(initData, kCencMimeType);
}
TEST_F(InitDataParserTest, FailsForWebMTooSmall) {
@@ -157,7 +157,7 @@
Vector<uint8_t> initData;
initData.appendArray(initDataRaw, 8);
- attemptParseExpectingFailure(initData, kWebMType);
+ attemptParseExpectingFailure(initData, kWebmMimeType);
}
TEST_F(InitDataParserTest, FailsForPsshBadSystemId) {
@@ -175,7 +175,7 @@
Vector<uint8_t> initData;
initData.appendArray(pssh, 52);
- attemptParseExpectingFailure(initData, kCencType);
+ attemptParseExpectingFailure(initData, kCencMimeType);
}
TEST_F(InitDataParserTest, FailsForPsshBadSize) {
@@ -193,7 +193,7 @@
Vector<uint8_t> initData;
initData.appendArray(pssh, 52);
- attemptParseExpectingFailure(initData, kCencType);
+ attemptParseExpectingFailure(initData, kCencMimeType);
}
TEST_F(InitDataParserTest, FailsForPsshWrongVersion) {
@@ -211,7 +211,7 @@
Vector<uint8_t> initData;
initData.appendArray(pssh, 52);
- attemptParseExpectingFailure(initData, kCencType);
+ attemptParseExpectingFailure(initData, kCencMimeType);
}
TEST_F(InitDataParserTest, FailsForPsshBadKeyCount) {
@@ -229,7 +229,7 @@
Vector<uint8_t> initData;
initData.appendArray(pssh, 52);
- attemptParseExpectingFailure(initData, kCencType);
+ attemptParseExpectingFailure(initData, kCencMimeType);
}
} // namespace clearkeydrm
diff --git a/include/camera/CameraMetadata.h b/include/camera/CameraMetadata.h
index 28f47a1..d284477 100644
--- a/include/camera/CameraMetadata.h
+++ b/include/camera/CameraMetadata.h
@@ -18,12 +18,15 @@
#define ANDROID_CLIENT_CAMERA2_CAMERAMETADATA_CPP
#include "system/camera_metadata.h"
+
#include <utils/String8.h>
#include <utils/Vector.h>
#include <binder/Parcelable.h>
namespace android {
+class VendorTagDescriptor;
+
/**
* A convenience wrapper around the C-based camera_metadata_t library.
*/
@@ -137,6 +140,8 @@
const camera_metadata_rational_t *data, size_t data_count);
status_t update(uint32_t tag,
const String8 &string);
+ status_t update(const camera_metadata_ro_entry &entry);
+
template<typename T>
status_t update(uint32_t tag, Vector<T> data) {
@@ -206,6 +211,15 @@
static status_t writeToParcel(Parcel &parcel,
const camera_metadata_t* metadata);
+ /**
+ * Find tag id for a given tag name, also checking vendor tags if available.
+ * On success, returns OK and writes the tag id into tag.
+ *
+ * This is a slow method.
+ */
+ static status_t getTagFromName(const char *name,
+ const VendorTagDescriptor* vTags, uint32_t *tag);
+
private:
camera_metadata_t *mBuffer;
mutable bool mLocked;
diff --git a/include/camera/VendorTagDescriptor.h b/include/camera/VendorTagDescriptor.h
index 06cad50..bfc8c96 100644
--- a/include/camera/VendorTagDescriptor.h
+++ b/include/camera/VendorTagDescriptor.h
@@ -81,8 +81,10 @@
/**
* Convenience method to get a vector containing all vendor tag
* sections, or an empty vector if none are defined.
+ * The pointer is valid for the lifetime of the VendorTagDescriptor,
+ * or until readParcel or copyFrom is invoked.
*/
- SortedVector<String8> getAllSectionNames() const;
+ const SortedVector<String8>* getAllSectionNames() const;
/**
* Lookup the tag id for a given tag name and section.
diff --git a/include/camera/camera2/OutputConfiguration.h b/include/camera/camera2/OutputConfiguration.h
index 72a3753..cf8f3c6 100644
--- a/include/camera/camera2/OutputConfiguration.h
+++ b/include/camera/camera2/OutputConfiguration.h
@@ -33,10 +33,17 @@
static const int INVALID_ROTATION;
static const int INVALID_SET_ID;
+ enum SurfaceType{
+ SURFACE_TYPE_UNKNOWN = -1,
+ SURFACE_TYPE_SURFACE_VIEW = 0,
+ SURFACE_TYPE_SURFACE_TEXTURE = 1
+ };
sp<IGraphicBufferProducer> getGraphicBufferProducer() const;
int getRotation() const;
int getSurfaceSetID() const;
-
+ int getSurfaceType() const;
+ int getWidth() const;
+ int getHeight() const;
/**
* Keep impl up-to-date with OutputConfiguration.java in frameworks/base
*/
@@ -60,7 +67,10 @@
bool operator == (const OutputConfiguration& other) const {
return (mGbp == other.mGbp &&
mRotation == other.mRotation &&
- mSurfaceSetID == other.mSurfaceSetID);
+ mSurfaceSetID == other.mSurfaceSetID &&
+ mSurfaceType == other.mSurfaceType &&
+ mWidth == other.mWidth &&
+ mHeight == other.mHeight);
}
bool operator != (const OutputConfiguration& other) const {
return !(*this == other);
@@ -71,6 +81,16 @@
if (mSurfaceSetID != other.mSurfaceSetID) {
return mSurfaceSetID < other.mSurfaceSetID;
}
+ if (mSurfaceType != other.mSurfaceType) {
+ return mSurfaceType < other.mSurfaceType;
+ }
+ if (mWidth != other.mWidth) {
+ return mWidth < other.mWidth;
+ }
+ if (mHeight != other.mHeight) {
+ return mHeight < other.mHeight;
+ }
+
return mRotation < other.mRotation;
}
bool operator > (const OutputConfiguration& other) const {
@@ -81,6 +101,9 @@
sp<IGraphicBufferProducer> mGbp;
int mRotation;
int mSurfaceSetID;
+ int mSurfaceType;
+ int mWidth;
+ int mHeight;
// helper function
static String16 readMaybeEmptyString16(const Parcel* parcel);
};
diff --git a/include/media/AudioRecord.h b/include/media/AudioRecord.h
index 2fa1a4e..63076e9 100644
--- a/include/media/AudioRecord.h
+++ b/include/media/AudioRecord.h
@@ -491,6 +491,9 @@
*/
uint32_t getInputFramesLost() const;
+ /* Get the flags */
+ audio_input_flags_t getFlags() const { AutoMutex _l(mLock); return mFlags; }
+
private:
/* copying audio record objects is not allowed */
AudioRecord(const AudioRecord& other);
diff --git a/include/media/IAudioFlinger.h b/include/media/IAudioFlinger.h
index 984bc02..096f7ef 100644
--- a/include/media/IAudioFlinger.h
+++ b/include/media/IAudioFlinger.h
@@ -44,16 +44,6 @@
public:
DECLARE_META_INTERFACE(AudioFlinger);
- // or-able bits shared by createTrack and openRecord, but not all combinations make sense
- enum {
- TRACK_DEFAULT = 0, // client requests a default AudioTrack
- // FIXME: obsolete
- // TRACK_TIMED= 1, // client requests a TimedAudioTrack
- TRACK_FAST = 2, // client requests a fast AudioTrack or AudioRecord
- TRACK_OFFLOAD = 4, // client requests offload to hw codec
- TRACK_DIRECT = 8, // client requests a direct output
- };
- typedef uint32_t track_flags_t;
// invariant on exit for all APIs that return an sp<>:
// (return value != 0) == (*status == NO_ERROR)
@@ -67,7 +57,7 @@
audio_format_t format,
audio_channel_mask_t channelMask,
size_t *pFrameCount,
- track_flags_t *flags,
+ audio_output_flags_t *flags,
const sp<IMemory>& sharedBuffer,
// On successful return, AudioFlinger takes over the handle
// reference and will release it when the track is destroyed.
@@ -89,7 +79,7 @@
audio_channel_mask_t channelMask,
const String16& callingPackage,
size_t *pFrameCount,
- track_flags_t *flags,
+ audio_input_flags_t *flags,
pid_t pid,
pid_t tid, // -1 means unused, otherwise must be valid non-0
int clientUid,
diff --git a/include/media/IDataSource.h b/include/media/IDataSource.h
index 838e29f..655f337 100644
--- a/include/media/IDataSource.h
+++ b/include/media/IDataSource.h
@@ -25,6 +25,7 @@
namespace android {
class IMemory;
+class DecryptHandle;
// A binder interface for implementing a stagefright DataSource remotely.
class IDataSource : public IInterface {
@@ -47,6 +48,8 @@
virtual uint32_t getFlags() = 0;
// get a description of the source, e.g. the url or filename it is based on
virtual String8 toString() = 0;
+ // Initialize DRM and return a DecryptHandle.
+ virtual sp<DecryptHandle> DrmInitialization(const char *mime) = 0;
private:
DISALLOW_EVIL_CONSTRUCTORS(IDataSource);
diff --git a/include/media/IMediaSource.h b/include/media/IMediaSource.h
index 4056d54..2bde782 100644
--- a/include/media/IMediaSource.h
+++ b/include/media/IMediaSource.h
@@ -18,14 +18,17 @@
#define IMEDIA_SOURCE_BASE_H_
+#include <map>
+
#include <binder/IInterface.h>
+#include <binder/IMemory.h>
+#include <media/stagefright/MediaBuffer.h>
#include <media/stagefright/MediaErrors.h>
namespace android {
struct MediaSource;
class MetaData;
-class MediaBuffer;
class MediaBufferGroup;
class IMediaSource : public IInterface {
@@ -56,7 +59,7 @@
// a) not request a seek
// b) not be late, i.e. lateness_us = 0
struct ReadOptions {
- enum SeekMode {
+ enum SeekMode : int32_t {
SEEK_PREVIOUS_SYNC,
SEEK_NEXT_SYNC,
SEEK_CLOSEST_SYNC,
@@ -72,6 +75,7 @@
void clearSeekTo();
bool getSeekTo(int64_t *time_us, SeekMode *mode) const;
+ // TODO: remove this if unused.
void setLateBy(int64_t lateness_us);
int64_t getLateBy() const;
@@ -79,6 +83,11 @@
void clearNonBlocking();
bool getNonBlocking() const;
+ // Used to clear all non-persistent options for multiple buffer reads.
+ void clearNonPersistent() {
+ clearSeekTo();
+ }
+
private:
enum Options {
kSeekTo_Option = 1,
@@ -98,21 +107,33 @@
// A result of INFO_FORMAT_CHANGED indicates that the format of this
// MediaSource has changed mid-stream, the client can continue reading
// but should be prepared for buffers of the new configuration.
+ //
+ // TODO: consider removing read() in favor of readMultiple().
virtual status_t read(
MediaBuffer **buffer, const ReadOptions *options = NULL) = 0;
- // Returns a vector of new buffers of data. The vector size could be
- // <= |maxNumBuffers|. Used for buffers with small size
- // since all buffer data are passed back by binder, not shared memory.
+ // Returns a vector of new buffers of data, where the new buffers are added
+ // to the end of the vector.
// Call blocks until an error is encountered, or the end of the stream is
// reached, or format change is hit, or |kMaxNumReadMultiple| buffers have
// been read.
- // End of stream is signalled by a result of ERROR_END_OF_STREAM.
+ // End of stream is signaled by a result of ERROR_END_OF_STREAM.
// A result of INFO_FORMAT_CHANGED indicates that the format of this
// MediaSource has changed mid-stream, the client can continue reading
// but should be prepared for buffers of the new configuration.
+ //
+ // ReadOptions may be specified. Persistent options apply to all reads;
+ // non-persistent options (e.g. seek) apply only to the first read.
virtual status_t readMultiple(
- Vector<MediaBuffer *> *buffers, uint32_t maxNumBuffers = 1) = 0;
+ Vector<MediaBuffer *> *buffers, uint32_t maxNumBuffers = 1,
+ const ReadOptions *options = nullptr) = 0;
+
+ // Returns true if |readMultiple| is supported, otherwise false.
+ virtual bool supportReadMultiple() = 0;
+
+ // Returns true if |read| supports nonblocking option, otherwise false.
+ // |readMultiple| if supported, always allows the nonblocking option.
+ virtual bool supportNonblockingRead() = 0;
// Causes this source to suspend pulling data from its upstream source
// until a subsequent read-with-seek. Currently only supported by
@@ -144,17 +165,102 @@
return ERROR_UNSUPPORTED;
}
+ // TODO: Implement this for local media sources.
virtual status_t readMultiple(
- Vector<MediaBuffer *> * /* buffers */, uint32_t /* maxNumBuffers = 1 */) {
+ Vector<MediaBuffer *> * /* buffers */, uint32_t /* maxNumBuffers = 1 */,
+ const ReadOptions * /* options = nullptr */) {
return ERROR_UNSUPPORTED;
}
+
+ virtual bool supportReadMultiple() {
+ return false;
+ }
+
+ // Override in source if nonblocking reads are supported.
+ virtual bool supportNonblockingRead() {
+ return false;
+ }
+
+ static const size_t kBinderMediaBuffers = 4; // buffers managed by BnMediaSource
+ static const size_t kTransferSharedAsSharedThreshold = 4 * 1024; // if >= shared, else inline
+ static const size_t kTransferInlineAsSharedThreshold = 64 * 1024; // if >= shared, else inline
+ static const size_t kInlineMaxTransfer = 256 * 1024; // Binder size limited to BINDER_VM_SIZE.
+
protected:
virtual ~BnMediaSource();
private:
- MediaBufferGroup *mGroup;
-};
+ uint32_t mBuffersSinceStop; // Buffer tracking variable
+ std::unique_ptr<MediaBufferGroup> mGroup;
+
+ // To prevent marshalling IMemory with each read transaction, we cache the IMemory pointer
+ // into a map.
+ //
+ // This is converted into an index, which is used to identify the associated memory
+ // on the receiving side. We hold a reference to the IMemory here to ensure it doesn't
+ // change underneath us.
+
+ struct IndexCache {
+ IndexCache() : mIndex(0) { }
+
+ // Returns the index of the IMemory stored in cache or 0 if not found.
+ uint64_t lookup(const sp<IMemory> &mem) {
+ auto p = mMemoryToIndex.find(mem.get());
+ if (p == mMemoryToIndex.end()) {
+ return 0;
+ }
+ if (MediaBuffer::isDeadObject(p->second.first)) {
+ // this object's dead
+ ALOGW("Attempting to lookup a dead IMemory");
+ (void)mMemoryToIndex.erase(p);
+ return 0;
+ }
+ ALOGW_IF(p->second.first.get() != mem.get(), "Mismatched buffers without reset");
+ return p->second.second;
+ }
+
+ // Returns the index of the IMemory stored in the index cache.
+ uint64_t insert(const sp<IMemory> &mem) {
+ auto p = mMemoryToIndex.find(mem.get());
+ if (p == mMemoryToIndex.end()) {
+ if (mIndex == UINT64_MAX) {
+ ALOGE("Index overflow");
+ mIndex = 1; // skip overflow condition and hope for the best
+ } else {
+ ++mIndex;
+ }
+ (void)mMemoryToIndex.emplace(// C++11 mem.get(), std::make_pair(mem, mIndex))
+ std::piecewise_construct,
+ std::forward_as_tuple(mem.get()), std::forward_as_tuple(mem, mIndex));
+ return mIndex;
+ }
+ ALOGW("IMemory already inserted into cache");
+ return p->second.second;
+ }
+
+ void reset() {
+ mMemoryToIndex.clear();
+ mIndex = 0;
+ }
+
+ void gc() {
+ for (auto it = mMemoryToIndex.begin(); it != mMemoryToIndex.end(); ) {
+ if (MediaBuffer::isDeadObject(it->second.first)) {
+ it = mMemoryToIndex.erase(it);
+ } else {
+ ++it;
+ }
+ }
+ }
+
+ private:
+ uint64_t mIndex;
+ // C++14 unordered_map erase on iterator is stable; C++11 has no guarantee.
+ // Could key on uintptr_t instead of IMemory *
+ std::map<IMemory *, std::pair<sp<IMemory>, uint64_t>> mMemoryToIndex;
+ } mIndexCache;
+};
} // namespace android
diff --git a/include/media/IOMX.h b/include/media/IOMX.h
index 15d691f..1c39b9c 100644
--- a/include/media/IOMX.h
+++ b/include/media/IOMX.h
@@ -192,6 +192,7 @@
INTERNAL_OPTION_START_TIME, // data is an int64_t
INTERNAL_OPTION_TIME_LAPSE, // data is an int64_t[2]
INTERNAL_OPTION_COLOR_ASPECTS, // data is ColorAspects
+ INTERNAL_OPTION_TIME_OFFSET, // data is an int64_t
};
virtual status_t setInternalOption(
node_id node,
diff --git a/include/media/ToneGenerator.h b/include/media/ToneGenerator.h
index 7b706b6..9fd5f61 100644
--- a/include/media/ToneGenerator.h
+++ b/include/media/ToneGenerator.h
@@ -193,15 +193,21 @@
TONE_JAPAN_DIAL, // Dial tone: 400Hz, continuous
TONE_JAPAN_BUSY, // Busy tone: 400Hz, 500ms ON, 500ms OFF...
TONE_JAPAN_RADIO_ACK, // Radio path acknowlegment: 400Hz, 1s ON, 2s OFF...
- // UK Supervisory tones
- TONE_UK_RINGTONE, // Ring Tone: A 400Hz + 450Hz tone repeated in a 0.4s on, 0.2s off, 0.4s on, 2.0s off pattern.
+ // GB Supervisory tones
+ TONE_GB_RINGTONE, // Ring Tone: A 400Hz + 450Hz tone repeated in a 0.4s on, 0.2s off, 0.4s on, 2.0s off pattern.
+ // AUSTRALIA Supervisory tones
+ TONE_AUSTRALIA_RINGTONE, // Ring tone: A 400Hz + 450Hz tone repeated in a 0.4s on, 0.2s off, 0.4s on, 2.0s off pattern.
+ TONE_AUSTRALIA_BUSY, // Busy tone: 425 Hz repeated in a 0.375s on, 0.375s off pattern.
+ TONE_AUSTRALIA_CALL_WAITING,// Call waiting tone: 425Hz tone repeated in a 0.2s on, 0.2s off, 0.2s on, 4.4s off pattern.
+ TONE_AUSTRALIA_CONGESTION, // Congestion tone: 425Hz tone repeated in a 0.375s on, 0.375s off pattern
NUM_ALTERNATE_TONES
};
enum region {
ANSI,
JAPAN,
- UK,
+ GB,
+ AUSTRALIA,
CEPT,
NUM_REGIONS
};
diff --git a/include/media/Visualizer.h b/include/media/Visualizer.h
index ec0dad5..7bb9e8b 100644
--- a/include/media/Visualizer.h
+++ b/include/media/Visualizer.h
@@ -95,8 +95,7 @@
// install a callback to receive periodic captures. The capture rate is specified in milliHertz
// and the capture format is according to flags (see callback_flags).
- status_t setCaptureCallBack(capture_cbk_t cbk, void* user, uint32_t flags, uint32_t rate,
- bool force = false);
+ status_t setCaptureCallBack(capture_cbk_t cbk, void* user, uint32_t flags, uint32_t rate);
// set the capture size capture size must be a power of two in the range
// [VISUALIZER_CAPTURE_SIZE_MAX. VISUALIZER_CAPTURE_SIZE_MIN]
diff --git a/include/media/stagefright/ACodec.h b/include/media/stagefright/ACodec.h
index c41d0f0..7b3e71c 100644
--- a/include/media/stagefright/ACodec.h
+++ b/include/media/stagefright/ACodec.h
@@ -315,11 +315,12 @@
status_t handleSetSurface(const sp<Surface> &surface);
status_t setupNativeWindowSizeFormatAndUsage(
- ANativeWindow *nativeWindow /* nonnull */, int *finalUsage /* nonnull */);
+ ANativeWindow *nativeWindow /* nonnull */, int *finalUsage /* nonnull */,
+ bool reconnect);
status_t configureOutputBuffersFromNativeWindow(
OMX_U32 *nBufferCount, OMX_U32 *nBufferSize,
- OMX_U32 *nMinUndequeuedBuffers);
+ OMX_U32 *nMinUndequeuedBuffers, bool preregister);
status_t allocateOutputMetadataBuffers();
status_t submitOutputMetadataBuffer();
void signalSubmitOutputMetadataBufferIfEOS_workaround();
@@ -371,6 +372,10 @@
int32_t width, int32_t height,
OMX_VIDEO_CODINGTYPE compressionFormat, float frameRate = -1.0);
+ // sets |portIndex| port buffer numbers to be |bufferNum|. NOTE: Component could reject
+ // this setting if the |bufferNum| is less than the minimum buffer num of the port.
+ status_t setPortBufferNum(OMX_U32 portIndex, int bufferNum);
+
// gets index or sets it to 0 on error. Returns error from codec.
status_t initDescribeColorAspectsIndex();
@@ -480,13 +485,19 @@
status_t getIntraRefreshPeriod(uint32_t *intraRefreshPeriod);
status_t setIntraRefreshPeriod(uint32_t intraRefreshPeriod, bool inConfigure);
+ // Configures temporal layering based on |msg|. |inConfigure| shall be true iff this is called
+ // during configure() call. on success the configured layering is set in |outputFormat|. If
+ // |outputFormat| is mOutputFormat, it is copied to trigger an output format changed event.
+ status_t configureTemporalLayers(
+ const sp<AMessage> &msg, bool inConfigure, sp<AMessage> &outputFormat);
+
status_t setMinBufferSize(OMX_U32 portIndex, size_t size);
status_t setupMPEG4EncoderParameters(const sp<AMessage> &msg);
status_t setupH263EncoderParameters(const sp<AMessage> &msg);
status_t setupAVCEncoderParameters(const sp<AMessage> &msg);
status_t setupHEVCEncoderParameters(const sp<AMessage> &msg);
- status_t setupVPXEncoderParameters(const sp<AMessage> &msg);
+ status_t setupVPXEncoderParameters(const sp<AMessage> &msg, sp<AMessage> &outputFormat);
status_t verifySupportForProfileAndLevel(int32_t profile, int32_t level);
diff --git a/include/media/stagefright/ColorConverter.h b/include/media/stagefright/ColorConverter.h
index 85ba920..270c809 100644
--- a/include/media/stagefright/ColorConverter.h
+++ b/include/media/stagefright/ColorConverter.h
@@ -70,6 +70,9 @@
status_t convertYUV420Planar(
const BitmapParams &src, const BitmapParams &dst);
+ status_t convertYUV420PlanarUseLibYUV(
+ const BitmapParams &src, const BitmapParams &dst);
+
status_t convertQCOMYUV420SemiPlanar(
const BitmapParams &src, const BitmapParams &dst);
diff --git a/include/media/stagefright/MPEG4Writer.h b/include/media/stagefright/MPEG4Writer.h
index a6901a8..8f0eaa7 100644
--- a/include/media/stagefright/MPEG4Writer.h
+++ b/include/media/stagefright/MPEG4Writer.h
@@ -65,6 +65,7 @@
status_t setGeoData(int latitudex10000, int longitudex10000);
status_t setCaptureRate(float captureFps);
+ status_t setTemporalLayerCount(uint32_t layerCount);
virtual void setStartTimeOffsetMs(int ms) { mStartTimeOffsetMs = ms; }
virtual int32_t getStartTimeOffsetMs() const { return mStartTimeOffsetMs; }
@@ -187,6 +188,7 @@
// Acquire lock before calling these methods
off64_t addSample_l(MediaBuffer *buffer);
off64_t addLengthPrefixedSample_l(MediaBuffer *buffer);
+ off64_t addMultipleLengthPrefixedSamples_l(MediaBuffer *buffer);
bool exceedsFileSizeLimit();
bool use32BitFileOffset() const;
diff --git a/include/media/stagefright/MediaBuffer.h b/include/media/stagefright/MediaBuffer.h
index 4baa68c..2c0ebe7 100644
--- a/include/media/stagefright/MediaBuffer.h
+++ b/include/media/stagefright/MediaBuffer.h
@@ -18,6 +18,8 @@
#define MEDIA_BUFFER_H_
+#include <atomic>
+#include <list>
#include <media/stagefright/foundation/MediaBufferBase.h>
#include <pthread.h>
@@ -60,11 +62,22 @@
explicit MediaBuffer(const sp<ABuffer> &buffer);
- // Decrements the reference count and returns the buffer to its
- // associated MediaBufferGroup if the reference count drops to 0.
+ MediaBuffer(const sp<IMemory> &mem) :
+ MediaBuffer((uint8_t *)mem->pointer() + sizeof(SharedControl), mem->size()) {
+ // delegate and override mMemory
+ mMemory = mem;
+ }
+
+ // If MediaBufferGroup is set, decrement the local reference count;
+ // if the local reference count drops to 0, return the buffer to the
+ // associated MediaBufferGroup.
+ //
+ // If no MediaBufferGroup is set, the local reference count must be zero
+ // when called, whereupon the MediaBuffer is deleted.
virtual void release();
- // Increments the reference count.
+ // Increments the local reference count.
+ // Use only when MediaBufferGroup is set.
virtual void add_ref();
void *data() const;
@@ -89,11 +102,56 @@
// MetaData.
MediaBuffer *clone();
- int refcount() const;
+ // sum of localRefcount() and remoteRefcount()
+ int refcount() const {
+ return localRefcount() + remoteRefcount();
+ }
+
+ int localRefcount() const {
+ return mRefCount;
+ }
+
+ int remoteRefcount() const {
+ if (mMemory.get() == nullptr || mMemory->pointer() == nullptr) return 0;
+ int32_t remoteRefcount =
+ reinterpret_cast<SharedControl *>(mMemory->pointer())->getRemoteRefcount();
+ // Sanity check so that remoteRefCount() is non-negative.
+ return remoteRefcount >= 0 ? remoteRefcount : 0; // do not allow corrupted data.
+ }
+
+ // returns old value
+ int addRemoteRefcount(int32_t value) {
+ if (mMemory.get() == nullptr || mMemory->pointer() == nullptr) return 0;
+ return reinterpret_cast<SharedControl *>(mMemory->pointer())->addRemoteRefcount(value);
+ }
+
+ bool isDeadObject() const {
+ return isDeadObject(mMemory);
+ }
+
+ static bool isDeadObject(const sp<IMemory> &memory) {
+ if (memory.get() == nullptr || memory->pointer() == nullptr) return false;
+ return reinterpret_cast<SharedControl *>(memory->pointer())->isDeadObject();
+ }
+
+ // Sticky on enabling of shared memory MediaBuffers. By default we don't use
+ // shared memory for MediaBuffers, but we enable this for those processes
+ // that export MediaBuffers.
+ static void useSharedMemory() {
+ std::atomic_store_explicit(
+ &mUseSharedMemory, (int_least32_t)1, std::memory_order_seq_cst);
+ }
protected:
+ // true if MediaBuffer is observed (part of a MediaBufferGroup).
+ inline bool isObserved() const {
+ return mObserver != nullptr;
+ }
+
virtual ~MediaBuffer();
+ sp<IMemory> mMemory;
+
private:
friend class MediaBufferGroup;
friend class OMXDecoder;
@@ -105,7 +163,6 @@
void claim();
MediaBufferObserver *mObserver;
- MediaBuffer *mNextBuffer;
int mRefCount;
void *mData;
@@ -119,12 +176,59 @@
MediaBuffer *mOriginal;
- void setNextBuffer(MediaBuffer *buffer);
- MediaBuffer *nextBuffer();
+ static std::atomic_int_least32_t mUseSharedMemory;
MediaBuffer(const MediaBuffer &);
MediaBuffer &operator=(const MediaBuffer &);
- sp<IMemory> mMemory;
+
+ // SharedControl block at the start of IMemory.
+ struct SharedControl {
+ enum {
+ FLAG_DEAD_OBJECT = (1 << 0),
+ };
+
+ // returns old value
+ inline int32_t addRemoteRefcount(int32_t value) {
+ return std::atomic_fetch_add_explicit(
+ &mRemoteRefcount, (int_least32_t)value, std::memory_order_seq_cst);
+ }
+
+ inline int32_t getRemoteRefcount() const {
+ return std::atomic_load_explicit(&mRemoteRefcount, std::memory_order_seq_cst);
+ }
+
+ inline void setRemoteRefcount(int32_t value) {
+ std::atomic_store_explicit(
+ &mRemoteRefcount, (int_least32_t)value, std::memory_order_seq_cst);
+ }
+
+ inline bool isDeadObject() const {
+ return (std::atomic_load_explicit(
+ &mFlags, std::memory_order_seq_cst) & FLAG_DEAD_OBJECT) != 0;
+ }
+
+ inline void setDeadObject() {
+ (void)std::atomic_fetch_or_explicit(
+ &mFlags, (int_least32_t)FLAG_DEAD_OBJECT, std::memory_order_seq_cst);
+ }
+
+ inline void clear() {
+ std::atomic_store_explicit(
+ &mFlags, (int_least32_t)0, std::memory_order_seq_cst);
+ std::atomic_store_explicit(
+ &mRemoteRefcount, (int_least32_t)0, std::memory_order_seq_cst);
+ }
+
+ private:
+ // Caution: atomic_int_fast32_t is 64 bits on LP64.
+ std::atomic_int_least32_t mFlags;
+ std::atomic_int_least32_t mRemoteRefcount;
+ int32_t unused[6]; // additional buffer space
+ };
+
+ inline SharedControl *getSharedControl() const {
+ return reinterpret_cast<SharedControl *>(mMemory->pointer());
+ }
};
} // namespace android
diff --git a/include/media/stagefright/MediaBufferGroup.h b/include/media/stagefright/MediaBufferGroup.h
index 7ca3fa1..3051406 100644
--- a/include/media/stagefright/MediaBufferGroup.h
+++ b/include/media/stagefright/MediaBufferGroup.h
@@ -29,11 +29,17 @@
class MediaBufferGroup : public MediaBufferObserver {
public:
- MediaBufferGroup();
+ MediaBufferGroup(size_t growthLimit = 0);
+
+ // create a media buffer group with preallocated buffers
+ MediaBufferGroup(size_t buffers, size_t buffer_size, size_t growthLimit = 0);
+
~MediaBufferGroup();
void add_buffer(MediaBuffer *buffer);
+ bool has_buffers();
+
// If nonBlocking is false, it blocks until a buffer is available and
// passes it to the caller in *buffer, while returning OK.
// The returned buffer will have a reference count of 1.
@@ -45,7 +51,9 @@
status_t acquire_buffer(
MediaBuffer **buffer, bool nonBlocking = false, size_t requestedSize = 0);
-protected:
+ size_t buffers() const { return mBuffers.size(); }
+
+ // If buffer is nullptr, have acquire_buffer() check for remote release.
virtual void signalBufferReturned(MediaBuffer *buffer);
private:
@@ -53,8 +61,8 @@
Mutex mLock;
Condition mCondition;
-
- MediaBuffer *mFirstBuffer, *mLastBuffer;
+ size_t mGrowthLimit; // Do not automatically grow group larger than this.
+ std::list<MediaBuffer *> mBuffers;
MediaBufferGroup(const MediaBufferGroup &);
MediaBufferGroup &operator=(const MediaBufferGroup &);
diff --git a/include/media/stagefright/MediaCodecSource.h b/include/media/stagefright/MediaCodecSource.h
index cc62786..18b1955 100644
--- a/include/media/stagefright/MediaCodecSource.h
+++ b/include/media/stagefright/MediaCodecSource.h
@@ -49,7 +49,8 @@
bool isVideo() const { return mIsVideo; }
sp<IGraphicBufferProducer> getGraphicBufferProducer();
- void setInputBufferTimeOffset(int64_t timeOffsetUs);
+ status_t setInputBufferTimeOffset(int64_t timeOffsetUs);
+ int64_t getFirstSampleSystemTimeUs();
// MediaSource
virtual status_t start(MetaData *params = NULL);
@@ -79,6 +80,7 @@
kWhatStop,
kWhatPause,
kWhatSetInputBufferTimeOffset,
+ kWhatGetFirstSampleSystemTimeUs,
kWhatStopStalled,
};
@@ -90,6 +92,7 @@
uint32_t flags = 0);
status_t onStart(MetaData *params);
+ void onPause();
status_t init();
status_t initEncoder();
void releaseEncoder();
@@ -123,6 +126,8 @@
List<size_t> mAvailEncoderInputIndices;
List<int64_t> mDecodingTimeQueue; // decoding time (us) for video
int64_t mInputBufferTimeOffsetUs;
+ int64_t mFirstSampleSystemTimeUs;
+ bool mPausePending;
// audio drift time
int64_t mFirstSampleTimeUs;
diff --git a/include/media/stagefright/MetaData.h b/include/media/stagefright/MetaData.h
index be7e5c1..6ba7b32 100644
--- a/include/media/stagefright/MetaData.h
+++ b/include/media/stagefright/MetaData.h
@@ -204,6 +204,8 @@
// transfer Function, value defined by ColorAspects.Transfer.
kKeyColorMatrix = 'cMtx', // int32_t,
// color Matrix, value defined by ColorAspects.MatrixCoeffs.
+ kKeyTemporalLayerId = 'iLyr', // int32_t, temporal layer-id. 0-based (0 => base layer)
+ kKeyTemporalLayerCount = 'cLyr', // int32_t, number of temporal layers encoded
};
enum {
diff --git a/include/media/stagefright/SurfaceUtils.h b/include/media/stagefright/SurfaceUtils.h
index c1a9c0a..13d580c 100644
--- a/include/media/stagefright/SurfaceUtils.h
+++ b/include/media/stagefright/SurfaceUtils.h
@@ -24,9 +24,14 @@
namespace android {
+/**
+ * Configures |nativeWindow| for given |width|x|height|, pixel |format|, |rotation| and |usage|.
+ * If |reconnect| is true, reconnects to the native window before hand.
+ * @return first error encountered, or NO_ERROR on success.
+ */
status_t setNativeWindowSizeFormatAndUsage(
ANativeWindow *nativeWindow /* nonnull */,
- int width, int height, int format, int rotation, int usage);
+ int width, int height, int format, int rotation, int usage, bool reconnect);
status_t pushBlankBuffersToNativeWindow(ANativeWindow *nativeWindow /* nonnull */);
} // namespace android
diff --git a/include/media/stagefright/foundation/AMessage.h b/include/media/stagefright/foundation/AMessage.h
index 04782ce..782f8e6 100644
--- a/include/media/stagefright/foundation/AMessage.h
+++ b/include/media/stagefright/foundation/AMessage.h
@@ -123,6 +123,9 @@
bool findBuffer(const char *name, sp<ABuffer> *buffer) const;
bool findMessage(const char *name, sp<AMessage> *obj) const;
+ // finds any numeric type cast to a float
+ bool findAsFloat(const char *name, float *value) const;
+
bool findRect(
const char *name,
int32_t *left, int32_t *top, int32_t *right, int32_t *bottom) const;
diff --git a/include/private/media/AudioTrackShared.h b/include/private/media/AudioTrackShared.h
index ffdb9b5..7becf57 100644
--- a/include/private/media/AudioTrackShared.h
+++ b/include/private/media/AudioTrackShared.h
@@ -522,6 +522,12 @@
mTimestampMutator.push(timestamp);
}
+ // Flushes the shared ring buffer if the client had requested it using mStreaming.mFlush.
+ // If flush occurs then:
+ // cblk->u.mStreaming.mFront, ServerProxy::mFlush and ServerProxy::mFlushed will be modified
+ // client will be notified via Futex
+ virtual void flushBufferIfNeeded();
+
// Total count of the number of flushed frames since creation (never reset).
virtual int64_t framesFlushed() const { return mFlushed; }
diff --git a/include/radio/Radio.h b/include/radio/Radio.h
index 302bf16..a4dfdd1 100644
--- a/include/radio/Radio.h
+++ b/include/radio/Radio.h
@@ -75,7 +75,7 @@
private:
Radio(radio_handle_t handle,
const sp<RadioCallback>&);
- static const sp<IRadioService>& getRadioService();
+ static const sp<IRadioService> getRadioService();
Mutex mLock;
sp<IRadio> mIRadio;
diff --git a/include/soundtrigger/SoundTrigger.h b/include/soundtrigger/SoundTrigger.h
index bf5e1de..9a05cac 100644
--- a/include/soundtrigger/SoundTrigger.h
+++ b/include/soundtrigger/SoundTrigger.h
@@ -68,7 +68,7 @@
private:
SoundTrigger(sound_trigger_module_handle_t module,
const sp<SoundTriggerCallback>&);
- static const sp<ISoundTriggerHwService>& getSoundTriggerHwService();
+ static const sp<ISoundTriggerHwService> getSoundTriggerHwService();
Mutex mLock;
sp<ISoundTrigger> mISoundTrigger;
diff --git a/media/audioserver/audioserver.rc b/media/audioserver/audioserver.rc
index 2409157..80f78b6 100644
--- a/media/audioserver/audioserver.rc
+++ b/media/audioserver/audioserver.rc
@@ -4,4 +4,4 @@
# media gid needed for /dev/fm (radio) and for /data/misc/media (tee)
group audio camera drmrpc inet media mediadrm net_bt net_bt_admin net_bw_acct
ioprio rt 4
- writepid /dev/cpuset/foreground/tasks
+ writepid /dev/cpuset/foreground/tasks /dev/stune/foreground/tasks
diff --git a/media/common_time/Android.mk b/media/common_time/Android.mk
index 632acbc..aaa0db2 100644
--- a/media/common_time/Android.mk
+++ b/media/common_time/Android.mk
@@ -19,4 +19,6 @@
libutils \
liblog
+LOCAL_CFLAGS := -Wall -Werror
+
include $(BUILD_SHARED_LIBRARY)
diff --git a/media/libaudioclient/AudioRecord.cpp b/media/libaudioclient/AudioRecord.cpp
index d9bb856..ff5903d 100644
--- a/media/libaudioclient/AudioRecord.cpp
+++ b/media/libaudioclient/AudioRecord.cpp
@@ -593,11 +593,10 @@
size_t notificationFrames = mNotificationFramesReq;
size_t frameCount = mReqFrameCount;
- IAudioFlinger::track_flags_t trackFlags = IAudioFlinger::TRACK_DEFAULT;
+ audio_input_flags_t flags = mFlags;
pid_t tid = -1;
if (mFlags & AUDIO_INPUT_FLAG_FAST) {
- trackFlags |= IAudioFlinger::TRACK_FAST;
if (mAudioRecordThread != 0) {
tid = mAudioRecordThread->getTid();
}
@@ -615,7 +614,7 @@
mChannelMask,
opPackageName,
&temp,
- &trackFlags,
+ &flags,
mClientPid,
tid,
mClientUid,
@@ -638,7 +637,7 @@
mAwaitBoost = false;
if (mFlags & AUDIO_INPUT_FLAG_FAST) {
- if (trackFlags & IAudioFlinger::TRACK_FAST) {
+ if (flags & AUDIO_INPUT_FLAG_FAST) {
ALOGI("AUDIO_INPUT_FLAG_FAST successful; frameCount %zu", frameCount);
mAwaitBoost = true;
} else {
@@ -648,6 +647,7 @@
continue; // retry
}
}
+ mFlags = flags;
if (iMem == 0) {
ALOGE("Could not get control block");
@@ -891,6 +891,9 @@
if (read > 0) {
break;
}
+ if (err == TIMED_OUT || err == -EINTR) {
+ err = WOULD_BLOCK;
+ }
return ssize_t(err);
}
@@ -921,7 +924,7 @@
int32_t tryCounter = kMaxTries;
uint32_t pollUs = 10000;
do {
- int policy = sched_getscheduler(0);
+ int policy = sched_getscheduler(0) & ~SCHED_RESET_ON_FORK;
if (policy == SCHED_FIFO || policy == SCHED_RR) {
break;
}
diff --git a/media/libaudioclient/AudioTrack.cpp b/media/libaudioclient/AudioTrack.cpp
index b2fc960..c96f16a 100644
--- a/media/libaudioclient/AudioTrack.cpp
+++ b/media/libaudioclient/AudioTrack.cpp
@@ -1372,24 +1372,15 @@
}
}
- IAudioFlinger::track_flags_t trackFlags = IAudioFlinger::TRACK_DEFAULT;
+ audio_output_flags_t flags = mFlags;
pid_t tid = -1;
if (mFlags & AUDIO_OUTPUT_FLAG_FAST) {
- trackFlags |= IAudioFlinger::TRACK_FAST;
if (mAudioTrackThread != 0 && !mThreadCanCallJava) {
tid = mAudioTrackThread->getTid();
}
}
- if (mFlags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) {
- trackFlags |= IAudioFlinger::TRACK_OFFLOAD;
- }
-
- if (mFlags & AUDIO_OUTPUT_FLAG_DIRECT) {
- trackFlags |= IAudioFlinger::TRACK_DIRECT;
- }
-
size_t temp = frameCount; // temp may be replaced by a revised value of frameCount,
// but we will still need the original value also
audio_session_t originalSessionId = mSessionId;
@@ -1398,7 +1389,7 @@
mFormat,
mChannelMask,
&temp,
- &trackFlags,
+ &flags,
mSharedBuffer,
output,
mClientPid,
@@ -1451,23 +1442,23 @@
mAwaitBoost = false;
if (mFlags & AUDIO_OUTPUT_FLAG_FAST) {
- if (trackFlags & IAudioFlinger::TRACK_FAST) {
+ if (flags & AUDIO_OUTPUT_FLAG_FAST) {
ALOGV("AUDIO_OUTPUT_FLAG_FAST successful; frameCount %zu", frameCount);
if (!mThreadCanCallJava) {
mAwaitBoost = true;
}
} else {
ALOGW("AUDIO_OUTPUT_FLAG_FAST denied by server; frameCount %zu", frameCount);
- mFlags = (audio_output_flags_t) (mFlags & ~AUDIO_OUTPUT_FLAG_FAST);
}
}
+ mFlags = flags;
// Make sure that application is notified with sufficient margin before underrun.
// The client can divide the AudioTrack buffer into sub-buffers,
// and expresses its desire to server as the notification frame count.
if (mSharedBuffer == 0 && audio_is_linear_pcm(mFormat)) {
size_t maxNotificationFrames;
- if (trackFlags & IAudioFlinger::TRACK_FAST) {
+ if (mFlags & AUDIO_OUTPUT_FLAG_FAST) {
// notify every HAL buffer, regardless of the size of the track buffer
maxNotificationFrames = afFrameCountHAL;
} else {
@@ -1748,6 +1739,9 @@
if (written > 0) {
break;
}
+ if (err == TIMED_OUT || err == -EINTR) {
+ err = WOULD_BLOCK;
+ }
return ssize_t(err);
}
@@ -1783,7 +1777,7 @@
int32_t tryCounter = kMaxTries;
uint32_t pollUs = 10000;
do {
- int policy = sched_getscheduler(0);
+ int policy = sched_getscheduler(0) & ~SCHED_RESET_ON_FORK;
if (policy == SCHED_FIFO || policy == SCHED_RR) {
break;
}
diff --git a/media/libaudioclient/AudioTrackShared.cpp b/media/libaudioclient/AudioTrackShared.cpp
index 7119517..846f8b8 100644
--- a/media/libaudioclient/AudioTrackShared.cpp
+++ b/media/libaudioclient/AudioTrackShared.cpp
@@ -622,6 +622,56 @@
}
__attribute__((no_sanitize("integer")))
+void ServerProxy::flushBufferIfNeeded()
+{
+ audio_track_cblk_t* cblk = mCblk;
+ // The acquire_load is not really required. But since the write is a release_store in the
+ // client, using acquire_load here makes it easier for people to maintain the code,
+ // and the logic for communicating ipc variables seems somewhat standard,
+ // and there really isn't much penalty for 4 or 8 byte atomics.
+ int32_t flush = android_atomic_acquire_load(&cblk->u.mStreaming.mFlush);
+ if (flush != mFlush) {
+ ALOGV("ServerProxy::flushBufferIfNeeded() mStreaming.mFlush = 0x%x, mFlush = 0x%0x",
+ flush, mFlush);
+ int32_t rear = android_atomic_acquire_load(&cblk->u.mStreaming.mRear);
+ int32_t front = cblk->u.mStreaming.mFront;
+
+ // effectively obtain then release whatever is in the buffer
+ const size_t overflowBit = mFrameCountP2 << 1;
+ const size_t mask = overflowBit - 1;
+ int32_t newFront = (front & ~mask) | (flush & mask);
+ ssize_t filled = rear - newFront;
+ if (filled >= (ssize_t)overflowBit) {
+ // front and rear offsets span the overflow bit of the p2 mask
+ // so rebasing newFront on the front offset is off by the overflow bit.
+ // adjust newFront to match rear offset.
+ ALOGV("flush wrap: filled %zx >= overflowBit %zx", filled, overflowBit);
+ newFront += overflowBit;
+ filled -= overflowBit;
+ }
+ // Rather than shutting down on a corrupt flush, just treat it as a full flush
+ if (!(0 <= filled && (size_t) filled <= mFrameCount)) {
+ ALOGE("mFlush %#x -> %#x, front %#x, rear %#x, mask %#x, newFront %#x, "
+ "filled %zd=%#x",
+ mFlush, flush, front, rear,
+ (unsigned)mask, newFront, filled, (unsigned)filled);
+ newFront = rear;
+ }
+ mFlush = flush;
+ android_atomic_release_store(newFront, &cblk->u.mStreaming.mFront);
+ // There is no danger from a false positive, so err on the side of caution
+ if (true /*front != newFront*/) {
+ int32_t old = android_atomic_or(CBLK_FUTEX_WAKE, &cblk->mFutex);
+ if (!(old & CBLK_FUTEX_WAKE)) {
+ (void) syscall(__NR_futex, &cblk->mFutex,
+ mClientInServer ? FUTEX_WAKE_PRIVATE : FUTEX_WAKE, 1);
+ }
+ }
+ mFlushed += (newFront - front) & mask;
+ }
+}
+
+__attribute__((no_sanitize("integer")))
status_t ServerProxy::obtainBuffer(Buffer* buffer, bool ackFlush)
{
LOG_ALWAYS_FATAL_IF(buffer == NULL || buffer->mFrameCount == 0);
@@ -636,44 +686,9 @@
int32_t rear;
// See notes on barriers at ClientProxy::obtainBuffer()
if (mIsOut) {
- int32_t flush = cblk->u.mStreaming.mFlush;
+ flushBufferIfNeeded(); // might modify mFront
rear = android_atomic_acquire_load(&cblk->u.mStreaming.mRear);
front = cblk->u.mStreaming.mFront;
- if (flush != mFlush) {
- // effectively obtain then release whatever is in the buffer
- const size_t overflowBit = mFrameCountP2 << 1;
- const size_t mask = overflowBit - 1;
- int32_t newFront = (front & ~mask) | (flush & mask);
- ssize_t filled = rear - newFront;
- if (filled >= (ssize_t)overflowBit) {
- // front and rear offsets span the overflow bit of the p2 mask
- // so rebasing newFront on the front offset is off by the overflow bit.
- // adjust newFront to match rear offset.
- ALOGV("flush wrap: filled %zx >= overflowBit %zx", filled, overflowBit);
- newFront += overflowBit;
- filled -= overflowBit;
- }
- // Rather than shutting down on a corrupt flush, just treat it as a full flush
- if (!(0 <= filled && (size_t) filled <= mFrameCount)) {
- ALOGE("mFlush %#x -> %#x, front %#x, rear %#x, mask %#x, newFront %#x, "
- "filled %zd=%#x",
- mFlush, flush, front, rear,
- (unsigned)mask, newFront, filled, (unsigned)filled);
- newFront = rear;
- }
- mFlush = flush;
- android_atomic_release_store(newFront, &cblk->u.mStreaming.mFront);
- // There is no danger from a false positive, so err on the side of caution
- if (true /*front != newFront*/) {
- int32_t old = android_atomic_or(CBLK_FUTEX_WAKE, &cblk->mFutex);
- if (!(old & CBLK_FUTEX_WAKE)) {
- (void) syscall(__NR_futex, &cblk->mFutex,
- mClientInServer ? FUTEX_WAKE_PRIVATE : FUTEX_WAKE, 1);
- }
- }
- mFlushed += (newFront - front) & mask;
- front = newFront;
- }
} else {
front = android_atomic_acquire_load(&cblk->u.mStreaming.mFront);
rear = cblk->u.mStreaming.mRear;
diff --git a/media/libaudioclient/IAudioFlinger.cpp b/media/libaudioclient/IAudioFlinger.cpp
index 4fc3c9f..65fdedb 100644
--- a/media/libaudioclient/IAudioFlinger.cpp
+++ b/media/libaudioclient/IAudioFlinger.cpp
@@ -101,7 +101,7 @@
audio_format_t format,
audio_channel_mask_t channelMask,
size_t *pFrameCount,
- track_flags_t *flags,
+ audio_output_flags_t *flags,
const sp<IMemory>& sharedBuffer,
audio_io_handle_t output,
pid_t pid,
@@ -119,7 +119,7 @@
data.writeInt32(channelMask);
size_t frameCount = pFrameCount != NULL ? *pFrameCount : 0;
data.writeInt64(frameCount);
- track_flags_t lFlags = flags != NULL ? *flags : (track_flags_t) TRACK_DEFAULT;
+ audio_output_flags_t lFlags = flags != NULL ? *flags : AUDIO_OUTPUT_FLAG_NONE;
data.writeInt32(lFlags);
// haveSharedBuffer
if (sharedBuffer != 0) {
@@ -145,7 +145,7 @@
if (pFrameCount != NULL) {
*pFrameCount = frameCount;
}
- lFlags = reply.readInt32();
+ lFlags = (audio_output_flags_t)reply.readInt32();
if (flags != NULL) {
*flags = lFlags;
}
@@ -180,7 +180,7 @@
audio_channel_mask_t channelMask,
const String16& opPackageName,
size_t *pFrameCount,
- track_flags_t *flags,
+ audio_input_flags_t *flags,
pid_t pid,
pid_t tid,
int clientUid,
@@ -200,7 +200,7 @@
data.writeString16(opPackageName);
size_t frameCount = pFrameCount != NULL ? *pFrameCount : 0;
data.writeInt64(frameCount);
- track_flags_t lFlags = flags != NULL ? *flags : (track_flags_t) TRACK_DEFAULT;
+ audio_input_flags_t lFlags = flags != NULL ? *flags : AUDIO_INPUT_FLAG_NONE;
data.writeInt32(lFlags);
data.writeInt32((int32_t) pid);
data.writeInt32((int32_t) tid);
@@ -221,7 +221,7 @@
if (pFrameCount != NULL) {
*pFrameCount = frameCount;
}
- lFlags = reply.readInt32();
+ lFlags = (audio_input_flags_t)reply.readInt32();
if (flags != NULL) {
*flags = lFlags;
}
@@ -947,7 +947,7 @@
audio_format_t format = (audio_format_t) data.readInt32();
audio_channel_mask_t channelMask = data.readInt32();
size_t frameCount = data.readInt64();
- track_flags_t flags = (track_flags_t) data.readInt32();
+ audio_output_flags_t flags = (audio_output_flags_t) data.readInt32();
bool haveSharedBuffer = data.readInt32() != 0;
sp<IMemory> buffer;
if (haveSharedBuffer) {
@@ -986,7 +986,7 @@
audio_channel_mask_t channelMask = data.readInt32();
const String16& opPackageName = data.readString16();
size_t frameCount = data.readInt64();
- track_flags_t flags = (track_flags_t) data.readInt32();
+ audio_input_flags_t flags = (audio_input_flags_t) data.readInt32();
pid_t pid = (pid_t) data.readInt32();
pid_t tid = (pid_t) data.readInt32();
int clientUid = data.readInt32();
diff --git a/media/libaudioclient/ToneGenerator.cpp b/media/libaudioclient/ToneGenerator.cpp
index 612d174..7a72237 100644
--- a/media/libaudioclient/ToneGenerator.cpp
+++ b/media/libaudioclient/ToneGenerator.cpp
@@ -746,10 +746,31 @@
{ .duration = 2000, .waveFreq = { 0 }, 0, 0},
{ .duration = 0, .waveFreq = { 0 }, 0, 0}},
.repeatCnt = ToneGenerator::TONEGEN_INF,
- .repeatSegment = 0 }, // TONE_UK_RINGTONE
-
-
-
+ .repeatSegment = 0 }, // TONE_GB_RINGTONE
+ { .segments = { { .duration = 400, .waveFreq = { 400, 450, 0 }, 0, 0 },
+ { .duration = 200, .waveFreq = { 0 }, 0, 0 },
+ { .duration = 400, .waveFreq = { 400, 450, 0 }, 0, 0 },
+ { .duration = 2000, .waveFreq = { 0 }, 0, 0},
+ { .duration = 0, .waveFreq = { 0 }, 0, 0}},
+ .repeatCnt = ToneGenerator::TONEGEN_INF,
+ .repeatSegment = 0 }, // TONE_AUSTRALIA_RINGTONE
+ { .segments = { { .duration = 375, .waveFreq = { 425, 0 }, 0, 0 },
+ { .duration = 375, .waveFreq = { 0 }, 0, 0 },
+ { .duration = 0 , .waveFreq = { 0 }, 0, 0}},
+ .repeatCnt = ToneGenerator::TONEGEN_INF,
+ .repeatSegment = 0 }, // TONE_AUSTRALIA_BUSY
+ { .segments = { { .duration = 200, .waveFreq = { 425, 0 }, 0, 0 },
+ { .duration = 200, .waveFreq = { 0 }, 0, 0 },
+ { .duration = 200, .waveFreq = { 425, 0 }, 0, 0 },
+ { .duration = 4400, .waveFreq = { 0 }, 0, 0 },
+ { .duration = 0 , .waveFreq = { 0 }, 0, 0}},
+ .repeatCnt = ToneGenerator::TONEGEN_INF,
+ .repeatSegment = 0 }, // TONE_AUSTRALIA_CALL_WAITING
+ { .segments = { { .duration = 375, .waveFreq = { 425, 0 }, 0, 0 },
+ { .duration = 375, .waveFreq = { 0 }, 0, 0 },
+ { .duration = 0 , .waveFreq = { 0 }, 0, 0}},
+ .repeatCnt = ToneGenerator::TONEGEN_INF,
+ .repeatSegment = 0 }, // TONE_AUSTRALIA_CONGESTION
};
// Used by ToneGenerator::getToneForRegion() to convert user specified supervisory tone type
@@ -775,7 +796,7 @@
TONE_SUP_CALL_WAITING, // TONE_SUP_CALL_WAITING
TONE_SUP_RINGTONE // TONE_SUP_RINGTONE
},
- { // UK
+ { // GB
TONE_SUP_DIAL, // TONE_SUP_DIAL
TONE_SUP_BUSY, // TONE_SUP_BUSY
TONE_SUP_CONGESTION, // TONE_SUP_CONGESTION
@@ -783,9 +804,18 @@
TONE_SUP_RADIO_NOTAVAIL, // TONE_SUP_RADIO_NOTAVAIL
TONE_SUP_ERROR, // TONE_SUP_ERROR
TONE_SUP_CALL_WAITING, // TONE_SUP_CALL_WAITING
- TONE_UK_RINGTONE // TONE_SUP_RINGTONE
+ TONE_GB_RINGTONE // TONE_SUP_RINGTONE
+ },
+ { // AUSTRALIA
+ TONE_ANSI_DIAL, // TONE_SUP_DIAL
+ TONE_AUSTRALIA_BUSY, // TONE_SUP_BUSY
+ TONE_AUSTRALIA_CONGESTION, // TONE_SUP_CONGESTION
+ TONE_SUP_RADIO_ACK, // TONE_SUP_RADIO_ACK
+ TONE_SUP_RADIO_NOTAVAIL, // TONE_SUP_RADIO_NOTAVAIL
+ TONE_SUP_ERROR, // TONE_SUP_ERROR
+ TONE_AUSTRALIA_CALL_WAITING,// TONE_SUP_CALL_WAITING
+ TONE_AUSTRALIA_RINGTONE // TONE_SUP_RINGTONE
}
-
};
@@ -839,9 +869,10 @@
mRegion = ANSI;
} else if (strstr(value, "jp") != NULL) {
mRegion = JAPAN;
- } else if (strcmp(value,"uk") == 0 ||
- strcmp(value,"uk,uk") == 0) {
- mRegion = UK;
+ } else if (strstr(value, "gb") != NULL) {
+ mRegion = GB;
+ } else if (strstr(value, "au") != NULL) {
+ mRegion = AUSTRALIA;
} else {
mRegion = CEPT;
}
@@ -1064,47 +1095,37 @@
//
////////////////////////////////////////////////////////////////////////////////
bool ToneGenerator::initAudioTrack() {
-
- // Open audio track in mono, PCM 16bit, default sampling rate, default buffer size
+ // Open audio track in mono, PCM 16bit, default sampling rate.
mpAudioTrack = new AudioTrack();
- ALOGV("Create Track: %p", mpAudioTrack.get());
+ ALOGV("AudioTrack(%p) created", mpAudioTrack.get());
- mpAudioTrack->set(mStreamType,
- 0, // sampleRate
- AUDIO_FORMAT_PCM_16_BIT,
- AUDIO_CHANNEL_OUT_MONO,
- 0, // frameCount
- AUDIO_OUTPUT_FLAG_FAST,
- audioCallback,
- this, // user
- 0, // notificationFrames
- 0, // sharedBuffer
- mThreadCanCallJava,
- AUDIO_SESSION_ALLOCATE,
- AudioTrack::TRANSFER_CALLBACK);
+ const size_t frameCount = mProcessSize;
+ status_t status = mpAudioTrack->set(
+ mStreamType,
+ 0, // sampleRate
+ AUDIO_FORMAT_PCM_16_BIT,
+ AUDIO_CHANNEL_OUT_MONO,
+ frameCount,
+ AUDIO_OUTPUT_FLAG_FAST,
+ audioCallback,
+ this, // user
+ 0, // notificationFrames
+ 0, // sharedBuffer
+ mThreadCanCallJava,
+ AUDIO_SESSION_ALLOCATE,
+ AudioTrack::TRANSFER_CALLBACK);
- if (mpAudioTrack->initCheck() != NO_ERROR) {
- ALOGE("AudioTrack->initCheck failed");
- goto initAudioTrack_exit;
+ if (status != NO_ERROR) {
+ ALOGE("AudioTrack(%p) set failed with error %d", mpAudioTrack.get(), status);
+ mpAudioTrack.clear();
+ return false;
}
mpAudioTrack->setVolume(mVolume);
-
mState = TONE_INIT;
-
return true;
-
-initAudioTrack_exit:
-
- ALOGV("Init failed: %p", mpAudioTrack.get());
-
- // Cleanup
- mpAudioTrack.clear();
-
- return false;
}
-
////////////////////////////////////////////////////////////////////////////////
//
// Method: ToneGenerator::audioCallback()
diff --git a/media/libeffects/visualizer/EffectVisualizer.cpp b/media/libeffects/visualizer/EffectVisualizer.cpp
index 0c310c5..21fddb1 100644
--- a/media/libeffects/visualizer/EffectVisualizer.cpp
+++ b/media/libeffects/visualizer/EffectVisualizer.cpp
@@ -25,6 +25,7 @@
#include <time.h>
#include <math.h>
#include <audio_effects/effect_visualizer.h>
+#include <cutils/log.h>
extern "C" {
@@ -599,6 +600,19 @@
} break;
case VISUALIZER_CMD_MEASURE: {
+ if (pReplyData == NULL || replySize == NULL ||
+ *replySize < (sizeof(int32_t) * MEASUREMENT_COUNT)) {
+ if (replySize == NULL) {
+ ALOGV("VISUALIZER_CMD_MEASURE() error replySize NULL");
+ } else {
+ ALOGV("VISUALIZER_CMD_MEASURE() error *replySize %" PRIu32
+ " < (sizeof(int32_t) * MEASUREMENT_COUNT) %" PRIu32,
+ *replySize,
+ uint32_t(sizeof(int32_t)) * MEASUREMENT_COUNT);
+ }
+ android_errorWriteLog(0x534e4554, "30229821");
+ return -EINVAL;
+ }
uint16_t peakU16 = 0;
float sumRmsSquared = 0.0f;
uint8_t nbValidMeasurements = 0;
diff --git a/media/libmedia/ICrypto.cpp b/media/libmedia/ICrypto.cpp
index ee7f757..7b261be 100644
--- a/media/libmedia/ICrypto.cpp
+++ b/media/libmedia/ICrypto.cpp
@@ -150,10 +150,10 @@
if (isCryptoError(result)) {
errorDetailMsg->setTo(reply.readCString());
- }
-
- if (dstType == kDestinationTypeVmPointer && result >= 0) {
- reply.read(dstPtr, result);
+ } else if (dstType == kDestinationTypeVmPointer) {
+ // For the non-secure case, copy the decrypted
+ // data from shared memory to its final destination
+ memcpy(dstPtr, sharedBuffer->pointer(), result);
}
return result;
@@ -369,7 +369,11 @@
if (dstType == kDestinationTypeVmPointer) {
if (result >= 0) {
CHECK_LE(result, static_cast<ssize_t>(totalSize));
- reply->write(dstPtr, result);
+ // For the non-secure case, pass the decrypted
+ // data back via the shared buffer rather than
+ // copying it separately over binder to avoid
+ // binder's 1MB limit.
+ memcpy(sharedBuffer->pointer(), dstPtr, result);
}
free(dstPtr);
dstPtr = NULL;
diff --git a/media/libmedia/IDataSource.cpp b/media/libmedia/IDataSource.cpp
index 9da1a70..7df3b65 100644
--- a/media/libmedia/IDataSource.cpp
+++ b/media/libmedia/IDataSource.cpp
@@ -23,6 +23,7 @@
#include <binder/IMemory.h>
#include <binder/Parcel.h>
+#include <drm/drm_framework_common.h>
#include <media/stagefright/foundation/ADebug.h>
namespace android {
@@ -34,6 +35,7 @@
CLOSE,
GET_FLAGS,
TO_STRING,
+ DRM_INITIALIZATION,
};
struct BpDataSource : public BpInterface<IDataSource> {
@@ -85,6 +87,47 @@
remote()->transact(TO_STRING, data, &reply);
return reply.readString8();
}
+
+ virtual sp<DecryptHandle> DrmInitialization(const char *mime) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IDataSource::getInterfaceDescriptor());
+ if (mime == NULL) {
+ data.writeInt32(0);
+ } else {
+ data.writeInt32(1);
+ data.writeCString(mime);
+ }
+ remote()->transact(DRM_INITIALIZATION, data, &reply);
+ sp<DecryptHandle> handle;
+ if (reply.dataAvail() != 0) {
+ handle = new DecryptHandle();
+ handle->decryptId = reply.readInt32();
+ handle->mimeType = reply.readString8();
+ handle->decryptApiType = reply.readInt32();
+ handle->status = reply.readInt32();
+
+ const int bufferLength = data.readInt32();
+ if (bufferLength != -1) {
+ handle->decryptInfo = new DecryptInfo();
+ handle->decryptInfo->decryptBufferLength = bufferLength;
+ }
+
+ size_t size = data.readInt32();
+ for (size_t i = 0; i < size; ++i) {
+ DrmCopyControl key = (DrmCopyControl)data.readInt32();
+ int value = data.readInt32();
+ handle->copyControlVector.add(key, value);
+ }
+
+ size = data.readInt32();
+ for (size_t i = 0; i < size; ++i) {
+ String8 key = data.readString8();
+ String8 value = data.readString8();
+ handle->extendedData.add(key, value);
+ }
+ }
+ return handle;
+ }
};
IMPLEMENT_META_INTERFACE(DataSource, "android.media.IDataSource");
@@ -127,6 +170,42 @@
reply->writeString8(toString());
return NO_ERROR;
} break;
+ case DRM_INITIALIZATION: {
+ CHECK_INTERFACE(IDataSource, data, reply);
+ const char *mime = NULL;
+ const int32_t flag = data.readInt32();
+ if (flag != 0) {
+ mime = data.readCString();
+ }
+ sp<DecryptHandle> handle = DrmInitialization(mime);
+ if (handle != NULL) {
+ reply->writeInt32(handle->decryptId);
+ reply->writeString8(handle->mimeType);
+ reply->writeInt32(handle->decryptApiType);
+ reply->writeInt32(handle->status);
+
+ if (handle->decryptInfo != NULL) {
+ reply->writeInt32(handle->decryptInfo->decryptBufferLength);
+ } else {
+ reply->writeInt32(-1);
+ }
+
+ size_t size = handle->copyControlVector.size();
+ reply->writeInt32(size);
+ for (size_t i = 0; i < size; ++i) {
+ reply->writeInt32(handle->copyControlVector.keyAt(i));
+ reply->writeInt32(handle->copyControlVector.valueAt(i));
+ }
+
+ size = handle->extendedData.size();
+ reply->writeInt32(size);
+ for (size_t i = 0; i < size; ++i) {
+ reply->writeString8(handle->extendedData.keyAt(i));
+ reply->writeString8(handle->extendedData.valueAt(i));
+ }
+ }
+ return NO_ERROR;
+ } break;
default:
return BBinder::onTransact(code, data, reply, flags);
diff --git a/media/libmedia/IDrm.cpp b/media/libmedia/IDrm.cpp
index d2b3aae..c4558c6 100644
--- a/media/libmedia/IDrm.cpp
+++ b/media/libmedia/IDrm.cpp
@@ -891,7 +891,7 @@
readVector(data, keyId);
readVector(data, message);
readVector(data, signature);
- bool match;
+ bool match = false;
uint32_t result = verify(sessionId, keyId, message, signature, match);
reply->writeInt32(match);
reply->writeInt32(result);
diff --git a/media/libmedia/IMediaExtractor.cpp b/media/libmedia/IMediaExtractor.cpp
index e8ad75b..72d1d7c 100644
--- a/media/libmedia/IMediaExtractor.cpp
+++ b/media/libmedia/IMediaExtractor.cpp
@@ -160,6 +160,9 @@
if (data.readUint32(&idx) == NO_ERROR &&
data.readUint32(&flags) == NO_ERROR) {
sp<MetaData> meta = getTrackMetaData(idx, flags);
+ if (meta == NULL) {
+ return UNKNOWN_ERROR;
+ }
meta->writeToParcel(*reply);
return NO_ERROR;
}
diff --git a/media/libmedia/IMediaExtractorService.cpp b/media/libmedia/IMediaExtractorService.cpp
index dcbbde2..d170c22 100644
--- a/media/libmedia/IMediaExtractorService.cpp
+++ b/media/libmedia/IMediaExtractorService.cpp
@@ -71,6 +71,9 @@
ALOGE("Error reading source from parcel");
return ret;
}
+ // If we make an extractor through Binder, enabled shared memory
+ // for MediaBuffers for this process.
+ MediaBuffer::useSharedMemory();
sp<IDataSource> source = interface_cast<IDataSource>(b);
const char *mime = data.readCString();
sp<IMediaExtractor> ex = makeExtractor(source, mime);
diff --git a/media/libmedia/IMediaSource.cpp b/media/libmedia/IMediaSource.cpp
index 7e40e4f..595bad9 100644
--- a/media/libmedia/IMediaSource.cpp
+++ b/media/libmedia/IMediaSource.cpp
@@ -36,79 +36,39 @@
STOP,
PAUSE,
GETFORMAT,
- READ,
+ // READ, deprecated
READMULTIPLE,
- RELEASE_BUFFER
+ RELEASE_BUFFER,
+ SUPPORT_NONBLOCKING_READ,
};
enum {
NULL_BUFFER,
SHARED_BUFFER,
- INLINE_BUFFER
+ INLINE_BUFFER,
+ SHARED_BUFFER_INDEX,
};
-class RemoteMediaBufferReleaser : public BBinder {
-public:
- RemoteMediaBufferReleaser(MediaBuffer *buf, sp<BnMediaSource> owner) {
- mBuf = buf;
- mOwner = owner;
- }
- ~RemoteMediaBufferReleaser() {
- if (mBuf) {
- ALOGW("RemoteMediaBufferReleaser dtor called while still holding buffer");
- mBuf->release();
- }
- }
- virtual status_t onTransact( uint32_t code,
- const Parcel& data,
- Parcel* reply,
- uint32_t flags = 0) {
- if (code == RELEASE_BUFFER) {
- mBuf->release();
- mBuf = NULL;
- return OK;
- } else {
- return BBinder::onTransact(code, data, reply, flags);
- }
- }
-private:
- MediaBuffer *mBuf;
- // Keep a ref to ensure MediaBuffer is released before the owner, i.e., BnMediaSource,
- // because BnMediaSource needs to delete MediaBufferGroup in its dtor and
- // MediaBufferGroup dtor requires all MediaBuffer's have 0 ref count.
- sp<BnMediaSource> mOwner;
-};
-
-
class RemoteMediaBufferWrapper : public MediaBuffer {
public:
- RemoteMediaBufferWrapper(sp<IMemory> mem, sp<IBinder> source);
+ RemoteMediaBufferWrapper(const sp<IMemory> &mem)
+ : MediaBuffer(mem) {
+ ALOGV("RemoteMediaBufferWrapper: creating %p", this);
+ }
+
protected:
- virtual ~RemoteMediaBufferWrapper();
-private:
- sp<IMemory> mMemory;
- sp<IBinder> mRemoteSource;
+ virtual ~RemoteMediaBufferWrapper() {
+ // Release our interest in the MediaBuffer's shared memory.
+ int32_t old = addRemoteRefcount(-1);
+ ALOGV("RemoteMediaBufferWrapper: releasing %p, refcount %d", this, old - 1);
+ mMemory.clear(); // don't set the dead object flag.
+ }
};
-RemoteMediaBufferWrapper::RemoteMediaBufferWrapper(sp<IMemory> mem, sp<IBinder> source)
-: MediaBuffer(mem->pointer(), mem->size()) {
- mMemory = mem;
- mRemoteSource = source;
-}
-
-RemoteMediaBufferWrapper::~RemoteMediaBufferWrapper() {
- mMemory.clear();
- // Explicitly ask the remote side to release the buffer. We could also just clear
- // mRemoteSource, but that doesn't immediately release the reference on the remote side.
- Parcel data, reply;
- mRemoteSource->transact(RELEASE_BUFFER, data, &reply);
- mRemoteSource.clear();
-}
-
class BpMediaSource : public BpInterface<IMediaSource> {
public:
BpMediaSource(const sp<IBinder>& impl)
- : BpInterface<IMediaSource>(impl)
+ : BpInterface<IMediaSource>(impl), mBuffersSinceStop(0)
{
}
@@ -135,7 +95,10 @@
ALOGV("stop");
Parcel data, reply;
data.writeInterfaceToken(BpMediaSource::getInterfaceDescriptor());
- return remote()->transact(STOP, data, &reply);
+ status_t status = remote()->transact(STOP, data, &reply);
+ mMemoryCache.reset();
+ mBuffersSinceStop = 0;
+ return status;
}
virtual sp<MetaData> getFormat() {
@@ -151,46 +114,16 @@
}
virtual status_t read(MediaBuffer **buffer, const ReadOptions *options) {
- ALOGV("read");
- Parcel data, reply;
- data.writeInterfaceToken(BpMediaSource::getInterfaceDescriptor());
- if (options) {
- data.writeByteArray(sizeof(*options), (uint8_t*) options);
- }
- status_t ret = remote()->transact(READ, data, &reply);
- if (ret != NO_ERROR) {
- return ret;
- }
- // wrap the returned data in a MediaBuffer
- ret = reply.readInt32();
- int32_t buftype = reply.readInt32();
- if (buftype == SHARED_BUFFER) {
- sp<IBinder> remote = reply.readStrongBinder();
- sp<IBinder> binder = reply.readStrongBinder();
- sp<IMemory> mem = interface_cast<IMemory>(binder);
- if (mem == NULL) {
- ALOGE("received NULL IMemory for shared buffer");
- }
- size_t offset = reply.readInt32();
- size_t length = reply.readInt32();
- MediaBuffer *buf = new RemoteMediaBufferWrapper(mem, remote);
- buf->set_range(offset, length);
- buf->meta_data()->updateFromParcel(reply);
- *buffer = buf;
- } else if (buftype == NULL_BUFFER) {
- ALOGV("got status %d and NULL buffer", ret);
- *buffer = NULL;
- } else {
- int32_t len = reply.readInt32();
- ALOGV("got status %d and len %d", ret, len);
- *buffer = new MediaBuffer(len);
- reply.read((*buffer)->data(), len);
- (*buffer)->meta_data()->updateFromParcel(reply);
- }
+ Vector<MediaBuffer *> buffers;
+ status_t ret = readMultiple(&buffers, 1 /* maxNumBuffers */, options);
+ *buffer = buffers.size() == 0 ? nullptr : buffers[0];
+ ALOGV("read status %d, bufferCount %u, sinceStop %u",
+ ret, *buffer != nullptr, mBuffersSinceStop);
return ret;
}
- virtual status_t readMultiple(Vector<MediaBuffer *> *buffers, uint32_t maxNumBuffers) {
+ virtual status_t readMultiple(
+ Vector<MediaBuffer *> *buffers, uint32_t maxNumBuffers, const ReadOptions *options) {
ALOGV("readMultiple");
if (buffers == NULL || !buffers->isEmpty()) {
return BAD_VALUE;
@@ -198,29 +131,78 @@
Parcel data, reply;
data.writeInterfaceToken(BpMediaSource::getInterfaceDescriptor());
data.writeUint32(maxNumBuffers);
+ if (options != nullptr) {
+ data.writeByteArray(sizeof(*options), (uint8_t*) options);
+ }
status_t ret = remote()->transact(READMULTIPLE, data, &reply);
+ mMemoryCache.gc();
if (ret != NO_ERROR) {
return ret;
}
// wrap the returned data in a vector of MediaBuffers
- int32_t bufCount = 0;
- while (1) {
- if (reply.readInt32() == 0) {
- break;
+ int32_t buftype;
+ uint32_t bufferCount = 0;
+ while ((buftype = reply.readInt32()) != NULL_BUFFER) {
+ LOG_ALWAYS_FATAL_IF(bufferCount >= maxNumBuffers,
+ "Received %u+ buffers and requested %u buffers",
+ bufferCount + 1, maxNumBuffers);
+ MediaBuffer *buf;
+ if (buftype == SHARED_BUFFER || buftype == SHARED_BUFFER_INDEX) {
+ uint64_t index = reply.readUint64();
+ ALOGV("Received %s index %llu",
+ buftype == SHARED_BUFFER ? "SHARED_BUFFER" : "SHARED_BUFFER_INDEX",
+ (unsigned long long) index);
+ sp<IMemory> mem;
+ if (buftype == SHARED_BUFFER) {
+ sp<IBinder> binder = reply.readStrongBinder();
+ mem = interface_cast<IMemory>(binder);
+ LOG_ALWAYS_FATAL_IF(mem.get() == nullptr,
+ "Received NULL IMemory for shared buffer");
+ mMemoryCache.insert(index, mem);
+ } else {
+ mem = mMemoryCache.lookup(index);
+ LOG_ALWAYS_FATAL_IF(mem.get() == nullptr,
+ "Received invalid IMemory index for shared buffer: %llu",
+ (unsigned long long)index);
+ }
+ size_t offset = reply.readInt32();
+ size_t length = reply.readInt32();
+ buf = new RemoteMediaBufferWrapper(mem);
+ buf->set_range(offset, length);
+ buf->meta_data()->updateFromParcel(reply);
+ } else { // INLINE_BUFFER
+ int32_t len = reply.readInt32();
+ ALOGV("INLINE_BUFFER status %d and len %d", ret, len);
+ buf = new MediaBuffer(len);
+ reply.read(buf->data(), len);
+ buf->meta_data()->updateFromParcel(reply);
}
- int32_t len = reply.readInt32();
- ALOGV("got len %d", len);
- MediaBuffer *buf = new MediaBuffer(len);
- reply.read(buf->data(), len);
- buf->meta_data()->updateFromParcel(reply);
buffers->push_back(buf);
- ++bufCount;
+ ++bufferCount;
+ ++mBuffersSinceStop;
}
ret = reply.readInt32();
- ALOGV("got status %d, bufCount %d", ret, bufCount);
+ ALOGV("readMultiple status %d, bufferCount %u, sinceStop %u",
+ ret, bufferCount, mBuffersSinceStop);
return ret;
}
+ // Binder proxy adds readMultiple support.
+ virtual bool supportReadMultiple() {
+ return true;
+ }
+
+ virtual bool supportNonblockingRead() {
+ ALOGV("supportNonblockingRead");
+ Parcel data, reply;
+ data.writeInterfaceToken(BpMediaSource::getInterfaceDescriptor());
+ status_t ret = remote()->transact(SUPPORT_NONBLOCKING_READ, data, &reply);
+ if (ret == NO_ERROR) {
+ return reply.readInt32() != 0;
+ }
+ return false;
+ }
+
virtual status_t pause() {
ALOGV("pause");
Parcel data, reply;
@@ -234,10 +216,51 @@
}
private:
+
+ uint32_t mBuffersSinceStop; // Buffer tracking variable
+
// NuPlayer passes pointers-to-metadata around, so we use this to keep the metadata alive
// XXX: could we use this for caching, or does metadata change on the fly?
sp<MetaData> mMetaData;
+ // Cache all IMemory objects received from MediaExtractor.
+ // We gc IMemory objects that are no longer active (referenced by a MediaBuffer).
+
+ struct MemoryCache {
+ sp<IMemory> lookup(uint64_t index) {
+ auto p = mIndexToMemory.find(index);
+ if (p == mIndexToMemory.end()) {
+ ALOGE("cannot find index!");
+ return nullptr;
+ }
+ return p->second;
+ }
+
+ void insert(uint64_t index, const sp<IMemory> &mem) {
+ if (mIndexToMemory.find(index) != mIndexToMemory.end()) {
+ ALOGE("index %llu already present", (unsigned long long)index);
+ return;
+ }
+ (void)mIndexToMemory.emplace(index, mem);
+ }
+
+ void reset() {
+ mIndexToMemory.clear();
+ }
+
+ void gc() {
+ for (auto it = mIndexToMemory.begin(); it != mIndexToMemory.end(); ) {
+ if (MediaBuffer::isDeadObject(it->second)) {
+ it = mIndexToMemory.erase(it);
+ } else {
+ ++it;
+ }
+ }
+ }
+ private:
+ // C++14 unordered_map erase on iterator is stable; C++11 has no guarantee.
+ std::map<uint64_t, sp<IMemory>> mIndexToMemory;
+ } mMemoryCache;
};
IMPLEMENT_META_INTERFACE(MediaSource, "android.media.IMediaSource");
@@ -246,12 +269,11 @@
#define LOG_TAG "BnMediaSource"
BnMediaSource::BnMediaSource()
- : mGroup(NULL) {
+ : mBuffersSinceStop(0)
+ , mGroup(new MediaBufferGroup(kBinderMediaBuffers /* growthLimit */)) {
}
BnMediaSource::~BnMediaSource() {
- delete mGroup;
- mGroup = NULL;
}
status_t BnMediaSource::onTransact(
@@ -274,11 +296,16 @@
case STOP: {
ALOGV("stop");
CHECK_INTERFACE(IMediaSource, data, reply);
- return stop();
+ mGroup->signalBufferReturned(nullptr);
+ status_t status = stop();
+ mIndexCache.reset();
+ mBuffersSinceStop = 0;
+ return status;
}
case PAUSE: {
ALOGV("pause");
CHECK_INTERFACE(IMediaSource, data, reply);
+ mGroup->signalBufferReturned(nullptr);
return pause();
}
case GETFORMAT: {
@@ -291,116 +318,130 @@
}
return UNKNOWN_ERROR;
}
- case READ: {
- ALOGV("read");
- CHECK_INTERFACE(IMediaSource, data, reply);
- status_t ret;
- MediaBuffer *buf = NULL;
- ReadOptions opts;
- uint32_t len;
- if (data.readUint32(&len) == NO_ERROR &&
- len == sizeof(opts) && data.read((void*)&opts, len) == NO_ERROR) {
- ret = read(&buf, &opts);
- } else {
- ret = read(&buf, NULL);
- }
-
- reply->writeInt32(ret);
- if (buf != NULL) {
- size_t usedSize = buf->range_length();
- // even if we're using shared memory, we might not want to use it, since for small
- // sizes it's faster to copy data through the Binder transaction
- // On the other hand, if the data size is large enough, it's better to use shared
- // memory. When data is too large, binder can't handle it.
- if (usedSize >= MediaBuffer::kSharedMemThreshold) {
- ALOGV("use shared memory: %zu", usedSize);
-
- MediaBuffer *transferBuf = buf;
- size_t offset = buf->range_offset();
- if (transferBuf->mMemory == NULL) {
- if (mGroup == NULL) {
- mGroup = new MediaBufferGroup;
- size_t allocateSize = usedSize;
- if (usedSize < SIZE_MAX / 3) {
- allocateSize = usedSize * 3 / 2;
- }
- mGroup->add_buffer(new MediaBuffer(allocateSize));
- }
-
- MediaBuffer *newBuf = NULL;
- ret = mGroup->acquire_buffer(
- &newBuf, false /* nonBlocking */, usedSize);
- if (ret != OK || newBuf == NULL || newBuf->mMemory == NULL) {
- ALOGW("failed to acquire shared memory, ret %d", ret);
- buf->release();
- if (newBuf != NULL) {
- newBuf->release();
- }
- reply->writeInt32(NULL_BUFFER);
- return NO_ERROR;
- }
- transferBuf = newBuf;
- memcpy(transferBuf->data(), (uint8_t*)buf->data() + buf->range_offset(),
- buf->range_length());
- offset = 0;
- }
-
- reply->writeInt32(SHARED_BUFFER);
- RemoteMediaBufferReleaser *wrapper =
- new RemoteMediaBufferReleaser(transferBuf, this);
- reply->writeStrongBinder(wrapper);
- reply->writeStrongBinder(IInterface::asBinder(transferBuf->mMemory));
- reply->writeInt32(offset);
- reply->writeInt32(usedSize);
- buf->meta_data()->writeToParcel(*reply);
- if (buf->mMemory == NULL) {
- buf->release();
- }
- } else {
- // buffer is small: copy it
- if (buf->mMemory != NULL) {
- ALOGV("%zu shared mem available, but only %zu used", buf->mMemory->size(), buf->range_length());
- }
- reply->writeInt32(INLINE_BUFFER);
- reply->writeByteArray(buf->range_length(), (uint8_t*)buf->data() + buf->range_offset());
- buf->meta_data()->writeToParcel(*reply);
- buf->release();
- }
- } else {
- ALOGV("ret %d, buf %p", ret, buf);
- reply->writeInt32(NULL_BUFFER);
- }
- return NO_ERROR;
- }
case READMULTIPLE: {
- ALOGV("readmultiple");
+ ALOGV("readMultiple");
CHECK_INTERFACE(IMediaSource, data, reply);
+
+ // Get max number of buffers to read.
uint32_t maxNumBuffers;
data.readUint32(&maxNumBuffers);
- status_t ret = NO_ERROR;
- uint32_t bufferCount = 0;
if (maxNumBuffers > kMaxNumReadMultiple) {
maxNumBuffers = kMaxNumReadMultiple;
}
- while (bufferCount < maxNumBuffers) {
- if (reply->dataSize() >= MediaBuffer::kSharedMemThreshold) {
+
+ // Get read options, if any.
+ ReadOptions opts;
+ uint32_t len;
+ const bool useOptions =
+ data.readUint32(&len) == NO_ERROR
+ && len == sizeof(opts)
+ && data.read((void *)&opts, len) == NO_ERROR;
+
+ mGroup->signalBufferReturned(nullptr);
+ mIndexCache.gc();
+ size_t inlineTransferSize = 0;
+ status_t ret = NO_ERROR;
+ uint32_t bufferCount = 0;
+ for (; bufferCount < maxNumBuffers; ++bufferCount, ++mBuffersSinceStop) {
+ MediaBuffer *buf = nullptr;
+ ret = read(&buf, useOptions ? &opts : nullptr);
+ opts.clearNonPersistent(); // Remove options that only apply to first buffer.
+ if (ret != NO_ERROR || buf == nullptr) {
break;
}
- MediaBuffer *buf = NULL;
- ret = read(&buf, NULL);
- if (ret != NO_ERROR || buf == NULL) {
- break;
+ // Even if we're using shared memory, we might not want to use it, since for small
+ // sizes it's faster to copy data through the Binder transaction
+ // On the other hand, if the data size is large enough, it's better to use shared
+ // memory. When data is too large, binder can't handle it.
+ //
+ // TODO: reduce MediaBuffer::kSharedMemThreshold
+ MediaBuffer *transferBuf = nullptr;
+ const size_t length = buf->range_length();
+ size_t offset = buf->range_offset();
+ if (length >= (supportNonblockingRead() && buf->mMemory != nullptr ?
+ kTransferSharedAsSharedThreshold : kTransferInlineAsSharedThreshold)) {
+ if (buf->mMemory != nullptr) {
+ ALOGV("Use shared memory: %zu", length);
+ transferBuf = buf;
+ } else {
+ ALOGD("Large buffer %zu without IMemory!", length);
+ ret = mGroup->acquire_buffer(
+ &transferBuf, false /* nonBlocking */, length);
+ if (ret != OK
+ || transferBuf == nullptr
+ || transferBuf->mMemory == nullptr) {
+ ALOGW("Failed to acquire shared memory, size %zu, ret %d",
+ length, ret);
+ if (transferBuf != nullptr) {
+ transferBuf->release();
+ transferBuf = nullptr;
+ }
+ // Current buffer transmit inline; no more additional buffers.
+ maxNumBuffers = 0;
+ } else {
+ memcpy(transferBuf->data(), (uint8_t*)buf->data() + offset, length);
+ offset = 0;
+ if (!mGroup->has_buffers()) {
+ maxNumBuffers = 0; // No more MediaBuffers, stop readMultiple.
+ }
+ }
+ }
}
- ++bufferCount;
- reply->writeInt32(1); // indicate one more MediaBuffer.
- reply->writeByteArray(
- buf->range_length(), (uint8_t*)buf->data() + buf->range_offset());
- buf->meta_data()->writeToParcel(*reply);
+ if (transferBuf != nullptr) { // Using shared buffers.
+ if (!transferBuf->isObserved()) {
+ // Transfer buffer must be part of a MediaBufferGroup.
+ ALOGV("adding shared memory buffer %p to local group", transferBuf);
+ mGroup->add_buffer(transferBuf);
+ transferBuf->add_ref(); // We have already acquired buffer.
+ }
+ uint64_t index = mIndexCache.lookup(transferBuf->mMemory);
+ if (index == 0) {
+ index = mIndexCache.insert(transferBuf->mMemory);
+ reply->writeInt32(SHARED_BUFFER);
+ reply->writeUint64(index);
+ reply->writeStrongBinder(IInterface::asBinder(transferBuf->mMemory));
+ ALOGV("SHARED_BUFFER(%p) %llu",
+ transferBuf, (unsigned long long)index);
+ } else {
+ reply->writeInt32(SHARED_BUFFER_INDEX);
+ reply->writeUint64(index);
+ ALOGV("SHARED_BUFFER_INDEX(%p) %llu",
+ transferBuf, (unsigned long long)index);
+ }
+ reply->writeInt32(offset);
+ reply->writeInt32(length);
+ buf->meta_data()->writeToParcel(*reply);
+ transferBuf->addRemoteRefcount(1);
+ if (transferBuf != buf) {
+ transferBuf->release(); // release local ref
+ } else if (!supportNonblockingRead()) {
+ maxNumBuffers = 0; // stop readMultiple with one shared buffer.
+ }
+ } else {
+ ALOGV_IF(buf->mMemory != nullptr,
+ "INLINE(%p) %zu shared mem available, but only %zu used",
+ buf, buf->mMemory->size(), length);
+ reply->writeInt32(INLINE_BUFFER);
+ reply->writeByteArray(length, (uint8_t*)buf->data() + offset);
+ buf->meta_data()->writeToParcel(*reply);
+ inlineTransferSize += length;
+ if (inlineTransferSize > kInlineMaxTransfer) {
+ maxNumBuffers = 0; // stop readMultiple if inline transfer is too large.
+ }
+ }
buf->release();
}
- reply->writeInt32(0); // indicate no more MediaBuffer.
+ reply->writeInt32(NULL_BUFFER); // Indicate no more MediaBuffers.
reply->writeInt32(ret);
+ ALOGV("readMultiple status %d, bufferCount %u, sinceStop %u",
+ ret, bufferCount, mBuffersSinceStop);
+ return NO_ERROR;
+ }
+ case SUPPORT_NONBLOCKING_READ: {
+ ALOGV("supportNonblockingRead");
+ CHECK_INTERFACE(IMediaSource, data, reply);
+ reply->writeInt32((int32_t)supportNonblockingRead());
return NO_ERROR;
}
default:
diff --git a/media/libmedia/IOMX.cpp b/media/libmedia/IOMX.cpp
index 6d5e7f6..ddfb6f1 100644
--- a/media/libmedia/IOMX.cpp
+++ b/media/libmedia/IOMX.cpp
@@ -775,31 +775,35 @@
// mark the last page as inaccessible, to avoid exploitation
// of codecs that access past the end of the allocation because
// they didn't check the size
- mprotect((char*)params + allocSize - pageSize, pageSize, PROT_NONE);
- switch (code) {
- case GET_PARAMETER:
- err = getParameter(node, index, params, size);
- break;
- case SET_PARAMETER:
- err = setParameter(node, index, params, size);
- break;
- case GET_CONFIG:
- err = getConfig(node, index, params, size);
- break;
- case SET_CONFIG:
- err = setConfig(node, index, params, size);
- break;
- case SET_INTERNAL_OPTION:
- {
- InternalOptionType type =
- (InternalOptionType)data.readInt32();
+ if (mprotect((char*)params + allocSize - pageSize, pageSize,
+ PROT_NONE) != 0) {
+ ALOGE("mprotect failed: %s", strerror(errno));
+ } else {
+ switch (code) {
+ case GET_PARAMETER:
+ err = getParameter(node, index, params, size);
+ break;
+ case SET_PARAMETER:
+ err = setParameter(node, index, params, size);
+ break;
+ case GET_CONFIG:
+ err = getConfig(node, index, params, size);
+ break;
+ case SET_CONFIG:
+ err = setConfig(node, index, params, size);
+ break;
+ case SET_INTERNAL_OPTION:
+ {
+ InternalOptionType type =
+ (InternalOptionType)data.readInt32();
- err = setInternalOption(node, index, type, params, size);
- break;
+ err = setInternalOption(node, index, type, params, size);
+ break;
+ }
+
+ default:
+ TRESPASS();
}
-
- default:
- TRESPASS();
}
}
}
diff --git a/media/libmedia/Visualizer.cpp b/media/libmedia/Visualizer.cpp
index 31e310b..37bf0bd 100644
--- a/media/libmedia/Visualizer.cpp
+++ b/media/libmedia/Visualizer.cpp
@@ -55,7 +55,7 @@
{
ALOGV("Visualizer::~Visualizer()");
setEnabled(false);
- setCaptureCallBack(NULL, NULL, 0, 0, true);
+ setCaptureCallBack(NULL, NULL, 0, 0);
}
status_t Visualizer::setEnabled(bool enabled)
@@ -77,13 +77,11 @@
status_t status = AudioEffect::setEnabled(enabled);
- if (status == NO_ERROR) {
- if (t != 0) {
- if (enabled) {
- t->run("Visualizer");
- } else {
- t->requestExit();
- }
+ if (t != 0) {
+ if (enabled && status == NO_ERROR) {
+ t->run("Visualizer");
+ } else {
+ t->requestExit();
}
}
@@ -95,14 +93,14 @@
}
status_t Visualizer::setCaptureCallBack(capture_cbk_t cbk, void* user, uint32_t flags,
- uint32_t rate, bool force)
+ uint32_t rate)
{
if (rate > CAPTURE_RATE_MAX) {
return BAD_VALUE;
}
Mutex::Autolock _l(mCaptureLock);
- if (force || mEnabled) {
+ if (mEnabled) {
return INVALID_OPERATION;
}
diff --git a/media/libmedia/mediaplayer.cpp b/media/libmedia/mediaplayer.cpp
index 8725dfe..fbe749c 100644
--- a/media/libmedia/mediaplayer.cpp
+++ b/media/libmedia/mediaplayer.cpp
@@ -395,7 +395,9 @@
return BAD_VALUE;
}
Mutex::Autolock _l(mLock);
- if (mPlayer == 0) return INVALID_OPERATION;
+ if (mPlayer == 0 || (mCurrentState & MEDIA_PLAYER_STOPPED)) {
+ return INVALID_OPERATION;
+ }
if (rate.mSpeed != 0.f && !(mCurrentState & MEDIA_PLAYER_STARTED)
&& (mCurrentState & (MEDIA_PLAYER_PREPARED | MEDIA_PLAYER_PAUSED
diff --git a/media/libmediaplayerservice/MediaPlayerService.cpp b/media/libmediaplayerservice/MediaPlayerService.cpp
index d3c6c5d..95c91d1 100644
--- a/media/libmediaplayerservice/MediaPlayerService.cpp
+++ b/media/libmediaplayerservice/MediaPlayerService.cpp
@@ -558,6 +558,12 @@
mClients.remove(client);
}
+bool MediaPlayerService::hasClient(wp<Client> client)
+{
+ Mutex::Autolock lock(mLock);
+ return mClients.indexOf(client) != NAME_NOT_FOUND;
+}
+
MediaPlayerService::Client::Client(
const sp<MediaPlayerService>& service, pid_t pid,
int32_t connId, const sp<IMediaPlayerClient>& client,
@@ -1086,6 +1092,10 @@
ALOGV("setNextPlayer");
Mutex::Autolock l(mLock);
sp<Client> c = static_cast<Client*>(player.get());
+ if (c != NULL && !mService->hasClient(c)) {
+ return BAD_VALUE;
+ }
+
mNextClient = c;
if (c != NULL) {
@@ -1845,7 +1855,7 @@
mCallbackData->setOutput(this);
}
delete newcbd;
- return OK;
+ return updateTrack();
}
}
@@ -1867,17 +1877,26 @@
mFrameSize = t->frameSize();
mTrack = t;
+ return updateTrack();
+}
+
+status_t MediaPlayerService::AudioOutput::updateTrack() {
+ if (mTrack == NULL) {
+ return NO_ERROR;
+ }
+
status_t res = NO_ERROR;
// Note some output devices may give us a direct track even though we don't specify it.
// Example: Line application b/17459982.
- if ((t->getFlags() & (AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD | AUDIO_OUTPUT_FLAG_DIRECT)) == 0) {
- res = t->setPlaybackRate(mPlaybackRate);
+ if ((mTrack->getFlags()
+ & (AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD | AUDIO_OUTPUT_FLAG_DIRECT)) == 0) {
+ res = mTrack->setPlaybackRate(mPlaybackRate);
if (res == NO_ERROR) {
- t->setAuxEffectSendLevel(mSendLevel);
- res = t->attachAuxEffect(mAuxEffectId);
+ mTrack->setAuxEffectSendLevel(mSendLevel);
+ res = mTrack->attachAuxEffect(mAuxEffectId);
}
}
- ALOGV("open() DONE status %d", res);
+ ALOGV("updateTrack() DONE status %d", res);
return res;
}
@@ -1929,6 +1948,7 @@
continue;
}
callbackData->mSwitching = true; // begin track switch
+ callbackData->setOutput(NULL);
#else
// tryBeginTrackSwitch() returns false if the callback has the lock.
if (!mCallbackData->tryBeginTrackSwitch()) {
diff --git a/media/libmediaplayerservice/MediaPlayerService.h b/media/libmediaplayerservice/MediaPlayerService.h
index 817d10f..601b046 100644
--- a/media/libmediaplayerservice/MediaPlayerService.h
+++ b/media/libmediaplayerservice/MediaPlayerService.h
@@ -135,6 +135,7 @@
int event, void *me, void *info);
void deleteRecycledTrack_l();
void close_l();
+ status_t updateTrack();
sp<AudioTrack> mTrack;
sp<AudioTrack> mRecycledTrack;
@@ -226,6 +227,7 @@
virtual status_t dump(int fd, const Vector<String16>& args);
void removeClient(const wp<Client>& client);
+ bool hasClient(wp<Client> client);
enum {
MEDIASERVER_PROCESS_DEATH = 0,
diff --git a/media/libmediaplayerservice/StagefrightRecorder.cpp b/media/libmediaplayerservice/StagefrightRecorder.cpp
index 2d8d029..cdb0a7b 100644
--- a/media/libmediaplayerservice/StagefrightRecorder.cpp
+++ b/media/libmediaplayerservice/StagefrightRecorder.cpp
@@ -22,6 +22,8 @@
#include "WebmWriter.h"
#include "StagefrightRecorder.h"
+#include <algorithm>
+
#include <android/hardware/ICamera.h>
#include <binder/IPCThreadState.h>
@@ -57,6 +59,11 @@
namespace android {
+static const float kTypicalDisplayRefreshingRate = 60.f;
+// display refresh rate drops on battery saver
+static const float kMinTypicalDisplayRefreshingRate = kTypicalDisplayRefreshingRate / 2;
+static const int kMaxNumVideoTemporalLayers = 8;
+
// To collect the encoder usage for the battery app
static void addBatteryData(uint32_t params) {
sp<IBinder> binder =
@@ -820,6 +827,9 @@
break;
}
+ ALOGV("Recording frameRate: %d captureFps: %f",
+ mFrameRate, mCaptureFps);
+
return status;
}
@@ -1562,9 +1572,44 @@
format->setInt32("level", mVideoEncoderLevel);
}
+ uint32_t tsLayers = 1;
+ bool preferBFrames = true; // we like B-frames as it produces better quality per bitrate
format->setInt32("priority", 0 /* realtime */);
+ float maxPlaybackFps = mFrameRate; // assume video is only played back at normal speed
+
if (mCaptureFpsEnable) {
format->setFloat("operating-rate", mCaptureFps);
+
+ // enable layering for all time lapse and high frame rate recordings
+ if (mFrameRate / mCaptureFps >= 1.9) { // time lapse
+ preferBFrames = false;
+ tsLayers = 2; // use at least two layers as resulting video will likely be sped up
+ } else if (mCaptureFps > maxPlaybackFps) { // slow-mo
+ maxPlaybackFps = mCaptureFps; // assume video will be played back at full capture speed
+ preferBFrames = false;
+ }
+ }
+
+ for (uint32_t tryLayers = 1; tryLayers <= kMaxNumVideoTemporalLayers; ++tryLayers) {
+ if (tryLayers > tsLayers) {
+ tsLayers = tryLayers;
+ }
+ // keep going until the base layer fps falls below the typical display refresh rate
+ float baseLayerFps = maxPlaybackFps / (1 << (tryLayers - 1));
+ if (baseLayerFps < kMinTypicalDisplayRefreshingRate / 0.9) {
+ break;
+ }
+ }
+
+ if (tsLayers > 1) {
+ uint32_t bLayers = std::min(2u, tsLayers - 1); // use up-to 2 B-layers
+ uint32_t pLayers = tsLayers - bLayers;
+ format->setString(
+ "ts-schema", AStringPrintf("android.generic.%u+%u", pLayers, bLayers));
+
+ // TODO: some encoders do not support B-frames with temporal layering, and we have a
+ // different preference based on use-case. We could move this into camera profiles.
+ format->setInt32("android._prefer-b-frames", preferBFrames);
}
if (mMetaDataStoredInVideoBuffers != kMetadataBufferTypeInvalid) {
@@ -1762,19 +1807,38 @@
return OK;
}
- // 30 ms buffer to avoid timestamp overlap
- mTotalPausedDurationUs += (systemTime() / 1000) - mPauseStartTimeUs - 30000;
+ int64_t bufferStartTimeUs = 0;
+ bool allSourcesStarted = true;
+ for (const auto &source : { mAudioEncoderSource, mVideoEncoderSource }) {
+ if (source == nullptr) {
+ continue;
+ }
+ int64_t timeUs = source->getFirstSampleSystemTimeUs();
+ if (timeUs < 0) {
+ allSourcesStarted = false;
+ }
+ if (bufferStartTimeUs < timeUs) {
+ bufferStartTimeUs = timeUs;
+ }
+ }
+
+ if (allSourcesStarted) {
+ if (mPauseStartTimeUs < bufferStartTimeUs) {
+ mPauseStartTimeUs = bufferStartTimeUs;
+ }
+ // 30 ms buffer to avoid timestamp overlap
+ mTotalPausedDurationUs += (systemTime() / 1000) - mPauseStartTimeUs - 30000;
+ }
double timeOffset = -mTotalPausedDurationUs;
if (mCaptureFpsEnable) {
timeOffset *= mCaptureFps / mFrameRate;
}
- if (mAudioEncoderSource != NULL) {
- mAudioEncoderSource->setInputBufferTimeOffset((int64_t)timeOffset);
- mAudioEncoderSource->start();
- }
- if (mVideoEncoderSource != NULL) {
- mVideoEncoderSource->setInputBufferTimeOffset((int64_t)timeOffset);
- mVideoEncoderSource->start();
+ for (const auto &source : { mAudioEncoderSource, mVideoEncoderSource }) {
+ if (source == nullptr) {
+ continue;
+ }
+ source->setInputBufferTimeOffset((int64_t)timeOffset);
+ source->start();
}
mPauseStartTimeUs = 0;
diff --git a/media/libmediaplayerservice/nuplayer/GenericSource.cpp b/media/libmediaplayerservice/nuplayer/GenericSource.cpp
index 60ead4a..6b88404 100644
--- a/media/libmediaplayerservice/nuplayer/GenericSource.cpp
+++ b/media/libmediaplayerservice/nuplayer/GenericSource.cpp
@@ -1298,6 +1298,13 @@
}
#endif
+ if (trackType == MEDIA_TRACK_TYPE_VIDEO) {
+ int32_t layerId;
+ if (mb->meta_data()->findInt32(kKeyTemporalLayerId, &layerId)) {
+ meta->setInt32("temporal-layer-id", layerId);
+ }
+ }
+
if (trackType == MEDIA_TRACK_TYPE_TIMEDTEXT) {
const char *mime;
CHECK(mTimedTextTrack.mSource != NULL
@@ -1378,7 +1385,7 @@
if (mIsWidevine) {
maxBuffers = 2;
} else {
- maxBuffers = 4;
+ maxBuffers = 8; // too large of a number may influence seeks
}
break;
case MEDIA_TRACK_TYPE_AUDIO:
@@ -1410,23 +1417,24 @@
MediaSource::ReadOptions options;
bool seeking = false;
-
if (seekTimeUs >= 0) {
options.setSeekTo(seekTimeUs, MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC);
seeking = true;
}
- if (mIsWidevine) {
+ const bool couldReadMultiple = (!mIsWidevine && track->mSource->supportReadMultiple());
+
+ if (mIsWidevine || couldReadMultiple) {
options.setNonBlocking();
}
- bool couldReadMultiple = (!mIsWidevine && trackType == MEDIA_TRACK_TYPE_AUDIO);
for (size_t numBuffers = 0; numBuffers < maxBuffers; ) {
Vector<MediaBuffer *> mediaBuffers;
status_t err = NO_ERROR;
- if (!seeking && couldReadMultiple) {
- err = track->mSource->readMultiple(&mediaBuffers, (maxBuffers - numBuffers));
+ if (couldReadMultiple) {
+ err = track->mSource->readMultiple(
+ &mediaBuffers, maxBuffers - numBuffers, &options);
} else {
MediaBuffer *mbuf = NULL;
err = track->mSource->read(&mbuf, &options);
@@ -1435,7 +1443,7 @@
}
}
- options.clearSeekTo();
+ options.clearNonPersistent();
size_t id = 0;
size_t count = mediaBuffers.size();
diff --git a/media/libmediaplayerservice/nuplayer/HTTPLiveSource.cpp b/media/libmediaplayerservice/nuplayer/HTTPLiveSource.cpp
index 3fffdc1a..5027e01 100644
--- a/media/libmediaplayerservice/nuplayer/HTTPLiveSource.cpp
+++ b/media/libmediaplayerservice/nuplayer/HTTPLiveSource.cpp
@@ -30,6 +30,7 @@
#include <media/stagefright/MediaErrors.h>
#include <media/stagefright/MetaData.h>
#include <media/stagefright/MediaDefs.h>
+#include <media/stagefright/Utils.h>
namespace android {
@@ -100,26 +101,38 @@
void NuPlayer::HTTPLiveSource::start() {
}
-sp<AMessage> NuPlayer::HTTPLiveSource::getFormat(bool audio) {
- sp<AMessage> format;
- status_t err = -EWOULDBLOCK;
+sp<MetaData> NuPlayer::HTTPLiveSource::getFormatMeta(bool audio) {
+ sp<MetaData> meta;
if (mLiveSession != NULL) {
- err = mLiveSession->getStreamFormat(
+ mLiveSession->getStreamFormatMeta(
audio ? LiveSession::STREAMTYPE_AUDIO
: LiveSession::STREAMTYPE_VIDEO,
- &format);
+ &meta);
}
+ return meta;
+}
+
+sp<AMessage> NuPlayer::HTTPLiveSource::getFormat(bool audio) {
+ sp<MetaData> meta;
+ status_t err = -EWOULDBLOCK;
+ if (mLiveSession != NULL) {
+ err = mLiveSession->getStreamFormatMeta(
+ audio ? LiveSession::STREAMTYPE_AUDIO
+ : LiveSession::STREAMTYPE_VIDEO,
+ &meta);
+ }
+
+ sp<AMessage> format;
if (err == -EWOULDBLOCK) {
format = new AMessage();
format->setInt32("err", err);
return format;
}
- if (err != OK) {
+ if (err != OK || convertMetaDataToMessage(meta, &format) != OK) {
return NULL;
}
-
return format;
}
diff --git a/media/libmediaplayerservice/nuplayer/HTTPLiveSource.h b/media/libmediaplayerservice/nuplayer/HTTPLiveSource.h
index 9e0ec2f..574937d 100644
--- a/media/libmediaplayerservice/nuplayer/HTTPLiveSource.h
+++ b/media/libmediaplayerservice/nuplayer/HTTPLiveSource.h
@@ -38,6 +38,7 @@
virtual void start();
virtual status_t dequeueAccessUnit(bool audio, sp<ABuffer> *accessUnit);
+ virtual sp<MetaData> getFormatMeta(bool audio);
virtual sp<AMessage> getFormat(bool audio);
virtual status_t feedMoreTSData();
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
index 258bc11..4e16fba 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
@@ -784,12 +784,9 @@
}
if (mVideoDecoder != NULL) {
- float rate = getFrameRate();
- if (rate > 0) {
- sp<AMessage> params = new AMessage();
- params->setFloat("operating-rate", rate * mPlaybackSettings.mSpeed);
- mVideoDecoder->setParameters(params);
- }
+ sp<AMessage> params = new AMessage();
+ params->setFloat("playback-speed", mPlaybackSettings.mSpeed);
+ mVideoDecoder->setParameters(params);
}
sp<AMessage> response = new AMessage;
@@ -1342,7 +1339,16 @@
}
sp<MetaData> audioMeta = mSource->getFormatMeta(true /* audio */);
+ sp<MetaData> videoMeta = mSource->getFormatMeta(false /* audio */);
+ if (audioMeta == NULL && videoMeta == NULL) {
+ ALOGE("no metadata for either audio or video source");
+ mSource->stop();
+ mSourceStarted = false;
+ notifyListener(MEDIA_ERROR, MEDIA_ERROR_UNKNOWN, ERROR_MALFORMED);
+ return;
+ }
ALOGV_IF(audioMeta == NULL, "no metadata for audio source"); // video only stream
+
audio_stream_type_t streamType = AUDIO_STREAM_MUSIC;
if (mAudioSink != NULL) {
streamType = mAudioSink->getAudioStreamType();
@@ -1680,6 +1686,27 @@
return err;
}
}
+
+ if (!audio) {
+ sp<AMessage> params = new AMessage();
+ float rate = getFrameRate();
+ if (rate > 0) {
+ params->setFloat("frame-rate-total", rate);
+ }
+
+ sp<MetaData> fileMeta = getFileMeta();
+ if (fileMeta != NULL) {
+ int32_t videoTemporalLayerCount;
+ if (fileMeta->findInt32(kKeyTemporalLayerCount, &videoTemporalLayerCount)
+ && videoTemporalLayerCount > 0) {
+ params->setInt32("temporal-layer-count", videoTemporalLayerCount);
+ }
+ }
+
+ if (params->countEntries() > 0) {
+ (*decoder)->setParameters(params);
+ }
+ }
return OK;
}
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
index 4678956..cf38efc 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
@@ -19,6 +19,8 @@
#include <utils/Log.h>
#include <inttypes.h>
+#include <algorithm>
+
#include "NuPlayerCCDecoder.h"
#include "NuPlayerDecoder.h"
#include "NuPlayerRenderer.h"
@@ -41,6 +43,12 @@
namespace android {
+static float kDisplayRefreshingRate = 60.f; // TODO: get this from the display
+
+// The default total video frame rate of a stream when that info is not available from
+// the source.
+static float kDefaultVideoFrameRateTotal = 30.f;
+
static inline bool getAudioDeepBufferSetting() {
return property_get_bool("media.stagefright.audio.deep", false /* default_value */);
}
@@ -69,11 +77,17 @@
mIsSecure(false),
mFormatChangePending(false),
mTimeChangePending(false),
+ mFrameRateTotal(kDefaultVideoFrameRateTotal),
+ mPlaybackSpeed(1.0f),
+ mNumVideoTemporalLayerTotal(1), // decode all layers
+ mNumVideoTemporalLayerAllowed(1),
+ mCurrentMaxVideoTemporalLayerId(0),
mResumePending(false),
mComponentName("decoder") {
mCodecLooper = new ALooper;
mCodecLooper->setName("NPDecoder-CL");
mCodecLooper->start(false, false, ANDROID_PRIORITY_AUDIO);
+ mVideoTemporalLayerAggregateFps[0] = mFrameRateTotal;
}
NuPlayer::Decoder::~Decoder() {
@@ -329,11 +343,68 @@
}
void NuPlayer::Decoder::onSetParameters(const sp<AMessage> ¶ms) {
- if (mCodec == NULL) {
- ALOGW("onSetParameters called before codec is created.");
- return;
+ bool needAdjustLayers = false;
+ float frameRateTotal;
+ if (params->findFloat("frame-rate-total", &frameRateTotal)
+ && mFrameRateTotal != frameRateTotal) {
+ needAdjustLayers = true;
+ mFrameRateTotal = frameRateTotal;
}
- mCodec->setParameters(params);
+
+ int32_t numVideoTemporalLayerTotal;
+ if (params->findInt32("temporal-layer-count", &numVideoTemporalLayerTotal)
+ && numVideoTemporalLayerTotal >= 0
+ && numVideoTemporalLayerTotal <= kMaxNumVideoTemporalLayers
+ && mNumVideoTemporalLayerTotal != numVideoTemporalLayerTotal) {
+ needAdjustLayers = true;
+ mNumVideoTemporalLayerTotal = std::max(numVideoTemporalLayerTotal, 1);
+ }
+
+ if (needAdjustLayers && mNumVideoTemporalLayerTotal > 1) {
+ // TODO: For now, layer fps is calculated for some specific architectures.
+ // But it really should be extracted from the stream.
+ mVideoTemporalLayerAggregateFps[0] =
+ mFrameRateTotal / (float)(1ll << (mNumVideoTemporalLayerTotal - 1));
+ for (int32_t i = 1; i < mNumVideoTemporalLayerTotal; ++i) {
+ mVideoTemporalLayerAggregateFps[i] =
+ mFrameRateTotal / (float)(1ll << (mNumVideoTemporalLayerTotal - i))
+ + mVideoTemporalLayerAggregateFps[i - 1];
+ }
+ }
+
+ float playbackSpeed;
+ if (params->findFloat("playback-speed", &playbackSpeed)
+ && mPlaybackSpeed != playbackSpeed) {
+ needAdjustLayers = true;
+ mPlaybackSpeed = playbackSpeed;
+ }
+
+ if (needAdjustLayers) {
+ float decodeFrameRate = mFrameRateTotal;
+ // enable temporal layering optimization only if we know the layering depth
+ if (mNumVideoTemporalLayerTotal > 1) {
+ int32_t layerId;
+ for (layerId = 0; layerId < mNumVideoTemporalLayerTotal - 1; ++layerId) {
+ if (mVideoTemporalLayerAggregateFps[layerId] * mPlaybackSpeed
+ >= kDisplayRefreshingRate * 0.9) {
+ break;
+ }
+ }
+ mNumVideoTemporalLayerAllowed = layerId + 1;
+ decodeFrameRate = mVideoTemporalLayerAggregateFps[layerId];
+ }
+ ALOGV("onSetParameters: allowed layers=%d, decodeFps=%g",
+ mNumVideoTemporalLayerAllowed, decodeFrameRate);
+
+ if (mCodec == NULL) {
+ ALOGW("onSetParameters called before codec is created.");
+ return;
+ }
+
+ sp<AMessage> codecParams = new AMessage();
+ codecParams->setFloat("operating-rate", decodeFrameRate * mPlaybackSpeed);
+ mCodec->setParameters(codecParams);
+ }
}
void NuPlayer::Decoder::onSetRenderer(const sp<Renderer> &renderer) {
@@ -742,13 +813,33 @@
}
dropAccessUnit = false;
- if (!mIsAudio
- && !mIsSecure
- && mRenderer->getVideoLateByUs() > 100000ll
- && mIsVideoAVC
- && !IsAVCReferenceFrame(accessUnit)) {
- dropAccessUnit = true;
- ++mNumInputFramesDropped;
+ if (!mIsAudio && !mIsSecure) {
+ int32_t layerId = 0;
+ bool haveLayerId = accessUnit->meta()->findInt32("temporal-layer-id", &layerId);
+ if (mRenderer->getVideoLateByUs() > 100000ll
+ && mIsVideoAVC
+ && !IsAVCReferenceFrame(accessUnit)) {
+ dropAccessUnit = true;
+ } else if (haveLayerId && mNumVideoTemporalLayerTotal > 1) {
+ // Add only one layer each time.
+ if (layerId > mCurrentMaxVideoTemporalLayerId + 1
+ || layerId >= mNumVideoTemporalLayerAllowed) {
+ dropAccessUnit = true;
+ ALOGV("dropping layer(%d), speed=%g, allowed layer count=%d, max layerId=%d",
+ layerId, mPlaybackSpeed, mNumVideoTemporalLayerAllowed,
+ mCurrentMaxVideoTemporalLayerId);
+ } else if (layerId > mCurrentMaxVideoTemporalLayerId) {
+ mCurrentMaxVideoTemporalLayerId = layerId;
+ } else if (layerId == 0 && mNumVideoTemporalLayerTotal > 1 && IsIDR(accessUnit)) {
+ mCurrentMaxVideoTemporalLayerId = mNumVideoTemporalLayerTotal - 1;
+ }
+ }
+ if (dropAccessUnit) {
+ if (layerId <= mCurrentMaxVideoTemporalLayerId && layerId > 0) {
+ mCurrentMaxVideoTemporalLayerId = layerId - 1;
+ }
+ ++mNumInputFramesDropped;
+ }
}
} while (dropAccessUnit);
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h
index ae08b4b..0c619ed 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h
@@ -57,6 +57,10 @@
kWhatSetVideoSurface = 'sSur'
};
+ enum {
+ kMaxNumVideoTemporalLayers = 32,
+ };
+
sp<Surface> mSurface;
sp<Source> mSource;
@@ -90,6 +94,12 @@
bool mIsSecure;
bool mFormatChangePending;
bool mTimeChangePending;
+ float mFrameRateTotal;
+ float mPlaybackSpeed;
+ int32_t mNumVideoTemporalLayerTotal;
+ int32_t mNumVideoTemporalLayerAllowed;
+ int32_t mCurrentMaxVideoTemporalLayerId;
+ float mVideoTemporalLayerAggregateFps[kMaxNumVideoTemporalLayers];
bool mResumePending;
AString mComponentName;
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
index b47a4f1..b742762 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
@@ -141,6 +141,17 @@
mAudioSink->flush();
mAudioSink->close();
}
+
+ // Try to avoid racing condition in case callback is still on.
+ Mutex::Autolock autoLock(mLock);
+ mUseAudioCallback = false;
+ flushQueue(&mAudioQueue);
+ flushQueue(&mVideoQueue);
+ mWakeLock.clear();
+ mMediaClock.clear();
+ mVideoScheduler.clear();
+ mNotify.clear();
+ mAudioSink.clear();
}
void NuPlayer::Renderer::queueBuffer(
@@ -744,7 +755,7 @@
case MediaPlayerBase::AudioSink::CB_EVENT_STREAM_END:
{
ALOGV("AudioSink::CB_EVENT_STREAM_END");
- me->notifyEOS(true /* audio */, ERROR_END_OF_STREAM);
+ me->notifyEOSCallback();
break;
}
@@ -759,6 +770,16 @@
return 0;
}
+void NuPlayer::Renderer::notifyEOSCallback() {
+ Mutex::Autolock autoLock(mLock);
+
+ if (!mUseAudioCallback) {
+ return;
+ }
+
+ notifyEOS(true /* audio */, ERROR_END_OF_STREAM);
+}
+
size_t NuPlayer::Renderer::fillAudioBuffer(void *buffer, size_t size) {
Mutex::Autolock autoLock(mLock);
@@ -1190,8 +1211,10 @@
msg->setWhat(kWhatPostDrainVideoQueue);
msg->post(postDelayUs);
mVideoScheduler->restart();
- ALOGI("possible video time jump of %dms or uninitialized media clock, retrying in %dms",
- (int)(delayUs / 1000), (int)(postDelayUs / 1000));
+ ALOGI("possible video time jump of %dms (%lld : %lld) or uninitialized media clock,"
+ " retrying in %dms",
+ (int)(delayUs / 1000), (long long)mediaTimeUs,
+ (long long)mAudioFirstAnchorTimeMediaUs, (int)(postDelayUs / 1000));
mDrainVideoQueuePending = true;
return;
}
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.h b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.h
index 004e21c..fe7f8fa 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.h
@@ -212,6 +212,7 @@
status_t getCurrentPositionFromAnchor(
int64_t *mediaUs, int64_t nowUs, bool allowPastQueuedVideo = false);
+ void notifyEOSCallback();
size_t fillAudioBuffer(void *buffer, size_t size);
bool onDrainAudioQueue();
diff --git a/media/libmediaplayerservice/nuplayer/RTSPSource.cpp b/media/libmediaplayerservice/nuplayer/RTSPSource.cpp
index 1b7dff5..c4e5df7 100644
--- a/media/libmediaplayerservice/nuplayer/RTSPSource.cpp
+++ b/media/libmediaplayerservice/nuplayer/RTSPSource.cpp
@@ -58,6 +58,7 @@
mDisconnectReplyID(0),
mBuffering(false),
mInPreparationPhase(true),
+ mEOSPending(false),
mSeekGeneration(0),
mEOSTimeoutAudio(0),
mEOSTimeoutVideo(0) {
@@ -200,34 +201,28 @@
status_t finalResult;
if (!source->hasBufferAvailable(&finalResult)) {
if (finalResult == OK) {
- int64_t mediaDurationUs = 0;
- getDuration(&mediaDurationUs);
- sp<AnotherPacketSource> otherSource = getSource(!audio);
- status_t otherFinalResult;
- // If other source already signaled EOS, this source should also signal EOS
- if (otherSource != NULL &&
- !otherSource->hasBufferAvailable(&otherFinalResult) &&
- otherFinalResult == ERROR_END_OF_STREAM) {
- source->signalEOS(ERROR_END_OF_STREAM);
+ // If other source already signaled EOS, this source should also return EOS
+ if (sourceReachedEOS(!audio)) {
return ERROR_END_OF_STREAM;
}
// If this source has detected near end, give it some time to retrieve more
- // data before signaling EOS
+ // data before returning EOS
+ int64_t mediaDurationUs = 0;
+ getDuration(&mediaDurationUs);
if (source->isFinished(mediaDurationUs)) {
int64_t eosTimeout = audio ? mEOSTimeoutAudio : mEOSTimeoutVideo;
if (eosTimeout == 0) {
setEOSTimeout(audio, ALooper::GetNowUs());
} else if ((ALooper::GetNowUs() - eosTimeout) > kNearEOSTimeoutUs) {
setEOSTimeout(audio, 0);
- source->signalEOS(ERROR_END_OF_STREAM);
return ERROR_END_OF_STREAM;
}
return -EWOULDBLOCK;
}
- if (!(otherSource != NULL && otherSource->isFinished(mediaDurationUs))) {
+ if (!sourceNearEOS(!audio)) {
// We should not enter buffering mode
// if any of the sources already have detected EOS.
startBufferingIfNecessary();
@@ -306,6 +301,7 @@
mState = SEEKING;
mHandler->seek(seekTimeUs);
+ mEOSPending = false;
}
void NuPlayer::RTSPSource::schedulePollBuffering() {
@@ -314,14 +310,20 @@
}
void NuPlayer::RTSPSource::checkBuffering(
- bool *prepared, bool *underflow, bool *overflow, bool *startServer) {
+ bool *prepared, bool *underflow, bool *overflow, bool *startServer, bool *finished) {
size_t numTracks = mTracks.size();
- size_t preparedCount, underflowCount, overflowCount, startCount;
- preparedCount = underflowCount = overflowCount = startCount = 0;
- for (size_t i = 0; i < numTracks; ++i) {
+ size_t preparedCount, underflowCount, overflowCount, startCount, finishedCount;
+ preparedCount = underflowCount = overflowCount = startCount = finishedCount = 0;
+
+ size_t count = numTracks;
+ for (size_t i = 0; i < count; ++i) {
status_t finalResult;
TrackInfo *info = &mTracks.editItemAt(i);
sp<AnotherPacketSource> src = info->mSource;
+ if (src == NULL) {
+ --numTracks;
+ continue;
+ }
int64_t bufferedDurationUs = src->getBufferedDurationUs(&finalResult);
// isFinished when duration is 0 checks for EOS result only
@@ -331,6 +333,7 @@
if (src->isFinished(/* duration */ 0)) {
++overflowCount;
+ ++finishedCount;
} else {
if (bufferedDurationUs < kUnderflowMarkUs) {
++underflowCount;
@@ -348,11 +351,12 @@
*underflow = (underflowCount > 0);
*overflow = (overflowCount == numTracks);
*startServer = (startCount > 0);
+ *finished = (finishedCount > 0);
}
void NuPlayer::RTSPSource::onPollBuffering() {
- bool prepared, underflow, overflow, startServer;
- checkBuffering(&prepared, &underflow, &overflow, &startServer);
+ bool prepared, underflow, overflow, startServer, finished;
+ checkBuffering(&prepared, &underflow, &overflow, &startServer, &finished);
if (prepared && mInPreparationPhase) {
mInPreparationPhase = false;
@@ -363,8 +367,11 @@
startBufferingIfNecessary();
}
- if (overflow && mHandler != NULL) {
+ if (haveSufficientDataOnAllTracks()) {
stopBufferingIfNecessary();
+ }
+
+ if (overflow && mHandler != NULL) {
mHandler->pause();
}
@@ -372,9 +379,72 @@
mHandler->resume();
}
+ if (finished && mHandler != NULL) {
+ mHandler->cancelAccessUnitTimeoutCheck();
+ }
+
schedulePollBuffering();
}
+void NuPlayer::RTSPSource::signalSourceEOS(status_t result) {
+ const bool audio = true;
+ const bool video = false;
+
+ sp<AnotherPacketSource> source = getSource(audio);
+ if (source != NULL) {
+ source->signalEOS(result);
+ }
+
+ source = getSource(video);
+ if (source != NULL) {
+ source->signalEOS(result);
+ }
+}
+
+bool NuPlayer::RTSPSource::sourceReachedEOS(bool audio) {
+ sp<AnotherPacketSource> source = getSource(audio);
+ status_t finalResult;
+ return (source != NULL &&
+ !source->hasBufferAvailable(&finalResult) &&
+ finalResult == ERROR_END_OF_STREAM);
+}
+
+bool NuPlayer::RTSPSource::sourceNearEOS(bool audio) {
+ sp<AnotherPacketSource> source = getSource(audio);
+ int64_t mediaDurationUs = 0;
+ getDuration(&mediaDurationUs);
+ return (source != NULL && source->isFinished(mediaDurationUs));
+}
+
+void NuPlayer::RTSPSource::onSignalEOS(const sp<AMessage> &msg) {
+ int32_t generation;
+ CHECK(msg->findInt32("generation", &generation));
+
+ if (generation != mSeekGeneration) {
+ return;
+ }
+
+ if (mEOSPending) {
+ signalSourceEOS(ERROR_END_OF_STREAM);
+ mEOSPending = false;
+ }
+}
+
+void NuPlayer::RTSPSource::postSourceEOSIfNecessary() {
+ const bool audio = true;
+ const bool video = false;
+ // If a source has detected near end, give it some time to retrieve more
+ // data before signaling EOS
+ if (sourceNearEOS(audio) || sourceNearEOS(video)) {
+ if (!mEOSPending) {
+ sp<AMessage> msg = new AMessage(kWhatSignalEOS, this);
+ msg->setInt32("generation", mSeekGeneration);
+ msg->post(kNearEOSTimeoutUs);
+ mEOSPending = true;
+ }
+ }
+}
+
void NuPlayer::RTSPSource::onMessageReceived(const sp<AMessage> &msg) {
if (msg->what() == kWhatDisconnect) {
sp<AReplyToken> replyID;
@@ -402,6 +472,9 @@
} else if (msg->what() == kWhatPollBuffering) {
onPollBuffering();
return;
+ } else if (msg->what() == kWhatSignalEOS) {
+ onSignalEOS(msg);
+ return;
}
CHECK_EQ(msg->what(), (int)kWhatNotify);
@@ -511,16 +584,10 @@
}
if (err != OK) {
- sp<AnotherPacketSource> source = getSource(false /* audio */);
- if (source != NULL) {
- source->signalEOS(err);
- }
-
- source = getSource(true /* audio */);
- if (source != NULL) {
- source->signalEOS(err);
- }
+ signalSourceEOS(err);
}
+
+ postSourceEOSIfNecessary();
break;
}
@@ -548,6 +615,7 @@
source->queueAccessUnit(accessUnit);
}
+ postSourceEOSIfNecessary();
break;
}
@@ -558,17 +626,7 @@
CHECK_NE(finalResult, (status_t)OK);
if (mTSParser != NULL) {
- sp<AnotherPacketSource> source = getSource(false /* audio */);
- if (source != NULL) {
- source->signalEOS(finalResult);
- }
-
- source = getSource(true /* audio */);
- if (source != NULL) {
- source->signalEOS(finalResult);
- }
-
- return;
+ signalSourceEOS(finalResult);
}
size_t trackIndex;
diff --git a/media/libmediaplayerservice/nuplayer/RTSPSource.h b/media/libmediaplayerservice/nuplayer/RTSPSource.h
index a6a7644..c7834ef 100644
--- a/media/libmediaplayerservice/nuplayer/RTSPSource.h
+++ b/media/libmediaplayerservice/nuplayer/RTSPSource.h
@@ -64,6 +64,7 @@
kWhatDisconnect = 'disc',
kWhatPerformSeek = 'seek',
kWhatPollBuffering = 'poll',
+ kWhatSignalEOS = 'eos ',
};
enum State {
@@ -106,6 +107,7 @@
Mutex mBufferingLock;
bool mBuffering;
bool mInPreparationPhase;
+ bool mEOSPending;
sp<ALooper> mLooper;
sp<MyHandler> mHandler;
@@ -133,7 +135,12 @@
void performSeek(int64_t seekTimeUs);
void schedulePollBuffering();
- void checkBuffering(bool *prepared, bool *underflow, bool *overflow, bool *startServer);
+ void checkBuffering(
+ bool *prepared,
+ bool *underflow,
+ bool *overflow,
+ bool *startServer,
+ bool *finished);
void onPollBuffering();
bool haveSufficientDataOnAllTracks();
@@ -144,6 +151,13 @@
bool stopBufferingIfNecessary();
void finishSeek(status_t err);
+ void postSourceEOSIfNecessary();
+ void signalSourceEOS(status_t result);
+ void onSignalEOS(const sp<AMessage> &msg);
+
+ bool sourceNearEOS(bool audio);
+ bool sourceReachedEOS(bool audio);
+
DISALLOW_EVIL_CONSTRUCTORS(RTSPSource);
};
diff --git a/media/libmediaplayerservice/nuplayer/StreamingSource.cpp b/media/libmediaplayerservice/nuplayer/StreamingSource.cpp
index 47ec3e9..7f9f913 100644
--- a/media/libmediaplayerservice/nuplayer/StreamingSource.cpp
+++ b/media/libmediaplayerservice/nuplayer/StreamingSource.cpp
@@ -233,7 +233,8 @@
return format;
}
status_t err = convertMetaDataToMessage(meta, &format);
- if (err != OK) {
+ if (err != OK) { // format may have been cleared on error
+ format = new AMessage;
format->setInt32("err", err);
}
return format;
diff --git a/media/libstagefright/AACExtractor.cpp b/media/libstagefright/AACExtractor.cpp
index 19efc53..7449aa7 100644
--- a/media/libstagefright/AACExtractor.cpp
+++ b/media/libstagefright/AACExtractor.cpp
@@ -174,7 +174,9 @@
if (mDataSource->getSize(&streamSize) == OK) {
while (offset < streamSize) {
if ((frameSize = getAdtsFrameLength(source, offset, NULL)) == 0) {
- return;
+ ALOGW("prematured AAC stream (%lld vs %lld)",
+ (long long)offset, (long long)streamSize);
+ break;
}
mOffsetVector.push(offset);
diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp
index 07f5960..e7057ce 100644
--- a/media/libstagefright/ACodec.cpp
+++ b/media/libstagefright/ACodec.cpp
@@ -675,7 +675,10 @@
}
int usageBits = 0;
- status_t err = setupNativeWindowSizeFormatAndUsage(nativeWindow, &usageBits);
+ // no need to reconnect as we will not dequeue all buffers
+ status_t err = setupNativeWindowSizeFormatAndUsage(
+ nativeWindow, &usageBits,
+ !storingMetadataInDecodedBuffers() || mLegacyAdaptiveExperiment /* reconnect */);
if (err != OK) {
return err;
}
@@ -949,7 +952,8 @@
}
status_t ACodec::setupNativeWindowSizeFormatAndUsage(
- ANativeWindow *nativeWindow /* nonnull */, int *finalUsage /* nonnull */) {
+ ANativeWindow *nativeWindow /* nonnull */, int *finalUsage /* nonnull */,
+ bool reconnect) {
OMX_PARAM_PORTDEFINITIONTYPE def;
InitOMXParams(&def);
def.nPortIndex = kPortIndexOutput;
@@ -987,12 +991,14 @@
def.format.video.nFrameHeight,
def.format.video.eColorFormat,
mRotationDegrees,
- usage);
+ usage,
+ reconnect);
}
status_t ACodec::configureOutputBuffersFromNativeWindow(
OMX_U32 *bufferCount, OMX_U32 *bufferSize,
- OMX_U32 *minUndequeuedBuffers) {
+ OMX_U32 *minUndequeuedBuffers, bool preregister) {
+
OMX_PARAM_PORTDEFINITIONTYPE def;
InitOMXParams(&def);
def.nPortIndex = kPortIndexOutput;
@@ -1001,7 +1007,8 @@
mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
if (err == OK) {
- err = setupNativeWindowSizeFormatAndUsage(mNativeWindow.get(), &mNativeWindowUsageBits);
+ err = setupNativeWindowSizeFormatAndUsage(
+ mNativeWindow.get(), &mNativeWindowUsageBits, preregister /* reconnect */);
}
if (err != OK) {
mNativeWindowUsageBits = 0;
@@ -1083,7 +1090,7 @@
status_t ACodec::allocateOutputBuffersFromNativeWindow() {
OMX_U32 bufferCount, bufferSize, minUndequeuedBuffers;
status_t err = configureOutputBuffersFromNativeWindow(
- &bufferCount, &bufferSize, &minUndequeuedBuffers);
+ &bufferCount, &bufferSize, &minUndequeuedBuffers, true /* preregister */);
if (err != 0)
return err;
mNumUndequeuedBuffers = minUndequeuedBuffers;
@@ -1169,7 +1176,8 @@
status_t ACodec::allocateOutputMetadataBuffers() {
OMX_U32 bufferCount, bufferSize, minUndequeuedBuffers;
status_t err = configureOutputBuffersFromNativeWindow(
- &bufferCount, &bufferSize, &minUndequeuedBuffers);
+ &bufferCount, &bufferSize, &minUndequeuedBuffers,
+ mLegacyAdaptiveExperiment /* preregister */);
if (err != 0)
return err;
mNumUndequeuedBuffers = minUndequeuedBuffers;
@@ -2455,6 +2463,109 @@
return OK;
}
+status_t ACodec::configureTemporalLayers(
+ const sp<AMessage> &msg, bool inConfigure, sp<AMessage> &outputFormat) {
+ if (!mIsVideo || !mIsEncoder) {
+ return INVALID_OPERATION;
+ }
+
+ AString tsSchema;
+ if (!msg->findString("ts-schema", &tsSchema)) {
+ return OK;
+ }
+
+ unsigned int numLayers = 0;
+ unsigned int numBLayers = 0;
+ int tags;
+ char dummy;
+ OMX_VIDEO_ANDROID_TEMPORALLAYERINGPATTERNTYPE pattern =
+ OMX_VIDEO_AndroidTemporalLayeringPatternNone;
+ if (sscanf(tsSchema.c_str(), "webrtc.vp8.%u-layer%c", &numLayers, &dummy) == 1
+ && numLayers > 0) {
+ pattern = OMX_VIDEO_AndroidTemporalLayeringPatternWebRTC;
+ } else if ((tags = sscanf(tsSchema.c_str(), "android.generic.%u%c%u%c",
+ &numLayers, &dummy, &numBLayers, &dummy))
+ && (tags == 1 || (tags == 3 && dummy == '+'))
+ && numLayers > 0 && numLayers < UINT32_MAX - numBLayers) {
+ numLayers += numBLayers;
+ pattern = OMX_VIDEO_AndroidTemporalLayeringPatternAndroid;
+ } else {
+ ALOGI("Ignoring unsupported ts-schema [%s]", tsSchema.c_str());
+ return BAD_VALUE;
+ }
+
+ OMX_VIDEO_PARAM_ANDROID_TEMPORALLAYERINGTYPE layerParams;
+ InitOMXParams(&layerParams);
+ layerParams.nPortIndex = kPortIndexOutput;
+
+ status_t err = mOMX->getParameter(
+ mNode, (OMX_INDEXTYPE)OMX_IndexParamAndroidVideoTemporalLayering,
+ &layerParams, sizeof(layerParams));
+
+ if (err != OK) {
+ return err;
+ } else if (!(layerParams.eSupportedPatterns & pattern)) {
+ return BAD_VALUE;
+ }
+
+ numLayers = min(numLayers, layerParams.nLayerCountMax);
+ numBLayers = min(numBLayers, layerParams.nBLayerCountMax);
+
+ if (!inConfigure) {
+ OMX_VIDEO_CONFIG_ANDROID_TEMPORALLAYERINGTYPE layerConfig;
+ InitOMXParams(&layerConfig);
+ layerConfig.nPortIndex = kPortIndexOutput;
+ layerConfig.ePattern = pattern;
+ layerConfig.nPLayerCountActual = numLayers - numBLayers;
+ layerConfig.nBLayerCountActual = numBLayers;
+ layerConfig.bBitrateRatiosSpecified = OMX_FALSE;
+
+ err = mOMX->setConfig(
+ mNode, (OMX_INDEXTYPE)OMX_IndexConfigAndroidVideoTemporalLayering,
+ &layerConfig, sizeof(layerConfig));
+ } else {
+ layerParams.ePattern = pattern;
+ layerParams.nPLayerCountActual = numLayers - numBLayers;
+ layerParams.nBLayerCountActual = numBLayers;
+ layerParams.bBitrateRatiosSpecified = OMX_FALSE;
+
+ err = mOMX->setParameter(
+ mNode, (OMX_INDEXTYPE)OMX_IndexParamAndroidVideoTemporalLayering,
+ &layerParams, sizeof(layerParams));
+ }
+
+ AString configSchema;
+ if (pattern == OMX_VIDEO_AndroidTemporalLayeringPatternAndroid) {
+ configSchema = AStringPrintf("android.generic.%u+%u", numLayers - numBLayers, numBLayers);
+ } else if (pattern == OMX_VIDEO_AndroidTemporalLayeringPatternWebRTC) {
+ configSchema = AStringPrintf("webrtc.vp8.%u", numLayers);
+ }
+
+ if (err != OK) {
+ ALOGW("Failed to set temporal layers to %s (requested %s)",
+ configSchema.c_str(), tsSchema.c_str());
+ return err;
+ }
+
+ err = mOMX->getParameter(
+ mNode, (OMX_INDEXTYPE)OMX_IndexParamAndroidVideoTemporalLayering,
+ &layerParams, sizeof(layerParams));
+
+ if (err == OK) {
+ ALOGD("Temporal layers requested:%s configured:%s got:%s(%u: P=%u, B=%u)",
+ tsSchema.c_str(), configSchema.c_str(),
+ asString(layerParams.ePattern), layerParams.ePattern,
+ layerParams.nPLayerCountActual, layerParams.nBLayerCountActual);
+
+ if (outputFormat.get() == mOutputFormat.get()) {
+ mOutputFormat = mOutputFormat->dup(); // trigger an output format change event
+ }
+ // assume we got what we configured
+ outputFormat->setString("ts-schema", configSchema);
+ }
+ return err;
+}
+
status_t ACodec::setMinBufferSize(OMX_U32 portIndex, size_t size) {
OMX_PARAM_PORTDEFINITIONTYPE def;
InitOMXParams(&def);
@@ -3140,6 +3251,29 @@
return ERROR_UNSUPPORTED;
}
+status_t ACodec::setPortBufferNum(OMX_U32 portIndex, int bufferNum) {
+ OMX_PARAM_PORTDEFINITIONTYPE def;
+ InitOMXParams(&def);
+ def.nPortIndex = portIndex;
+ status_t err;
+ ALOGD("Setting [%s] %s port buffer number: %d", mComponentName.c_str(),
+ portIndex == kPortIndexInput ? "input" : "output", bufferNum);
+ err = mOMX->getParameter(
+ mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
+ if (err != OK) {
+ return err;
+ }
+ def.nBufferCountActual = bufferNum;
+ err = mOMX->setParameter(
+ mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
+ if (err != OK) {
+ // Component could reject this request.
+ ALOGW("Fail to set [%s] %s port buffer number: %d", mComponentName.c_str(),
+ portIndex == kPortIndexInput ? "input" : "output", bufferNum);
+ }
+ return OK;
+}
+
status_t ACodec::setupVideoDecoder(
const char *mime, const sp<AMessage> &msg, bool haveNativeWindow,
bool usingSwRenderer, sp<AMessage> &outputFormat) {
@@ -3196,6 +3330,24 @@
return err;
}
+ // Set the component input buffer number to be |tmp|. If succeed,
+ // component will set input port buffer number to be |tmp|. If fail,
+ // component will keep the same buffer number as before.
+ if (msg->findInt32("android._num-input-buffers", &tmp)) {
+ err = setPortBufferNum(kPortIndexInput, tmp);
+ if (err != OK)
+ return err;
+ }
+
+ // Set the component output buffer number to be |tmp|. If succeed,
+ // component will set output port buffer number to be |tmp|. If fail,
+ // component will keep the same buffer number as before.
+ if (msg->findInt32("android._num-output-buffers", &tmp)) {
+ err = setPortBufferNum(kPortIndexOutput, tmp);
+ if (err != OK)
+ return err;
+ }
+
int32_t frameRateInt;
float frameRateFloat;
if (!msg->findFloat("frame-rate", &frameRateFloat)) {
@@ -3722,13 +3874,17 @@
case OMX_VIDEO_CodingVP8:
case OMX_VIDEO_CodingVP9:
- err = setupVPXEncoderParameters(msg);
+ err = setupVPXEncoderParameters(msg, outputFormat);
break;
default:
break;
}
+ if (err != OK) {
+ return err;
+ }
+
// Set up color aspects on input, but propagate them to the output format, as they will
// not be read back from encoder.
err = setColorAspectsForVideoEncoder(msg, outputFormat, inputFormat);
@@ -3747,6 +3903,29 @@
err = OK;
}
+ if (err != OK) {
+ return err;
+ }
+
+ switch (compressionFormat) {
+ case OMX_VIDEO_CodingAVC:
+ case OMX_VIDEO_CodingHEVC:
+ err = configureTemporalLayers(msg, true /* inConfigure */, outputFormat);
+ if (err != OK) {
+ err = OK; // ignore failure
+ }
+ break;
+
+ case OMX_VIDEO_CodingVP8:
+ case OMX_VIDEO_CodingVP9:
+ // TODO: do we need to support android.generic layering? webrtc layering is
+ // already set up in setupVPXEncoderParameters.
+ break;
+
+ default:
+ break;
+ }
+
if (err == OK) {
ALOGI("setupVideoEncoder succeeded");
}
@@ -3791,14 +3970,31 @@
return err;
}
-static OMX_U32 setPFramesSpacing(int32_t iFramesInterval, int32_t frameRate) {
- if (iFramesInterval < 0) {
- return 0xFFFFFFFF;
- } else if (iFramesInterval == 0) {
+static OMX_U32 setPFramesSpacing(
+ float iFramesInterval /* seconds */, int32_t frameRate, uint32_t BFramesSpacing = 0) {
+ // BFramesSpacing is the number of B frames between I/P frames
+ // PFramesSpacing (the value to be returned) is the number of P frames between I frames
+ //
+ // keyFrameInterval = ((PFramesSpacing + 1) * BFramesSpacing) + PFramesSpacing + 1
+ // ^^^ ^^^ ^^^
+ // number of B frames number of P I frame
+ //
+ // = (PFramesSpacing + 1) * (BFramesSpacing + 1)
+ //
+ // E.g.
+ // I P I : I-interval: 8, nPFrames 1, nBFrames 3
+ // BBB BBB
+
+ if (iFramesInterval < 0) { // just 1 key frame
+ return 0xFFFFFFFE; // don't use maxint as key-frame-interval calculation will add 1
+ } else if (iFramesInterval == 0) { // just key frames
return 0;
}
- OMX_U32 ret = frameRate * iFramesInterval;
- return ret;
+
+ // round down as key-frame-interval is an upper limit
+ uint32_t keyFrameInterval = uint32_t(frameRate * iFramesInterval);
+ OMX_U32 ret = keyFrameInterval / (BFramesSpacing + 1);
+ return ret > 0 ? ret - 1 : 0;
}
static OMX_VIDEO_CONTROLRATETYPE getBitrateMode(const sp<AMessage> &msg) {
@@ -3811,9 +4007,10 @@
}
status_t ACodec::setupMPEG4EncoderParameters(const sp<AMessage> &msg) {
- int32_t bitrate, iFrameInterval;
+ int32_t bitrate;
+ float iFrameInterval;
if (!msg->findInt32("bitrate", &bitrate)
- || !msg->findInt32("i-frame-interval", &iFrameInterval)) {
+ || !msg->findAsFloat("i-frame-interval", &iFrameInterval)) {
return INVALID_OPERATION;
}
@@ -3846,11 +4043,11 @@
mpeg4type.nAllowedPictureTypes =
OMX_VIDEO_PictureTypeI | OMX_VIDEO_PictureTypeP;
- mpeg4type.nPFrames = setPFramesSpacing(iFrameInterval, frameRate);
+ mpeg4type.nBFrames = 0;
+ mpeg4type.nPFrames = setPFramesSpacing(iFrameInterval, frameRate, mpeg4type.nBFrames);
if (mpeg4type.nPFrames == 0) {
mpeg4type.nAllowedPictureTypes = OMX_VIDEO_PictureTypeI;
}
- mpeg4type.nBFrames = 0;
mpeg4type.nIDCVLCThreshold = 0;
mpeg4type.bACPred = OMX_TRUE;
mpeg4type.nMaxPacketSize = 256;
@@ -3892,9 +4089,10 @@
}
status_t ACodec::setupH263EncoderParameters(const sp<AMessage> &msg) {
- int32_t bitrate, iFrameInterval;
+ int32_t bitrate;
+ float iFrameInterval;
if (!msg->findInt32("bitrate", &bitrate)
- || !msg->findInt32("i-frame-interval", &iFrameInterval)) {
+ || !msg->findAsFloat("i-frame-interval", &iFrameInterval)) {
return INVALID_OPERATION;
}
@@ -3923,11 +4121,11 @@
h263type.nAllowedPictureTypes =
OMX_VIDEO_PictureTypeI | OMX_VIDEO_PictureTypeP;
- h263type.nPFrames = setPFramesSpacing(iFrameInterval, frameRate);
+ h263type.nBFrames = 0;
+ h263type.nPFrames = setPFramesSpacing(iFrameInterval, frameRate, h263type.nBFrames);
if (h263type.nPFrames == 0) {
h263type.nAllowedPictureTypes = OMX_VIDEO_PictureTypeI;
}
- h263type.nBFrames = 0;
int32_t profile;
if (msg->findInt32("profile", &profile)) {
@@ -4020,9 +4218,10 @@
}
status_t ACodec::setupAVCEncoderParameters(const sp<AMessage> &msg) {
- int32_t bitrate, iFrameInterval;
+ int32_t bitrate;
+ float iFrameInterval;
if (!msg->findInt32("bitrate", &bitrate)
- || !msg->findInt32("i-frame-interval", &iFrameInterval)) {
+ || !msg->findAsFloat("i-frame-interval", &iFrameInterval)) {
return INVALID_OPERATION;
}
@@ -4078,8 +4277,17 @@
h264type.eProfile = static_cast<OMX_VIDEO_AVCPROFILETYPE>(profile);
h264type.eLevel = static_cast<OMX_VIDEO_AVCLEVELTYPE>(level);
} else {
- // Use baseline profile for AVC recording if profile is not specified.
h264type.eProfile = OMX_VIDEO_AVCProfileBaseline;
+#if 0 /* DON'T YET DEFAULT TO HIGHEST PROFILE */
+ // Use largest supported profile for AVC recording if profile is not specified.
+ for (OMX_VIDEO_AVCPROFILETYPE profile : {
+ OMX_VIDEO_AVCProfileHigh, OMX_VIDEO_AVCProfileMain }) {
+ if (verifySupportForProfileAndLevel(profile, 0) == OK) {
+ h264type.eProfile = profile;
+ break;
+ }
+ }
+#endif
}
ALOGI("setupAVCEncoderParameters with [profile: %s] [level: %s]",
@@ -4090,7 +4298,7 @@
h264type.bUseHadamard = OMX_TRUE;
h264type.nRefFrames = 1;
h264type.nBFrames = 0;
- h264type.nPFrames = setPFramesSpacing(iFrameInterval, frameRate);
+ h264type.nPFrames = setPFramesSpacing(iFrameInterval, frameRate, h264type.nBFrames);
if (h264type.nPFrames == 0) {
h264type.nAllowedPictureTypes = OMX_VIDEO_PictureTypeI;
}
@@ -4108,7 +4316,7 @@
h264type.bUseHadamard = OMX_TRUE;
h264type.nRefFrames = 2;
h264type.nBFrames = 1;
- h264type.nPFrames = setPFramesSpacing(iFrameInterval, frameRate);
+ h264type.nPFrames = setPFramesSpacing(iFrameInterval, frameRate, h264type.nBFrames);
h264type.nAllowedPictureTypes =
OMX_VIDEO_PictureTypeI | OMX_VIDEO_PictureTypeP | OMX_VIDEO_PictureTypeB;
h264type.nRefIdx10ActiveMinus1 = 0;
@@ -4140,13 +4348,42 @@
return err;
}
+ // TRICKY: if we are enabling temporal layering as well, some codecs may not support layering
+ // when B-frames are enabled. Detect this now so we can disable B frames if temporal layering
+ // is preferred.
+ AString tsSchema;
+ int32_t preferBFrames = (int32_t)false;
+ if (msg->findString("ts-schema", &tsSchema)
+ && (!msg->findInt32("android._prefer-b-frames", &preferBFrames) || !preferBFrames)) {
+ OMX_VIDEO_PARAM_ANDROID_TEMPORALLAYERINGTYPE layering;
+ InitOMXParams(&layering);
+ layering.nPortIndex = kPortIndexOutput;
+ if (mOMX->getParameter(
+ mNode, (OMX_INDEXTYPE)OMX_IndexParamAndroidVideoTemporalLayering,
+ &layering, sizeof(layering)) == OK
+ && layering.eSupportedPatterns
+ && layering.nBLayerCountMax == 0) {
+ h264type.nBFrames = 0;
+ h264type.nPFrames = setPFramesSpacing(iFrameInterval, frameRate, h264type.nBFrames);
+ h264type.nAllowedPictureTypes &= ~OMX_VIDEO_PictureTypeB;
+ ALOGI("disabling B-frames");
+ err = mOMX->setParameter(
+ mNode, OMX_IndexParamVideoAvc, &h264type, sizeof(h264type));
+
+ if (err != OK) {
+ return err;
+ }
+ }
+ }
+
return configureBitrate(bitrate, bitrateMode);
}
status_t ACodec::setupHEVCEncoderParameters(const sp<AMessage> &msg) {
- int32_t bitrate, iFrameInterval;
+ int32_t bitrate;
+ float iFrameInterval;
if (!msg->findInt32("bitrate", &bitrate)
- || !msg->findInt32("i-frame-interval", &iFrameInterval)) {
+ || !msg->findAsFloat("i-frame-interval", &iFrameInterval)) {
return INVALID_OPERATION;
}
@@ -4188,7 +4425,7 @@
hevcType.eLevel = static_cast<OMX_VIDEO_HEVCLEVELTYPE>(level);
}
// TODO: finer control?
- hevcType.nKeyFrameInterval = setPFramesSpacing(iFrameInterval, frameRate);
+ hevcType.nKeyFrameInterval = setPFramesSpacing(iFrameInterval, frameRate) + 1;
err = mOMX->setParameter(
mNode, (OMX_INDEXTYPE)OMX_IndexParamVideoHevc, &hevcType, sizeof(hevcType));
@@ -4199,9 +4436,9 @@
return configureBitrate(bitrate, bitrateMode);
}
-status_t ACodec::setupVPXEncoderParameters(const sp<AMessage> &msg) {
+status_t ACodec::setupVPXEncoderParameters(const sp<AMessage> &msg, sp<AMessage> &outputFormat) {
int32_t bitrate;
- int32_t iFrameInterval = 0;
+ float iFrameInterval = 0;
size_t tsLayers = 0;
OMX_VIDEO_ANDROID_VPXTEMPORALLAYERPATTERNTYPE pattern =
OMX_VIDEO_VPXTemporalLayerPatternNone;
@@ -4215,7 +4452,7 @@
if (!msg->findInt32("bitrate", &bitrate)) {
return INVALID_OPERATION;
}
- msg->findInt32("i-frame-interval", &iFrameInterval);
+ msg->findAsFloat("i-frame-interval", &iFrameInterval);
OMX_VIDEO_CONTROLRATETYPE bitrateMode = getBitrateMode(msg);
@@ -4229,19 +4466,31 @@
}
AString tsSchema;
+ OMX_VIDEO_ANDROID_TEMPORALLAYERINGPATTERNTYPE tsType =
+ OMX_VIDEO_AndroidTemporalLayeringPatternNone;
+
if (msg->findString("ts-schema", &tsSchema)) {
- if (tsSchema == "webrtc.vp8.1-layer") {
+ unsigned int numLayers = 0;
+ unsigned int numBLayers = 0;
+ int tags;
+ char dummy;
+ if (sscanf(tsSchema.c_str(), "webrtc.vp8.%u-layer%c", &numLayers, &dummy) == 1
+ && numLayers > 0) {
pattern = OMX_VIDEO_VPXTemporalLayerPatternWebRTC;
- tsLayers = 1;
- } else if (tsSchema == "webrtc.vp8.2-layer") {
+ tsType = OMX_VIDEO_AndroidTemporalLayeringPatternWebRTC;
+ tsLayers = numLayers;
+ } else if ((tags = sscanf(tsSchema.c_str(), "android.generic.%u%c%u%c",
+ &numLayers, &dummy, &numBLayers, &dummy))
+ && (tags == 1 || (tags == 3 && dummy == '+'))
+ && numLayers > 0 && numLayers < UINT32_MAX - numBLayers) {
pattern = OMX_VIDEO_VPXTemporalLayerPatternWebRTC;
- tsLayers = 2;
- } else if (tsSchema == "webrtc.vp8.3-layer") {
- pattern = OMX_VIDEO_VPXTemporalLayerPatternWebRTC;
- tsLayers = 3;
+ // VPX does not have a concept of B-frames, so just count all layers
+ tsType = OMX_VIDEO_AndroidTemporalLayeringPatternAndroid;
+ tsLayers = numLayers + numBLayers;
} else {
- ALOGW("Unsupported ts-schema [%s]", tsSchema.c_str());
+ ALOGW("Ignoring unsupported ts-schema [%s]", tsSchema.c_str());
}
+ tsLayers = min(tsLayers, (size_t)OMX_VIDEO_ANDROID_MAXVP8TEMPORALLAYERS);
}
OMX_VIDEO_PARAM_ANDROID_VP8ENCODERTYPE vp8type;
@@ -4253,7 +4502,7 @@
if (err == OK) {
if (iFrameInterval > 0) {
- vp8type.nKeyFrameInterval = setPFramesSpacing(iFrameInterval, frameRate);
+ vp8type.nKeyFrameInterval = setPFramesSpacing(iFrameInterval, frameRate) + 1;
}
vp8type.eTemporalPattern = pattern;
vp8type.nTemporalLayerCount = tsLayers;
@@ -4273,6 +4522,12 @@
&vp8type, sizeof(vp8type));
if (err != OK) {
ALOGW("Extended VP8 parameters set failed: %d", err);
+ } else if (tsType == OMX_VIDEO_AndroidTemporalLayeringPatternWebRTC) {
+ // advertise even single layer WebRTC layering, as it is defined
+ outputFormat->setString("ts-schema", AStringPrintf("webrtc.vp8.%u-layer", tsLayers));
+ } else if (tsLayers > 0) {
+ // tsType == OMX_VIDEO_AndroidTemporalLayeringPatternAndroid
+ outputFormat->setString("ts-schema", AStringPrintf("android.generic.%u", tsLayers));
}
}
@@ -4803,32 +5058,21 @@
sizeof(vp8type));
if (err == OK) {
- AString tsSchema = "none";
- if (vp8type.eTemporalPattern
- == OMX_VIDEO_VPXTemporalLayerPatternWebRTC) {
- switch (vp8type.nTemporalLayerCount) {
- case 1:
- {
- tsSchema = "webrtc.vp8.1-layer";
- break;
- }
- case 2:
- {
- tsSchema = "webrtc.vp8.2-layer";
- break;
- }
- case 3:
- {
- tsSchema = "webrtc.vp8.3-layer";
- break;
- }
- default:
- {
- break;
- }
+ if (vp8type.eTemporalPattern == OMX_VIDEO_VPXTemporalLayerPatternWebRTC
+ && vp8type.nTemporalLayerCount > 0
+ && vp8type.nTemporalLayerCount
+ <= OMX_VIDEO_ANDROID_MAXVP8TEMPORALLAYERS) {
+ // advertise as android.generic if we configured for android.generic
+ AString origSchema;
+ if (notify->findString("ts-schema", &origSchema)
+ && origSchema.startsWith("android.generic")) {
+ notify->setString("ts-schema", AStringPrintf(
+ "android.generic.%u", vp8type.nTemporalLayerCount));
+ } else {
+ notify->setString("ts-schema", AStringPrintf(
+ "webrtc.vp8.%u-layer", vp8type.nTemporalLayerCount));
}
}
- notify->setString("ts-schema", tsSchema);
}
// Fall through to set up mime.
}
@@ -7237,6 +7481,23 @@
}
}
+ int64_t timeOffsetUs;
+ if (params->findInt64("time-offset-us", &timeOffsetUs)) {
+ status_t err = mOMX->setInternalOption(
+ mNode,
+ kPortIndexInput,
+ IOMX::INTERNAL_OPTION_TIME_OFFSET,
+ &timeOffsetUs,
+ sizeof(timeOffsetUs));
+
+ if (err != OK) {
+ ALOGE("[%s] Unable to set input buffer time offset (err %d)",
+ mComponentName.c_str(),
+ err);
+ return err;
+ }
+ }
+
int64_t skipFramesBeforeUs;
if (params->findInt64("skip-frames-before", &skipFramesBeforeUs)) {
status_t err =
@@ -7301,7 +7562,12 @@
}
}
- return OK;
+ status_t err = configureTemporalLayers(params, false /* inConfigure */, mOutputFormat);
+ if (err != OK) {
+ err = OK; // ignore failure
+ }
+
+ return err;
}
void ACodec::onSignalEndOfInputStream() {
diff --git a/media/libstagefright/AMRExtractor.cpp b/media/libstagefright/AMRExtractor.cpp
index 1458802..0e98db8 100644
--- a/media/libstagefright/AMRExtractor.cpp
+++ b/media/libstagefright/AMRExtractor.cpp
@@ -100,7 +100,10 @@
static status_t getFrameSizeByOffset(const sp<DataSource> &source,
off64_t offset, bool isWide, size_t *frameSize) {
uint8_t header;
- if (source->readAt(offset, &header, 1) < 1) {
+ ssize_t count = source->readAt(offset, &header, 1);
+ if (count == 0) {
+ return ERROR_END_OF_STREAM;
+ } else if (count < 0) {
return ERROR_IO;
}
@@ -140,7 +143,10 @@
if (mDataSource->getSize(&streamSize) == OK) {
while (offset < streamSize) {
- if (getFrameSizeByOffset(source, offset, mIsWide, &frameSize) != OK) {
+ status_t status = getFrameSizeByOffset(source, offset, mIsWide, &frameSize);
+ if (status == ERROR_END_OF_STREAM) {
+ break;
+ } else if (status != OK) {
return;
}
diff --git a/media/libstagefright/Android.mk b/media/libstagefright/Android.mk
index c4214c6..604ad7c 100644
--- a/media/libstagefright/Android.mk
+++ b/media/libstagefright/Android.mk
@@ -99,6 +99,7 @@
LOCAL_STATIC_LIBRARIES := \
libstagefright_color_conversion \
+ libyuv_static \
libstagefright_aacenc \
libstagefright_matroska \
libstagefright_mediafilter \
diff --git a/media/libstagefright/CallbackDataSource.cpp b/media/libstagefright/CallbackDataSource.cpp
index f14d34d..0df7da4 100644
--- a/media/libstagefright/CallbackDataSource.cpp
+++ b/media/libstagefright/CallbackDataSource.cpp
@@ -109,6 +109,10 @@
}
}
+sp<DecryptHandle> CallbackDataSource::DrmInitialization(const char *mime) {
+ return mIDataSource->DrmInitialization(mime);
+}
+
TinyCacheSource::TinyCacheSource(const sp<DataSource>& source)
: mSource(source), mCachedOffset(0), mCachedSize(0) {
mName = String8::format("TinyCacheSource(%s)", mSource->toString().string());
@@ -146,12 +150,19 @@
}
}
+
// Fill the cache and copy to the caller.
const ssize_t numRead = mSource->readAt(offset, mCache, kCacheSize);
if (numRead <= 0) {
+ // Flush cache on error
+ mCachedSize = 0;
+ mCachedOffset = 0;
return numRead;
}
if ((size_t)numRead > kCacheSize) {
+ // Flush cache on error
+ mCachedSize = 0;
+ mCachedOffset = 0;
return ERROR_OUT_OF_RANGE;
}
@@ -172,4 +183,11 @@
return mSource->flags();
}
+sp<DecryptHandle> TinyCacheSource::DrmInitialization(const char *mime) {
+ // flush cache when DrmInitialization occurs since decrypted
+ // data may differ from what is in cache.
+ mCachedOffset = 0;
+ mCachedSize = 0;
+ return mSource->DrmInitialization(mime);
+}
} // namespace android
diff --git a/media/libstagefright/CameraSource.cpp b/media/libstagefright/CameraSource.cpp
index 5fc9237..408ad7a 100644
--- a/media/libstagefright/CameraSource.cpp
+++ b/media/libstagefright/CameraSource.cpp
@@ -1117,6 +1117,9 @@
int64_t token = IPCThreadState::self()->clearCallingIdentity();
mCamera->releaseRecordingFrameHandle(handle);
IPCThreadState::self()->restoreCallingIdentity(token);
+ } else {
+ native_handle_close(handle);
+ native_handle_delete(handle);
}
}
diff --git a/media/libstagefright/MPEG4Extractor.cpp b/media/libstagefright/MPEG4Extractor.cpp
index e873dce..6765282 100644
--- a/media/libstagefright/MPEG4Extractor.cpp
+++ b/media/libstagefright/MPEG4Extractor.cpp
@@ -44,6 +44,7 @@
#include <byteswap.h>
#include "include/ID3.h"
+#include "include/avc_utils.h"
#ifndef UINT32_MAX
#define UINT32_MAX (4294967295U)
@@ -54,6 +55,10 @@
enum {
// max track header chunk to return
kMaxTrackHeaderSize = 32,
+
+ // maximum size of an atom. Some atoms can be bigger according to the spec,
+ // but we only allow up to this size.
+ kMaxAtomSize = 64 * 1024 * 1024,
};
class MPEG4Source : public MediaSource {
@@ -74,6 +79,7 @@
virtual sp<MetaData> getFormat();
virtual status_t read(MediaBuffer **buffer, const ReadOptions *options = NULL);
+ virtual bool supportNonblockingRead() { return true; }
virtual status_t fragmentedRead(MediaBuffer **buffer, const ReadOptions *options = NULL);
protected:
@@ -368,8 +374,8 @@
mMdatFound(false),
mDataSource(source),
mInitCheck(NO_INIT),
- mHasVideo(false),
mHeaderTimescale(0),
+ mIsQT(false),
mFirstTrack(NULL),
mLastTrack(NULL),
mFileMetaData(new MetaData),
@@ -472,7 +478,8 @@
} else {
uint32_t sampleIndex;
uint32_t sampleTime;
- if (track->sampleTable->findThumbnailSample(&sampleIndex) == OK
+ if (track->timescale != 0 &&
+ track->sampleTable->findThumbnailSample(&sampleIndex) == OK
&& track->sampleTable->getMetaDataForSample(
sampleIndex, NULL /* offset */, NULL /* size */,
&sampleTime) == OK) {
@@ -538,11 +545,13 @@
}
if (mInitCheck == OK) {
- if (mHasVideo) {
+ if (findTrackByMimePrefix("video/") != NULL) {
mFileMetaData->setCString(
kKeyMIMEType, MEDIA_MIMETYPE_CONTAINER_MPEG4);
- } else {
+ } else if (findTrackByMimePrefix("audio/") != NULL) {
mFileMetaData->setCString(kKeyMIMEType, "audio/mp4");
+ } else {
+ mFileMetaData->setCString(kKeyMIMEType, "application/octet-stream");
}
} else {
mInitCheck = err;
@@ -877,6 +886,13 @@
ALOGE("b/23540914");
return ERROR_MALFORMED;
}
+ if (chunk_type != FOURCC('m', 'd', 'a', 't') && chunk_data_size > kMaxAtomSize) {
+ char errMsg[100];
+ sprintf(errMsg, "%s atom has size %" PRId64, chunk, chunk_data_size);
+ ALOGE("%s (b/28615448)", errMsg);
+ android_errorWriteWithInfoLog(0x534e4554, "28615448", -1, errMsg, strlen(errMsg));
+ return ERROR_MALFORMED;
+ }
if (chunk_type != FOURCC('c', 'p', 'r', 't')
&& chunk_type != FOURCC('c', 'o', 'v', 'r')
@@ -913,7 +929,12 @@
case FOURCC('s', 'i', 'n', 'f'):
case FOURCC('s', 'c', 'h', 'i'):
case FOURCC('e', 'd', 't', 's'):
+ case FOURCC('w', 'a', 'v', 'e'):
{
+ if (chunk_type == FOURCC('m', 'o', 'o', 'v') && depth != 0) {
+ ALOGE("moov: depth %d", depth);
+ return ERROR_MALFORMED;
+ }
if (chunk_type == FOURCC('m', 'o', 'o', 'f') && !mMoofFound) {
// store the offset of the first segment
mMoofFound = true;
@@ -942,6 +963,10 @@
bool isTrack = false;
if (chunk_type == FOURCC('t', 'r', 'a', 'k')) {
+ if (depth != 1) {
+ ALOGE("trak: depth %d", depth);
+ return ERROR_MALFORMED;
+ }
isTrack = true;
Track *track = new Track;
@@ -965,6 +990,10 @@
while (*offset < stop_offset) {
status_t err = parseChunk(offset, depth + 1);
if (err != OK) {
+ if (isTrack) {
+ mLastTrack->skipTrack = true;
+ break;
+ }
return err;
}
}
@@ -1310,10 +1339,6 @@
case FOURCC('s', 't', 's', 'd'):
{
- if (chunk_data_size < 8) {
- return ERROR_MALFORMED;
- }
-
uint8_t buffer[8];
if (chunk_data_size < (off64_t)sizeof(buffer)) {
return ERROR_MALFORMED;
@@ -1370,6 +1395,13 @@
case FOURCC('s', 'a', 'm', 'r'):
case FOURCC('s', 'a', 'w', 'b'):
{
+ if (mIsQT && chunk_type == FOURCC('m', 'p', '4', 'a')
+ && depth >= 1 && mPath[depth - 1] == FOURCC('w', 'a', 'v', 'e')) {
+ // Ignore mp4a embedded in QT wave atom
+ *offset += chunk_size;
+ break;
+ }
+
uint8_t buffer[8 + 20];
if (chunk_data_size < (ssize_t)sizeof(buffer)) {
// Basic AudioSampleEntry size.
@@ -1382,6 +1414,7 @@
}
uint16_t data_ref_index __unused = U16_AT(&buffer[6]);
+ uint16_t version = U16_AT(&buffer[8]);
uint32_t num_channels = U16_AT(&buffer[16]);
uint16_t sample_size = U16_AT(&buffer[18]);
@@ -1390,6 +1423,42 @@
if (mLastTrack == NULL)
return ERROR_MALFORMED;
+ off64_t stop_offset = *offset + chunk_size;
+ *offset = data_offset + sizeof(buffer);
+
+ if (mIsQT && chunk_type == FOURCC('m', 'p', '4', 'a')) {
+ if (version == 1) {
+ if (mDataSource->readAt(*offset, buffer, 16) < 16) {
+ return ERROR_IO;
+ }
+
+#if 0
+ U32_AT(buffer); // samples per packet
+ U32_AT(&buffer[4]); // bytes per packet
+ U32_AT(&buffer[8]); // bytes per frame
+ U32_AT(&buffer[12]); // bytes per sample
+#endif
+ *offset += 16;
+ } else if (version == 2) {
+ uint8_t v2buffer[36];
+ if (mDataSource->readAt(*offset, v2buffer, 36) < 36) {
+ return ERROR_IO;
+ }
+
+#if 0
+ U32_AT(v2buffer); // size of struct only
+ sample_rate = (uint32_t)U64_AT(&v2buffer[4]); // audio sample rate
+ num_channels = U32_AT(&v2buffer[12]); // num audio channels
+ U32_AT(&v2buffer[16]); // always 0x7f000000
+ sample_size = (uint16_t)U32_AT(&v2buffer[20]); // const bits per channel
+ U32_AT(&v2buffer[24]); // format specifc flags
+ U32_AT(&v2buffer[28]); // const bytes per audio packet
+ U32_AT(&v2buffer[32]); // const LPCM frames per audio packet
+#endif
+ *offset += 36;
+ }
+ }
+
if (chunk_type != FOURCC('e', 'n', 'c', 'a')) {
// if the chunk type is enca, we'll get the type from the sinf/frma box later
mLastTrack->meta->setCString(kKeyMIMEType, FourCC2MIME(chunk_type));
@@ -1400,8 +1469,6 @@
mLastTrack->meta->setInt32(kKeyChannelCount, num_channels);
mLastTrack->meta->setInt32(kKeySampleRate, sample_rate);
- off64_t stop_offset = *offset + chunk_size;
- *offset = data_offset + sizeof(buffer);
while (*offset < stop_offset) {
status_t err = parseChunk(offset, depth + 1);
if (err != OK) {
@@ -1424,8 +1491,6 @@
case FOURCC('h', 'v', 'c', '1'):
case FOURCC('h', 'e', 'v', '1'):
{
- mHasVideo = true;
-
uint8_t buffer[78];
if (chunk_data_size < (ssize_t)sizeof(buffer)) {
// Basic VideoSampleEntry size.
@@ -1686,8 +1751,9 @@
// Worst case the location string length would be 18,
// for instance +90.0000-180.0000, without the trailing "/" and
- // the string length + language code.
- char buffer[18];
+ // the string length + language code, and some devices include
+ // an additional 8 bytes of altitude, e.g. +007.186
+ char buffer[18 + 8];
// Substracting 5 from the data size is because the text string length +
// language code takes 4 bytes, and the trailing slash "/" takes 1 byte.
@@ -1767,6 +1833,9 @@
case FOURCC('b', 't', 'r', 't'):
{
*offset += chunk_size;
+ if (mLastTrack == NULL) {
+ return ERROR_MALFORMED;
+ }
uint8_t buffer[12];
if (chunk_data_size != sizeof(buffer)) {
@@ -1936,6 +2005,10 @@
{
*offset += chunk_size;
+ if (depth != 1) {
+ ALOGE("mvhd: depth %d", depth);
+ return ERROR_MALFORMED;
+ }
if (chunk_data_size < 32) {
return ERROR_MALFORMED;
}
@@ -2238,6 +2311,38 @@
return UNKNOWN_ERROR; // stop parsing after sidx
}
+ case FOURCC('f', 't', 'y', 'p'):
+ {
+ if (chunk_data_size < 8 || depth != 0) {
+ return ERROR_MALFORMED;
+ }
+
+ off64_t stop_offset = *offset + chunk_size;
+ uint32_t numCompatibleBrands = (chunk_data_size - 8) / 4;
+ for (size_t i = 0; i < numCompatibleBrands + 2; ++i) {
+ if (i == 1) {
+ // Skip this index, it refers to the minorVersion,
+ // not a brand.
+ continue;
+ }
+
+ uint32_t brand;
+ if (mDataSource->readAt(data_offset + 4 * i, &brand, 4) < 4) {
+ return ERROR_MALFORMED;
+ }
+
+ brand = ntohl(brand);
+ if (brand == FOURCC('q', 't', ' ', ' ')) {
+ mIsQT = true;
+ break;
+ }
+ }
+
+ *offset = stop_offset;
+
+ break;
+ }
+
default:
{
// check if we're parsing 'ilst' for meta keys
@@ -2471,6 +2576,15 @@
if (!strcasecmp(mMetaKeyMap[index].c_str(), "com.android.capture.fps")) {
mFileMetaData->setFloat(kKeyCaptureFramerate, *(float *)&val);
}
+ } else if (dataType == 67 && dataSize >= 4) {
+ // BE signed int32
+ uint32_t val;
+ if (!mDataSource->getUInt32(offset, &val)) {
+ return ERROR_MALFORMED;
+ }
+ if (!strcasecmp(mMetaKeyMap[index].c_str(), "com.android.video.temporal_layers_count")) {
+ mFileMetaData->setInt32(kKeyTemporalLayerCount, val);
+ }
} else {
// add more keys if needed
ALOGV("ignoring key: type %d, size %d", dataType, dataSize);
@@ -3553,13 +3667,20 @@
// A somewhat arbitrary limit that should be sufficient for 8k video frames
// If you see the message below for a valid input stream: increase the limit
- if (max_size > 64 * 1024 * 1024) {
- ALOGE("bogus max input size: %zu", max_size);
+ const size_t kMaxBufferSize = 64 * 1024 * 1024;
+ if (max_size > kMaxBufferSize) {
+ ALOGE("bogus max input size: %zu > %zu", max_size, kMaxBufferSize);
return ERROR_MALFORMED;
}
- mGroup = new MediaBufferGroup;
- mGroup->add_buffer(new MediaBuffer(max_size));
+ if (max_size == 0) {
+ ALOGE("zero max input size");
+ return ERROR_MALFORMED;
+ }
+ // Allow up to kMaxBuffers, but not if the total exceeds kMaxBufferSize.
+ const size_t kMaxBuffers = 8;
+ const size_t buffers = min(kMaxBufferSize / max_size, kMaxBuffers);
+ mGroup = new MediaBufferGroup(buffers, max_size);
mSrcBuffer = new (std::nothrow) uint8_t[max_size];
if (mSrcBuffer == NULL) {
// file probably specified a bad max size
@@ -4015,11 +4136,13 @@
if (!mDataSource->getUInt32(offset, &flags)) {
return ERROR_MALFORMED;
}
- ALOGV("fragment run flags: %08x", flags);
-
- if (flags & 0xff000000) {
- return -EINVAL;
- }
+ // |version| only affects SampleCompositionTimeOffset field.
+ // If version == 0, SampleCompositionTimeOffset is uint32_t;
+ // Otherwise, SampleCompositionTimeOffset is int32_t.
+ // Sample.compositionOffset is defined as int32_t.
+ uint8_t version = flags >> 24;
+ flags &= 0xffffff;
+ ALOGV("fragment run version: 0x%02x, flags: 0x%06x", version, flags);
if ((flags & kFirstSampleFlagsPresent) && (flags & kSampleFlagsPresent)) {
// These two shall not be used together.
@@ -4186,6 +4309,11 @@
CHECK(mStarted);
+ if (options != nullptr && options->getNonBlocking() && !mGroup->has_buffers()) {
+ *out = nullptr;
+ return WOULD_BLOCK;
+ }
+
if (mFirstMoofOffset > 0) {
return fragmentedRead(out, options);
}
@@ -4464,6 +4592,12 @@
kKeyTargetTime, targetSampleTimeUs);
}
+ if (mIsAVC) {
+ uint32_t layerId = FindAVCLayerId(
+ (const uint8_t *)mBuffer->data(), mBuffer->range_length());
+ mBuffer->meta_data()->setInt32(kKeyTemporalLayerId, layerId);
+ }
+
if (isSyncSample) {
mBuffer->meta_data()->setInt32(kKeyIsSyncFrame, 1);
}
@@ -4627,6 +4761,12 @@
kKeyTargetTime, targetSampleTimeUs);
}
+ if (mIsAVC) {
+ uint32_t layerId = FindAVCLayerId(
+ (const uint8_t *)mBuffer->data(), mBuffer->range_length());
+ mBuffer->meta_data()->setInt32(kKeyTemporalLayerId, layerId);
+ }
+
if (isSyncSample) {
mBuffer->meta_data()->setInt32(kKeyIsSyncFrame, 1);
}
diff --git a/media/libstagefright/MPEG4Writer.cpp b/media/libstagefright/MPEG4Writer.cpp
index 16e52a2..c9bcfc3 100644
--- a/media/libstagefright/MPEG4Writer.cpp
+++ b/media/libstagefright/MPEG4Writer.cpp
@@ -17,6 +17,8 @@
//#define LOG_NDEBUG 0
#define LOG_TAG "MPEG4Writer"
+#include <algorithm>
+
#include <arpa/inet.h>
#include <fcntl.h>
#include <inttypes.h>
@@ -28,8 +30,11 @@
#include <utils/Log.h>
+#include <functional>
+
#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/foundation/AMessage.h>
+#include <media/stagefright/foundation/AUtils.h>
#include <media/stagefright/foundation/ColorUtils.h>
#include <media/stagefright/MPEG4Writer.h>
#include <media/stagefright/MediaBuffer.h>
@@ -43,6 +48,7 @@
#include "include/ESDS.h"
#include "include/HevcUtils.h"
+#include "include/avc_utils.h"
#ifndef __predict_false
#define __predict_false(exp) __builtin_expect((exp) != 0, 0)
@@ -70,6 +76,7 @@
static const char kMetaKey_Build[] = "com.android.build";
#endif
static const char kMetaKey_CaptureFps[] = "com.android.capture.fps";
+static const char kMetaKey_TemporalLayerCount[] = "com.android.video.temporal_layers_count";
static const uint8_t kMandatoryHevcNalUnitTypes[3] = {
kHevcNalUnitTypeVps,
@@ -117,18 +124,18 @@
};
// A helper class to handle faster write box with table entries
- template<class TYPE>
+ template<class TYPE, unsigned ENTRY_SIZE>
+ // ENTRY_SIZE: # of values in each entry
struct ListTableEntries {
- ListTableEntries(uint32_t elementCapacity, uint32_t entryCapacity)
+ static_assert(ENTRY_SIZE > 0, "ENTRY_SIZE must be positive");
+ ListTableEntries(uint32_t elementCapacity)
: mElementCapacity(elementCapacity),
- mEntryCapacity(entryCapacity),
mTotalNumTableEntries(0),
mNumValuesInCurrEntry(0),
mCurrTableEntriesElement(NULL) {
CHECK_GT(mElementCapacity, 0);
- CHECK_GT(mEntryCapacity, 0);
// Ensure no integer overflow on allocation in add().
- CHECK_LT(mEntryCapacity, UINT32_MAX / mElementCapacity);
+ CHECK_LT(ENTRY_SIZE, UINT32_MAX / mElementCapacity);
}
// Free the allocated memory.
@@ -145,10 +152,10 @@
// @arg value must be in network byte order
// @arg pos location the value must be in.
void set(const TYPE& value, uint32_t pos) {
- CHECK_LT(pos, mTotalNumTableEntries * mEntryCapacity);
+ CHECK_LT(pos, mTotalNumTableEntries * ENTRY_SIZE);
typename List<TYPE *>::iterator it = mTableEntryList.begin();
- uint32_t iterations = (pos / (mElementCapacity * mEntryCapacity));
+ uint32_t iterations = (pos / (mElementCapacity * ENTRY_SIZE));
while (it != mTableEntryList.end() && iterations > 0) {
++it;
--iterations;
@@ -156,7 +163,7 @@
CHECK(it != mTableEntryList.end());
CHECK_EQ(iterations, 0);
- (*it)[(pos % (mElementCapacity * mEntryCapacity))] = value;
+ (*it)[(pos % (mElementCapacity * ENTRY_SIZE))] = value;
}
// Get the value at the given position by the given value.
@@ -164,12 +171,12 @@
// @arg pos location the value must be in.
// @return true if a value is found.
bool get(TYPE& value, uint32_t pos) const {
- if (pos >= mTotalNumTableEntries * mEntryCapacity) {
+ if (pos >= mTotalNumTableEntries * ENTRY_SIZE) {
return false;
}
typename List<TYPE *>::iterator it = mTableEntryList.begin();
- uint32_t iterations = (pos / (mElementCapacity * mEntryCapacity));
+ uint32_t iterations = (pos / (mElementCapacity * ENTRY_SIZE));
while (it != mTableEntryList.end() && iterations > 0) {
++it;
--iterations;
@@ -177,27 +184,42 @@
CHECK(it != mTableEntryList.end());
CHECK_EQ(iterations, 0);
- value = (*it)[(pos % (mElementCapacity * mEntryCapacity))];
+ value = (*it)[(pos % (mElementCapacity * ENTRY_SIZE))];
return true;
}
+ // adjusts all values by |adjust(value)|
+ void adjustEntries(
+ std::function<void(size_t /* ix */, TYPE(& /* entry */)[ENTRY_SIZE])> update) {
+ size_t nEntries = mTotalNumTableEntries + mNumValuesInCurrEntry / ENTRY_SIZE;
+ size_t ix = 0;
+ for (TYPE *entryArray : mTableEntryList) {
+ size_t num = std::min(nEntries, (size_t)mElementCapacity);
+ for (size_t i = 0; i < num; ++i) {
+ update(ix++, (TYPE(&)[ENTRY_SIZE])(*entryArray));
+ entryArray += ENTRY_SIZE;
+ }
+ nEntries -= num;
+ }
+ }
+
// Store a single value.
// @arg value must be in network byte order.
void add(const TYPE& value) {
CHECK_LT(mNumValuesInCurrEntry, mElementCapacity);
uint32_t nEntries = mTotalNumTableEntries % mElementCapacity;
- uint32_t nValues = mNumValuesInCurrEntry % mEntryCapacity;
+ uint32_t nValues = mNumValuesInCurrEntry % ENTRY_SIZE;
if (nEntries == 0 && nValues == 0) {
- mCurrTableEntriesElement = new TYPE[mEntryCapacity * mElementCapacity];
+ mCurrTableEntriesElement = new TYPE[ENTRY_SIZE * mElementCapacity];
CHECK(mCurrTableEntriesElement != NULL);
mTableEntryList.push_back(mCurrTableEntriesElement);
}
- uint32_t pos = nEntries * mEntryCapacity + nValues;
+ uint32_t pos = nEntries * ENTRY_SIZE + nValues;
mCurrTableEntriesElement[pos] = value;
++mNumValuesInCurrEntry;
- if ((mNumValuesInCurrEntry % mEntryCapacity) == 0) {
+ if ((mNumValuesInCurrEntry % ENTRY_SIZE) == 0) {
++mTotalNumTableEntries;
mNumValuesInCurrEntry = 0;
}
@@ -208,17 +230,17 @@
// 2. followed by the values in the table enties in order
// @arg writer the writer to actual write to the storage
void write(MPEG4Writer *writer) const {
- CHECK_EQ(mNumValuesInCurrEntry % mEntryCapacity, 0);
+ CHECK_EQ(mNumValuesInCurrEntry % ENTRY_SIZE, 0);
uint32_t nEntries = mTotalNumTableEntries;
writer->writeInt32(nEntries);
for (typename List<TYPE *>::iterator it = mTableEntryList.begin();
it != mTableEntryList.end(); ++it) {
CHECK_GT(nEntries, 0);
if (nEntries >= mElementCapacity) {
- writer->write(*it, sizeof(TYPE) * mEntryCapacity, mElementCapacity);
+ writer->write(*it, sizeof(TYPE) * ENTRY_SIZE, mElementCapacity);
nEntries -= mElementCapacity;
} else {
- writer->write(*it, sizeof(TYPE) * mEntryCapacity, nEntries);
+ writer->write(*it, sizeof(TYPE) * ENTRY_SIZE, nEntries);
break;
}
}
@@ -229,9 +251,8 @@
private:
uint32_t mElementCapacity; // # entries in an element
- uint32_t mEntryCapacity; // # of values in each entry
uint32_t mTotalNumTableEntries;
- uint32_t mNumValuesInCurrEntry; // up to mEntryCapacity
+ uint32_t mNumValuesInCurrEntry; // up to ENTRY_SIZE
TYPE *mCurrTableEntriesElement;
mutable List<TYPE *> mTableEntryList;
@@ -251,9 +272,11 @@
bool mIsHevc;
bool mIsAudio;
bool mIsMPEG4;
+ bool mIsMalformed;
int32_t mTrackId;
int64_t mTrackDurationUs;
int64_t mMaxChunkDurationUs;
+ int64_t mLastDecodingTimeUs;
int64_t mEstimatedTrackSizeBytes;
int64_t mMdatSizeBytes;
@@ -265,14 +288,14 @@
List<MediaBuffer *> mChunkSamples;
bool mSamplesHaveSameSize;
- ListTableEntries<uint32_t> *mStszTableEntries;
+ ListTableEntries<uint32_t, 1> *mStszTableEntries;
- ListTableEntries<uint32_t> *mStcoTableEntries;
- ListTableEntries<off64_t> *mCo64TableEntries;
- ListTableEntries<uint32_t> *mStscTableEntries;
- ListTableEntries<uint32_t> *mStssTableEntries;
- ListTableEntries<uint32_t> *mSttsTableEntries;
- ListTableEntries<uint32_t> *mCttsTableEntries;
+ ListTableEntries<uint32_t, 1> *mStcoTableEntries;
+ ListTableEntries<off64_t, 1> *mCo64TableEntries;
+ ListTableEntries<uint32_t, 3> *mStscTableEntries;
+ ListTableEntries<uint32_t, 1> *mStssTableEntries;
+ ListTableEntries<uint32_t, 2> *mSttsTableEntries;
+ ListTableEntries<uint32_t, 2> *mCttsTableEntries;
int64_t mMinCttsOffsetTimeUs;
int64_t mMaxCttsOffsetTimeUs;
@@ -1162,6 +1185,37 @@
}
}
+off64_t MPEG4Writer::addMultipleLengthPrefixedSamples_l(MediaBuffer *buffer) {
+ off64_t old_offset = mOffset;
+
+ const size_t kExtensionNALSearchRange = 64; // bytes to look for non-VCL NALUs
+
+ const uint8_t *dataStart = (const uint8_t *)buffer->data() + buffer->range_offset();
+ const uint8_t *currentNalStart = dataStart;
+ const uint8_t *nextNalStart;
+ const uint8_t *data = dataStart;
+ size_t nextNalSize;
+ size_t searchSize = buffer->range_length() > kExtensionNALSearchRange ?
+ kExtensionNALSearchRange : buffer->range_length();
+
+ while (getNextNALUnit(&data, &searchSize, &nextNalStart,
+ &nextNalSize, true) == OK) {
+ size_t currentNalSize = nextNalStart - currentNalStart - 4 /* strip start-code */;
+ MediaBuffer *nalBuf = new MediaBuffer((void *)currentNalStart, currentNalSize);
+ addLengthPrefixedSample_l(nalBuf);
+ nalBuf->release();
+
+ currentNalStart = nextNalStart;
+ }
+
+ size_t currentNalOffset = currentNalStart - dataStart;
+ buffer->set_range(buffer->range_offset() + currentNalOffset,
+ buffer->range_length() - currentNalOffset);
+ addLengthPrefixedSample_l(buffer);
+
+ return old_offset;
+}
+
off64_t MPEG4Writer::addLengthPrefixedSample_l(MediaBuffer *buffer) {
off64_t old_offset = mOffset;
@@ -1381,6 +1435,19 @@
return OK;
}
+status_t MPEG4Writer::setTemporalLayerCount(uint32_t layerCount) {
+ if (layerCount > 9) {
+ return BAD_VALUE;
+ }
+
+ if (layerCount > 0) {
+ mMetaKeys->setInt32(kMetaKey_TemporalLayerCount, layerCount);
+ mMoovExtraSize += sizeof(kMetaKey_TemporalLayerCount) + 4 + 32;
+ }
+
+ return OK;
+}
+
void MPEG4Writer::write(const void *data, size_t size) {
write(data, 1, size);
}
@@ -1470,17 +1537,18 @@
mPaused(false),
mResumed(false),
mStarted(false),
+ mIsMalformed(false),
mTrackId(trackId),
mTrackDurationUs(0),
mEstimatedTrackSizeBytes(0),
mSamplesHaveSameSize(true),
- mStszTableEntries(new ListTableEntries<uint32_t>(1000, 1)),
- mStcoTableEntries(new ListTableEntries<uint32_t>(1000, 1)),
- mCo64TableEntries(new ListTableEntries<off64_t>(1000, 1)),
- mStscTableEntries(new ListTableEntries<uint32_t>(1000, 3)),
- mStssTableEntries(new ListTableEntries<uint32_t>(1000, 1)),
- mSttsTableEntries(new ListTableEntries<uint32_t>(1000, 2)),
- mCttsTableEntries(new ListTableEntries<uint32_t>(1000, 2)),
+ mStszTableEntries(new ListTableEntries<uint32_t, 1>(1000)),
+ mStcoTableEntries(new ListTableEntries<uint32_t, 1>(1000)),
+ mCo64TableEntries(new ListTableEntries<off64_t, 1>(1000)),
+ mStscTableEntries(new ListTableEntries<uint32_t, 3>(1000)),
+ mStssTableEntries(new ListTableEntries<uint32_t, 1>(1000)),
+ mSttsTableEntries(new ListTableEntries<uint32_t, 2>(1000)),
+ mCttsTableEntries(new ListTableEntries<uint32_t, 2>(1000)),
mCodecSpecificData(NULL),
mCodecSpecificDataSize(0),
mGotAllCodecSpecificData(false),
@@ -1496,6 +1564,14 @@
mIsMPEG4 = !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_MPEG4) ||
!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AAC);
+ // store temporal layer count
+ if (!mIsAudio) {
+ int32_t count;
+ if (mMeta->findInt32(kKeyTemporalLayerCount, &count) && count > 1) {
+ mOwner->setTemporalLayerCount(count);
+ }
+ }
+
setTimeScale();
}
@@ -1684,7 +1760,7 @@
List<MediaBuffer *>::iterator it = chunk->mSamples.begin();
off64_t offset = (chunk->mTrack->isAvc() || chunk->mTrack->isHevc())
- ? addLengthPrefixedSample_l(*it)
+ ? addMultipleLengthPrefixedSamples_l(*it)
: addSample_l(*it);
if (isFirstSample) {
@@ -1874,6 +1950,7 @@
mEstimatedTrackSizeBytes = 0;
mMdatSizeBytes = 0;
mMaxChunkDurationUs = 0;
+ mLastDecodingTimeUs = -1;
pthread_create(&mThread, &attr, ThreadWrapper, this);
pthread_attr_destroy(&attr);
@@ -2404,12 +2481,16 @@
ALOGW("Recorded file size exceeds limit %" PRId64 "bytes",
mOwner->mMaxFileSizeLimitBytes);
mOwner->notify(MEDIA_RECORDER_EVENT_INFO, MEDIA_RECORDER_INFO_MAX_FILESIZE_REACHED, 0);
+ copy->release();
+ mSource->stop();
break;
}
if (mOwner->exceedsFileDurationLimit()) {
ALOGW("Recorded file duration exceeds limit %" PRId64 "microseconds",
mOwner->mMaxFileDurationLimitUs);
mOwner->notify(MEDIA_RECORDER_EVENT_INFO, MEDIA_RECORDER_INFO_MAX_DURATION_REACHED, 0);
+ copy->release();
+ mSource->stop();
break;
}
@@ -2430,13 +2511,17 @@
int64_t durExcludingEarlierPausesUs = timestampUs - previousPausedDurationUs;
if (WARN_UNLESS(durExcludingEarlierPausesUs >= 0ll, "for %s track", trackName)) {
copy->release();
- return ERROR_MALFORMED;
+ mSource->stop();
+ mIsMalformed = true;
+ break;
}
int64_t pausedDurationUs = durExcludingEarlierPausesUs - mTrackDurationUs;
if (WARN_UNLESS(pausedDurationUs >= lastDurationUs, "for %s track", trackName)) {
copy->release();
- return ERROR_MALFORMED;
+ mSource->stop();
+ mIsMalformed = true;
+ break;
}
previousPausedDurationUs += pausedDurationUs - lastDurationUs;
@@ -2446,7 +2531,9 @@
timestampUs -= previousPausedDurationUs;
if (WARN_UNLESS(timestampUs >= 0ll, "for %s track", trackName)) {
copy->release();
- return ERROR_MALFORMED;
+ mSource->stop();
+ mIsMalformed = true;
+ break;
}
if (!mIsAudio) {
@@ -2458,11 +2545,24 @@
int64_t decodingTimeUs;
CHECK(meta_data->findInt64(kKeyDecodingTime, &decodingTimeUs));
decodingTimeUs -= previousPausedDurationUs;
+
+ // ensure non-negative, monotonic decoding time
+ if (mLastDecodingTimeUs < 0) {
+ decodingTimeUs = std::max((int64_t)0, decodingTimeUs);
+ } else {
+ // increase decoding time by at least 1 tick
+ decodingTimeUs = std::max(
+ mLastDecodingTimeUs + divUp(1000000, mTimeScale), decodingTimeUs);
+ }
+
+ mLastDecodingTimeUs = decodingTimeUs;
cttsOffsetTimeUs =
timestampUs + kMaxCttsOffsetTimeUs - decodingTimeUs;
if (WARN_UNLESS(cttsOffsetTimeUs >= 0ll, "for %s track", trackName)) {
copy->release();
- return ERROR_MALFORMED;
+ mSource->stop();
+ mIsMalformed = true;
+ break;
}
timestampUs = decodingTimeUs;
@@ -2474,7 +2574,9 @@
(cttsOffsetTimeUs * mTimeScale + 500000LL) / 1000000LL;
if (WARN_UNLESS(currCttsOffsetTimeTicks <= 0x0FFFFFFFFLL, "for %s track", trackName)) {
copy->release();
- return ERROR_MALFORMED;
+ mSource->stop();
+ mIsMalformed = true;
+ break;
}
if (mStszTableEntries->count() == 0) {
@@ -2516,7 +2618,9 @@
if (WARN_UNLESS(timestampUs >= 0ll, "for %s track", trackName)) {
copy->release();
- return ERROR_MALFORMED;
+ mSource->stop();
+ mIsMalformed = true;
+ break;
}
ALOGV("%s media time stamp: %" PRId64 " and previous paused duration %" PRId64,
@@ -2538,7 +2642,8 @@
(long long)timestampUs, (long long)lastTimestampUs, trackName);
copy->release();
mSource->stop();
- return UNKNOWN_ERROR;
+ mIsMalformed = true;
+ break;
}
// if the duration is different for this sample, see if it is close enough to the previous
@@ -2593,7 +2698,7 @@
trackProgressStatus(timestampUs);
}
if (!hasMultipleTracks) {
- off64_t offset = (mIsAvc || mIsHevc) ? mOwner->addLengthPrefixedSample_l(copy)
+ off64_t offset = (mIsAvc || mIsHevc) ? mOwner->addMultipleLengthPrefixedSamples_l(copy)
: mOwner->addSample_l(copy);
uint32_t count = (mOwner->use32BitFileOffset()
@@ -2694,6 +2799,10 @@
}
bool MPEG4Writer::Track::isTrackMalFormed() const {
+ if (mIsMalformed) {
+ return true;
+ }
+
if (mStszTableEntries->count() == 0) { // no samples written
ALOGE("The number of recorded samples is 0");
return true;
@@ -2944,8 +3053,6 @@
mOwner->writeInt16(0x18); // depth
mOwner->writeInt16(-1); // predefined
- CHECK_LT(23 + mCodecSpecificDataSize, 128);
-
if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_MPEG4, mime)) {
writeMp4vEsdsBox();
} else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_H263, mime)) {
@@ -3065,6 +3172,10 @@
void MPEG4Writer::Track::writeMp4vEsdsBox() {
CHECK(mCodecSpecificData);
CHECK_GT(mCodecSpecificDataSize, 0);
+
+ // Make sure all sizes encode to a single byte.
+ CHECK_LT(23 + mCodecSpecificDataSize, 128);
+
mOwner->beginBox("esds");
mOwner->writeInt32(0); // version=0, flags=0
@@ -3312,10 +3423,12 @@
mOwner->beginBox("ctts");
mOwner->writeInt32(0); // version=0, flags=0
- uint32_t duration;
- CHECK(mCttsTableEntries->get(duration, 1));
- duration = htonl(duration); // Back host byte order
- mCttsTableEntries->set(htonl(duration + getStartTimeOffsetScaledTime() - mMinCttsOffsetTimeUs), 1);
+ uint32_t delta = mMinCttsOffsetTimeUs - getStartTimeOffsetScaledTime();
+ mCttsTableEntries->adjustEntries([delta](size_t /* ix */, uint32_t (&value)[2]) {
+ // entries are <count, ctts> pairs; adjust only ctts
+ uint32_t duration = htonl(value[1]); // back to host byte order
+ value[1] = htonl(duration - delta);
+ });
mCttsTableEntries->write(mOwner);
mOwner->endBox(); // ctts
}
diff --git a/media/libstagefright/MediaCodec.cpp b/media/libstagefright/MediaCodec.cpp
index b51853e..b088775 100644
--- a/media/libstagefright/MediaCodec.cpp
+++ b/media/libstagefright/MediaCodec.cpp
@@ -1791,9 +1791,8 @@
err = BAD_VALUE;
} else {
err = connectToSurface(surface);
- if (err == BAD_VALUE) {
- // assuming reconnecting to same surface
- // TODO: check if it is the same surface
+ if (err == ALREADY_EXISTS) {
+ // reconnecting to same surface
err = OK;
} else {
if (err == OK) {
@@ -2161,14 +2160,19 @@
CHECK(msg->findPointer("buffers", (void **)&dstBuffers));
dstBuffers->clear();
- const Vector<BufferInfo> &srcBuffers = mPortBuffers[portIndex];
+ // If we're using input surface (either non-persistent created by
+ // createInputSurface(), or persistent set by setInputSurface()),
+ // give the client an empty input buffers array.
+ if (portIndex != kPortIndexInput || !mHaveInputSurface) {
+ const Vector<BufferInfo> &srcBuffers = mPortBuffers[portIndex];
- for (size_t i = 0; i < srcBuffers.size(); ++i) {
- const BufferInfo &info = srcBuffers.itemAt(i);
+ for (size_t i = 0; i < srcBuffers.size(); ++i) {
+ const BufferInfo &info = srcBuffers.itemAt(i);
- dstBuffers->push_back(
- (portIndex == kPortIndexInput && mCrypto != NULL)
- ? info.mEncryptedData : info.mData);
+ dstBuffers->push_back(
+ (portIndex == kPortIndexInput && mCrypto != NULL)
+ ? info.mEncryptedData : info.mData);
+ }
}
(new AMessage)->postReply(replyID);
@@ -2683,11 +2687,17 @@
status_t MediaCodec::connectToSurface(const sp<Surface> &surface) {
status_t err = OK;
if (surface != NULL) {
+ uint64_t oldId, newId;
+ if (mSurface != NULL
+ && surface->getUniqueId(&newId) == NO_ERROR
+ && mSurface->getUniqueId(&oldId) == NO_ERROR
+ && newId == oldId) {
+ ALOGI("[%s] connecting to the same surface. Nothing to do.", mComponentName.c_str());
+ return ALREADY_EXISTS;
+ }
+
err = native_window_api_connect(surface.get(), NATIVE_WINDOW_API_MEDIA);
- if (err == BAD_VALUE) {
- ALOGI("native window already connected. Assuming no change of surface");
- return err;
- } else if (err == OK) {
+ if (err == OK) {
// Require a fresh set of buffers after each connect by using a unique generation
// number. Rely on the fact that max supported process id by Linux is 2^22.
// PID is never 0 so we don't have to worry that we use the default generation of 0.
@@ -2709,7 +2719,8 @@
ALOGE("native_window_api_connect returned an error: %s (%d)", strerror(-err), err);
}
}
- return err;
+ // do not return ALREADY_EXISTS unless surfaces are the same
+ return err == ALREADY_EXISTS ? BAD_VALUE : err;
}
status_t MediaCodec::disconnectFromSurface() {
diff --git a/media/libstagefright/MediaCodecSource.cpp b/media/libstagefright/MediaCodecSource.cpp
index d7cf1e9..35c07ca 100644
--- a/media/libstagefright/MediaCodecSource.cpp
+++ b/media/libstagefright/MediaCodecSource.cpp
@@ -336,10 +336,21 @@
return NULL;
}
-void MediaCodecSource::setInputBufferTimeOffset(int64_t timeOffsetUs) {
+status_t MediaCodecSource::setInputBufferTimeOffset(int64_t timeOffsetUs) {
sp<AMessage> msg = new AMessage(kWhatSetInputBufferTimeOffset, mReflector);
msg->setInt64("time-offset-us", timeOffsetUs);
- postSynchronouslyAndReturnError(msg);
+ return postSynchronouslyAndReturnError(msg);
+}
+
+int64_t MediaCodecSource::getFirstSampleSystemTimeUs() {
+ sp<AMessage> msg = new AMessage(kWhatGetFirstSampleSystemTimeUs, mReflector);
+ sp<AMessage> response;
+ msg->postAndAwaitResponse(&response);
+ int64_t timeUs;
+ if (!response->findInt64("time-us", &timeUs)) {
+ timeUs = -1ll;
+ }
+ return timeUs;
}
status_t MediaCodecSource::start(MetaData* params) {
@@ -408,6 +419,8 @@
mEncoderDataSpace(0),
mGraphicBufferConsumer(consumer),
mInputBufferTimeOffsetUs(0),
+ mFirstSampleSystemTimeUs(-1ll),
+ mPausePending(false),
mFirstSampleTimeUs(-1ll),
mGeneration(0) {
CHECK(mLooper != NULL);
@@ -646,6 +659,17 @@
if (mbuf != NULL) {
CHECK(mbuf->meta_data()->findInt64(kKeyTime, &timeUs));
+ if (mFirstSampleSystemTimeUs < 0ll) {
+ mFirstSampleSystemTimeUs = systemTime() / 1000;
+ if (mPausePending) {
+ mPausePending = false;
+ onPause();
+ mbuf->release();
+ mAvailEncoderInputIndices.push_back(bufferIndex);
+ return OK;
+ }
+ }
+
timeUs += mInputBufferTimeOffsetUs;
// push decoding time for video, or drift time for audio
@@ -656,7 +680,6 @@
if (mFirstSampleTimeUs < 0ll) {
mFirstSampleTimeUs = timeUs;
}
-
int64_t driftTimeUs = 0;
if (mbuf->meta_data()->findInt64(kKeyDriftTime, &driftTimeUs)
&& driftTimeUs) {
@@ -708,6 +731,10 @@
if (mStarted) {
ALOGI("MediaCodecSource (%s) resuming", mIsVideo ? "video" : "audio");
+ if (mPausePending) {
+ mPausePending = false;
+ return OK;
+ }
if (mIsVideo) {
mEncoder->requestIDRFrame();
}
@@ -754,6 +781,15 @@
return OK;
}
+void MediaCodecSource::onPause() {
+ if (mFlags & FLAG_USE_SURFACE_INPUT) {
+ suspend();
+ } else {
+ CHECK(mPuller != NULL);
+ mPuller->pause();
+ }
+}
+
void MediaCodecSource::onMessageReceived(const sp<AMessage> &msg) {
switch (msg->what()) {
case kWhatPullerNotify:
@@ -823,15 +859,23 @@
}
MediaBuffer *mbuf = new MediaBuffer(outbuf->size());
- memcpy(mbuf->data(), outbuf->data(), outbuf->size());
+ mbuf->setObserver(this);
+ mbuf->add_ref();
if (!(flags & MediaCodec::BUFFER_FLAG_CODECCONFIG)) {
if (mIsVideo) {
int64_t decodingTimeUs;
if (mFlags & FLAG_USE_SURFACE_INPUT) {
- // Time offset is not applied at
- // feedEncoderInputBuffer() in surface input case.
- timeUs += mInputBufferTimeOffsetUs;
+ if (mFirstSampleSystemTimeUs < 0ll) {
+ mFirstSampleSystemTimeUs = systemTime() / 1000;
+ if (mPausePending) {
+ mPausePending = false;
+ onPause();
+ mbuf->release();
+ break;
+ }
+ }
+ // Timestamp offset is already adjusted in GraphicBufferSource.
// GraphicBufferSource is supposed to discard samples
// queued before start, and offset timeUs by start time
CHECK_GE(timeUs, 0ll);
@@ -867,8 +911,7 @@
if (flags & MediaCodec::BUFFER_FLAG_SYNCFRAME) {
mbuf->meta_data()->setInt32(kKeyIsSyncFrame, true);
}
- mbuf->setObserver(this);
- mbuf->add_ref();
+ memcpy(mbuf->data(), outbuf->data(), outbuf->size());
{
Mutexed<Output>::Locked output(mOutput);
@@ -959,11 +1002,10 @@
case kWhatPause:
{
- if (mFlags & FLAG_USE_SURFACE_INPUT) {
- suspend();
+ if (mFirstSampleSystemTimeUs < 0) {
+ mPausePending = true;
} else {
- CHECK(mPuller != NULL);
- mPuller->pause();
+ onPause();
}
break;
}
@@ -971,10 +1013,28 @@
{
sp<AReplyToken> replyID;
CHECK(msg->senderAwaitsResponse(&replyID));
-
+ status_t err = OK;
CHECK(msg->findInt64("time-offset-us", &mInputBufferTimeOffsetUs));
+ // Propagate the timestamp offset to GraphicBufferSource.
+ if (mIsVideo) {
+ sp<AMessage> params = new AMessage;
+ params->setInt64("time-offset-us", mInputBufferTimeOffsetUs);
+ err = mEncoder->setParameters(params);
+ }
+
sp<AMessage> response = new AMessage;
+ response->setInt32("err", err);
+ response->postReply(replyID);
+ break;
+ }
+ case kWhatGetFirstSampleSystemTimeUs:
+ {
+ sp<AReplyToken> replyID;
+ CHECK(msg->senderAwaitsResponse(&replyID));
+
+ sp<AMessage> response = new AMessage;
+ response->setInt64("time-us", mFirstSampleSystemTimeUs);
response->postReply(replyID);
break;
}
diff --git a/media/libstagefright/MediaExtractor.cpp b/media/libstagefright/MediaExtractor.cpp
index 1c6c882..92ce88c 100644
--- a/media/libstagefright/MediaExtractor.cpp
+++ b/media/libstagefright/MediaExtractor.cpp
@@ -88,6 +88,7 @@
virtual void close();
virtual uint32_t getFlags();
virtual String8 toString();
+ virtual sp<DecryptHandle> DrmInitialization(const char *mime);
private:
sp<IMemory> mMemory;
@@ -134,6 +135,10 @@
return mName;
}
+sp<DecryptHandle> RemoteDataSource::DrmInitialization(const char *mime) {
+ return mSource->DrmInitialization(mime);
+}
+
// static
sp<IMediaExtractor> MediaExtractor::Create(
const sp<DataSource> &source, const char *mime) {
@@ -146,10 +151,6 @@
ALOGW("creating media extractor in calling process");
return CreateFromService(source, mime);
} else {
- // remote extractor
- ALOGV("get service manager");
- sp<IBinder> binder = defaultServiceManager()->getService(String16("media.extractor"));
-
// Check if it's WVM, since WVMExtractor needs to be created in the media server process,
// not the extractor process.
String8 mime8;
@@ -160,6 +161,21 @@
return new WVMExtractor(source);
}
+ // Check if it's es-based DRM, since DRMExtractor needs to be created in the media server
+ // process, not the extractor process.
+ if (SniffDRM(source, &mime8, &confidence, &meta)) {
+ const char *drmMime = mime8.string();
+ ALOGV("Detected media content as '%s' with confidence %.2f", drmMime, confidence);
+ if (!strncmp(drmMime, "drm+es_based+", 13)) {
+ // DRMExtractor sets container metadata kKeyIsDRM to 1
+ return new DRMExtractor(source, drmMime + 14);
+ }
+ }
+
+ // remote extractor
+ ALOGV("get service manager");
+ sp<IBinder> binder = defaultServiceManager()->getService(String16("media.extractor"));
+
if (binder != 0) {
sp<IMediaExtractorService> mediaExService(interface_cast<IMediaExtractorService>(binder));
sp<IMediaExtractor> ex = mediaExService->makeExtractor(RemoteDataSource::wrap(source), mime);
diff --git a/media/libstagefright/NuMediaExtractor.cpp b/media/libstagefright/NuMediaExtractor.cpp
index a669dca..276d731 100644
--- a/media/libstagefright/NuMediaExtractor.cpp
+++ b/media/libstagefright/NuMediaExtractor.cpp
@@ -305,7 +305,10 @@
sp<IMediaSource> source = mImpl->getTrack(index);
- CHECK_EQ((status_t)OK, source->start());
+ status_t ret = source->start();
+ if (ret != OK) {
+ return ret;
+ }
mSelectedTracks.push();
TrackInfo *info = &mSelectedTracks.editItemAt(mSelectedTracks.size() - 1);
diff --git a/media/libstagefright/OMXClient.cpp b/media/libstagefright/OMXClient.cpp
index e994069..e40dbcf 100644
--- a/media/libstagefright/OMXClient.cpp
+++ b/media/libstagefright/OMXClient.cpp
@@ -303,6 +303,12 @@
status_t MuxOMX::freeNode(node_id node) {
Mutex::Autolock autoLock(mLock);
+ // exit if we have already freed the node
+ if (mNodeLocation.indexOfKey(node) < 0) {
+ ALOGD("MuxOMX::freeNode: node %d seems to be released already --- ignoring.", node);
+ return OK;
+ }
+
status_t err = getOMX_l(node)->freeNode(node);
if (err != OK) {
diff --git a/media/libstagefright/SampleIterator.cpp b/media/libstagefright/SampleIterator.cpp
index ad7b6fd..54c9fa3 100644
--- a/media/libstagefright/SampleIterator.cpp
+++ b/media/libstagefright/SampleIterator.cpp
@@ -94,8 +94,6 @@
+ mFirstChunk;
if (!mInitialized || chunk != mCurrentChunkIndex) {
- mCurrentChunkIndex = chunk;
-
status_t err;
if ((err = getChunkOffset(chunk, &mCurrentChunkOffset)) != OK) {
ALOGE("getChunkOffset return error");
@@ -106,18 +104,21 @@
uint32_t firstChunkSampleIndex =
mFirstChunkSampleIndex
- + mSamplesPerChunk * (mCurrentChunkIndex - mFirstChunk);
+ + mSamplesPerChunk * (chunk - mFirstChunk);
for (uint32_t i = 0; i < mSamplesPerChunk; ++i) {
size_t sampleSize;
if ((err = getSampleSizeDirect(
firstChunkSampleIndex + i, &sampleSize)) != OK) {
ALOGE("getSampleSizeDirect return error");
+ mCurrentChunkSampleSizes.clear();
return err;
}
mCurrentChunkSampleSizes.push(sampleSize);
}
+
+ mCurrentChunkIndex = chunk;
}
uint32_t chunkRelativeSampleIndex =
diff --git a/media/libstagefright/SampleTable.cpp b/media/libstagefright/SampleTable.cpp
index 8e49e9b..8061bc6 100644
--- a/media/libstagefright/SampleTable.cpp
+++ b/media/libstagefright/SampleTable.cpp
@@ -18,6 +18,8 @@
//#define LOG_NDEBUG 0
#include <utils/Log.h>
+#include <limits>
+
#include "include/SampleTable.h"
#include "include/SampleIterator.h"
@@ -45,6 +47,8 @@
////////////////////////////////////////////////////////////////////////////////
+const off64_t kMaxOffset = std::numeric_limits<off64_t>::max();
+
struct SampleTable::CompositionDeltaLookup {
CompositionDeltaLookup();
@@ -123,7 +127,7 @@
mNumSampleSizes(0),
mHasTimeToSample(false),
mTimeToSampleCount(0),
- mTimeToSample(),
+ mTimeToSample(NULL),
mSampleTimeEntries(NULL),
mCompositionTimeDeltaEntries(NULL),
mNumCompositionTimeDeltaEntries(0),
@@ -132,7 +136,8 @@
mNumSyncSamples(0),
mSyncSamples(NULL),
mLastSyncSampleIndex(0),
- mSampleToChunkEntries(NULL) {
+ mSampleToChunkEntries(NULL),
+ mTotalSize(0) {
mSampleIterator = new SampleIterator(this);
}
@@ -143,6 +148,9 @@
delete[] mSyncSamples;
mSyncSamples = NULL;
+ delete[] mTimeToSample;
+ mTimeToSample = NULL;
+
delete mCompositionDeltaLookup;
mCompositionDeltaLookup = NULL;
@@ -234,27 +242,55 @@
mNumSampleToChunkOffsets = U32_AT(&header[4]);
- if ((data_size - 8) / 12 < mNumSampleToChunkOffsets) {
+ if ((data_size - 8) / sizeof(SampleToChunkEntry) < mNumSampleToChunkOffsets) {
return ERROR_MALFORMED;
}
- if (SIZE_MAX / sizeof(SampleToChunkEntry) <= (size_t)mNumSampleToChunkOffsets)
+ if ((uint64_t)kMaxTotalSize / sizeof(SampleToChunkEntry) <=
+ (uint64_t)mNumSampleToChunkOffsets) {
+ ALOGE("Sample-to-chunk table size too large.");
return ERROR_OUT_OF_RANGE;
+ }
+
+ mTotalSize += (uint64_t)mNumSampleToChunkOffsets *
+ sizeof(SampleToChunkEntry);
+ if (mTotalSize > kMaxTotalSize) {
+ ALOGE("Sample-to-chunk table size would make sample table too large.\n"
+ " Requested sample-to-chunk table size = %llu\n"
+ " Eventual sample table size >= %llu\n"
+ " Allowed sample table size = %llu\n",
+ (unsigned long long)mNumSampleToChunkOffsets *
+ sizeof(SampleToChunkEntry),
+ (unsigned long long)mTotalSize,
+ (unsigned long long)kMaxTotalSize);
+ return ERROR_OUT_OF_RANGE;
+ }
mSampleToChunkEntries =
new (std::nothrow) SampleToChunkEntry[mNumSampleToChunkOffsets];
- if (!mSampleToChunkEntries)
+ if (!mSampleToChunkEntries) {
+ ALOGE("Cannot allocate sample-to-chunk table with %llu entries.",
+ (unsigned long long)mNumSampleToChunkOffsets);
return ERROR_OUT_OF_RANGE;
+ }
+
+ if (mNumSampleToChunkOffsets == 0) {
+ return OK;
+ }
+
+ if ((off64_t)(kMaxOffset - 8 -
+ ((mNumSampleToChunkOffsets - 1) * sizeof(SampleToChunkEntry)))
+ < mSampleToChunkOffset) {
+ return ERROR_MALFORMED;
+ }
for (uint32_t i = 0; i < mNumSampleToChunkOffsets; ++i) {
- uint8_t buffer[12];
-
- if ((SIZE_MAX - 8 - (i * 12)) < (size_t)mSampleToChunkOffset) {
- return ERROR_MALFORMED;
- }
+ uint8_t buffer[sizeof(SampleToChunkEntry)];
if (mDataSource->readAt(
- mSampleToChunkOffset + 8 + i * 12, buffer, sizeof(buffer))
+ mSampleToChunkOffset + 8 + i * sizeof(SampleToChunkEntry),
+ buffer,
+ sizeof(buffer))
!= (ssize_t)sizeof(buffer)) {
return ERROR_IO;
}
@@ -355,28 +391,48 @@
}
mTimeToSampleCount = U32_AT(&header[4]);
- if ((uint64_t)mTimeToSampleCount >
- (uint64_t)UINT32_MAX / (2 * sizeof(uint32_t))) {
+ if (mTimeToSampleCount > UINT32_MAX / (2 * sizeof(uint32_t))) {
// Choose this bound because
// 1) 2 * sizeof(uint32_t) is the amount of memory needed for one
// time-to-sample entry in the time-to-sample table.
// 2) mTimeToSampleCount is the number of entries of the time-to-sample
// table.
// 3) We hope that the table size does not exceed UINT32_MAX.
- ALOGE(" Error: Time-to-sample table size too large.");
+ ALOGE("Time-to-sample table size too large.");
return ERROR_OUT_OF_RANGE;
}
// Note: At this point, we know that mTimeToSampleCount * 2 will not
// overflow because of the above condition.
- if (!mDataSource->getVector(data_offset + 8, &mTimeToSample,
- mTimeToSampleCount * 2)) {
- ALOGE(" Error: Incomplete data read for time-to-sample table.");
+
+ uint64_t allocSize = (uint64_t)mTimeToSampleCount * 2 * sizeof(uint32_t);
+ mTotalSize += allocSize;
+ if (mTotalSize > kMaxTotalSize) {
+ ALOGE("Time-to-sample table size would make sample table too large.\n"
+ " Requested time-to-sample table size = %llu\n"
+ " Eventual sample table size >= %llu\n"
+ " Allowed sample table size = %llu\n",
+ (unsigned long long)allocSize,
+ (unsigned long long)mTotalSize,
+ (unsigned long long)kMaxTotalSize);
+ return ERROR_OUT_OF_RANGE;
+ }
+
+ mTimeToSample = new (std::nothrow) uint32_t[mTimeToSampleCount * 2];
+ if (!mTimeToSample) {
+ ALOGE("Cannot allocate time-to-sample table with %llu entries.",
+ (unsigned long long)mTimeToSampleCount);
+ return ERROR_OUT_OF_RANGE;
+ }
+
+ if (mDataSource->readAt(data_offset + 8, mTimeToSample,
+ (size_t)allocSize) < (ssize_t)allocSize) {
+ ALOGE("Incomplete data read for time-to-sample table.");
return ERROR_IO;
}
- for (size_t i = 0; i < mTimeToSample.size(); ++i) {
- mTimeToSample.editItemAt(i) = ntohl(mTimeToSample[i]);
+ for (size_t i = 0; i < mTimeToSampleCount * 2; ++i) {
+ mTimeToSample[i] = ntohl(mTimeToSample[i]);
}
mHasTimeToSample = true;
@@ -418,18 +474,33 @@
}
mNumCompositionTimeDeltaEntries = numEntries;
- uint64_t allocSize = (uint64_t)numEntries * 2 * sizeof(uint32_t);
- if (allocSize > UINT32_MAX) {
+ uint64_t allocSize = (uint64_t)numEntries * 2 * sizeof(int32_t);
+ if (allocSize > kMaxTotalSize) {
+ ALOGE("Composition-time-to-sample table size too large.");
+ return ERROR_OUT_OF_RANGE;
+ }
+
+ mTotalSize += allocSize;
+ if (mTotalSize > kMaxTotalSize) {
+ ALOGE("Composition-time-to-sample table would make sample table too large.\n"
+ " Requested composition-time-to-sample table size = %llu\n"
+ " Eventual sample table size >= %llu\n"
+ " Allowed sample table size = %llu\n",
+ (unsigned long long)allocSize,
+ (unsigned long long)mTotalSize,
+ (unsigned long long)kMaxTotalSize);
return ERROR_OUT_OF_RANGE;
}
mCompositionTimeDeltaEntries = new (std::nothrow) int32_t[2 * numEntries];
- if (!mCompositionTimeDeltaEntries)
+ if (!mCompositionTimeDeltaEntries) {
+ ALOGE("Cannot allocate composition-time-to-sample table with %llu "
+ "entries.", (unsigned long long)numEntries);
return ERROR_OUT_OF_RANGE;
+ }
- if (mDataSource->readAt(
- data_offset + 8, mCompositionTimeDeltaEntries, numEntries * 8)
- < (ssize_t)numEntries * 8) {
+ if (mDataSource->readAt(data_offset + 8, mCompositionTimeDeltaEntries,
+ (size_t)allocSize) < (ssize_t)allocSize) {
delete[] mCompositionTimeDeltaEntries;
mCompositionTimeDeltaEntries = NULL;
@@ -470,18 +541,33 @@
ALOGV("Table of sync samples is empty or has only a single entry!");
}
- uint64_t allocSize = mNumSyncSamples * (uint64_t)sizeof(uint32_t);
- if (allocSize > SIZE_MAX) {
+ uint64_t allocSize = (uint64_t)mNumSyncSamples * sizeof(uint32_t);
+ if (allocSize > kMaxTotalSize) {
+ ALOGE("Sync sample table size too large.");
+ return ERROR_OUT_OF_RANGE;
+ }
+
+ mTotalSize += allocSize;
+ if (mTotalSize > kMaxTotalSize) {
+ ALOGE("Sync sample table size would make sample table too large.\n"
+ " Requested sync sample table size = %llu\n"
+ " Eventual sample table size >= %llu\n"
+ " Allowed sample table size = %llu\n",
+ (unsigned long long)allocSize,
+ (unsigned long long)mTotalSize,
+ (unsigned long long)kMaxTotalSize);
return ERROR_OUT_OF_RANGE;
}
mSyncSamples = new (std::nothrow) uint32_t[mNumSyncSamples];
- if (!mSyncSamples)
+ if (!mSyncSamples) {
+ ALOGE("Cannot allocate sync sample table with %llu entries.",
+ (unsigned long long)mNumSyncSamples);
return ERROR_OUT_OF_RANGE;
+ }
- size_t size = mNumSyncSamples * sizeof(uint32_t);
- if (mDataSource->readAt(mSyncSampleOffset + 8, mSyncSamples, size)
- != (ssize_t)size) {
+ if (mDataSource->readAt(mSyncSampleOffset + 8, mSyncSamples,
+ (size_t)allocSize) != (ssize_t)allocSize) {
return ERROR_IO;
}
@@ -553,9 +639,24 @@
return;
}
- mSampleTimeEntries = new (std::nothrow) SampleTimeEntry[mNumSampleSizes];
- if (!mSampleTimeEntries)
+ mTotalSize += (uint64_t)mNumSampleSizes * sizeof(SampleTimeEntry);
+ if (mTotalSize > kMaxTotalSize) {
+ ALOGE("Sample entry table size would make sample table too large.\n"
+ " Requested sample entry table size = %llu\n"
+ " Eventual sample table size >= %llu\n"
+ " Allowed sample table size = %llu\n",
+ (unsigned long long)mNumSampleSizes * sizeof(SampleTimeEntry),
+ (unsigned long long)mTotalSize,
+ (unsigned long long)kMaxTotalSize);
return;
+ }
+
+ mSampleTimeEntries = new (std::nothrow) SampleTimeEntry[mNumSampleSizes];
+ if (!mSampleTimeEntries) {
+ ALOGE("Cannot allocate sample entry table with %llu entries.",
+ (unsigned long long)mNumSampleSizes);
+ return;
+ }
uint32_t sampleIndex = 0;
uint32_t sampleTime = 0;
diff --git a/media/libstagefright/SimpleDecodingSource.cpp b/media/libstagefright/SimpleDecodingSource.cpp
index 1b44a00..de21c5e 100644
--- a/media/libstagefright/SimpleDecodingSource.cpp
+++ b/media/libstagefright/SimpleDecodingSource.cpp
@@ -43,7 +43,9 @@
CHECK(meta->findCString(kKeyMIMEType, &mime));
sp<AMessage> format = new AMessage;
- convertMetaDataToMessage(source->getFormat(), &format);
+ if (convertMetaDataToMessage(source->getFormat(), &format) != OK) {
+ return NULL;
+ }
Vector<AString> matchingCodecs;
MediaCodecList::findMatchingCodecs(
diff --git a/media/libstagefright/StagefrightMediaScanner.cpp b/media/libstagefright/StagefrightMediaScanner.cpp
index db33e83..f0c27ac 100644
--- a/media/libstagefright/StagefrightMediaScanner.cpp
+++ b/media/libstagefright/StagefrightMediaScanner.cpp
@@ -40,7 +40,7 @@
".mpeg", ".ogg", ".mid", ".smf", ".imy", ".wma", ".aac",
".wav", ".amr", ".midi", ".xmf", ".rtttl", ".rtx", ".ota",
".mkv", ".mka", ".webm", ".ts", ".fl", ".flac", ".mxmf",
- ".avi", ".mpeg", ".mpg", ".awb", ".mpga"
+ ".avi", ".mpeg", ".mpg", ".awb", ".mpga", ".mov"
};
static const size_t kNumValidExtensions =
sizeof(kValidExtensions) / sizeof(kValidExtensions[0]);
@@ -122,6 +122,7 @@
{ "writer", METADATA_KEY_WRITER },
{ "compilation", METADATA_KEY_COMPILATION },
{ "isdrm", METADATA_KEY_IS_DRM },
+ { "date", METADATA_KEY_DATE },
{ "width", METADATA_KEY_VIDEO_WIDTH },
{ "height", METADATA_KEY_VIDEO_HEIGHT },
};
diff --git a/media/libstagefright/StagefrightMetadataRetriever.cpp b/media/libstagefright/StagefrightMetadataRetriever.cpp
index a62e1a2..be5067d 100644
--- a/media/libstagefright/StagefrightMetadataRetriever.cpp
+++ b/media/libstagefright/StagefrightMetadataRetriever.cpp
@@ -22,6 +22,7 @@
#include <utils/Log.h>
#include <gui/Surface.h>
+#include "include/avc_utils.h"
#include "include/StagefrightMetadataRetriever.h"
#include <media/ICrypto.h>
@@ -157,6 +158,15 @@
// TODO: Use Flexible color instead
videoFormat->setInt32("color-format", OMX_COLOR_FormatYUV420Planar);
+ // For the thumbnail extraction case, try to allocate single buffer in both
+ // input and output ports, if seeking to a sync frame. NOTE: This request may
+ // fail if component requires more than that for decoding.
+ bool isSeekingClosest = (seekMode == MediaSource::ReadOptions::SEEK_CLOSEST);
+ if (!isSeekingClosest) {
+ videoFormat->setInt32("android._num-input-buffers", 1);
+ videoFormat->setInt32("android._num-output-buffers", 1);
+ }
+
status_t err;
sp<ALooper> looper = new ALooper;
looper->start();
@@ -237,6 +247,18 @@
int64_t timeUs;
size_t retriesLeft = kRetryCount;
bool done = false;
+ const char *mime;
+ bool success = format->findCString(kKeyMIMEType, &mime);
+ if (!success) {
+ ALOGE("Could not find mime type");
+ return NULL;
+ }
+
+ bool isAvcOrHevc = !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AVC)
+ || !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_HEVC);
+
+ bool firstSample = true;
+ int64_t targetTimeUs = -1ll;
do {
size_t inputIndex = -1;
@@ -264,6 +286,11 @@
haveMoreInputs = false;
break;
}
+ if (firstSample && isSeekingClosest) {
+ mediaBuffer->meta_data()->findInt64(kKeyTargetTime, &targetTimeUs);
+ ALOGV("Seeking closest: targetTimeUs=%lld", (long long)targetTimeUs);
+ }
+ firstSample = false;
if (mediaBuffer->range_length() > codecBuffer->capacity()) {
ALOGE("buffer size (%zu) too large for codec input size (%zu)",
@@ -276,6 +303,12 @@
memcpy(codecBuffer->data(),
(const uint8_t*)mediaBuffer->data() + mediaBuffer->range_offset(),
mediaBuffer->range_length());
+ if (isAvcOrHevc && IsIDR(codecBuffer) && !isSeekingClosest) {
+ // Only need to decode one IDR frame, unless we're seeking with CLOSEST
+ // option, in which case we need to actually decode to targetTimeUs.
+ haveMoreInputs = false;
+ flags |= MediaCodec::BUFFER_FLAG_EOS;
+ }
}
mediaBuffer->release();
@@ -319,8 +352,13 @@
ALOGV("Timed-out waiting for output.. retries left = %zu", retriesLeft);
err = OK;
} else if (err == OK) {
- ALOGV("Received an output buffer");
- done = true;
+ // If we're seeking with CLOSEST option and obtained a valid targetTimeUs
+ // from the extractor, decode to the specified frame. Otherwise we're done.
+ done = (targetTimeUs < 0ll) || (timeUs >= targetTimeUs);
+ ALOGV("Received an output buffer, timeUs=%lld", (long long)timeUs);
+ if (!done) {
+ err = decoder->releaseOutputBuffer(index);
+ }
} else {
ALOGW("Received error %d (%s) instead of output", err, asString(err));
done = true;
diff --git a/media/libstagefright/SurfaceUtils.cpp b/media/libstagefright/SurfaceUtils.cpp
index 9940822..568837a 100644
--- a/media/libstagefright/SurfaceUtils.cpp
+++ b/media/libstagefright/SurfaceUtils.cpp
@@ -26,8 +26,25 @@
status_t setNativeWindowSizeFormatAndUsage(
ANativeWindow *nativeWindow /* nonnull */,
- int width, int height, int format, int rotation, int usage) {
- status_t err = native_window_set_buffers_dimensions(nativeWindow, width, height);
+ int width, int height, int format, int rotation, int usage, bool reconnect) {
+ status_t err = NO_ERROR;
+
+ // In some cases we need to reconnect so that we can dequeue all buffers
+ if (reconnect) {
+ err = native_window_api_disconnect(nativeWindow, NATIVE_WINDOW_API_MEDIA);
+ if (err != NO_ERROR) {
+ ALOGE("native_window_api_disconnect failed: %s (%d)", strerror(-err), -err);
+ return err;
+ }
+
+ err = native_window_api_connect(nativeWindow, NATIVE_WINDOW_API_MEDIA);
+ if (err != NO_ERROR) {
+ ALOGE("native_window_api_connect failed: %s (%d)", strerror(-err), -err);
+ return err;
+ }
+ }
+
+ err = native_window_set_buffers_dimensions(nativeWindow, width, height);
if (err != NO_ERROR) {
ALOGE("native_window_set_buffers_dimensions failed: %s (%d)", strerror(-err), -err);
return err;
@@ -124,7 +141,8 @@
}
err = setNativeWindowSizeFormatAndUsage(
- nativeWindow, 1, 1, HAL_PIXEL_FORMAT_RGBX_8888, 0, GRALLOC_USAGE_SW_WRITE_OFTEN);
+ nativeWindow, 1, 1, HAL_PIXEL_FORMAT_RGBX_8888, 0, GRALLOC_USAGE_SW_WRITE_OFTEN,
+ false /* reconnect */);
if (err != NO_ERROR) {
goto error;
}
diff --git a/media/libstagefright/Utils.cpp b/media/libstagefright/Utils.cpp
index 2b3fd7b..36be7a0 100644
--- a/media/libstagefright/Utils.cpp
+++ b/media/libstagefright/Utils.cpp
@@ -22,6 +22,7 @@
#include <sys/stat.h>
#include <utility>
+#include <vector>
#include "include/ESDS.h"
#include "include/HevcUtils.h"
@@ -1316,6 +1317,20 @@
}
convertMessageToMetaDataColorAspects(msg, meta);
+
+ AString tsSchema;
+ if (msg->findString("ts-schema", &tsSchema)) {
+ unsigned int numLayers = 0;
+ unsigned int numBLayers = 0;
+ char dummy;
+ int tags = sscanf(tsSchema.c_str(), "android.generic.%u%c%u%c",
+ &numLayers, &dummy, &numBLayers, &dummy);
+ if ((tags == 1 || (tags == 3 && dummy == '+'))
+ && numLayers > 0 && numLayers < UINT32_MAX - numBLayers
+ && numLayers + numBLayers <= INT32_MAX) {
+ meta->setInt32(kKeyTemporalLayerCount, numLayers + numBLayers);
+ }
+ }
} else if (mime.startsWith("audio/")) {
int32_t numChannels;
if (msg->findInt32("channel-count", &numChannels)) {
@@ -1377,24 +1392,24 @@
// reassemble the csd data into its original form
sp<ABuffer> csd0, csd1, csd2;
if (msg->findBuffer("csd-0", &csd0)) {
+ int csd0size = csd0->size();
if (mime == MEDIA_MIMETYPE_VIDEO_AVC) {
sp<ABuffer> csd1;
if (msg->findBuffer("csd-1", &csd1)) {
- char avcc[1024]; // that oughta be enough, right?
- size_t outsize = reassembleAVCC(csd0, csd1, avcc);
- meta->setData(kKeyAVCC, kKeyAVCC, avcc, outsize);
+ std::vector<char> avcc(csd0size + csd1->size() + 1024);
+ size_t outsize = reassembleAVCC(csd0, csd1, avcc.data());
+ meta->setData(kKeyAVCC, kKeyAVCC, avcc.data(), outsize);
}
} else if (mime == MEDIA_MIMETYPE_AUDIO_AAC || mime == MEDIA_MIMETYPE_VIDEO_MPEG4) {
- int csd0size = csd0->size();
- char esds[csd0size + 31];
+ std::vector<char> esds(csd0size + 31);
// The written ESDS is actually for an audio stream, but it's enough
// for transporting the CSD to muxers.
- reassembleESDS(csd0, esds);
- meta->setData(kKeyESDS, kKeyESDS, esds, sizeof(esds));
+ reassembleESDS(csd0, esds.data());
+ meta->setData(kKeyESDS, kKeyESDS, esds.data(), esds.size());
} else if (mime == MEDIA_MIMETYPE_VIDEO_HEVC) {
- uint8_t hvcc[1024]; // that oughta be enough, right?
- size_t outsize = reassembleHVCC(csd0, hvcc, 1024, 4);
- meta->setData(kKeyHVCC, kKeyHVCC, hvcc, outsize);
+ std::vector<uint8_t> hvcc(csd0size + 1024);
+ size_t outsize = reassembleHVCC(csd0, hvcc.data(), hvcc.size(), 4);
+ meta->setData(kKeyHVCC, kKeyHVCC, hvcc.data(), outsize);
} else if (mime == MEDIA_MIMETYPE_VIDEO_VP9) {
meta->setData(kKeyVp9CodecPrivate, 0, csd0->data(), csd0->size());
} else if (mime == MEDIA_MIMETYPE_AUDIO_OPUS) {
diff --git a/media/libstagefright/WAVExtractor.cpp b/media/libstagefright/WAVExtractor.cpp
index 4bc2c6d..780b746 100644
--- a/media/libstagefright/WAVExtractor.cpp
+++ b/media/libstagefright/WAVExtractor.cpp
@@ -70,6 +70,8 @@
virtual status_t read(
MediaBuffer **buffer, const ReadOptions *options = NULL);
+ virtual bool supportNonblockingRead() { return true; }
+
protected:
virtual ~WAVSource();
@@ -378,8 +380,8 @@
CHECK(!mStarted);
- mGroup = new MediaBufferGroup;
- mGroup->add_buffer(new MediaBuffer(kMaxFrameSize));
+ // some WAV files may have large audio buffers that use shared memory transfer.
+ mGroup = new MediaBufferGroup(4 /* buffers */, kMaxFrameSize);
if (mBitsPerSample == 8) {
// As a temporary buffer for 8->16 bit conversion.
@@ -416,6 +418,10 @@
MediaBuffer **out, const ReadOptions *options) {
*out = NULL;
+ if (options != nullptr && options->getNonBlocking() && !mGroup->has_buffers()) {
+ return WOULD_BLOCK;
+ }
+
int64_t seekTimeUs;
ReadOptions::SeekMode mode;
if (options != NULL && options->getSeekTo(&seekTimeUs, &mode)) {
diff --git a/media/libstagefright/avc_utils.cpp b/media/libstagefright/avc_utils.cpp
index ccf3440..0396dc6 100644
--- a/media/libstagefright/avc_utils.cpp
+++ b/media/libstagefright/avc_utils.cpp
@@ -491,6 +491,28 @@
return true;
}
+uint32_t FindAVCLayerId(const uint8_t *data, size_t size) {
+ CHECK(data != NULL);
+
+ const unsigned kSvcNalType = 0xE;
+ const unsigned kSvcNalSearchRange = 32;
+ // SVC NAL
+ // |---0 1110|1--- ----|---- ----|iii- ---|
+ // ^ ^
+ // NAL-type = 0xE layer-Id
+ //
+ // layer_id 0 is for base layer, while 1, 2, ... are enhancement layers.
+ // Layer n uses reference frames from layer 0, 1, ..., n-1.
+
+ uint32_t layerId = 0;
+ sp<ABuffer> svcNAL = FindNAL(
+ data, size > kSvcNalSearchRange ? kSvcNalSearchRange : size, kSvcNalType);
+ if (svcNAL != NULL && svcNAL->size() >= 4) {
+ layerId = (*(svcNAL->data() + 3) >> 5) & 0x7;
+ }
+ return layerId;
+}
+
sp<MetaData> MakeAACCodecSpecificData(
unsigned profile, unsigned sampling_freq_index,
unsigned channel_configuration) {
diff --git a/media/libstagefright/codecs/avcdec/SoftAVCDec.cpp b/media/libstagefright/codecs/avcdec/SoftAVCDec.cpp
index 801eebd..8694c73 100644
--- a/media/libstagefright/codecs/avcdec/SoftAVCDec.cpp
+++ b/media/libstagefright/codecs/avcdec/SoftAVCDec.cpp
@@ -123,7 +123,8 @@
mStride(mWidth),
mInputOffset(0){
initPorts(
- kNumBuffers, INPUT_BUF_SIZE, kNumBuffers, CODEC_MIME_TYPE);
+ 1 /* numMinInputBuffers */, kNumBuffers, INPUT_BUF_SIZE,
+ 1 /* numMinOutputBuffers */, kNumBuffers, CODEC_MIME_TYPE);
GETTIME(&mTimeStart, NULL);
@@ -383,6 +384,48 @@
resetPlugin();
}
+bool SoftAVC::getVUIParams() {
+ IV_API_CALL_STATUS_T status;
+ ih264d_ctl_get_vui_params_ip_t s_ctl_get_vui_params_ip;
+ ih264d_ctl_get_vui_params_op_t s_ctl_get_vui_params_op;
+
+ s_ctl_get_vui_params_ip.e_cmd = IVD_CMD_VIDEO_CTL;
+ s_ctl_get_vui_params_ip.e_sub_cmd =
+ (IVD_CONTROL_API_COMMAND_TYPE_T)IH264D_CMD_CTL_GET_VUI_PARAMS;
+
+ s_ctl_get_vui_params_ip.u4_size =
+ sizeof(ih264d_ctl_get_vui_params_ip_t);
+
+ s_ctl_get_vui_params_op.u4_size = sizeof(ih264d_ctl_get_vui_params_op_t);
+
+ status = ivdec_api_function(
+ (iv_obj_t *)mCodecCtx, (void *)&s_ctl_get_vui_params_ip,
+ (void *)&s_ctl_get_vui_params_op);
+
+ if (status != IV_SUCCESS) {
+ ALOGW("Error in getting VUI params: 0x%x",
+ s_ctl_get_vui_params_op.u4_error_code);
+ return false;
+ }
+
+ int32_t primaries = s_ctl_get_vui_params_op.u1_colour_primaries;
+ int32_t transfer = s_ctl_get_vui_params_op.u1_tfr_chars;
+ int32_t coeffs = s_ctl_get_vui_params_op.u1_matrix_coeffs;
+ bool fullRange = s_ctl_get_vui_params_op.u1_video_full_range_flag;
+
+ ColorAspects colorAspects;
+ ColorUtils::convertIsoColorAspectsToCodecAspects(
+ primaries, transfer, coeffs, fullRange, colorAspects);
+
+ // Update color aspects if necessary.
+ if (colorAspectsDiffer(colorAspects, mBitstreamColorAspects)) {
+ mBitstreamColorAspects = colorAspects;
+ status_t err = handleColorAspectsChange();
+ CHECK(err == OK);
+ }
+ return true;
+}
+
bool SoftAVC::setDecodeArgs(
ivd_video_decode_ip_t *ps_dec_ip,
ivd_video_decode_op_t *ps_dec_op,
@@ -604,6 +647,8 @@
bool resChanged = (IVD_RES_CHANGED == (s_dec_op.u4_error_code & 0xFF));
+ getVUIParams();
+
GETTIME(&mTimeEnd, NULL);
/* Compute time taken for decode() */
TIME_DIFF(mTimeStart, mTimeEnd, timeTaken);
@@ -639,16 +684,22 @@
continue;
}
+ // Combine the resolution change and coloraspects change in one PortSettingChange event
+ // if necessary.
if ((0 < s_dec_op.u4_pic_wd) && (0 < s_dec_op.u4_pic_ht)) {
uint32_t width = s_dec_op.u4_pic_wd;
uint32_t height = s_dec_op.u4_pic_ht;
bool portWillReset = false;
handlePortSettingsChange(&portWillReset, width, height);
-
if (portWillReset) {
resetDecoder();
return;
}
+ } else if (mUpdateColorAspects) {
+ notify(OMX_EventPortSettingsChanged, kOutputPortIndex,
+ kDescribeColorAspectsIndex, NULL);
+ mUpdateColorAspects = false;
+ return;
}
if (s_dec_op.u4_output_present) {
@@ -705,6 +756,10 @@
}
}
+int SoftAVC::getColorAspectPreference() {
+ return kPreferBitstream;
+}
+
} // namespace android
android::SoftOMXComponent *createSoftOMXComponent(
diff --git a/media/libstagefright/codecs/avcdec/SoftAVCDec.h b/media/libstagefright/codecs/avcdec/SoftAVCDec.h
index 38b24a6..2a71188 100644
--- a/media/libstagefright/codecs/avcdec/SoftAVCDec.h
+++ b/media/libstagefright/codecs/avcdec/SoftAVCDec.h
@@ -59,6 +59,7 @@
virtual void onQueueFilled(OMX_U32 portIndex);
virtual void onPortFlushCompleted(OMX_U32 portIndex);
virtual void onReset();
+ virtual int getColorAspectPreference();
private:
// Number of input and output buffers
enum {
@@ -117,6 +118,8 @@
OMX_BUFFERHEADERTYPE *outHeader,
size_t timeStampIx);
+ bool getVUIParams();
+
DISALLOW_EVIL_CONSTRUCTORS(SoftAVC);
};
diff --git a/media/libstagefright/codecs/avcenc/SoftAVCEnc.cpp b/media/libstagefright/codecs/avcenc/SoftAVCEnc.cpp
index 6ec8c41..9e7a3be 100644
--- a/media/libstagefright/codecs/avcenc/SoftAVCEnc.cpp
+++ b/media/libstagefright/codecs/avcenc/SoftAVCEnc.cpp
@@ -1016,10 +1016,10 @@
if ((avcType->nAllowedPictureTypes & OMX_VIDEO_PictureTypeB) &&
avcType->nPFrames) {
- mBframes = avcType->nBFrames / avcType->nPFrames;
+ mBframes = avcType->nBFrames;
}
- mIInterval = avcType->nPFrames + avcType->nBFrames;
+ mIInterval = (avcType->nPFrames + 1) * (avcType->nBFrames + 1);
mConstrainedIntraFlag = avcType->bconstIpred;
if (OMX_VIDEO_AVCLoopFilterDisable == avcType->eLoopFilterMode)
@@ -1033,7 +1033,9 @@
|| avcType->bDirect8x8Inference != OMX_FALSE
|| avcType->bDirectSpatialTemporal != OMX_FALSE
|| avcType->nCabacInitIdc != 0) {
- return OMX_ErrorUndefined;
+ // OMX does not allow a way to signal what values are wrong, so it's
+ // best for components to just do best effort in supporting these values
+ ALOGV("ignoring unsupported settings");
}
if (OK != ConvertOmxAvcLevelToAvcSpecLevel(avcType->eLevel, &mAVCEncLevel)) {
diff --git a/media/libstagefright/codecs/hevcdec/SoftHEVC.cpp b/media/libstagefright/codecs/hevcdec/SoftHEVC.cpp
index 0215a11..5c70387 100644
--- a/media/libstagefright/codecs/hevcdec/SoftHEVC.cpp
+++ b/media/libstagefright/codecs/hevcdec/SoftHEVC.cpp
@@ -182,6 +182,48 @@
return OK;
}
+bool SoftHEVC::getVUIParams() {
+ IV_API_CALL_STATUS_T status;
+ ihevcd_cxa_ctl_get_vui_params_ip_t s_ctl_get_vui_params_ip;
+ ihevcd_cxa_ctl_get_vui_params_op_t s_ctl_get_vui_params_op;
+
+ s_ctl_get_vui_params_ip.e_cmd = IVD_CMD_VIDEO_CTL;
+ s_ctl_get_vui_params_ip.e_sub_cmd =
+ (IVD_CONTROL_API_COMMAND_TYPE_T)IHEVCD_CXA_CMD_CTL_GET_VUI_PARAMS;
+
+ s_ctl_get_vui_params_ip.u4_size =
+ sizeof(ihevcd_cxa_ctl_get_vui_params_ip_t);
+
+ s_ctl_get_vui_params_op.u4_size = sizeof(ihevcd_cxa_ctl_get_vui_params_op_t);
+
+ status = ivdec_api_function(
+ (iv_obj_t *)mCodecCtx, (void *)&s_ctl_get_vui_params_ip,
+ (void *)&s_ctl_get_vui_params_op);
+
+ if (status != IV_SUCCESS) {
+ ALOGW("Error in getting VUI params: 0x%x",
+ s_ctl_get_vui_params_op.u4_error_code);
+ return false;
+ }
+
+ int32_t primaries = s_ctl_get_vui_params_op.u1_colour_primaries;
+ int32_t transfer = s_ctl_get_vui_params_op.u1_transfer_characteristics;
+ int32_t coeffs = s_ctl_get_vui_params_op.u1_matrix_coefficients;
+ bool fullRange = s_ctl_get_vui_params_op.u1_video_full_range_flag;
+
+ ColorAspects colorAspects;
+ ColorUtils::convertIsoColorAspectsToCodecAspects(
+ primaries, transfer, coeffs, fullRange, colorAspects);
+
+ // Update color aspects if necessary.
+ if (colorAspectsDiffer(colorAspects, mBitstreamColorAspects)) {
+ mBitstreamColorAspects = colorAspects;
+ status_t err = handleColorAspectsChange();
+ CHECK(err == OK);
+ }
+ return true;
+}
+
status_t SoftHEVC::resetDecoder() {
ivd_ctl_reset_ip_t s_ctl_ip;
ivd_ctl_reset_op_t s_ctl_op;
@@ -554,6 +596,8 @@
bool resChanged = (IVD_RES_CHANGED == (s_dec_op.u4_error_code & 0xFF));
+ getVUIParams();
+
GETTIME(&mTimeEnd, NULL);
/* Compute time taken for decode() */
TIME_DIFF(mTimeStart, mTimeEnd, timeTaken);
@@ -589,6 +633,8 @@
continue;
}
+ // Combine the resolution change and coloraspects change in one PortSettingChange event
+ // if necessary.
if ((0 < s_dec_op.u4_pic_wd) && (0 < s_dec_op.u4_pic_ht)) {
uint32_t width = s_dec_op.u4_pic_wd;
uint32_t height = s_dec_op.u4_pic_ht;
@@ -599,6 +645,11 @@
resetDecoder();
return;
}
+ } else if (mUpdateColorAspects) {
+ notify(OMX_EventPortSettingsChanged, kOutputPortIndex,
+ kDescribeColorAspectsIndex, NULL);
+ mUpdateColorAspects = false;
+ return;
}
if (s_dec_op.u4_output_present) {
@@ -654,6 +705,10 @@
}
}
+int SoftHEVC::getColorAspectPreference() {
+ return kPreferBitstream;
+}
+
} // namespace android
android::SoftOMXComponent *createSoftOMXComponent(const char *name,
diff --git a/media/libstagefright/codecs/hevcdec/SoftHEVC.h b/media/libstagefright/codecs/hevcdec/SoftHEVC.h
index 3c76b59..e7c2127 100644
--- a/media/libstagefright/codecs/hevcdec/SoftHEVC.h
+++ b/media/libstagefright/codecs/hevcdec/SoftHEVC.h
@@ -61,6 +61,7 @@
virtual void onQueueFilled(OMX_U32 portIndex);
virtual void onPortFlushCompleted(OMX_U32 portIndex);
virtual void onReset();
+ virtual int getColorAspectPreference();
private:
// Number of input and output buffers
enum {
@@ -112,6 +113,8 @@
OMX_BUFFERHEADERTYPE *outHeader,
size_t timeStampIx);
+ bool getVUIParams();
+
DISALLOW_EVIL_CONSTRUCTORS (SoftHEVC);
};
diff --git a/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.cpp b/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.cpp
index bb59ae4..1dd631a 100644
--- a/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.cpp
+++ b/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.cpp
@@ -210,8 +210,17 @@
PortInfo *port = editPortInfo(1);
OMX_BUFFERHEADERTYPE *outHeader = port->mBuffers.editItemAt(1).mHeader;
+ OMX_U32 yFrameSize = sizeof(uint8) * mHandle->size;
+ if ((outHeader->nAllocLen < yFrameSize) ||
+ (outHeader->nAllocLen - yFrameSize < yFrameSize / 2)) {
+ ALOGE("Too small output buffer for reference frame: %lu bytes",
+ (unsigned long)outHeader->nAllocLen);
+ android_errorWriteLog(0x534e4554, "30033990");
+ notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL);
+ mSignalledError = true;
+ return;
+ }
PVSetReferenceYUV(mHandle, outHeader->pBuffer);
-
mFramesConfigured = true;
}
@@ -229,7 +238,16 @@
int32_t bufferSize = inHeader->nFilledLen;
int32_t tmp = bufferSize;
- OMX_U32 frameSize = (mWidth * mHeight * 3) / 2;
+ OMX_U32 frameSize;
+ OMX_U64 yFrameSize = (OMX_U64)mWidth * (OMX_U64)mHeight;
+ if (yFrameSize > ((OMX_U64)UINT32_MAX / 3) * 2) {
+ ALOGE("Frame size too large");
+ notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL);
+ mSignalledError = true;
+ return;
+ }
+ frameSize = (OMX_U32)(yFrameSize + (yFrameSize / 2));
+
if (outHeader->nAllocLen < frameSize) {
android_errorWriteLog(0x534e4554, "27833616");
ALOGE("Insufficient output buffer size");
diff --git a/media/libstagefright/codecs/m4v_h263/enc/SoftMPEG4Encoder.cpp b/media/libstagefright/codecs/m4v_h263/enc/SoftMPEG4Encoder.cpp
index 7638bb7..f496b0c 100644
--- a/media/libstagefright/codecs/m4v_h263/enc/SoftMPEG4Encoder.cpp
+++ b/media/libstagefright/codecs/m4v_h263/enc/SoftMPEG4Encoder.cpp
@@ -76,7 +76,7 @@
176 /* width */, 144 /* height */,
callbacks, appData, component),
mEncodeMode(COMBINE_MODE_WITH_ERR_RES),
- mIDRFrameRefreshIntervalInSec(1),
+ mKeyFrameInterval(30),
mNumInputFrames(-1),
mStarted(false),
mSawInputEOS(false),
@@ -116,6 +116,10 @@
ALOGE("Failed to get default encoding parameters");
return OMX_ErrorUndefined;
}
+ if (mFramerate == 0) {
+ ALOGE("Framerate should not be 0");
+ return OMX_ErrorUndefined;
+ }
mEncParams->encMode = mEncodeMode;
mEncParams->encWidth[0] = mWidth;
mEncParams->encHeight[0] = mHeight;
@@ -159,14 +163,7 @@
}
// Set IDR frame refresh interval
- if (mIDRFrameRefreshIntervalInSec < 0) {
- mEncParams->intraPeriod = -1;
- } else if (mIDRFrameRefreshIntervalInSec == 0) {
- mEncParams->intraPeriod = 1; // All I frames
- } else {
- mEncParams->intraPeriod =
- (mIDRFrameRefreshIntervalInSec * mFramerate) >> 16;
- }
+ mEncParams->intraPeriod = mKeyFrameInterval;
mEncParams->numIntraMB = 0;
mEncParams->sceneDetect = PV_ON;
@@ -378,6 +375,8 @@
return OMX_ErrorUndefined;
}
+ mKeyFrameInterval = int32_t(mpeg4type->nPFrames + 1);
+
return OMX_ErrorNone;
}
diff --git a/media/libstagefright/codecs/m4v_h263/enc/SoftMPEG4Encoder.h b/media/libstagefright/codecs/m4v_h263/enc/SoftMPEG4Encoder.h
index 3389c37..bb6ea92 100644
--- a/media/libstagefright/codecs/m4v_h263/enc/SoftMPEG4Encoder.h
+++ b/media/libstagefright/codecs/m4v_h263/enc/SoftMPEG4Encoder.h
@@ -63,7 +63,7 @@
} InputBufferInfo;
MP4EncodingMode mEncodeMode;
- int32_t mIDRFrameRefreshIntervalInSec;
+ int32_t mKeyFrameInterval; // 1: all I-frames, <0: infinite
int64_t mNumInputFrames;
bool mStarted;
diff --git a/media/libstagefright/codecs/mp3dec/SoftMP3.cpp b/media/libstagefright/codecs/mp3dec/SoftMP3.cpp
index 4cde54e..0822c34 100644
--- a/media/libstagefright/codecs/mp3dec/SoftMP3.cpp
+++ b/media/libstagefright/codecs/mp3dec/SoftMP3.cpp
@@ -120,6 +120,17 @@
mIsFirst = true;
}
+void *SoftMP3::memsetSafe(OMX_BUFFERHEADERTYPE *outHeader, int c, size_t len) {
+ if (len > outHeader->nAllocLen) {
+ ALOGE("memset buffer too small: got %u, expected %zu", outHeader->nAllocLen, len);
+ android_errorWriteLog(0x534e4554, "29422022");
+ notify(OMX_EventError, OMX_ErrorUndefined, OUTPUT_BUFFER_TOO_SMALL, NULL);
+ mSignalledError = true;
+ return NULL;
+ }
+ return memset(outHeader->pBuffer, c, len);
+}
+
OMX_ERRORTYPE SoftMP3::internalGetParameter(
OMX_INDEXTYPE index, OMX_PTR params) {
switch (index) {
@@ -300,7 +311,10 @@
outHeader->nOffset = 0;
outHeader->nFilledLen = kPVMP3DecoderDelay * mNumChannels * sizeof(int16_t);
- memset(outHeader->pBuffer, 0, outHeader->nFilledLen);
+ if (!memsetSafe(outHeader, 0, outHeader->nFilledLen)) {
+ return;
+ }
+
}
outHeader->nFlags = OMX_BUFFERFLAG_EOS;
mSignalledOutputEos = true;
@@ -312,9 +326,9 @@
// if mIsFirst is true as we may not have a valid
// mConfig->samplingRate and mConfig->num_channels?
ALOGV_IF(mIsFirst, "insufficient data for first frame, sending silence");
- memset(outHeader->pBuffer,
- 0,
- mConfig->outputFrameSize * sizeof(int16_t));
+ if (!memsetSafe(outHeader, 0, mConfig->outputFrameSize * sizeof(int16_t))) {
+ return;
+ }
if (inHeader) {
mConfig->inputBufferUsedLength = inHeader->nFilledLen;
diff --git a/media/libstagefright/codecs/mp3dec/SoftMP3.h b/media/libstagefright/codecs/mp3dec/SoftMP3.h
index f9e7b53..3bfa6c7 100644
--- a/media/libstagefright/codecs/mp3dec/SoftMP3.h
+++ b/media/libstagefright/codecs/mp3dec/SoftMP3.h
@@ -72,6 +72,7 @@
void initPorts();
void initDecoder();
+ void *memsetSafe(OMX_BUFFERHEADERTYPE *outHeader, int c, size_t len);
DISALLOW_EVIL_CONSTRUCTORS(SoftMP3);
};
diff --git a/media/libstagefright/codecs/mpeg2dec/SoftMPEG2.cpp b/media/libstagefright/codecs/mpeg2dec/SoftMPEG2.cpp
index 5210683..5ed037a 100644
--- a/media/libstagefright/codecs/mpeg2dec/SoftMPEG2.cpp
+++ b/media/libstagefright/codecs/mpeg2dec/SoftMPEG2.cpp
@@ -460,6 +460,47 @@
resetPlugin();
}
+bool SoftMPEG2::getSeqInfo() {
+ IV_API_CALL_STATUS_T status;
+ impeg2d_ctl_get_seq_info_ip_t s_ctl_get_seq_info_ip;
+ impeg2d_ctl_get_seq_info_op_t s_ctl_get_seq_info_op;
+
+ s_ctl_get_seq_info_ip.e_cmd = IVD_CMD_VIDEO_CTL;
+ s_ctl_get_seq_info_ip.e_sub_cmd =
+ (IVD_CONTROL_API_COMMAND_TYPE_T)IMPEG2D_CMD_CTL_GET_SEQ_INFO;
+
+ s_ctl_get_seq_info_ip.u4_size = sizeof(impeg2d_ctl_get_seq_info_ip_t);
+ s_ctl_get_seq_info_op.u4_size = sizeof(impeg2d_ctl_get_seq_info_op_t);
+
+ status = ivdec_api_function(
+ (iv_obj_t *)mCodecCtx, (void *)&s_ctl_get_seq_info_ip,
+ (void *)&s_ctl_get_seq_info_op);
+
+ if (status != IV_SUCCESS) {
+ ALOGW("Error in getting Sequence info: 0x%x",
+ s_ctl_get_seq_info_op.u4_error_code);
+ return false;
+ }
+
+
+ int32_t primaries = s_ctl_get_seq_info_op.u1_colour_primaries;
+ int32_t transfer = s_ctl_get_seq_info_op.u1_transfer_characteristics;
+ int32_t coeffs = s_ctl_get_seq_info_op.u1_matrix_coefficients;
+ bool fullRange = false; // mpeg2 video has limited range.
+
+ ColorAspects colorAspects;
+ ColorUtils::convertIsoColorAspectsToCodecAspects(
+ primaries, transfer, coeffs, fullRange, colorAspects);
+
+ // Update color aspects if necessary.
+ if (colorAspectsDiffer(colorAspects, mBitstreamColorAspects)) {
+ mBitstreamColorAspects = colorAspects;
+ status_t err = handleColorAspectsChange();
+ CHECK(err == OK);
+ }
+ return true;
+}
+
OMX_ERRORTYPE SoftMPEG2::internalSetParameter(OMX_INDEXTYPE index, const OMX_PTR params) {
const uint32_t oldWidth = mWidth;
const uint32_t oldHeight = mHeight;
@@ -650,6 +691,8 @@
bool unsupportedDimensions = (IMPEG2D_UNSUPPORTED_DIMENSIONS == s_dec_op.u4_error_code);
bool resChanged = (IVD_RES_CHANGED == (s_dec_op.u4_error_code & 0xFF));
+ getSeqInfo();
+
GETTIME(&mTimeEnd, NULL);
/* Compute time taken for decode() */
TIME_DIFF(mTimeStart, mTimeEnd, timeTaken);
@@ -705,6 +748,8 @@
continue;
}
+ // Combine the resolution change and coloraspects change in one PortSettingChange event
+ // if necessary.
if ((0 < s_dec_op.u4_pic_wd) && (0 < s_dec_op.u4_pic_ht)) {
uint32_t width = s_dec_op.u4_pic_wd;
uint32_t height = s_dec_op.u4_pic_ht;
@@ -715,6 +760,11 @@
resetDecoder();
return;
}
+ } else if (mUpdateColorAspects) {
+ notify(OMX_EventPortSettingsChanged, kOutputPortIndex,
+ kDescribeColorAspectsIndex, NULL);
+ mUpdateColorAspects = false;
+ return;
}
if (s_dec_op.u4_output_present) {
@@ -783,6 +833,10 @@
}
}
+int SoftMPEG2::getColorAspectPreference() {
+ return kPreferBitstream;
+}
+
} // namespace android
android::SoftOMXComponent *createSoftOMXComponent(
diff --git a/media/libstagefright/codecs/mpeg2dec/SoftMPEG2.h b/media/libstagefright/codecs/mpeg2dec/SoftMPEG2.h
index 9d82c6d..1921a23 100644
--- a/media/libstagefright/codecs/mpeg2dec/SoftMPEG2.h
+++ b/media/libstagefright/codecs/mpeg2dec/SoftMPEG2.h
@@ -63,6 +63,7 @@
virtual void onQueueFilled(OMX_U32 portIndex);
virtual void onPortFlushCompleted(OMX_U32 portIndex);
virtual void onReset();
+ virtual int getColorAspectPreference();
virtual OMX_ERRORTYPE internalSetParameter(OMX_INDEXTYPE index, const OMX_PTR params);
private:
// Number of input and output buffers
@@ -125,6 +126,8 @@
OMX_BUFFERHEADERTYPE *outHeader,
size_t timeStampIx);
+ bool getSeqInfo();
+
DISALLOW_EVIL_CONSTRUCTORS(SoftMPEG2);
};
diff --git a/media/libstagefright/codecs/on2/dec/SoftVPX.cpp b/media/libstagefright/codecs/on2/dec/SoftVPX.cpp
index 8022467..3490008 100644
--- a/media/libstagefright/codecs/on2/dec/SoftVPX.cpp
+++ b/media/libstagefright/codecs/on2/dec/SoftVPX.cpp
@@ -156,7 +156,7 @@
outHeader->nFlags = 0;
outHeader->nFilledLen = (outputBufferWidth() * outputBufferHeight() * 3) / 2;
outHeader->nTimeStamp = *(OMX_TICKS *)mImg->user_priv;
- if (outHeader->nAllocLen >= outHeader->nFilledLen) {
+ if (outputBufferSafe(outHeader)) {
uint8_t *dst = outHeader->pBuffer;
const uint8_t *srcY = (const uint8_t *)mImg->planes[VPX_PLANE_Y];
const uint8_t *srcU = (const uint8_t *)mImg->planes[VPX_PLANE_U];
@@ -166,8 +166,6 @@
size_t srcVStride = mImg->stride[VPX_PLANE_V];
copyYV12FrameToOutputBuffer(dst, srcY, srcU, srcV, srcYStride, srcUStride, srcVStride);
} else {
- ALOGE("b/27597103, buffer too small");
- android_errorWriteLog(0x534e4554, "27597103");
outHeader->nFilledLen = 0;
}
@@ -197,6 +195,25 @@
return true;
}
+bool SoftVPX::outputBufferSafe(OMX_BUFFERHEADERTYPE *outHeader) {
+ uint32_t width = outputBufferWidth();
+ uint32_t height = outputBufferHeight();
+ uint64_t nFilledLen = width;
+ nFilledLen *= height;
+ if (nFilledLen > UINT32_MAX / 3) {
+ ALOGE("b/29421675, nFilledLen overflow %llu w %u h %u",
+ (unsigned long long)nFilledLen, width, height);
+ android_errorWriteLog(0x534e4554, "29421675");
+ return false;
+ } else if (outHeader->nAllocLen < outHeader->nFilledLen) {
+ ALOGE("b/27597103, buffer too small");
+ android_errorWriteLog(0x534e4554, "27597103");
+ return false;
+ }
+
+ return true;
+}
+
void SoftVPX::onQueueFilled(OMX_U32 /* portIndex */) {
if (mOutputPortSettingsChange != NONE || mEOSStatus == OUTPUT_FRAMES_FLUSHED) {
return;
diff --git a/media/libstagefright/codecs/on2/dec/SoftVPX.h b/media/libstagefright/codecs/on2/dec/SoftVPX.h
index 8ccbae2..84cf79c 100644
--- a/media/libstagefright/codecs/on2/dec/SoftVPX.h
+++ b/media/libstagefright/codecs/on2/dec/SoftVPX.h
@@ -66,6 +66,7 @@
status_t initDecoder();
status_t destroyDecoder();
bool outputBuffers(bool flushDecoder, bool display, bool eos, bool *portWillReset);
+ bool outputBufferSafe(OMX_BUFFERHEADERTYPE *outHeader);
DISALLOW_EVIL_CONSTRUCTORS(SoftVPX);
};
diff --git a/media/libstagefright/colorconversion/Android.mk b/media/libstagefright/colorconversion/Android.mk
index 32e2dfd..0bf9701 100644
--- a/media/libstagefright/colorconversion/Android.mk
+++ b/media/libstagefright/colorconversion/Android.mk
@@ -7,7 +7,11 @@
LOCAL_C_INCLUDES := \
$(TOP)/frameworks/native/include/media/openmax \
- $(TOP)/hardware/msm7k
+ $(TOP)/hardware/msm7k \
+ $(TOP)/external/libyuv/files/include
+
+LOCAL_STATIC_LIBRARIES := \
+ libyuv_static \
LOCAL_CFLAGS += -Werror
LOCAL_CLANG := true
diff --git a/media/libstagefright/colorconversion/ColorConverter.cpp b/media/libstagefright/colorconversion/ColorConverter.cpp
index 597167f..3ca7cc0 100644
--- a/media/libstagefright/colorconversion/ColorConverter.cpp
+++ b/media/libstagefright/colorconversion/ColorConverter.cpp
@@ -22,6 +22,10 @@
#include <media/stagefright/ColorConverter.h>
#include <media/stagefright/MediaErrors.h>
+#include "libyuv/convert_from.h"
+
+#define USE_LIBYUV
+
namespace android {
ColorConverter::ColorConverter(
@@ -103,7 +107,11 @@
switch (mSrcFormat) {
case OMX_COLOR_FormatYUV420Planar:
+#ifdef USE_LIBYUV
+ err = convertYUV420PlanarUseLibYUV(src, dst);
+#else
err = convertYUV420Planar(src, dst);
+#endif
break;
case OMX_COLOR_FormatCbYCrY:
@@ -196,6 +204,34 @@
return OK;
}
+status_t ColorConverter::convertYUV420PlanarUseLibYUV(
+ const BitmapParams &src, const BitmapParams &dst) {
+ if (!((src.mCropLeft & 1) == 0
+ && src.cropWidth() == dst.cropWidth()
+ && src.cropHeight() == dst.cropHeight())) {
+ return ERROR_UNSUPPORTED;
+ }
+
+ uint16_t *dst_ptr = (uint16_t *)dst.mBits
+ + dst.mCropTop * dst.mWidth + dst.mCropLeft;
+
+ const uint8_t *src_y =
+ (const uint8_t *)src.mBits + src.mCropTop * src.mWidth + src.mCropLeft;
+
+ const uint8_t *src_u =
+ (const uint8_t *)src_y + src.mWidth * src.mHeight
+ + src.mCropTop * (src.mWidth / 2) + src.mCropLeft / 2;
+
+ const uint8_t *src_v =
+ src_u + (src.mWidth / 2) * (src.mHeight / 2);
+
+
+ libyuv::I420ToRGB565(src_y, src.mWidth, src_u, src.mWidth / 2, src_v, src.mWidth / 2,
+ (uint8 *)dst_ptr, dst.mWidth * 2, dst.mWidth, dst.mHeight);
+
+ return OK;
+}
+
status_t ColorConverter::convertYUV420Planar(
const BitmapParams &src, const BitmapParams &dst) {
if (!((src.mCropLeft & 1) == 0
diff --git a/media/libstagefright/foundation/AMessage.cpp b/media/libstagefright/foundation/AMessage.cpp
index 00f2755..0406f6a 100644
--- a/media/libstagefright/foundation/AMessage.cpp
+++ b/media/libstagefright/foundation/AMessage.cpp
@@ -212,6 +212,33 @@
return NULL;
}
+bool AMessage::findAsFloat(const char *name, float *value) const {
+ size_t i = findItemIndex(name, strlen(name));
+ if (i < mNumItems) {
+ const Item *item = &mItems[i];
+ switch (item->mType) {
+ case kTypeFloat:
+ *value = item->u.floatValue;
+ return true;
+ case kTypeDouble:
+ *value = (float)item->u.doubleValue;
+ return true;
+ case kTypeInt64:
+ *value = (float)item->u.int64Value;
+ return true;
+ case kTypeInt32:
+ *value = (float)item->u.int32Value;
+ return true;
+ case kTypeSize:
+ *value = (float)item->u.sizeValue;
+ return true;
+ default:
+ return false;
+ }
+ }
+ return false;
+}
+
bool AMessage::contains(const char *name) const {
size_t i = findItemIndex(name, strlen(name));
return i < mNumItems;
diff --git a/media/libstagefright/foundation/ColorUtils.cpp b/media/libstagefright/foundation/ColorUtils.cpp
index e329766..d7439b2 100644
--- a/media/libstagefright/foundation/ColorUtils.cpp
+++ b/media/libstagefright/foundation/ColorUtils.cpp
@@ -286,6 +286,7 @@
{ 15, ColorAspects::TransferSMPTE170M },
{ 16, ColorAspects::TransferST2084 },
{ 17, ColorAspects::TransferST428 },
+ { 18, ColorAspects::TransferHLG },
}
};
diff --git a/media/libstagefright/foundation/MediaBuffer.cpp b/media/libstagefright/foundation/MediaBuffer.cpp
index fa8e241..16000ef 100644
--- a/media/libstagefright/foundation/MediaBuffer.cpp
+++ b/media/libstagefright/foundation/MediaBuffer.cpp
@@ -30,9 +30,11 @@
namespace android {
+/* static */
+std::atomic_int_least32_t MediaBuffer::mUseSharedMemory(0);
+
MediaBuffer::MediaBuffer(void *data, size_t size)
: mObserver(NULL),
- mNextBuffer(NULL),
mRefCount(0),
mData(data),
mSize(size),
@@ -45,7 +47,6 @@
MediaBuffer::MediaBuffer(size_t size)
: mObserver(NULL),
- mNextBuffer(NULL),
mRefCount(0),
mData(NULL),
mSize(size),
@@ -54,11 +55,14 @@
mOwnsData(true),
mMetaData(new MetaData),
mOriginal(NULL) {
- if (size < kSharedMemThreshold) {
+ if (size < kSharedMemThreshold
+ || std::atomic_load_explicit(&mUseSharedMemory, std::memory_order_seq_cst) == 0) {
mData = malloc(size);
} else {
- sp<MemoryDealer> memoryDealer = new MemoryDealer(size, "MediaBuffer");
- mMemory = memoryDealer->allocate(size);
+ ALOGV("creating memoryDealer");
+ sp<MemoryDealer> memoryDealer =
+ new MemoryDealer(size + sizeof(SharedControl), "MediaBuffer");
+ mMemory = memoryDealer->allocate(size + sizeof(SharedControl));
if (mMemory == NULL) {
ALOGW("Failed to allocate shared memory, trying regular allocation!");
mData = malloc(size);
@@ -66,7 +70,8 @@
ALOGE("Out of memory");
}
} else {
- mData = mMemory->pointer();
+ getSharedControl()->clear();
+ mData = (uint8_t *)mMemory->pointer() + sizeof(SharedControl);
ALOGV("Allocated shared mem buffer of size %zu @ %p", size, mData);
}
}
@@ -74,7 +79,6 @@
MediaBuffer::MediaBuffer(const sp<GraphicBuffer>& graphicBuffer)
: mObserver(NULL),
- mNextBuffer(NULL),
mRefCount(0),
mData(NULL),
mSize(1),
@@ -88,7 +92,6 @@
MediaBuffer::MediaBuffer(const sp<ABuffer> &buffer)
: mObserver(NULL),
- mNextBuffer(NULL),
mRefCount(0),
mData(buffer->data()),
mSize(buffer->size()),
@@ -102,6 +105,7 @@
void MediaBuffer::release() {
if (mObserver == NULL) {
+ // Legacy contract for MediaBuffer without a MediaBufferGroup.
CHECK_EQ(mRefCount, 0);
delete this;
return;
@@ -183,6 +187,10 @@
mOriginal->release();
mOriginal = NULL;
}
+
+ if (mMemory.get() != nullptr) {
+ getSharedControl()->setDeadObject();
+ }
}
void MediaBuffer::setObserver(MediaBufferObserver *observer) {
@@ -190,18 +198,6 @@
mObserver = observer;
}
-void MediaBuffer::setNextBuffer(MediaBuffer *buffer) {
- mNextBuffer = buffer;
-}
-
-MediaBuffer *MediaBuffer::nextBuffer() {
- return mNextBuffer;
-}
-
-int MediaBuffer::refcount() const {
- return mRefCount;
-}
-
MediaBuffer *MediaBuffer::clone() {
CHECK(mGraphicBuffer == NULL);
diff --git a/media/libstagefright/foundation/MediaBufferGroup.cpp b/media/libstagefright/foundation/MediaBufferGroup.cpp
index 9022324..54f768a 100644
--- a/media/libstagefright/foundation/MediaBufferGroup.cpp
+++ b/media/libstagefright/foundation/MediaBufferGroup.cpp
@@ -23,20 +23,78 @@
namespace android {
-MediaBufferGroup::MediaBufferGroup()
- : mFirstBuffer(NULL),
- mLastBuffer(NULL) {
+// std::min is not constexpr in C++11
+template<typename T>
+constexpr T MIN(const T &a, const T &b) { return a <= b ? a : b; }
+
+// MediaBufferGroup may create shared memory buffers at a
+// smaller threshold than an isolated new MediaBuffer.
+static const size_t kSharedMemoryThreshold = MIN(
+ (size_t)MediaBuffer::kSharedMemThreshold, (size_t)(4 * 1024));
+
+MediaBufferGroup::MediaBufferGroup(size_t growthLimit) :
+ mGrowthLimit(growthLimit) {
+}
+
+MediaBufferGroup::MediaBufferGroup(size_t buffers, size_t buffer_size, size_t growthLimit)
+ : mGrowthLimit(growthLimit) {
+
+ if (buffer_size >= kSharedMemoryThreshold) {
+ ALOGD("creating MemoryDealer");
+ // Using a single MemoryDealer is efficient for a group of shared memory objects.
+ // This loop guarantees that we use shared memory (no fallback to malloc).
+
+ size_t alignment = MemoryDealer::getAllocationAlignment();
+ size_t augmented_size = buffer_size + sizeof(MediaBuffer::SharedControl);
+ size_t total = (augmented_size + alignment - 1) / alignment * alignment * buffers;
+ sp<MemoryDealer> memoryDealer = new MemoryDealer(total, "MediaBufferGroup");
+
+ for (size_t i = 0; i < buffers; ++i) {
+ sp<IMemory> mem = memoryDealer->allocate(augmented_size);
+ if (mem.get() == nullptr || mem->pointer() == nullptr) {
+ ALOGW("Only allocated %zu shared buffers of size %zu", i, buffer_size);
+ break;
+ }
+ MediaBuffer *buffer = new MediaBuffer(mem);
+ buffer->getSharedControl()->clear();
+ add_buffer(buffer);
+ }
+ return;
+ }
+
+ // Non-shared memory allocation.
+ for (size_t i = 0; i < buffers; ++i) {
+ MediaBuffer *buffer = new MediaBuffer(buffer_size);
+ if (buffer->data() == nullptr) {
+ delete buffer; // don't call release, it's not properly formed
+ ALOGW("Only allocated %zu malloc buffers of size %zu", i, buffer_size);
+ break;
+ }
+ add_buffer(buffer);
+ }
}
MediaBufferGroup::~MediaBufferGroup() {
- MediaBuffer *next;
- for (MediaBuffer *buffer = mFirstBuffer; buffer != NULL;
- buffer = next) {
- next = buffer->nextBuffer();
+ for (MediaBuffer *buffer : mBuffers) {
+ if (buffer->refcount() != 0) {
+ const int localRefcount = buffer->localRefcount();
+ const int remoteRefcount = buffer->remoteRefcount();
- CHECK_EQ(buffer->refcount(), 0);
+ // Fatal if we have a local refcount.
+ LOG_ALWAYS_FATAL_IF(localRefcount != 0,
+ "buffer(%p) localRefcount %d != 0, remoteRefcount %d",
+ buffer, localRefcount, remoteRefcount);
- buffer->setObserver(NULL);
+ // Log an error if we have a remaining remote refcount,
+ // as the remote process may have died or may have inappropriate behavior.
+ // The shared memory associated with the MediaBuffer will
+ // automatically be reclaimed when there are no remaining fds
+ // associated with it.
+ ALOGE("buffer(%p) has residual remoteRefcount %d",
+ buffer, remoteRefcount);
+ }
+ // gracefully delete.
+ buffer->setObserver(nullptr);
buffer->release();
}
}
@@ -45,87 +103,83 @@
Mutex::Autolock autoLock(mLock);
buffer->setObserver(this);
+ mBuffers.emplace_back(buffer);
+ // optionally: mGrowthLimit = max(mGrowthLimit, mBuffers.size());
+}
- if (mLastBuffer) {
- mLastBuffer->setNextBuffer(buffer);
- } else {
- mFirstBuffer = buffer;
+bool MediaBufferGroup::has_buffers() {
+ if (mBuffers.size() < mGrowthLimit) {
+ return true; // We can add more buffers internally.
}
-
- mLastBuffer = buffer;
+ for (MediaBuffer *buffer : mBuffers) {
+ if (buffer->refcount() == 0) {
+ return true;
+ }
+ }
+ return false;
}
status_t MediaBufferGroup::acquire_buffer(
MediaBuffer **out, bool nonBlocking, size_t requestedSize) {
Mutex::Autolock autoLock(mLock);
-
for (;;) {
- MediaBuffer *freeBuffer = NULL;
- MediaBuffer *freeBufferPrevious = NULL;
- MediaBuffer *buffer = NULL;
- MediaBuffer *bufferPrevious = NULL;
size_t smallest = requestedSize;
- for (buffer = mFirstBuffer;
- buffer != NULL; buffer = buffer->nextBuffer()) {
- if (buffer->refcount() == 0) {
- if (buffer->size() >= requestedSize) {
- break;
- } else if (buffer->size() < smallest) {
- freeBuffer = buffer;
- freeBufferPrevious = bufferPrevious;
- }
+ MediaBuffer *buffer = nullptr;
+ auto free = mBuffers.end();
+ for (auto it = mBuffers.begin(); it != mBuffers.end(); ++it) {
+ if ((*it)->refcount() == 0) {
+ const size_t size = (*it)->size();
+ if (size >= requestedSize) {
+ buffer = *it;
+ break;
+ }
+ if (size < smallest) {
+ smallest = size; // always free the smallest buf
+ free = it;
+ }
}
- bufferPrevious = buffer;
}
-
- if (buffer == NULL && freeBuffer != NULL) {
- ALOGV("allocate new buffer, requested size %zu vs available %zu",
- requestedSize, freeBuffer->size());
- size_t allocateSize = requestedSize;
- if (requestedSize < SIZE_MAX / 3) {
- allocateSize = requestedSize * 3 / 2;
+ if (buffer == nullptr
+ && (free != mBuffers.end() || mBuffers.size() < mGrowthLimit)) {
+ // We alloc before we free so failure leaves group unchanged.
+ const size_t allocateSize = requestedSize < SIZE_MAX / 3 * 2 /* NB: ordering */ ?
+ requestedSize * 3 / 2 : requestedSize;
+ buffer = new MediaBuffer(allocateSize);
+ if (buffer->data() == nullptr) {
+ ALOGE("Allocation failure for size %zu", allocateSize);
+ delete buffer; // Invalid alloc, prefer not to call release.
+ buffer = nullptr;
+ } else {
+ buffer->setObserver(this);
+ if (free != mBuffers.end()) {
+ ALOGV("reallocate buffer, requested size %zu vs available %zu",
+ requestedSize, (*free)->size());
+ (*free)->setObserver(nullptr);
+ (*free)->release();
+ *free = buffer; // in-place replace
+ } else {
+ ALOGV("allocate buffer, requested size %zu", requestedSize);
+ mBuffers.emplace_back(buffer);
+ }
}
- MediaBuffer *newBuffer = new MediaBuffer(allocateSize);
- newBuffer->setObserver(this);
- if (freeBuffer == mFirstBuffer) {
- mFirstBuffer = newBuffer;
- }
- if (freeBuffer == mLastBuffer) {
- mLastBuffer = newBuffer;
- }
- newBuffer->setNextBuffer(freeBuffer->nextBuffer());
- if (freeBufferPrevious != NULL) {
- freeBufferPrevious->setNextBuffer(newBuffer);
- }
- freeBuffer->setObserver(NULL);
- freeBuffer->release();
-
- buffer = newBuffer;
}
-
- if (buffer != NULL) {
+ if (buffer != nullptr) {
buffer->add_ref();
buffer->reset();
-
*out = buffer;
- goto exit;
+ return OK;
}
-
if (nonBlocking) {
- *out = NULL;
+ *out = nullptr;
return WOULD_BLOCK;
}
-
- // All buffers are in use. Block until one of them is returned to us.
+ // All buffers are in use, block until one of them is returned.
mCondition.wait(mLock);
}
-
-exit:
- return OK;
+ // Never gets here.
}
void MediaBufferGroup::signalBufferReturned(MediaBuffer *) {
- Mutex::Autolock autoLock(mLock);
mCondition.signal();
}
diff --git a/media/libstagefright/foundation/MetaData.cpp b/media/libstagefright/foundation/MetaData.cpp
index b4abc60..a8965f0 100644
--- a/media/libstagefright/foundation/MetaData.cpp
+++ b/media/libstagefright/foundation/MetaData.cpp
@@ -392,8 +392,12 @@
}
status_t MetaData::writeToParcel(Parcel &parcel) {
+ status_t ret;
size_t numItems = mItems.size();
- parcel.writeUint32(uint32_t(numItems));
+ ret = parcel.writeUint32(uint32_t(numItems));
+ if (ret) {
+ return ret;
+ }
for (size_t i = 0; i < numItems; i++) {
int32_t key = mItems.keyAt(i);
const typed_data &item = mItems.valueAt(i);
@@ -401,9 +405,32 @@
const void *data;
size_t size;
item.getData(&type, &data, &size);
- parcel.writeInt32(key);
- parcel.writeUint32(type);
- parcel.writeByteArray(size, (uint8_t*)data);
+ ret = parcel.writeInt32(key);
+ if (ret) {
+ return ret;
+ }
+ ret = parcel.writeUint32(type);
+ if (ret) {
+ return ret;
+ }
+ if (type == TYPE_NONE) {
+ android::Parcel::WritableBlob blob;
+ ret = parcel.writeUint32(static_cast<uint32_t>(size));
+ if (ret) {
+ return ret;
+ }
+ ret = parcel.writeBlob(size, false, &blob);
+ if (ret) {
+ return ret;
+ }
+ memcpy(blob.data(), data, size);
+ blob.release();
+ } else {
+ ret = parcel.writeByteArray(size, (uint8_t*)data);
+ if (ret) {
+ return ret;
+ }
+ }
}
return OK;
}
@@ -422,8 +449,20 @@
if (ret != OK) {
break;
}
- // copy data directly from Parcel storage, then advance position
- setData(key, type, parcel.readInplace(size), size);
+ // copy data from Blob, which may be inline in Parcel storage,
+ // then advance position
+ if (type == TYPE_NONE) {
+ android::Parcel::ReadableBlob blob;
+ ret = parcel.readBlob(size, &blob);
+ if (ret != OK) {
+ break;
+ }
+ setData(key, type, blob.data(), size);
+ blob.release();
+ } else {
+ // copy data directly from Parcel storage, then advance position
+ setData(key, type, parcel.readInplace(size), size);
+ }
}
return OK;
diff --git a/media/libstagefright/http/MediaHTTP.cpp b/media/libstagefright/http/MediaHTTP.cpp
index 76ec625..5b18814 100644
--- a/media/libstagefright/http/MediaHTTP.cpp
+++ b/media/libstagefright/http/MediaHTTP.cpp
@@ -58,15 +58,19 @@
extHeaders.add(String8("User-Agent"), String8(MakeUserAgent().c_str()));
}
- bool success = mHTTPConnection->connect(uri, &extHeaders);
+ mLastURI = uri;
+ // reconnect() calls with uri == old mLastURI.c_str(), which gets zapped
+ // as part of the above assignment. Ensure no accidental later use.
+ uri = NULL;
+
+ bool success = mHTTPConnection->connect(mLastURI.c_str(), &extHeaders);
mLastHeaders = extHeaders;
- mLastURI = uri;
mCachedSizeValid = false;
if (success) {
- AString sanitized = uriDebugString(uri);
+ AString sanitized = uriDebugString(mLastURI);
mName = String8::format("MediaHTTP(%s)", sanitized.c_str());
}
diff --git a/media/libstagefright/httplive/LiveSession.cpp b/media/libstagefright/httplive/LiveSession.cpp
index 8e8fa52..f673426 100644
--- a/media/libstagefright/httplive/LiveSession.cpp
+++ b/media/libstagefright/httplive/LiveSession.cpp
@@ -467,28 +467,28 @@
return err;
}
-status_t LiveSession::getStreamFormat(StreamType stream, sp<AMessage> *format) {
+status_t LiveSession::getStreamFormatMeta(StreamType stream, sp<MetaData> *meta) {
if (!(mStreamMask & stream)) {
return UNKNOWN_ERROR;
}
sp<AnotherPacketSource> packetSource = mPacketSources.valueFor(stream);
- sp<MetaData> meta = packetSource->getFormat();
+ *meta = packetSource->getFormat();
- if (meta == NULL) {
+ if (*meta == NULL) {
return -EWOULDBLOCK;
}
if (stream == STREAMTYPE_AUDIO) {
// set AAC input buffer size to 32K bytes (256kbps x 1sec)
- meta->setInt32(kKeyMaxInputSize, 32 * 1024);
+ (*meta)->setInt32(kKeyMaxInputSize, 32 * 1024);
} else if (stream == STREAMTYPE_VIDEO) {
- meta->setInt32(kKeyMaxWidth, mMaxWidth);
- meta->setInt32(kKeyMaxHeight, mMaxHeight);
+ (*meta)->setInt32(kKeyMaxWidth, mMaxWidth);
+ (*meta)->setInt32(kKeyMaxHeight, mMaxHeight);
}
- return convertMetaDataToMessage(meta, format);
+ return OK;
}
sp<HTTPDownloader> LiveSession::getHTTPDownloader() {
diff --git a/media/libstagefright/httplive/LiveSession.h b/media/libstagefright/httplive/LiveSession.h
index de7cefd..65a824e 100644
--- a/media/libstagefright/httplive/LiveSession.h
+++ b/media/libstagefright/httplive/LiveSession.h
@@ -75,7 +75,7 @@
int64_t calculateMediaTimeUs(int64_t firstTimeUs, int64_t timeUs, int32_t discontinuitySeq);
status_t dequeueAccessUnit(StreamType stream, sp<ABuffer> *accessUnit);
- status_t getStreamFormat(StreamType stream, sp<AMessage> *format);
+ status_t getStreamFormatMeta(StreamType stream, sp<MetaData> *meta);
sp<HTTPDownloader> getHTTPDownloader();
diff --git a/media/libstagefright/id3/ID3.cpp b/media/libstagefright/id3/ID3.cpp
index 86b668c..aaf6b3d 100644
--- a/media/libstagefright/id3/ID3.cpp
+++ b/media/libstagefright/id3/ID3.cpp
@@ -77,7 +77,10 @@
mFirstFrameOffset(0),
mVersion(ID3_UNKNOWN),
mRawSize(0) {
- sp<MemorySource> source = new MemorySource(data, size);
+ sp<MemorySource> source = new (std::nothrow) MemorySource(data, size);
+
+ if (source == NULL)
+ return;
mIsValid = parseV2(source, 0);
@@ -545,6 +548,10 @@
n -= skipped;
}
+ if (n <= 0) {
+ return;
+ }
+
if (encoding == 0x00) {
// supposedly ISO 8859-1
id->setTo((const char*)frameData + 1, n);
@@ -558,11 +565,16 @@
const char16_t *framedata = (const char16_t *) (frameData + 1);
char16_t *framedatacopy = NULL;
#if BYTE_ORDER == LITTLE_ENDIAN
- framedatacopy = new char16_t[len];
- for (int i = 0; i < len; i++) {
- framedatacopy[i] = bswap_16(framedata[i]);
+ if (len > 0) {
+ framedatacopy = new (std::nothrow) char16_t[len];
+ if (framedatacopy == NULL) {
+ return;
+ }
+ for (int i = 0; i < len; i++) {
+ framedatacopy[i] = bswap_16(framedata[i]);
+ }
+ framedata = framedatacopy;
}
- framedata = framedatacopy;
#endif
id->setTo(framedata, len);
if (framedatacopy != NULL) {
@@ -575,15 +587,26 @@
const char16_t *framedata = (const char16_t *) (frameData + 1);
char16_t *framedatacopy = NULL;
if (*framedata == 0xfffe) {
- // endianness marker doesn't match host endianness, convert
- framedatacopy = new char16_t[len];
+ // endianness marker != host endianness, convert & skip
+ if (len <= 1) {
+ return; // nothing after the marker
+ }
+ framedatacopy = new (std::nothrow) char16_t[len];
+ if (framedatacopy == NULL) {
+ return;
+ }
for (int i = 0; i < len; i++) {
framedatacopy[i] = bswap_16(framedata[i]);
}
framedata = framedatacopy;
- }
- // If the string starts with an endianness marker, skip it
- if (*framedata == 0xfeff) {
+ // and skip over the marker
+ framedata++;
+ len--;
+ } else if (*framedata == 0xfeff) {
+ // endianness marker == host endianness, skip it
+ if (len <= 1) {
+ return; // nothing after the marker
+ }
framedata++;
len--;
}
@@ -598,12 +621,16 @@
}
if (eightBit) {
// collapse to 8 bit, then let the media scanner client figure out the real encoding
- char *frame8 = new char[len];
- for (int i = 0; i < len; i++) {
- frame8[i] = framedata[i];
+ char *frame8 = new (std::nothrow) char[len];
+ if (frame8 != NULL) {
+ for (int i = 0; i < len; i++) {
+ frame8[i] = framedata[i];
+ }
+ id->setTo(frame8, len);
+ delete [] frame8;
+ } else {
+ id->setTo(framedata, len);
}
- id->setTo(frame8, len);
- delete [] frame8;
} else {
id->setTo(framedata, len);
}
diff --git a/media/libstagefright/include/CallbackDataSource.h b/media/libstagefright/include/CallbackDataSource.h
index f582c53..80cd1f7 100644
--- a/media/libstagefright/include/CallbackDataSource.h
+++ b/media/libstagefright/include/CallbackDataSource.h
@@ -41,6 +41,7 @@
virtual String8 toString() {
return mName;
}
+ virtual sp<DecryptHandle> DrmInitialization(const char *mime = NULL);
private:
sp<IDataSource> mIDataSource;
@@ -68,6 +69,7 @@
virtual String8 toString() {
return mName;
}
+ virtual sp<DecryptHandle> DrmInitialization(const char *mime = NULL);
private:
// 2kb comes from experimenting with the time-to-first-frame from a MediaPlayer
diff --git a/media/libstagefright/include/MPEG4Extractor.h b/media/libstagefright/include/MPEG4Extractor.h
index 9ed116a..89ad137 100644
--- a/media/libstagefright/include/MPEG4Extractor.h
+++ b/media/libstagefright/include/MPEG4Extractor.h
@@ -93,8 +93,8 @@
sp<DataSource> mDataSource;
status_t mInitCheck;
- bool mHasVideo;
uint32_t mHeaderTimescale;
+ bool mIsQT;
Track *mFirstTrack, *mLastTrack;
diff --git a/media/libstagefright/include/OMXNodeInstance.h b/media/libstagefright/include/OMXNodeInstance.h
index bdd4811..6411267 100644
--- a/media/libstagefright/include/OMXNodeInstance.h
+++ b/media/libstagefright/include/OMXNodeInstance.h
@@ -21,6 +21,7 @@
#include "OMX.h"
#include <utils/RefBase.h>
+#include <utils/SortedVector.h>
#include <utils/threads.h>
namespace android {
@@ -151,6 +152,9 @@
OMX_HANDLETYPE mHandle;
sp<IOMXObserver> mObserver;
bool mDying;
+ bool mSailed; // configuration is set (no more meta-mode changes)
+ bool mQueriedProhibitedExtensions;
+ SortedVector<OMX_INDEXTYPE> mProhibitedExtensions;
bool mIsSecure;
// Lock only covers mGraphicBufferSource. We can't always use mLock
@@ -204,6 +208,8 @@
OMX::buffer_id findBufferID(OMX_BUFFERHEADERTYPE *bufferHeader);
void invalidateBufferID(OMX::buffer_id buffer);
+ bool isProhibitedIndex_l(OMX_INDEXTYPE index);
+
status_t useGraphicBuffer2_l(
OMX_U32 portIndex, const sp<GraphicBuffer> &graphicBuffer,
OMX::buffer_id *buffer);
diff --git a/media/libstagefright/include/SampleTable.h b/media/libstagefright/include/SampleTable.h
index 0b42229..eb1a674 100644
--- a/media/libstagefright/include/SampleTable.h
+++ b/media/libstagefright/include/SampleTable.h
@@ -24,7 +24,6 @@
#include <media/stagefright/MediaErrors.h>
#include <utils/RefBase.h>
#include <utils/threads.h>
-#include <utils/Vector.h>
namespace android {
@@ -96,6 +95,9 @@
static const uint32_t kSampleSizeType32;
static const uint32_t kSampleSizeTypeCompact;
+ // Limit the total size of all internal tables to 200MiB.
+ static const size_t kMaxTotalSize = 200 * (1 << 20);
+
sp<DataSource> mDataSource;
Mutex mLock;
@@ -113,7 +115,7 @@
bool mHasTimeToSample;
uint32_t mTimeToSampleCount;
- Vector<uint32_t> mTimeToSample;
+ uint32_t* mTimeToSample;
struct SampleTimeEntry {
uint32_t mSampleIndex;
@@ -139,6 +141,9 @@
};
SampleToChunkEntry *mSampleToChunkEntries;
+ // Approximate size of all tables combined.
+ uint64_t mTotalSize;
+
friend struct SampleIterator;
// normally we don't round
diff --git a/media/libstagefright/include/SoftVideoDecoderOMXComponent.h b/media/libstagefright/include/SoftVideoDecoderOMXComponent.h
index 4529007..c9fd745 100644
--- a/media/libstagefright/include/SoftVideoDecoderOMXComponent.h
+++ b/media/libstagefright/include/SoftVideoDecoderOMXComponent.h
@@ -21,6 +21,7 @@
#include "SimpleSoftOMXComponent.h"
#include <media/stagefright/foundation/AHandlerReflector.h>
+#include <media/stagefright/foundation/ColorUtils.h>
#include <media/IOMX.h>
#include <utils/RefBase.h>
@@ -43,6 +44,16 @@
OMX_COMPONENTTYPE **component);
protected:
+ enum {
+ kDescribeColorAspectsIndex = kPrepareForAdaptivePlaybackIndex + 1,
+ };
+
+ enum {
+ kNotSupported,
+ kPreferBitstream,
+ kPreferContainer,
+ };
+
virtual void onPortEnableCompleted(OMX_U32 portIndex, bool enabled);
virtual void onReset();
@@ -55,15 +66,37 @@
virtual OMX_ERRORTYPE getConfig(
OMX_INDEXTYPE index, OMX_PTR params);
+ virtual OMX_ERRORTYPE setConfig(
+ OMX_INDEXTYPE index, const OMX_PTR params);
+
virtual OMX_ERRORTYPE getExtensionIndex(
const char *name, OMX_INDEXTYPE *index);
+ virtual bool supportsDescribeColorAspects();
+
+ virtual int getColorAspectPreference();
+
+ // This function sets both minimum buffer count and actual buffer count of
+ // input port to be |numInputBuffers|. It will also set both minimum buffer
+ // count and actual buffer count of output port to be |numOutputBuffers|.
void initPorts(OMX_U32 numInputBuffers,
OMX_U32 inputBufferSize,
OMX_U32 numOutputBuffers,
const char *mimeType,
OMX_U32 minCompressionRatio = 1u);
+ // This function sets input port's minimum buffer count to |numMinInputBuffers|,
+ // sets input port's actual buffer count to |numInputBuffers|, sets output port's
+ // minimum buffer count to |numMinOutputBuffers| and sets output port's actual buffer
+ // count to be |numOutputBuffers|.
+ void initPorts(OMX_U32 numMinInputBuffers,
+ OMX_U32 numInputBuffers,
+ OMX_U32 inputBufferSize,
+ OMX_U32 numMinOutputBuffers,
+ OMX_U32 numOutputBuffers,
+ const char *mimeType,
+ OMX_U32 minCompressionRatio = 1u);
+
virtual void updatePortDefinitions(bool updateCrop = true, bool updateInputSize = false);
uint32_t outputBufferWidth();
@@ -74,6 +107,10 @@
kCropSet,
kCropChanged,
};
+
+ // This function will handle several port change events which include
+ // size changed, crop changed, stride changed and coloraspects changed.
+ // It will trigger OMX_EventPortSettingsChanged event if necessary.
void handlePortSettingsChange(
bool *portWillReset, uint32_t width, uint32_t height,
CropSettingsMode cropSettingsMode = kCropUnSet, bool fakeStride = false);
@@ -99,6 +136,29 @@
AWAITING_ENABLED
} mOutputPortSettingsChange;
+ bool mUpdateColorAspects;
+
+ Mutex mColorAspectsLock;
+ // color aspects passed from the framework.
+ ColorAspects mDefaultColorAspects;
+ // color aspects parsed from the bitstream.
+ ColorAspects mBitstreamColorAspects;
+ // final color aspects after combining the above two aspects.
+ ColorAspects mFinalColorAspects;
+
+ bool colorAspectsDiffer(const ColorAspects &a, const ColorAspects &b);
+
+ // This functions takes two color aspects and updates the mFinalColorAspects
+ // based on |preferredAspects|.
+ void updateFinalColorAspects(
+ const ColorAspects &otherAspects, const ColorAspects &preferredAspects);
+
+ // This function will update the mFinalColorAspects based on codec preference.
+ status_t handleColorAspectsChange();
+
+ // Helper function to dump the ColorAspects.
+ void dumpColorAspects(const ColorAspects &colorAspects);
+
private:
uint32_t mMinInputBufferSize;
uint32_t mMinCompressionRatio;
diff --git a/media/libstagefright/include/avc_utils.h b/media/libstagefright/include/avc_utils.h
index 8e308e9..b2ef360 100644
--- a/media/libstagefright/include/avc_utils.h
+++ b/media/libstagefright/include/avc_utils.h
@@ -85,6 +85,7 @@
bool IsIDR(const sp<ABuffer> &accessUnit);
bool IsAVCReferenceFrame(const sp<ABuffer> &accessUnit);
+uint32_t FindAVCLayerId(const uint8_t *data, size_t size);
const char *AVCProfileToString(uint8_t profile);
diff --git a/media/libstagefright/mpeg2ts/ATSParser.cpp b/media/libstagefright/mpeg2ts/ATSParser.cpp
index b863d67..844479e 100644
--- a/media/libstagefright/mpeg2ts/ATSParser.cpp
+++ b/media/libstagefright/mpeg2ts/ATSParser.cpp
@@ -701,6 +701,10 @@
}
mPayloadStarted = true;
+ // There should be at most 2 elements in |mPesStartOffsets|.
+ while (mPesStartOffsets.size() >= 2) {
+ mPesStartOffsets.erase(mPesStartOffsets.begin());
+ }
mPesStartOffsets.push_back(offset);
}
@@ -1104,15 +1108,20 @@
mSource->queueAccessUnit(accessUnit);
}
- if ((event != NULL) && !found && mQueue->getFormat() != NULL) {
+ // Every access unit has a pesStartOffset queued in |mPesStartOffsets|.
+ off64_t pesStartOffset = -1;
+ if (!mPesStartOffsets.empty()) {
+ pesStartOffset = *mPesStartOffsets.begin();
+ mPesStartOffsets.erase(mPesStartOffsets.begin());
+ }
+
+ if (pesStartOffset >= 0 && (event != NULL) && !found && mQueue->getFormat() != NULL) {
int32_t sync = 0;
if (accessUnit->meta()->findInt32("isSync", &sync) && sync) {
int64_t timeUs;
if (accessUnit->meta()->findInt64("timeUs", &timeUs)) {
found = true;
- off64_t pesStartOffset = *mPesStartOffsets.begin();
event->init(pesStartOffset, mSource, timeUs);
- mPesStartOffsets.erase(mPesStartOffsets.begin());
}
}
}
diff --git a/media/libstagefright/omx/GraphicBufferSource.cpp b/media/libstagefright/omx/GraphicBufferSource.cpp
index 63424f7..1d819b5 100644
--- a/media/libstagefright/omx/GraphicBufferSource.cpp
+++ b/media/libstagefright/omx/GraphicBufferSource.cpp
@@ -139,13 +139,13 @@
mRepeatLastFrameTimestamp(-1ll),
mLatestBufferId(-1),
mLatestBufferFrameNum(0),
- mLatestBufferUseCount(0),
mLatestBufferFence(Fence::NO_FENCE),
mRepeatBufferDeferred(false),
mTimePerCaptureUs(-1ll),
mTimePerFrameUs(-1ll),
mPrevCaptureUs(-1ll),
- mPrevFrameUs(-1ll) {
+ mPrevFrameUs(-1ll),
+ mInputBufferTimeOffsetUs(0ll) {
ALOGV("GraphicBufferSource w=%u h=%u c=%u",
bufferWidth, bufferHeight, bufferCount);
@@ -397,12 +397,16 @@
sp<Fence> fence = new Fence(fenceFd);
if (mBufferSlot[id] != NULL &&
mBufferSlot[id]->handle == codecBuffer.mGraphicBuffer->handle) {
- ALOGV("cbi %d matches bq slot %d, handle=%p",
- cbi, id, mBufferSlot[id]->handle);
+ mBufferUseCount[id]--;
- if (id == mLatestBufferId) {
- CHECK_GT(mLatestBufferUseCount--, 0);
- } else {
+ ALOGV("codecBufferEmptied: slot=%d, cbi=%d, useCount=%d, handle=%p",
+ id, cbi, mBufferUseCount[id], mBufferSlot[id]->handle);
+
+ if (mBufferUseCount[id] < 0) {
+ ALOGW("mBufferUseCount for bq slot %d < 0 (=%d)", id, mBufferUseCount[id]);
+ mBufferUseCount[id] = 0;
+ }
+ if (id != mLatestBufferId && mBufferUseCount[id] == 0) {
releaseBuffer(id, codecBuffer.mFrameNumber, mBufferSlot[id], fence);
}
} else {
@@ -613,6 +617,7 @@
if (item.mGraphicBuffer != NULL) {
ALOGV("fillCodecBuffer_l: setting mBufferSlot %d", item.mSlot);
mBufferSlot[item.mSlot] = item.mGraphicBuffer;
+ mBufferUseCount[item.mSlot] = 0;
}
if (item.mDataSpace != mLastDataSpace) {
@@ -698,7 +703,7 @@
return false;
}
- ++mLatestBufferUseCount;
+ ++mBufferUseCount[item.mSlot];
/* repeat last frame up to kRepeatLastFrameCount times.
* in case of static scene, a single repeat might not get rid of encoder
@@ -719,10 +724,8 @@
void GraphicBufferSource::setLatestBuffer_l(
const BufferItem &item, bool dropped) {
- ALOGV("setLatestBuffer_l");
-
if (mLatestBufferId >= 0) {
- if (mLatestBufferUseCount == 0) {
+ if (mBufferUseCount[mLatestBufferId] == 0) {
releaseBuffer(mLatestBufferId, mLatestBufferFrameNum,
mBufferSlot[mLatestBufferId], mLatestBufferFence);
// mLatestBufferFence will be set to new fence just below
@@ -733,7 +736,13 @@
mLatestBufferFrameNum = item.mFrameNumber;
mRepeatLastFrameTimestamp = item.mTimestamp + mRepeatAfterUs * 1000;
- mLatestBufferUseCount = dropped ? 0 : 1;
+ if (!dropped) {
+ ++mBufferUseCount[item.mSlot];
+ }
+
+ ALOGV("setLatestBuffer_l: slot=%d, useCount=%d",
+ item.mSlot, mBufferUseCount[item.mSlot]);
+
mRepeatBufferDeferred = false;
mRepeatLastFrameCount = kRepeatLastFrameCount;
mLatestBufferFence = item.mFence;
@@ -774,8 +783,11 @@
int64_t GraphicBufferSource::getTimestamp(const BufferItem &item) {
int64_t timeUs = item.mTimestamp / 1000;
+ timeUs += mInputBufferTimeOffsetUs;
- if (mTimePerCaptureUs > 0ll) {
+ if (mTimePerCaptureUs > 0ll
+ && (mTimePerCaptureUs > 2 * mTimePerFrameUs
+ || mTimePerFrameUs > 2 * mTimePerCaptureUs)) {
// Time lapse or slow motion mode
if (mPrevCaptureUs < 0ll) {
// first capture
@@ -800,40 +812,45 @@
static_cast<long long>(mPrevFrameUs));
return mPrevFrameUs;
- } else if (mMaxTimestampGapUs > 0ll) {
- /* Cap timestamp gap between adjacent frames to specified max
- *
- * In the scenario of cast mirroring, encoding could be suspended for
- * prolonged periods. Limiting the pts gap to workaround the problem
- * where encoder's rate control logic produces huge frames after a
- * long period of suspension.
- */
-
+ } else {
int64_t originalTimeUs = timeUs;
- if (mPrevOriginalTimeUs >= 0ll) {
- if (originalTimeUs < mPrevOriginalTimeUs) {
+ if (originalTimeUs <= mPrevOriginalTimeUs) {
// Drop the frame if it's going backward in time. Bad timestamp
// could disrupt encoder's rate control completely.
- ALOGW("Dropping frame that's going backward in time");
- return -1;
- }
- int64_t timestampGapUs = originalTimeUs - mPrevOriginalTimeUs;
- timeUs = (timestampGapUs < mMaxTimestampGapUs ?
- timestampGapUs : mMaxTimestampGapUs) + mPrevModifiedTimeUs;
+ ALOGW("Dropping frame that's going backward in time");
+ return -1;
}
+
+ if (mMaxTimestampGapUs > 0ll) {
+ //TODO: Fix the case when mMaxTimestampGapUs and mTimePerCaptureUs are both set.
+
+ /* Cap timestamp gap between adjacent frames to specified max
+ *
+ * In the scenario of cast mirroring, encoding could be suspended for
+ * prolonged periods. Limiting the pts gap to workaround the problem
+ * where encoder's rate control logic produces huge frames after a
+ * long period of suspension.
+ */
+ if (mPrevOriginalTimeUs >= 0ll) {
+ int64_t timestampGapUs = originalTimeUs - mPrevOriginalTimeUs;
+ timeUs = (timestampGapUs < mMaxTimestampGapUs ?
+ timestampGapUs : mMaxTimestampGapUs) + mPrevModifiedTimeUs;
+ }
+ mOriginalTimeUs.add(timeUs, originalTimeUs);
+ ALOGV("IN timestamp: %lld -> %lld",
+ static_cast<long long>(originalTimeUs),
+ static_cast<long long>(timeUs));
+ }
+
mPrevOriginalTimeUs = originalTimeUs;
mPrevModifiedTimeUs = timeUs;
- mOriginalTimeUs.add(timeUs, originalTimeUs);
- ALOGV("IN timestamp: %lld -> %lld",
- static_cast<long long>(originalTimeUs),
- static_cast<long long>(timeUs));
}
return timeUs;
}
status_t GraphicBufferSource::submitBuffer_l(const BufferItem &item, int cbi) {
- ALOGV("submitBuffer_l cbi=%d", cbi);
+ ALOGV("submitBuffer_l: slot=%d, cbi=%d", item.mSlot, cbi);
int64_t timeUs = getTimestamp(item);
if (timeUs < 0ll) {
@@ -927,6 +944,7 @@
void GraphicBufferSource::releaseBuffer(
int &id, uint64_t frameNum,
const sp<GraphicBuffer> buffer, const sp<Fence> &fence) { // NOLINT
+ ALOGV("releaseBuffer: slot=%d", id);
if (mIsPersistent) {
mConsumer->detachBuffer(id);
mBufferSlot[id] = NULL;
@@ -970,6 +988,7 @@
if (item.mGraphicBuffer != NULL) {
ALOGV("onFrameAvailable: setting mBufferSlot %d", item.mSlot);
mBufferSlot[item.mSlot] = item.mGraphicBuffer;
+ mBufferUseCount[item.mSlot] = 0;
}
releaseBuffer(item.mSlot, item.mFrameNumber,
@@ -1003,6 +1022,7 @@
for (int i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) {
if ((slotMask & 0x01) != 0) {
mBufferSlot[i] = NULL;
+ mBufferUseCount[i] = 0;
}
slotMask >>= 1;
}
@@ -1045,6 +1065,18 @@
return OK;
}
+status_t GraphicBufferSource::setInputBufferTimeOffset(int64_t timeOffsetUs) {
+ Mutex::Autolock autoLock(mMutex);
+
+ // timeOffsetUs must be negative for adjustment.
+ if (timeOffsetUs >= 0ll) {
+ return INVALID_OPERATION;
+ }
+
+ mInputBufferTimeOffsetUs = timeOffsetUs;
+ return OK;
+}
+
status_t GraphicBufferSource::setMaxFps(float maxFps) {
Mutex::Autolock autoLock(mMutex);
diff --git a/media/libstagefright/omx/GraphicBufferSource.h b/media/libstagefright/omx/GraphicBufferSource.h
index c8b0e62..aa4ceb3 100644
--- a/media/libstagefright/omx/GraphicBufferSource.h
+++ b/media/libstagefright/omx/GraphicBufferSource.h
@@ -131,6 +131,10 @@
// of suspension on input.
status_t setMaxTimestampGapUs(int64_t maxGapUs);
+ // Sets the input buffer timestamp offset.
+ // When set, the sample's timestamp will be adjusted with the timeOffsetUs.
+ status_t setInputBufferTimeOffset(int64_t timeOffsetUs);
+
// When set, the max frame rate fed to the encoder will be capped at maxFps.
status_t setMaxFps(float maxFps);
@@ -291,6 +295,7 @@
// is done processing a GraphicBuffer, we can use this to map back
// to a slot number.
sp<GraphicBuffer> mBufferSlot[BufferQueue::NUM_BUFFER_SLOTS];
+ int32_t mBufferUseCount[BufferQueue::NUM_BUFFER_SLOTS];
// Tracks codec buffers.
Vector<CodecBuffer> mCodecBuffers;
@@ -323,7 +328,6 @@
int mLatestBufferId;
uint64_t mLatestBufferFrameNum;
- int32_t mLatestBufferUseCount;
sp<Fence> mLatestBufferFence;
// The previous buffer should've been repeated but
@@ -336,6 +340,8 @@
int64_t mPrevCaptureUs;
int64_t mPrevFrameUs;
+ int64_t mInputBufferTimeOffsetUs;
+
MetadataBufferType mMetadataBufferType;
ColorAspects mColorAspects;
diff --git a/media/libstagefright/omx/OMXNodeInstance.cpp b/media/libstagefright/omx/OMXNodeInstance.cpp
index 0c1b2a2..8c10310 100644
--- a/media/libstagefright/omx/OMXNodeInstance.cpp
+++ b/media/libstagefright/omx/OMXNodeInstance.cpp
@@ -93,26 +93,34 @@
namespace android {
struct BufferMeta {
- explicit BufferMeta(const sp<IMemory> &mem, OMX_U32 portIndex, bool is_backup = false)
+ explicit BufferMeta(
+ const sp<IMemory> &mem, OMX_U32 portIndex, bool copyToOmx,
+ bool copyFromOmx, OMX_U8 *backup)
: mMem(mem),
- mIsBackup(is_backup),
- mPortIndex(portIndex) {
+ mCopyFromOmx(copyFromOmx),
+ mCopyToOmx(copyToOmx),
+ mPortIndex(portIndex),
+ mBackup(backup) {
}
explicit BufferMeta(size_t size, OMX_U32 portIndex)
: mSize(size),
- mIsBackup(false),
- mPortIndex(portIndex) {
+ mCopyFromOmx(false),
+ mCopyToOmx(false),
+ mPortIndex(portIndex),
+ mBackup(NULL) {
}
explicit BufferMeta(const sp<GraphicBuffer> &graphicBuffer, OMX_U32 portIndex)
: mGraphicBuffer(graphicBuffer),
- mIsBackup(false),
- mPortIndex(portIndex) {
+ mCopyFromOmx(false),
+ mCopyToOmx(false),
+ mPortIndex(portIndex),
+ mBackup(NULL) {
}
void CopyFromOMX(const OMX_BUFFERHEADERTYPE *header) {
- if (!mIsBackup) {
+ if (!mCopyFromOmx) {
return;
}
@@ -123,7 +131,7 @@
}
void CopyToOMX(const OMX_BUFFERHEADERTYPE *header) {
- if (!mIsBackup) {
+ if (!mCopyToOmx) {
return;
}
@@ -163,13 +171,19 @@
return mPortIndex;
}
+ ~BufferMeta() {
+ delete[] mBackup;
+ }
+
private:
sp<GraphicBuffer> mGraphicBuffer;
sp<NativeHandle> mNativeHandle;
sp<IMemory> mMem;
size_t mSize;
- bool mIsBackup;
+ bool mCopyFromOmx;
+ bool mCopyToOmx;
OMX_U32 mPortIndex;
+ OMX_U8 *mBackup;
BufferMeta(const BufferMeta &);
BufferMeta &operator=(const BufferMeta &);
@@ -196,6 +210,8 @@
mHandle(NULL),
mObserver(observer),
mDying(false),
+ mSailed(false),
+ mQueriedProhibitedExtensions(false),
mBufferIDCount(0)
{
mName = ADebug::GetDebugName(name);
@@ -355,7 +371,11 @@
status_t OMXNodeInstance::sendCommand(
OMX_COMMANDTYPE cmd, OMX_S32 param) {
- const sp<GraphicBufferSource>& bufferSource(getGraphicBufferSource());
+ if (cmd == OMX_CommandStateSet) {
+ // There are no configurations past first StateSet command.
+ mSailed = true;
+ }
+ const sp<GraphicBufferSource> bufferSource(getGraphicBufferSource());
if (bufferSource != NULL && cmd == OMX_CommandStateSet) {
if (param == OMX_StateIdle) {
// Initiating transition from Executing -> Idle
@@ -388,10 +408,57 @@
return StatusFromOMXError(err);
}
+bool OMXNodeInstance::isProhibitedIndex_l(OMX_INDEXTYPE index) {
+ // these extensions can only be used from OMXNodeInstance, not by clients directly.
+ static const char *restricted_extensions[] = {
+ "OMX.google.android.index.storeMetaDataInBuffers",
+ "OMX.google.android.index.storeANWBufferInMetadata",
+ "OMX.google.android.index.prepareForAdaptivePlayback",
+ "OMX.google.android.index.configureVideoTunnelMode",
+ "OMX.google.android.index.useAndroidNativeBuffer2",
+ "OMX.google.android.index.useAndroidNativeBuffer",
+ "OMX.google.android.index.enableAndroidNativeBuffers",
+ "OMX.google.android.index.allocateNativeHandle",
+ "OMX.google.android.index.getAndroidNativeBufferUsage",
+ };
+
+ if ((index > OMX_IndexComponentStartUnused && index <= OMX_IndexParamStandardComponentRole)
+ || (index > OMX_IndexPortStartUnused && index <= OMX_IndexParamCompBufferSupplier)
+ || (index > OMX_IndexAudioStartUnused && index <= OMX_IndexConfigAudioChannelVolume)
+ || (index > OMX_IndexVideoStartUnused && index <= OMX_IndexConfigVideoNalSize)
+ || (index > OMX_IndexCommonStartUnused
+ && index <= OMX_IndexConfigCommonTransitionEffect)
+ || (index > (OMX_INDEXTYPE)OMX_IndexExtAudioStartUnused
+ && index <= (OMX_INDEXTYPE)OMX_IndexParamAudioProfileQuerySupported)
+ || (index > (OMX_INDEXTYPE)OMX_IndexExtVideoStartUnused
+ && index <= (OMX_INDEXTYPE)OMX_IndexConfigAndroidVideoTemporalLayering)
+ || (index > (OMX_INDEXTYPE)OMX_IndexExtOtherStartUnused
+ && index <= (OMX_INDEXTYPE)OMX_IndexParamConsumerUsageBits)) {
+ return false;
+ }
+
+ if (!mQueriedProhibitedExtensions) {
+ for (size_t i = 0; i < NELEM(restricted_extensions); ++i) {
+ OMX_INDEXTYPE ext;
+ if (OMX_GetExtensionIndex(mHandle, (OMX_STRING)restricted_extensions[i], &ext) == OMX_ErrorNone) {
+ mProhibitedExtensions.add(ext);
+ }
+ }
+ mQueriedProhibitedExtensions = true;
+ }
+
+ return mProhibitedExtensions.indexOf(index) >= 0;
+}
+
status_t OMXNodeInstance::getParameter(
OMX_INDEXTYPE index, void *params, size_t /* size */) {
Mutex::Autolock autoLock(mLock);
+ if (isProhibitedIndex_l(index)) {
+ android_errorWriteLog(0x534e4554, "29422020");
+ return BAD_INDEX;
+ }
+
OMX_ERRORTYPE err = OMX_GetParameter(mHandle, index, params);
OMX_INDEXEXTTYPE extIndex = (OMX_INDEXEXTTYPE)index;
// some errors are expected for getParameter
@@ -407,6 +474,11 @@
OMX_INDEXEXTTYPE extIndex = (OMX_INDEXEXTTYPE)index;
CLOG_CONFIG(setParameter, "%s(%#x), %zu@%p)", asString(extIndex), index, size, params);
+ if (isProhibitedIndex_l(index)) {
+ android_errorWriteLog(0x534e4554, "29422020");
+ return BAD_INDEX;
+ }
+
OMX_ERRORTYPE err = OMX_SetParameter(
mHandle, index, const_cast<void *>(params));
CLOG_IF_ERROR(setParameter, err, "%s(%#x)", asString(extIndex), index);
@@ -417,6 +489,11 @@
OMX_INDEXTYPE index, void *params, size_t /* size */) {
Mutex::Autolock autoLock(mLock);
+ if (isProhibitedIndex_l(index)) {
+ android_errorWriteLog(0x534e4554, "29422020");
+ return BAD_INDEX;
+ }
+
OMX_ERRORTYPE err = OMX_GetConfig(mHandle, index, params);
OMX_INDEXEXTTYPE extIndex = (OMX_INDEXEXTTYPE)index;
// some errors are expected for getConfig
@@ -432,6 +509,11 @@
OMX_INDEXEXTTYPE extIndex = (OMX_INDEXEXTTYPE)index;
CLOG_CONFIG(setConfig, "%s(%#x), %zu@%p)", asString(extIndex), index, size, params);
+ if (isProhibitedIndex_l(index)) {
+ android_errorWriteLog(0x534e4554, "29422020");
+ return BAD_INDEX;
+ }
+
OMX_ERRORTYPE err = OMX_SetConfig(
mHandle, index, const_cast<void *>(params));
CLOG_IF_ERROR(setConfig, err, "%s(%#x)", asString(extIndex), index);
@@ -448,6 +530,12 @@
status_t OMXNodeInstance::enableNativeBuffers(
OMX_U32 portIndex, OMX_BOOL graphic, OMX_BOOL enable) {
+ if (portIndex >= NELEM(mSecureBufferType)) {
+ ALOGE("b/31385713, portIndex(%u)", portIndex);
+ android_errorWriteLog(0x534e4554, "31385713");
+ return BAD_VALUE;
+ }
+
Mutex::Autolock autoLock(mLock);
CLOG_CONFIG(enableNativeBuffers, "%s:%u%s, %d", portString(portIndex), portIndex,
graphic ? ", graphic" : "", enable);
@@ -534,6 +622,10 @@
status_t OMXNodeInstance::storeMetaDataInBuffers_l(
OMX_U32 portIndex, OMX_BOOL enable, MetadataBufferType *type) {
+ if (mSailed) {
+ android_errorWriteLog(0x534e4554, "29422020");
+ return INVALID_OPERATION;
+ }
if (portIndex != kPortIndexInput && portIndex != kPortIndexOutput) {
android_errorWriteLog(0x534e4554, "26324358");
if (type != NULL) {
@@ -611,6 +703,10 @@
OMX_U32 portIndex, OMX_BOOL enable, OMX_U32 maxFrameWidth,
OMX_U32 maxFrameHeight) {
Mutex::Autolock autolock(mLock);
+ if (mSailed) {
+ android_errorWriteLog(0x534e4554, "29422020");
+ return INVALID_OPERATION;
+ }
CLOG_CONFIG(prepareForAdaptivePlayback, "%s:%u en=%d max=%ux%u",
portString(portIndex), portIndex, enable, maxFrameWidth, maxFrameHeight);
@@ -641,6 +737,10 @@
OMX_U32 portIndex, OMX_BOOL tunneled, OMX_U32 audioHwSync,
native_handle_t **sidebandHandle) {
Mutex::Autolock autolock(mLock);
+ if (mSailed) {
+ android_errorWriteLog(0x534e4554, "29422020");
+ return INVALID_OPERATION;
+ }
CLOG_CONFIG(configureVideoTunnelMode, "%s:%u tun=%d sync=%u",
portString(portIndex), portIndex, tunneled, audioHwSync);
@@ -688,21 +788,46 @@
}
Mutex::Autolock autoLock(mLock);
- if (allottedSize > params->size()) {
+ if (allottedSize > params->size() || portIndex >= NELEM(mNumPortBuffers)) {
return BAD_VALUE;
}
- BufferMeta *buffer_meta = new BufferMeta(params, portIndex);
+ // metadata buffers are not connected cross process
+ // use a backup buffer instead of the actual buffer
+ BufferMeta *buffer_meta;
+ bool useBackup = mMetadataType[portIndex] != kMetadataBufferTypeInvalid;
+ OMX_U8 *data = static_cast<OMX_U8 *>(params->pointer());
+ // allocate backup buffer
+ if (useBackup) {
+ data = new (std::nothrow) OMX_U8[allottedSize];
+ if (data == NULL) {
+ return NO_MEMORY;
+ }
+ memset(data, 0, allottedSize);
+
+ // if we are not connecting the buffers, the sizes must match
+ if (allottedSize != params->size()) {
+ CLOG_ERROR(useBuffer, BAD_VALUE, SIMPLE_BUFFER(portIndex, (size_t)allottedSize, data));
+ delete[] data;
+ return BAD_VALUE;
+ }
+
+ buffer_meta = new BufferMeta(
+ params, portIndex, false /* copyToOmx */, false /* copyFromOmx */, data);
+ } else {
+ buffer_meta = new BufferMeta(
+ params, portIndex, false /* copyToOmx */, false /* copyFromOmx */, NULL);
+ }
OMX_BUFFERHEADERTYPE *header;
OMX_ERRORTYPE err = OMX_UseBuffer(
mHandle, &header, portIndex, buffer_meta,
- allottedSize, static_cast<OMX_U8 *>(params->pointer()));
+ allottedSize, data);
if (err != OMX_ErrorNone) {
CLOG_ERROR(useBuffer, err, SIMPLE_BUFFER(
- portIndex, (size_t)allottedSize, params->pointer()));
+ portIndex, (size_t)allottedSize, data));
delete buffer_meta;
buffer_meta = NULL;
@@ -901,7 +1026,7 @@
// update backup buffer for input, codec buffer for output
return updateGraphicBufferInMeta_l(
portIndex, graphicBuffer, buffer, header,
- portIndex == kPortIndexOutput /* updateCodecBuffer */);
+ true /* updateCodecBuffer */);
}
status_t OMXNodeInstance::updateNativeHandleInMeta(
@@ -919,9 +1044,9 @@
}
BufferMeta *bufferMeta = (BufferMeta *)(header->pAppPrivate);
- // update backup buffer for input, codec buffer for output
+ // update backup buffer
sp<ABuffer> data = bufferMeta->getBuffer(
- header, portIndex == kPortIndexInput /* backup */, false /* limit */);
+ header, false /* backup */, false /* limit */);
bufferMeta->setNativeHandle(nativeHandle);
if (mMetadataType[portIndex] == kMetadataBufferTypeNativeHandleSource
&& data->capacity() >= sizeof(VideoNativeHandleMetadata)) {
@@ -945,7 +1070,16 @@
OMX_U32 portIndex, const sp<IGraphicBufferConsumer> &bufferConsumer, MetadataBufferType *type) {
status_t err;
- const sp<GraphicBufferSource>& surfaceCheck = getGraphicBufferSource();
+ // only allow graphic source on input port, when there are no allocated buffers yet
+ if (portIndex != kPortIndexInput) {
+ android_errorWriteLog(0x534e4554, "29422020");
+ return BAD_VALUE;
+ } else if (mNumPortBuffers[portIndex] > 0) {
+ android_errorWriteLog(0x534e4554, "29422020");
+ return INVALID_OPERATION;
+ }
+
+ const sp<GraphicBufferSource> surfaceCheck = getGraphicBufferSource();
if (surfaceCheck != NULL) {
if (portIndex < NELEM(mMetadataType) && type != NULL) {
*type = mMetadataType[portIndex];
@@ -1088,6 +1222,12 @@
return BAD_VALUE;
}
+ if (portIndex >= NELEM(mSecureBufferType)) {
+ ALOGE("b/31385713, portIndex(%u)", portIndex);
+ android_errorWriteLog(0x534e4554, "31385713");
+ return BAD_VALUE;
+ }
+
Mutex::Autolock autoLock(mLock);
BufferMeta *buffer_meta = new BufferMeta(size, portIndex);
@@ -1141,11 +1281,18 @@
}
Mutex::Autolock autoLock(mLock);
- if (allottedSize > params->size()) {
+ if (allottedSize > params->size() || portIndex >= NELEM(mNumPortBuffers)) {
return BAD_VALUE;
}
- BufferMeta *buffer_meta = new BufferMeta(params, portIndex, true);
+ // metadata buffers are not connected cross process; only copy if not meta
+ bool copy = mMetadataType[portIndex] == kMetadataBufferTypeInvalid;
+
+ BufferMeta *buffer_meta = new BufferMeta(
+ params, portIndex,
+ (portIndex == kPortIndexInput) && copy /* copyToOmx */,
+ (portIndex == kPortIndexOutput) && copy /* copyFromOmx */,
+ NULL /* data */);
OMX_BUFFERHEADERTYPE *header;
@@ -1243,6 +1390,12 @@
OMX_U32 flags, OMX_TICKS timestamp, int fenceFd) {
Mutex::Autolock autoLock(mLock);
+ // no emptybuffer if using input surface
+ if (getGraphicBufferSource() != NULL) {
+ android_errorWriteLog(0x534e4554, "29422020");
+ return INVALID_OPERATION;
+ }
+
OMX_BUFFERHEADERTYPE *header = findBufferHeader(buffer, kPortIndexInput);
if (header == NULL) {
ALOGE("b/25884056");
@@ -1250,23 +1403,11 @@
}
BufferMeta *buffer_meta =
static_cast<BufferMeta *>(header->pAppPrivate);
- sp<ABuffer> backup = buffer_meta->getBuffer(header, true /* backup */, false /* limit */);
- sp<ABuffer> codec = buffer_meta->getBuffer(header, false /* backup */, false /* limit */);
- // convert incoming ANW meta buffers if component is configured for gralloc metadata mode
- // ignore rangeOffset in this case
- if (mMetadataType[kPortIndexInput] == kMetadataBufferTypeGrallocSource
- && backup->capacity() >= sizeof(VideoNativeMetadata)
- && codec->capacity() >= sizeof(VideoGrallocMetadata)
- && ((VideoNativeMetadata *)backup->base())->eType
- == kMetadataBufferTypeANWBuffer) {
- VideoNativeMetadata &backupMeta = *(VideoNativeMetadata *)backup->base();
- VideoGrallocMetadata &codecMeta = *(VideoGrallocMetadata *)codec->base();
- CLOG_BUFFER(emptyBuffer, "converting ANWB %p to handle %p",
- backupMeta.pBuffer, backupMeta.pBuffer->handle);
- codecMeta.pHandle = backupMeta.pBuffer != NULL ? backupMeta.pBuffer->handle : NULL;
- codecMeta.eType = kMetadataBufferTypeGrallocSource;
- header->nFilledLen = rangeLength ? sizeof(codecMeta) : 0;
+ // set up proper filled length if component is configured for gralloc metadata mode
+ // ignore rangeOffset in this case (as client may be assuming ANW meta buffers).
+ if (mMetadataType[kPortIndexInput] == kMetadataBufferTypeGrallocSource) {
+ header->nFilledLen = rangeLength ? sizeof(VideoGrallocMetadata) : 0;
header->nOffset = 0;
} else {
// rangeLength and rangeOffset must be a subset of the allocated data in the buffer.
@@ -1445,6 +1586,7 @@
case IOMX::INTERNAL_OPTION_MAX_FPS: return "MAX_FPS";
case IOMX::INTERNAL_OPTION_START_TIME: return "START_TIME";
case IOMX::INTERNAL_OPTION_TIME_LAPSE: return "TIME_LAPSE";
+ case IOMX::INTERNAL_OPTION_TIME_OFFSET: return "TIME_OFFSET";
default: return def;
}
}
@@ -1473,6 +1615,7 @@
case IOMX::INTERNAL_OPTION_MAX_FPS:
case IOMX::INTERNAL_OPTION_START_TIME:
case IOMX::INTERNAL_OPTION_TIME_LAPSE:
+ case IOMX::INTERNAL_OPTION_TIME_OFFSET:
case IOMX::INTERNAL_OPTION_COLOR_ASPECTS:
{
const sp<GraphicBufferSource> &bufferSource =
@@ -1499,6 +1642,13 @@
CLOG_CONFIG(setInternalOption, "delayUs=%lld", (long long)delayUs);
return bufferSource->setRepeatPreviousFrameDelayUs(delayUs);
+ } else if (type == IOMX::INTERNAL_OPTION_TIME_OFFSET) {
+ int64_t timeOffsetUs;
+ if (!getInternalOption(data, size, &timeOffsetUs)) {
+ return INVALID_OPERATION;
+ }
+ CLOG_CONFIG(setInternalOption, "bufferOffsetUs=%lld", (long long)timeOffsetUs);
+ return bufferSource->setInputBufferTimeOffset(timeOffsetUs);
} else if (type == IOMX::INTERNAL_OPTION_MAX_TIMESTAMP_GAP) {
int64_t maxGapUs;
if (!getInternalOption(data, size, &maxGapUs)) {
@@ -1692,6 +1842,13 @@
&& arg2 == OMX_StateExecuting) {
bufferSource->omxExecuting();
}
+
+ // allow configuration if we return to the loaded state
+ if (event == OMX_EventCmdComplete
+ && arg1 == OMX_CommandStateSet
+ && arg2 == OMX_StateLoaded) {
+ mSailed = false;
+ }
}
// static
diff --git a/media/libstagefright/omx/SimpleSoftOMXComponent.cpp b/media/libstagefright/omx/SimpleSoftOMXComponent.cpp
index 60c1e2e..7c975f7 100644
--- a/media/libstagefright/omx/SimpleSoftOMXComponent.cpp
+++ b/media/libstagefright/omx/SimpleSoftOMXComponent.cpp
@@ -426,7 +426,19 @@
}
void SimpleSoftOMXComponent::onChangeState(OMX_STATETYPE state) {
+ ALOGV("%p requesting change from %d to %d", this, mState, state);
// We shouldn't be in a state transition already.
+
+ if (mState == OMX_StateLoaded
+ && mTargetState == OMX_StateIdle
+ && state == OMX_StateLoaded) {
+ // OMX specifically allows "canceling" a state transition from loaded
+ // to idle. Pretend we made it to idle, and go back to loaded
+ ALOGV("load->idle canceled");
+ mState = mTargetState = OMX_StateIdle;
+ state = OMX_StateLoaded;
+ }
+
CHECK_EQ((int)mState, (int)mTargetState);
switch (mState) {
@@ -469,6 +481,13 @@
CHECK_EQ((int)port->mTransition, (int)PortInfo::NONE);
CHECK(port->mDef.bEnabled == !enable);
+ if (port->mDef.eDir != OMX_DirOutput) {
+ ALOGE("Port enable/disable allowed only on output ports.");
+ notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL);
+ android_errorWriteLog(0x534e4554, "29421804");
+ return;
+ }
+
if (!enable) {
port->mDef.bEnabled = OMX_FALSE;
port->mTransition = PortInfo::DISABLING;
@@ -599,6 +618,7 @@
}
if (transitionComplete) {
+ ALOGV("state transition from %d to %d complete", mState, mTargetState);
mState = mTargetState;
if (mState == OMX_StateLoaded) {
@@ -606,6 +626,8 @@
}
notify(OMX_EventCmdComplete, OMX_CommandStateSet, mState, NULL);
+ } else {
+ ALOGV("state transition from %d to %d not yet complete", mState, mTargetState);
}
}
diff --git a/media/libstagefright/omx/SoftVideoDecoderOMXComponent.cpp b/media/libstagefright/omx/SoftVideoDecoderOMXComponent.cpp
index d3553bd..409cef7 100644
--- a/media/libstagefright/omx/SoftVideoDecoderOMXComponent.cpp
+++ b/media/libstagefright/omx/SoftVideoDecoderOMXComponent.cpp
@@ -68,6 +68,11 @@
mCodingType(codingType),
mProfileLevels(profileLevels),
mNumProfileLevels(numProfileLevels) {
+
+ // init all the color aspects to be Unspecified.
+ memset(&mDefaultColorAspects, 0, sizeof(ColorAspects));
+ memset(&mBitstreamColorAspects, 0, sizeof(ColorAspects));
+ memset(&mFinalColorAspects, 0, sizeof(ColorAspects));
}
void SoftVideoDecoderOMXComponent::initPorts(
@@ -76,6 +81,18 @@
OMX_U32 numOutputBuffers,
const char *mimeType,
OMX_U32 minCompressionRatio) {
+ initPorts(numInputBuffers, numInputBuffers, inputBufferSize,
+ numOutputBuffers, numOutputBuffers, mimeType, minCompressionRatio);
+}
+
+void SoftVideoDecoderOMXComponent::initPorts(
+ OMX_U32 numMinInputBuffers,
+ OMX_U32 numInputBuffers,
+ OMX_U32 inputBufferSize,
+ OMX_U32 numMinOutputBuffers,
+ OMX_U32 numOutputBuffers,
+ const char *mimeType,
+ OMX_U32 minCompressionRatio) {
mMinInputBufferSize = inputBufferSize;
mMinCompressionRatio = minCompressionRatio;
@@ -84,8 +101,8 @@
def.nPortIndex = kInputPortIndex;
def.eDir = OMX_DirInput;
- def.nBufferCountMin = numInputBuffers;
- def.nBufferCountActual = def.nBufferCountMin;
+ def.nBufferCountMin = numMinInputBuffers;
+ def.nBufferCountActual = numInputBuffers;
def.nBufferSize = inputBufferSize;
def.bEnabled = OMX_TRUE;
def.bPopulated = OMX_FALSE;
@@ -107,8 +124,8 @@
def.nPortIndex = kOutputPortIndex;
def.eDir = OMX_DirOutput;
- def.nBufferCountMin = numOutputBuffers;
- def.nBufferCountActual = def.nBufferCountMin;
+ def.nBufferCountMin = numMinOutputBuffers;
+ def.nBufferCountActual = numOutputBuffers;
def.bEnabled = OMX_TRUE;
def.bPopulated = OMX_FALSE;
def.eDomain = OMX_PortDomainVideo;
@@ -224,9 +241,66 @@
notify(OMX_EventPortSettingsChanged, kOutputPortIndex,
OMX_IndexConfigCommonOutputCrop, NULL);
}
+ } else if (mUpdateColorAspects) {
+ notify(OMX_EventPortSettingsChanged, kOutputPortIndex,
+ kDescribeColorAspectsIndex, NULL);
+ mUpdateColorAspects = false;
}
}
+void SoftVideoDecoderOMXComponent::dumpColorAspects(const ColorAspects &colorAspects) {
+ ALOGD("dumpColorAspects: (R:%d(%s), P:%d(%s), M:%d(%s), T:%d(%s)) ",
+ colorAspects.mRange, asString(colorAspects.mRange),
+ colorAspects.mPrimaries, asString(colorAspects.mPrimaries),
+ colorAspects.mMatrixCoeffs, asString(colorAspects.mMatrixCoeffs),
+ colorAspects.mTransfer, asString(colorAspects.mTransfer));
+}
+
+bool SoftVideoDecoderOMXComponent::colorAspectsDiffer(
+ const ColorAspects &a, const ColorAspects &b) {
+ if (a.mRange != b.mRange
+ || a.mPrimaries != b.mPrimaries
+ || a.mTransfer != b.mTransfer
+ || a.mMatrixCoeffs != b.mMatrixCoeffs) {
+ return true;
+ }
+ return false;
+}
+
+void SoftVideoDecoderOMXComponent::updateFinalColorAspects(
+ const ColorAspects &otherAspects, const ColorAspects &preferredAspects) {
+ Mutex::Autolock autoLock(mColorAspectsLock);
+ ColorAspects newAspects;
+ newAspects.mRange = preferredAspects.mRange != ColorAspects::RangeUnspecified ?
+ preferredAspects.mRange : otherAspects.mRange;
+ newAspects.mPrimaries = preferredAspects.mPrimaries != ColorAspects::PrimariesUnspecified ?
+ preferredAspects.mPrimaries : otherAspects.mPrimaries;
+ newAspects.mTransfer = preferredAspects.mTransfer != ColorAspects::TransferUnspecified ?
+ preferredAspects.mTransfer : otherAspects.mTransfer;
+ newAspects.mMatrixCoeffs = preferredAspects.mMatrixCoeffs != ColorAspects::MatrixUnspecified ?
+ preferredAspects.mMatrixCoeffs : otherAspects.mMatrixCoeffs;
+
+ // Check to see if need update mFinalColorAspects.
+ if (colorAspectsDiffer(mFinalColorAspects, newAspects)) {
+ mFinalColorAspects = newAspects;
+ mUpdateColorAspects = true;
+ }
+}
+
+status_t SoftVideoDecoderOMXComponent::handleColorAspectsChange() {
+ int perference = getColorAspectPreference();
+ ALOGD("Color Aspects preference: %d ", perference);
+
+ if (perference == kPreferBitstream) {
+ updateFinalColorAspects(mDefaultColorAspects, mBitstreamColorAspects);
+ } else if (perference == kPreferContainer) {
+ updateFinalColorAspects(mBitstreamColorAspects, mDefaultColorAspects);
+ } else {
+ return OMX_ErrorUnsupportedSetting;
+ }
+ return OK;
+}
+
void SoftVideoDecoderOMXComponent::copyYV12FrameToOutputBuffer(
uint8_t *dst, const uint8_t *srcY, const uint8_t *srcU, const uint8_t *srcV,
size_t srcYStride, size_t srcUStride, size_t srcVStride) {
@@ -450,7 +524,7 @@
OMX_ERRORTYPE SoftVideoDecoderOMXComponent::getConfig(
OMX_INDEXTYPE index, OMX_PTR params) {
- switch (index) {
+ switch ((int)index) {
case OMX_IndexConfigCommonOutputCrop:
{
OMX_CONFIG_RECTTYPE *rectParams = (OMX_CONFIG_RECTTYPE *)params;
@@ -470,22 +544,88 @@
return OMX_ErrorNone;
}
+ case kDescribeColorAspectsIndex:
+ {
+ if (!supportsDescribeColorAspects()) {
+ return OMX_ErrorUnsupportedIndex;
+ }
+
+ DescribeColorAspectsParams* colorAspectsParams =
+ (DescribeColorAspectsParams *)params;
+
+ if (colorAspectsParams->nPortIndex != kOutputPortIndex) {
+ return OMX_ErrorBadParameter;
+ }
+
+ colorAspectsParams->sAspects = mFinalColorAspects;
+ if (colorAspectsParams->bRequestingDataSpace || colorAspectsParams->bDataSpaceChanged) {
+ return OMX_ErrorUnsupportedSetting;
+ }
+
+ return OMX_ErrorNone;
+ }
default:
return OMX_ErrorUnsupportedIndex;
}
}
+OMX_ERRORTYPE SoftVideoDecoderOMXComponent::setConfig(
+ OMX_INDEXTYPE index, const OMX_PTR params){
+ switch ((int)index) {
+ case kDescribeColorAspectsIndex:
+ {
+ if (!supportsDescribeColorAspects()) {
+ return OMX_ErrorUnsupportedIndex;
+ }
+ const DescribeColorAspectsParams* colorAspectsParams =
+ (const DescribeColorAspectsParams *)params;
+
+ if (!isValidOMXParam(colorAspectsParams)) {
+ return OMX_ErrorBadParameter;
+ }
+
+ if (colorAspectsParams->nPortIndex != kOutputPortIndex) {
+ return OMX_ErrorBadParameter;
+ }
+
+ // Update color aspects if necessary.
+ if (colorAspectsDiffer(colorAspectsParams->sAspects, mDefaultColorAspects)) {
+ mDefaultColorAspects = colorAspectsParams->sAspects;
+ status_t err = handleColorAspectsChange();
+ CHECK(err == OK);
+ }
+ return OMX_ErrorNone;
+ }
+
+ default:
+ return OMX_ErrorUnsupportedIndex;
+ }
+}
+
+
OMX_ERRORTYPE SoftVideoDecoderOMXComponent::getExtensionIndex(
const char *name, OMX_INDEXTYPE *index) {
if (!strcmp(name, "OMX.google.android.index.prepareForAdaptivePlayback")) {
*(int32_t*)index = kPrepareForAdaptivePlaybackIndex;
return OMX_ErrorNone;
+ } else if (!strcmp(name, "OMX.google.android.index.describeColorAspects")
+ && supportsDescribeColorAspects()) {
+ *(int32_t*)index = kDescribeColorAspectsIndex;
+ return OMX_ErrorNone;
}
return SimpleSoftOMXComponent::getExtensionIndex(name, index);
}
+bool SoftVideoDecoderOMXComponent::supportsDescribeColorAspects() {
+ return getColorAspectPreference() != kNotSupported;
+}
+
+int SoftVideoDecoderOMXComponent::getColorAspectPreference() {
+ return kNotSupported;
+}
+
void SoftVideoDecoderOMXComponent::onReset() {
mOutputPortSettingsChange = NONE;
}
diff --git a/media/libstagefright/rtsp/ASessionDescription.cpp b/media/libstagefright/rtsp/ASessionDescription.cpp
index 98498e9..8b0331a 100644
--- a/media/libstagefright/rtsp/ASessionDescription.cpp
+++ b/media/libstagefright/rtsp/ASessionDescription.cpp
@@ -17,6 +17,7 @@
//#define LOG_NDEBUG 0
#define LOG_TAG "ASessionDescription"
#include <utils/Log.h>
+#include <cutils/log.h>
#include "ASessionDescription.h"
@@ -211,13 +212,15 @@
*PT = x;
- char key[20];
- sprintf(key, "a=rtpmap:%lu", x);
-
- CHECK(findAttribute(index, key, desc));
-
- sprintf(key, "a=fmtp:%lu", x);
- if (!findAttribute(index, key, params)) {
+ char key[32];
+ snprintf(key, sizeof(key), "a=rtpmap:%lu", x);
+ if (findAttribute(index, key, desc)) {
+ snprintf(key, sizeof(key), "a=fmtp:%lu", x);
+ if (!findAttribute(index, key, params)) {
+ params->clear();
+ }
+ } else {
+ desc->clear();
params->clear();
}
}
@@ -228,8 +231,11 @@
*width = 0;
*height = 0;
- char key[20];
- sprintf(key, "a=framesize:%lu", PT);
+ char key[33];
+ snprintf(key, sizeof(key), "a=framesize:%lu", PT);
+ if (PT > 9999999) {
+ android_errorWriteLog(0x534e4554, "25747670");
+ }
AString value;
if (!findAttribute(index, key, &value)) {
return false;
diff --git a/media/libstagefright/rtsp/MyHandler.h b/media/libstagefright/rtsp/MyHandler.h
index f9a9ab9..76e2e6e 100644
--- a/media/libstagefright/rtsp/MyHandler.h
+++ b/media/libstagefright/rtsp/MyHandler.h
@@ -1042,49 +1042,13 @@
return;
}
- sp<ABuffer> accessUnit;
- CHECK(msg->findBuffer("access-unit", &accessUnit));
-
- uint32_t seqNum = (uint32_t)accessUnit->int32Data();
-
if (mSeekPending) {
ALOGV("we're seeking, dropping stale packet.");
break;
}
- if (track->mNewSegment) {
- // The sequence number from RTP packet has only 16 bits and is extended
- // by ARTPSource. Only the low 16 bits of seq in RTP-Info of reply of
- // RTSP "PLAY" command should be used to detect the first RTP packet
- // after seeking.
- if (track->mAllowedStaleAccessUnits > 0) {
- if ((((seqNum ^ track->mFirstSeqNumInSegment) & 0xffff) != 0)) {
- // Not the first rtp packet of the stream after seeking, discarding.
- track->mAllowedStaleAccessUnits--;
- ALOGV("discarding stale access unit (0x%x : 0x%x)",
- seqNum, track->mFirstSeqNumInSegment);
- break;
- }
- } else { // track->mAllowedStaleAccessUnits <= 0
- mNumAccessUnitsReceived = 0;
- ALOGW_IF(track->mAllowedStaleAccessUnits == 0,
- "Still no first rtp packet after %d stale ones",
- kMaxAllowedStaleAccessUnits);
- track->mAllowedStaleAccessUnits = -1;
- break;
- }
-
- // Now found the first rtp packet of the stream after seeking.
- track->mFirstSeqNumInSegment = seqNum;
- track->mNewSegment = false;
- }
-
- if (seqNum < track->mFirstSeqNumInSegment) {
- ALOGV("dropping stale access-unit (%d < %d)",
- seqNum, track->mFirstSeqNumInSegment);
- break;
- }
-
+ sp<ABuffer> accessUnit;
+ CHECK(msg->findBuffer("access-unit", &accessUnit));
onAccessUnitComplete(trackIndex, accessUnit);
break;
}
@@ -1444,6 +1408,11 @@
msg->post((mKeepAliveTimeoutUs * 9) / 10);
}
+ void cancelAccessUnitTimeoutCheck() {
+ ALOGV("cancelAccessUnitTimeoutCheck");
+ ++mCheckGeneration;
+ }
+
void postAccessUnitTimeoutCheck() {
if (mCheckPending) {
return;
@@ -1828,14 +1797,8 @@
// Time is now established, lets start timestamping immediately
for (size_t i = 0; i < mTracks.size(); ++i) {
- TrackInfo *trackInfo = &mTracks.editItemAt(i);
- while (!trackInfo->mPackets.empty()) {
- sp<ABuffer> accessUnit = *trackInfo->mPackets.begin();
- trackInfo->mPackets.erase(trackInfo->mPackets.begin());
-
- if (addMediaTimestamp(i, trackInfo, accessUnit)) {
- postQueueAccessUnit(i, accessUnit);
- }
+ if (OK != processAccessUnitQueue(i)) {
+ return;
}
}
for (size_t i = 0; i < mTracks.size(); ++i) {
@@ -1848,38 +1811,83 @@
}
}
- void onAccessUnitComplete(
- int32_t trackIndex, const sp<ABuffer> &accessUnit) {
- ALOGV("onAccessUnitComplete track %d", trackIndex);
-
- if(!mPlayResponseParsed){
- ALOGI("play response is not parsed, storing accessunit");
- TrackInfo *track = &mTracks.editItemAt(trackIndex);
- track->mPackets.push_back(accessUnit);
- return;
- }
-
- handleFirstAccessUnit();
-
+ status_t processAccessUnitQueue(int32_t trackIndex) {
TrackInfo *track = &mTracks.editItemAt(trackIndex);
-
- if (!mAllTracksHaveTime) {
- ALOGV("storing accessUnit, no time established yet");
- track->mPackets.push_back(accessUnit);
- return;
- }
-
while (!track->mPackets.empty()) {
sp<ABuffer> accessUnit = *track->mPackets.begin();
track->mPackets.erase(track->mPackets.begin());
+ uint32_t seqNum = (uint32_t)accessUnit->int32Data();
+ if (track->mNewSegment) {
+ // The sequence number from RTP packet has only 16 bits and is extended
+ // by ARTPSource. Only the low 16 bits of seq in RTP-Info of reply of
+ // RTSP "PLAY" command should be used to detect the first RTP packet
+ // after seeking.
+ if (mSeekable) {
+ if (track->mAllowedStaleAccessUnits > 0) {
+ uint32_t seqNum16 = seqNum & 0xffff;
+ uint32_t firstSeqNumInSegment16 = track->mFirstSeqNumInSegment & 0xffff;
+ if (seqNum16 > firstSeqNumInSegment16 + kMaxAllowedStaleAccessUnits
+ || seqNum16 < firstSeqNumInSegment16) {
+ // Not the first rtp packet of the stream after seeking, discarding.
+ track->mAllowedStaleAccessUnits--;
+ ALOGV("discarding stale access unit (0x%x : 0x%x)",
+ seqNum, track->mFirstSeqNumInSegment);
+ continue;
+ }
+ ALOGW_IF(seqNum16 != firstSeqNumInSegment16,
+ "Missing the first packet(%u), now take packet(%u) as first one",
+ track->mFirstSeqNumInSegment, seqNum);
+ } else { // track->mAllowedStaleAccessUnits <= 0
+ mNumAccessUnitsReceived = 0;
+ ALOGW_IF(track->mAllowedStaleAccessUnits == 0,
+ "Still no first rtp packet after %d stale ones",
+ kMaxAllowedStaleAccessUnits);
+ track->mAllowedStaleAccessUnits = -1;
+ return UNKNOWN_ERROR;
+ }
+ }
+
+ // Now found the first rtp packet of the stream after seeking.
+ track->mFirstSeqNumInSegment = seqNum;
+ track->mNewSegment = false;
+ }
+
+ if (seqNum < track->mFirstSeqNumInSegment) {
+ ALOGV("dropping stale access-unit (%d < %d)",
+ seqNum, track->mFirstSeqNumInSegment);
+ continue;
+ }
+
if (addMediaTimestamp(trackIndex, track, accessUnit)) {
postQueueAccessUnit(trackIndex, accessUnit);
}
}
+ return OK;
+ }
- if (addMediaTimestamp(trackIndex, track, accessUnit)) {
- postQueueAccessUnit(trackIndex, accessUnit);
+ void onAccessUnitComplete(
+ int32_t trackIndex, const sp<ABuffer> &accessUnit) {
+ TrackInfo *track = &mTracks.editItemAt(trackIndex);
+ track->mPackets.push_back(accessUnit);
+
+ uint32_t seqNum = (uint32_t)accessUnit->int32Data();
+ ALOGV("onAccessUnitComplete track %d storing accessunit %u", trackIndex, seqNum);
+
+ if(!mPlayResponseParsed){
+ ALOGV("play response is not parsed");
+ return;
+ }
+
+ handleFirstAccessUnit();
+
+ if (!mAllTracksHaveTime) {
+ ALOGV("storing accessUnit, no time established yet");
+ return;
+ }
+
+ if (OK != processAccessUnitQueue(trackIndex)) {
+ return;
}
if (track->mEOSReceived) {
diff --git a/media/mediaserver/mediaserver.rc b/media/mediaserver/mediaserver.rc
index b777d5c..f6c325c 100644
--- a/media/mediaserver/mediaserver.rc
+++ b/media/mediaserver/mediaserver.rc
@@ -3,4 +3,4 @@
user media
group audio camera inet net_bt net_bt_admin net_bw_acct drmrpc mediadrm
ioprio rt 4
- writepid /dev/cpuset/foreground/tasks
+ writepid /dev/cpuset/foreground/tasks /dev/stune/foreground/tasks
diff --git a/media/mtp/MtpServer.cpp b/media/mtp/MtpServer.cpp
index 90f1a77..e39dcdd 100644
--- a/media/mtp/MtpServer.cpp
+++ b/media/mtp/MtpServer.cpp
@@ -927,6 +927,10 @@
if (!mData.getUInt32(temp32)) return MTP_RESPONSE_INVALID_PARAMETER; // sequence number
MtpStringBuffer name, created, modified;
if (!mData.getString(name)) return MTP_RESPONSE_INVALID_PARAMETER; // file name
+ if (name.getCharCount() == 0) {
+ ALOGE("empty name");
+ return MTP_RESPONSE_INVALID_PARAMETER;
+ }
if (!mData.getString(created)) return MTP_RESPONSE_INVALID_PARAMETER; // date created
if (!mData.getString(modified)) return MTP_RESPONSE_INVALID_PARAMETER; // date modified
// keywords follow
diff --git a/media/ndk/Android.mk b/media/ndk/Android.mk
index f287761..a4f999f 100644
--- a/media/ndk/Android.mk
+++ b/media/ndk/Android.mk
@@ -40,7 +40,7 @@
LOCAL_CFLAGS += -fvisibility=hidden -D EXPORT='__attribute__ ((visibility ("default")))'
-LOCAL_CFLAGS += -Werror
+LOCAL_CFLAGS += -Werror -Wall
LOCAL_SHARED_LIBRARIES := \
libbinder \
diff --git a/media/ndk/NdkMediaExtractor.cpp b/media/ndk/NdkMediaExtractor.cpp
index 1118959..89f2d9c 100644
--- a/media/ndk/NdkMediaExtractor.cpp
+++ b/media/ndk/NdkMediaExtractor.cpp
@@ -148,7 +148,14 @@
EXPORT
bool AMediaExtractor_advance(AMediaExtractor *mData) {
//ALOGV("advance");
- return mData->mImpl->advance();
+ status_t err = mData->mImpl->advance();
+ if (err == ERROR_END_OF_STREAM) {
+ return false;
+ } else if (err != OK) {
+ ALOGE("sf error code: %d", err);
+ return false;
+ }
+ return true;
}
EXPORT
@@ -343,9 +350,9 @@
const void *key;
size_t keysize;
- if (meta->findData(kKeyCryptoIV, &type, &key, &keysize)) {
+ if (meta->findData(kKeyCryptoKey, &type, &key, &keysize)) {
if (keysize != 16) {
- // IVs must be 16 bytes in length.
+ // Keys must be 16 bytes in length.
return NULL;
}
}
diff --git a/radio/Radio.cpp b/radio/Radio.cpp
index e3554c2..3c04fb0 100644
--- a/radio/Radio.cpp
+++ b/radio/Radio.cpp
@@ -55,7 +55,7 @@
sp<DeathNotifier> gDeathNotifier;
}; // namespace anonymous
-const sp<IRadioService>& Radio::getRadioService()
+const sp<IRadioService> Radio::getRadioService()
{
Mutex::Autolock _l(gLock);
if (gRadioService.get() == 0) {
@@ -84,7 +84,7 @@
uint32_t *numModules)
{
ALOGV("listModules()");
- const sp<IRadioService>& service = getRadioService();
+ const sp<IRadioService> service = getRadioService();
if (service == 0) {
return NO_INIT;
}
@@ -98,7 +98,7 @@
{
ALOGV("attach()");
sp<Radio> radio;
- const sp<IRadioService>& service = getRadioService();
+ const sp<IRadioService> service = getRadioService();
if (service == 0) {
return radio;
}
diff --git a/services/audioflinger/Android.mk b/services/audioflinger/Android.mk
index 35c1853..5c28e46 100644
--- a/services/audioflinger/Android.mk
+++ b/services/audioflinger/Android.mk
@@ -14,6 +14,8 @@
liblog \
libbinder
+LOCAL_CFLAGS := -Wall -Werror
+
include $(BUILD_SHARED_LIBRARY)
include $(CLEAR_VARS)
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index c6d4fe3..60093cc 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -132,6 +132,9 @@
case AUDIO_FORMAT_AC3: return "ac-3";
case AUDIO_FORMAT_E_AC3: return "e-ac-3";
case AUDIO_FORMAT_IEC61937: return "iec61937";
+ case AUDIO_FORMAT_DTS: return "dts";
+ case AUDIO_FORMAT_DTS_HD: return "dts-hd";
+ case AUDIO_FORMAT_DOLBY_TRUEHD: return "dolby-truehd";
default:
break;
}
@@ -576,7 +579,7 @@
audio_format_t format,
audio_channel_mask_t channelMask,
size_t *frameCount,
- IAudioFlinger::track_flags_t *flags,
+ audio_output_flags_t *flags,
const sp<IMemory>& sharedBuffer,
audio_io_handle_t output,
pid_t pid,
@@ -660,7 +663,7 @@
sp<PlaybackThread> t = mPlaybackThreads.valueAt(i);
if (mPlaybackThreads.keyAt(i) != output) {
uint32_t sessions = t->hasAudioSession(lSessionId);
- if (sessions & PlaybackThread::EFFECT_SESSION) {
+ if (sessions & ThreadBase::EFFECT_SESSION) {
effectThread = t.get();
break;
}
@@ -1101,14 +1104,20 @@
// AUDIO_IO_HANDLE_NONE means the parameters are global to the audio hardware interface
if (ioHandle == AUDIO_IO_HANDLE_NONE) {
Mutex::Autolock _l(mLock);
- status_t final_result = NO_ERROR;
+ // result will remain NO_INIT if no audio device is present
+ status_t final_result = NO_INIT;
{
AutoMutex lock(mHardwareLock);
mHardwareStatus = AUDIO_HW_SET_PARAMETER;
for (size_t i = 0; i < mAudioHwDevs.size(); i++) {
audio_hw_device_t *dev = mAudioHwDevs.valueAt(i)->hwDevice();
status_t result = dev->set_parameters(dev, keyValuePairs.string());
- final_result = result ?: final_result;
+ // return success if at least one audio device accepts the parameters as not all
+ // HALs are requested to support all parameters. If no audio device supports the
+ // requested parameters, the last error is reported.
+ if (final_result != NO_ERROR) {
+ final_result = result;
+ }
}
mHardwareStatus = AUDIO_HW_IDLE;
}
@@ -1467,7 +1476,7 @@
audio_channel_mask_t channelMask,
const String16& opPackageName,
size_t *frameCount,
- IAudioFlinger::track_flags_t *flags,
+ audio_input_flags_t *flags,
pid_t pid,
pid_t tid,
int clientUid,
@@ -1698,14 +1707,14 @@
uint32_t AudioFlinger::getPrimaryOutputSamplingRate()
{
Mutex::Autolock _l(mLock);
- PlaybackThread *thread = primaryPlaybackThread_l();
+ PlaybackThread *thread = fastPlaybackThread_l();
return thread != NULL ? thread->sampleRate() : 0;
}
size_t AudioFlinger::getPrimaryOutputFrameCount()
{
Mutex::Autolock _l(mLock);
- PlaybackThread *thread = primaryPlaybackThread_l();
+ PlaybackThread *thread = fastPlaybackThread_l();
return thread != NULL ? thread->frameCountHAL() : 0;
}
@@ -1766,7 +1775,7 @@
for (size_t i = 0; i < mPlaybackThreads.size(); i++) {
sp<PlaybackThread> thread = mPlaybackThreads.valueAt(i);
uint32_t sessions = thread->hasAudioSession(sessionId);
- if (sessions & PlaybackThread::TRACK_SESSION) {
+ if (sessions & ThreadBase::TRACK_SESSION) {
AudioParameter param = AudioParameter();
param.addInt(String8(AUDIO_PARAMETER_STREAM_HW_AV_SYNC), value);
thread->setParameters(param.toString());
@@ -2199,7 +2208,7 @@
}
#endif
- AudioStreamIn *inputStream = new AudioStreamIn(inHwDev, inStream);
+ AudioStreamIn *inputStream = new AudioStreamIn(inHwDev, inStream, flags);
// Start record thread
// RecordThread requires both input and output device indication to forward to audio
@@ -2532,6 +2541,25 @@
return thread->outDevice();
}
+AudioFlinger::PlaybackThread *AudioFlinger::fastPlaybackThread_l() const
+{
+ size_t minFrameCount = 0;
+ PlaybackThread *minThread = NULL;
+ for (size_t i = 0; i < mPlaybackThreads.size(); i++) {
+ PlaybackThread *thread = mPlaybackThreads.valueAt(i).get();
+ if (!thread->isDuplicating()) {
+ size_t frameCount = thread->frameCountHAL();
+ if (frameCount != 0 && (minFrameCount == 0 || frameCount < minFrameCount ||
+ (frameCount == minFrameCount && thread->hasFastMixer() &&
+ /*minThread != NULL &&*/ !minThread->hasFastMixer()))) {
+ minFrameCount = frameCount;
+ minThread = thread;
+ }
+ }
+ }
+ return minThread;
+}
+
sp<AudioFlinger::SyncEvent> AudioFlinger::createSyncEvent(AudioSystem::sync_event_t type,
audio_session_t triggerSession,
audio_session_t listenerSession,
@@ -2824,14 +2852,11 @@
return INVALID_OPERATION;
}
- // Check whether the destination thread has a channel count of FCC_2, which is
- // currently required for (most) effects. Prevent moving the effect chain here rather
- // than disabling the addEffect_l() call in dstThread below.
- if ((dstThread->type() == ThreadBase::MIXER || dstThread->isDuplicating()) &&
- dstThread->mChannelCount != FCC_2) {
+ // Check whether the destination thread and all effects in the chain are compatible
+ if (!chain->isCompatibleWithThread_l(dstThread)) {
ALOGW("moveEffectChain_l() effect chain failed because"
- " destination thread %p channel count(%u) != %u",
- dstThread, dstThread->mChannelCount, FCC_2);
+ " destination thread %p is not compatible with effects in the chain",
+ dstThread);
return INVALID_OPERATION;
}
@@ -3039,7 +3064,7 @@
}
} else {
if (fd >= 0) {
- dprintf(fd, "unable to rotate tees in %.*s: %s\n", teePathLen, teePath,
+ dprintf(fd, "unable to rotate tees in %.*s: %s\n", (int) teePathLen, teePath,
strerror(errno));
}
}
diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h
index 10762e6..c4b89f8 100644
--- a/services/audioflinger/AudioFlinger.h
+++ b/services/audioflinger/AudioFlinger.h
@@ -104,7 +104,7 @@
audio_format_t format,
audio_channel_mask_t channelMask,
size_t *pFrameCount,
- IAudioFlinger::track_flags_t *flags,
+ audio_output_flags_t *flags,
const sp<IMemory>& sharedBuffer,
audio_io_handle_t output,
pid_t pid,
@@ -120,7 +120,7 @@
audio_channel_mask_t channelMask,
const String16& opPackageName,
size_t *pFrameCount,
- IAudioFlinger::track_flags_t *flags,
+ audio_input_flags_t *flags,
pid_t pid,
pid_t tid,
int clientUid,
@@ -575,6 +575,9 @@
PlaybackThread *primaryPlaybackThread_l() const;
audio_devices_t primaryOutputDevice_l() const;
+ // return the playback thread with smallest HAL buffer size, and prefer fast
+ PlaybackThread *fastPlaybackThread_l() const;
+
sp<PlaybackThread> getEffectThread_l(audio_session_t sessionId, int EffectId);
@@ -609,11 +612,12 @@
struct AudioStreamIn {
AudioHwDevice* const audioHwDev;
audio_stream_in_t* const stream;
+ audio_input_flags_t flags;
audio_hw_device_t* hwDev() const { return audioHwDev->hwDevice(); }
- AudioStreamIn(AudioHwDevice *dev, audio_stream_in_t *in) :
- audioHwDev(dev), stream(in) {}
+ AudioStreamIn(AudioHwDevice *dev, audio_stream_in_t *in, audio_input_flags_t flags) :
+ audioHwDev(dev), stream(in), flags(flags) {}
};
// for mAudioSessionRefs only
diff --git a/services/audioflinger/AudioMixer.cpp b/services/audioflinger/AudioMixer.cpp
index 567dbeb..945f4b3 100644
--- a/services/audioflinger/AudioMixer.cpp
+++ b/services/audioflinger/AudioMixer.cpp
@@ -430,7 +430,7 @@
{
ALOGV("AudioMixer::deleteTrackName(%d)", name);
name -= TRACK0;
- ALOG_ASSERT(uint32_t(name) < MAX_NUM_TRACKS, "bad track name %d", name);
+ LOG_ALWAYS_FATAL_IF(name < 0 || name >= (int)MAX_NUM_TRACKS, "bad track name %d", name);
ALOGV("deleteTrackName(%d)", name);
track_t& track(mState.tracks[ name ]);
if (track.enabled) {
diff --git a/services/audioflinger/BufferProviders.cpp b/services/audioflinger/BufferProviders.cpp
index 2ca2cac..7b6dfcb 100644
--- a/services/audioflinger/BufferProviders.cpp
+++ b/services/audioflinger/BufferProviders.cpp
@@ -474,18 +474,18 @@
ALOGV("processFrames(%zu %zu) remaining(%zu)", *dstFrames, *srcFrames, mRemaining);
// Note dstFrames is the required number of frames.
- // Ensure consumption from src is as expected.
- //TODO: add logic to track "very accurate" consumption related to speed, original sampling
- //rate, actual frames processed.
- const size_t targetSrc = *dstFrames * mPlaybackRate.mSpeed;
- if (*srcFrames < targetSrc) { // limit dst frames to that possible
- *dstFrames = *srcFrames / mPlaybackRate.mSpeed;
- } else if (*srcFrames > targetSrc + 1) {
- *srcFrames = targetSrc + 1;
- }
-
if (!mAudioPlaybackRateValid) {
//fallback mode
+ // Ensure consumption from src is as expected.
+ // TODO: add logic to track "very accurate" consumption related to speed, original sampling
+ // rate, actual frames processed.
+
+ const size_t targetSrc = *dstFrames * mPlaybackRate.mSpeed;
+ if (*srcFrames < targetSrc) { // limit dst frames to that possible
+ *dstFrames = *srcFrames / mPlaybackRate.mSpeed;
+ } else if (*srcFrames > targetSrc + 1) {
+ *srcFrames = targetSrc + 1;
+ }
if (*dstFrames > 0) {
switch(mPlaybackRate.mFallbackMode) {
case AUDIO_TIMESTRETCH_FALLBACK_CUT_REPEAT:
diff --git a/services/audioflinger/Effects.cpp b/services/audioflinger/Effects.cpp
index 53b0ff1..20d2896 100644
--- a/services/audioflinger/Effects.cpp
+++ b/services/audioflinger/Effects.cpp
@@ -216,9 +216,10 @@
return mHandles.size();
}
-void AudioFlinger::EffectModule::updateState() {
+bool AudioFlinger::EffectModule::updateState() {
Mutex::Autolock _l(mLock);
+ bool started = false;
switch (mState) {
case RESTART:
reset_l();
@@ -233,6 +234,7 @@
}
if (start_l() == NO_ERROR) {
mState = ACTIVE;
+ started = true;
} else {
mState = IDLE;
}
@@ -256,6 +258,8 @@
default: //IDLE , ACTIVE, DESTROYED
break;
}
+
+ return started;
}
void AudioFlinger::EffectModule::process()
@@ -275,12 +279,29 @@
mConfig.inputCfg.buffer.s32,
mConfig.inputCfg.buffer.frameCount/2);
}
+ int ret;
+ if (isProcessImplemented()) {
+ // do the actual processing in the effect engine
+ ret = (*mEffectInterface)->process(mEffectInterface,
+ &mConfig.inputCfg.buffer,
+ &mConfig.outputCfg.buffer);
+ } else {
+ if (mConfig.inputCfg.buffer.raw != mConfig.outputCfg.buffer.raw) {
+ size_t frameCnt = mConfig.inputCfg.buffer.frameCount * FCC_2; //always stereo here
+ int16_t *in = mConfig.inputCfg.buffer.s16;
+ int16_t *out = mConfig.outputCfg.buffer.s16;
- // do the actual processing in the effect engine
- int ret = (*mEffectInterface)->process(mEffectInterface,
- &mConfig.inputCfg.buffer,
- &mConfig.outputCfg.buffer);
-
+ if (mConfig.outputCfg.accessMode == EFFECT_BUFFER_ACCESS_ACCUMULATE) {
+ for (size_t i = 0; i < frameCnt; i++) {
+ out[i] = clamp16((int32_t)out[i] + (int32_t)in[i]);
+ }
+ } else {
+ memcpy(mConfig.outputCfg.buffer.raw, mConfig.inputCfg.buffer.raw,
+ frameCnt * sizeof(int16_t));
+ }
+ }
+ ret = -ENODATA;
+ }
// force transition to IDLE state when engine is ready
if (mState == STOPPED && ret == -ENODATA) {
mDisableWaitCnt = 1;
@@ -297,7 +318,7 @@
// accumulate input onto output
sp<EffectChain> chain = mChain.promote();
if (chain != 0 && chain->activeTrackCnt() != 0) {
- size_t frameCnt = mConfig.inputCfg.buffer.frameCount * 2; //always stereo here
+ size_t frameCnt = mConfig.inputCfg.buffer.frameCount * FCC_2; //always stereo here
int16_t *in = mConfig.inputCfg.buffer.s16;
int16_t *out = mConfig.outputCfg.buffer.s16;
for (size_t i = 0; i < frameCnt; i++) {
@@ -464,10 +485,22 @@
}
}
+// start() must be called with PlaybackThread::mLock or EffectChain::mLock held
status_t AudioFlinger::EffectModule::start()
{
- Mutex::Autolock _l(mLock);
- return start_l();
+ sp<EffectChain> chain;
+ status_t status;
+ {
+ Mutex::Autolock _l(mLock);
+ status = start_l();
+ if (status == NO_ERROR) {
+ chain = mChain.promote();
+ }
+ }
+ if (chain != 0) {
+ chain->resetVolume_l();
+ }
+ return status;
}
status_t AudioFlinger::EffectModule::start_l()
@@ -491,10 +524,6 @@
}
if (status == 0) {
addEffectToHal_l();
- sp<EffectChain> chain = mChain.promote();
- if (chain != 0) {
- chain->forceVolume();
- }
}
return status;
}
@@ -545,6 +574,13 @@
return NO_ERROR;
}
+// round up delta valid if value and divisor are positive.
+template <typename T>
+static T roundUpDelta(const T &value, const T &divisor) {
+ T remainder = value % divisor;
+ return remainder == 0 ? 0 : divisor - remainder;
+}
+
status_t AudioFlinger::EffectModule::command(uint32_t cmdCode,
uint32_t cmdSize,
void *pCmdData,
@@ -566,6 +602,22 @@
android_errorWriteLog(0x534e4554, "29251553");
return -EINVAL;
}
+ if ((cmdCode == EFFECT_CMD_SET_PARAM
+ || cmdCode == EFFECT_CMD_SET_PARAM_DEFERRED) && // DEFERRED not generally used
+ (sizeof(effect_param_t) > cmdSize
+ || ((effect_param_t *)pCmdData)->psize > cmdSize
+ - sizeof(effect_param_t)
+ || ((effect_param_t *)pCmdData)->vsize > cmdSize
+ - sizeof(effect_param_t)
+ - ((effect_param_t *)pCmdData)->psize
+ || roundUpDelta(((effect_param_t *)pCmdData)->psize, (uint32_t)sizeof(int)) >
+ cmdSize
+ - sizeof(effect_param_t)
+ - ((effect_param_t *)pCmdData)->psize
+ - ((effect_param_t *)pCmdData)->vsize)) {
+ android_errorWriteLog(0x534e4554, "30204301");
+ return -EINVAL;
+ }
status_t status = (*mEffectInterface)->command(mEffectInterface,
cmdCode,
cmdSize,
@@ -1351,7 +1403,7 @@
audio_session_t sessionId)
: mThread(thread), mSessionId(sessionId), mActiveTrackCnt(0), mTrackCnt(0), mTailBufferCount(0),
mOwnInBuffer(false), mVolumeCtrlIdx(-1), mLeftVolume(UINT_MAX), mRightVolume(UINT_MAX),
- mNewLeftVolume(UINT_MAX), mNewRightVolume(UINT_MAX), mForceVolume(false)
+ mNewLeftVolume(UINT_MAX), mNewRightVolume(UINT_MAX)
{
mStrategy = AudioSystem::getStrategyForStream(AUDIO_STREAM_MUSIC);
if (thread == NULL) {
@@ -1473,8 +1525,12 @@
mEffects[i]->process();
}
}
+ bool doResetVolume = false;
for (size_t i = 0; i < size; i++) {
- mEffects[i]->updateState();
+ doResetVolume = mEffects[i]->updateState() || doResetVolume;
+ }
+ if (doResetVolume) {
+ resetVolume_l();
}
}
@@ -1655,8 +1711,8 @@
}
}
-// setVolume_l() must be called with PlaybackThread::mLock held
-bool AudioFlinger::EffectChain::setVolume_l(uint32_t *left, uint32_t *right)
+// setVolume_l() must be called with PlaybackThread::mLock or EffectChain::mLock held
+bool AudioFlinger::EffectChain::setVolume_l(uint32_t *left, uint32_t *right, bool force)
{
uint32_t newLeft = *left;
uint32_t newRight = *right;
@@ -1674,7 +1730,7 @@
}
}
- if (!isVolumeForced() && ctrlIdx == mVolumeCtrlIdx &&
+ if (!force && ctrlIdx == mVolumeCtrlIdx &&
*left == mLeftVolume && *right == mRightVolume) {
if (hasControl) {
*left = mNewLeftVolume;
@@ -1716,6 +1772,16 @@
return hasControl;
}
+// resetVolume_l() must be called with PlaybackThread::mLock or EffectChain::mLock held
+void AudioFlinger::EffectChain::resetVolume_l()
+{
+ if ((mLeftVolume != UINT_MAX) && (mRightVolume != UINT_MAX)) {
+ uint32_t left = mLeftVolume;
+ uint32_t right = mRightVolume;
+ (void)setVolume_l(&left, &right, true);
+ }
+}
+
void AudioFlinger::EffectChain::syncHalEffectsState()
{
Mutex::Autolock _l(mLock);
@@ -1968,4 +2034,61 @@
}
}
+void AudioFlinger::EffectChain::checkOutputFlagCompatibility(audio_output_flags_t *flags) const
+{
+ if ((*flags & AUDIO_OUTPUT_FLAG_RAW) != 0 && !isRawCompatible()) {
+ *flags = (audio_output_flags_t)(*flags & ~AUDIO_OUTPUT_FLAG_RAW);
+ }
+ if ((*flags & AUDIO_OUTPUT_FLAG_FAST) != 0 && !isFastCompatible()) {
+ *flags = (audio_output_flags_t)(*flags & ~AUDIO_OUTPUT_FLAG_FAST);
+ }
+}
+
+void AudioFlinger::EffectChain::checkInputFlagCompatibility(audio_input_flags_t *flags) const
+{
+ if ((*flags & AUDIO_INPUT_FLAG_RAW) != 0 && !isRawCompatible()) {
+ *flags = (audio_input_flags_t)(*flags & ~AUDIO_INPUT_FLAG_RAW);
+ }
+ if ((*flags & AUDIO_INPUT_FLAG_FAST) != 0 && !isFastCompatible()) {
+ *flags = (audio_input_flags_t)(*flags & ~AUDIO_INPUT_FLAG_FAST);
+ }
+}
+
+bool AudioFlinger::EffectChain::isRawCompatible() const
+{
+ Mutex::Autolock _l(mLock);
+ for (const auto &effect : mEffects) {
+ if (effect->isProcessImplemented()) {
+ return false;
+ }
+ }
+ // Allow effects without processing.
+ return true;
+}
+
+bool AudioFlinger::EffectChain::isFastCompatible() const
+{
+ Mutex::Autolock _l(mLock);
+ for (const auto &effect : mEffects) {
+ if (effect->isProcessImplemented()
+ && effect->isImplementationSoftware()) {
+ return false;
+ }
+ }
+ // Allow effects without processing or hw accelerated effects.
+ return true;
+}
+
+// isCompatibleWithThread_l() must be called with thread->mLock held
+bool AudioFlinger::EffectChain::isCompatibleWithThread_l(const sp<ThreadBase>& thread) const
+{
+ Mutex::Autolock _l(mLock);
+ for (size_t i = 0; i < mEffects.size(); i++) {
+ if (thread->checkEffectCompatibility_l(&(mEffects[i]->desc()), mSessionId) != NO_ERROR) {
+ return false;
+ }
+ }
+ return true;
+}
+
} // namespace android
diff --git a/services/audioflinger/Effects.h b/services/audioflinger/Effects.h
index 37be54c..96cb607 100644
--- a/services/audioflinger/Effects.h
+++ b/services/audioflinger/Effects.h
@@ -60,7 +60,7 @@
int id() const { return mId; }
void process();
- void updateState();
+ bool updateState();
status_t command(uint32_t cmdCode,
uint32_t cmdSize,
void *pCmdData,
@@ -117,6 +117,10 @@
void unlock() { mLock.unlock(); }
bool isOffloadable() const
{ return (mDescriptor.flags & EFFECT_FLAG_OFFLOAD_SUPPORTED) != 0; }
+ bool isImplementationSoftware() const
+ { return (mDescriptor.flags & EFFECT_FLAG_HW_ACC_MASK) == 0; }
+ bool isProcessImplemented() const
+ { return (mDescriptor.flags & EFFECT_FLAG_NO_PROCESS) == 0; }
status_t setOffloaded(bool offloaded, audio_io_handle_t io);
bool isOffloaded() const;
void addEffectToHal_l();
@@ -275,7 +279,8 @@
sp<EffectModule> getEffectFromId_l(int id);
sp<EffectModule> getEffectFromType_l(const effect_uuid_t *type);
// FIXME use float to improve the dynamic range
- bool setVolume_l(uint32_t *left, uint32_t *right);
+ bool setVolume_l(uint32_t *left, uint32_t *right, bool force = false);
+ void resetVolume_l();
void setDevice_l(audio_devices_t device);
void setMode_l(audio_mode_t mode);
void setAudioSource_l(audio_source_t source);
@@ -321,15 +326,23 @@
// At least one non offloadable effect in the chain is enabled
bool isNonOffloadableEnabled();
- // use release_cas because we don't care about the observed value, just want to make sure the
- // new value is observable.
- void forceVolume() { android_atomic_release_cas(false, true, &mForceVolume); }
- // use acquire_cas because we are interested in the observed value and
- // we are the only observers.
- bool isVolumeForced() { return (android_atomic_acquire_cas(true, false, &mForceVolume) == 0); }
-
void syncHalEffectsState();
+ // flags is an ORed set of audio_output_flags_t which is updated on return.
+ void checkOutputFlagCompatibility(audio_output_flags_t *flags) const;
+
+ // flags is an ORed set of audio_input_flags_t which is updated on return.
+ void checkInputFlagCompatibility(audio_input_flags_t *flags) const;
+
+ // Is this EffectChain compatible with the RAW audio flag.
+ bool isRawCompatible() const;
+
+ // Is this EffectChain compatible with the FAST audio flag.
+ bool isFastCompatible() const;
+
+ // isCompatibleWithThread_l() must be called with thread->mLock held
+ bool isCompatibleWithThread_l(const sp<ThreadBase>& thread) const;
+
void dump(int fd, const Vector<String16>& args);
protected:
@@ -361,30 +374,29 @@
void setThread(const sp<ThreadBase>& thread);
- wp<ThreadBase> mThread; // parent mixer thread
- Mutex mLock; // mutex protecting effect list
- Vector< sp<EffectModule> > mEffects; // list of effect modules
- audio_session_t mSessionId; // audio session ID
- int16_t *mInBuffer; // chain input buffer
- int16_t *mOutBuffer; // chain output buffer
+ wp<ThreadBase> mThread; // parent mixer thread
+ mutable Mutex mLock; // mutex protecting effect list
+ Vector< sp<EffectModule> > mEffects; // list of effect modules
+ audio_session_t mSessionId; // audio session ID
+ int16_t *mInBuffer; // chain input buffer
+ int16_t *mOutBuffer; // chain output buffer
// 'volatile' here means these are accessed with atomic operations instead of mutex
volatile int32_t mActiveTrackCnt; // number of active tracks connected
volatile int32_t mTrackCnt; // number of tracks connected
- int32_t mTailBufferCount; // current effect tail buffer count
- int32_t mMaxTailBuffers; // maximum effect tail buffers
- bool mOwnInBuffer; // true if the chain owns its input buffer
- int mVolumeCtrlIdx; // index of insert effect having control over volume
- uint32_t mLeftVolume; // previous volume on left channel
- uint32_t mRightVolume; // previous volume on right channel
- uint32_t mNewLeftVolume; // new volume on left channel
- uint32_t mNewRightVolume; // new volume on right channel
- uint32_t mStrategy; // strategy for this effect chain
- // mSuspendedEffects lists all effects currently suspended in the chain.
- // Use effect type UUID timelow field as key. There is no real risk of identical
- // timeLow fields among effect type UUIDs.
- // Updated by updateSuspendedSessions_l() only.
- KeyedVector< int, sp<SuspendedEffectDesc> > mSuspendedEffects;
- volatile int32_t mForceVolume; // force next volume command because a new effect was enabled
+ int32_t mTailBufferCount; // current effect tail buffer count
+ int32_t mMaxTailBuffers; // maximum effect tail buffers
+ bool mOwnInBuffer; // true if the chain owns its input buffer
+ int mVolumeCtrlIdx; // index of insert effect having control over volume
+ uint32_t mLeftVolume; // previous volume on left channel
+ uint32_t mRightVolume; // previous volume on right channel
+ uint32_t mNewLeftVolume; // new volume on left channel
+ uint32_t mNewRightVolume; // new volume on right channel
+ uint32_t mStrategy; // strategy for this effect chain
+ // mSuspendedEffects lists all effects currently suspended in the chain.
+ // Use effect type UUID timelow field as key. There is no real risk of identical
+ // timeLow fields among effect type UUIDs.
+ // Updated by updateSuspendedSessions_l() only.
+ KeyedVector< int, sp<SuspendedEffectDesc> > mSuspendedEffects;
};
diff --git a/services/audioflinger/FastCapture.cpp b/services/audioflinger/FastCapture.cpp
index d202169..873a9ad 100644
--- a/services/audioflinger/FastCapture.cpp
+++ b/services/audioflinger/FastCapture.cpp
@@ -31,7 +31,7 @@
/*static*/ const FastCaptureState FastCapture::sInitial;
-FastCapture::FastCapture() : FastThread(),
+FastCapture::FastCapture() : FastThread("cycleC_ms", "loadC_us"),
mInputSource(NULL), mInputSourceGen(0), mPipeSink(NULL), mPipeSinkGen(0),
mReadBuffer(NULL), mReadBufferState(-1), mFormat(Format_Invalid), mSampleRate(0),
// mDummyDumpState
diff --git a/services/audioflinger/FastMixer.cpp b/services/audioflinger/FastMixer.cpp
index e41d7bc..93f7ce5 100644
--- a/services/audioflinger/FastMixer.cpp
+++ b/services/audioflinger/FastMixer.cpp
@@ -46,7 +46,7 @@
/*static*/ const FastMixerState FastMixer::sInitial;
-FastMixer::FastMixer() : FastThread(),
+FastMixer::FastMixer() : FastThread("cycle_ms", "load_us"),
// mFastTrackNames
// mGenerations
mOutputSink(NULL),
diff --git a/services/audioflinger/FastThread.cpp b/services/audioflinger/FastThread.cpp
index 5ca579b..dca7bf9 100644
--- a/services/audioflinger/FastThread.cpp
+++ b/services/audioflinger/FastThread.cpp
@@ -35,7 +35,7 @@
namespace android {
-FastThread::FastThread() : Thread(false /*canCallJava*/),
+FastThread::FastThread(const char *cycleMs, const char *loadUs) : Thread(false /*canCallJava*/),
// re-initialized to &sInitial by subclass constructor
mPrevious(NULL), mCurrent(NULL),
/* mOldTs({0, 0}), */
@@ -72,11 +72,15 @@
frameCount(0),
#endif
mAttemptedWrite(false)
+ // mCycleMs(cycleMs)
+ // mLoadUs(loadUs)
{
mOldTs.tv_sec = 0;
mOldTs.tv_nsec = 0;
mMeasuredWarmupTs.tv_sec = 0;
mMeasuredWarmupTs.tv_nsec = 0;
+ strlcpy(mCycleMs, cycleMs, sizeof(mCycleMs));
+ strlcpy(mLoadUs, loadUs, sizeof(mLoadUs));
}
FastThread::~FastThread()
@@ -163,7 +167,7 @@
if (old <= 0) {
syscall(__NR_futex, coldFutexAddr, FUTEX_WAIT_PRIVATE, old - 1, NULL);
}
- int policy = sched_getscheduler(0);
+ int policy = sched_getscheduler(0) & ~SCHED_RESET_ON_FORK;
if (!(policy == SCHED_FIFO || policy == SCHED_RR)) {
ALOGE("did not receive expected priority boost");
}
@@ -336,8 +340,8 @@
// this store #4 is not atomic with respect to stores #1, #2, #3 above, but
// the newest open & oldest closed halves are atomic with respect to each other
mDumpState->mBounds = mBounds;
- ATRACE_INT("cycle_ms", monotonicNs / 1000000);
- ATRACE_INT("load_us", loadNs / 1000);
+ ATRACE_INT(mCycleMs, monotonicNs / 1000000);
+ ATRACE_INT(mLoadUs, loadNs / 1000);
}
#endif
} else {
diff --git a/services/audioflinger/FastThread.h b/services/audioflinger/FastThread.h
index 2efb6de..816b666 100644
--- a/services/audioflinger/FastThread.h
+++ b/services/audioflinger/FastThread.h
@@ -30,7 +30,7 @@
class FastThread : public Thread {
public:
- FastThread();
+ FastThread(const char *cycleMs, const char *loadUs);
virtual ~FastThread();
private:
@@ -88,6 +88,9 @@
FastThreadState::Command mCommand;
bool mAttemptedWrite;
+ char mCycleMs[16]; // cycle_ms + suffix
+ char mLoadUs[16]; // load_us + suffix
+
}; // class FastThread
} // android
diff --git a/services/audioflinger/PatchPanel.cpp b/services/audioflinger/PatchPanel.cpp
index f8671b5..bee17fd 100644
--- a/services/audioflinger/PatchPanel.cpp
+++ b/services/audioflinger/PatchPanel.cpp
@@ -168,12 +168,46 @@
ALOGV("createAudioPatch() removing patch handle %d", *handle);
halHandle = mPatches[index]->mHalHandle;
Patch *removedPatch = mPatches[index];
+ // free resources owned by the removed patch if applicable
+ // 1) if a software patch is present, release the playback and capture threads and
+ // tracks created. This will also release the corresponding audio HAL patches
if ((removedPatch->mRecordPatchHandle
!= AUDIO_PATCH_HANDLE_NONE) ||
(removedPatch->mPlaybackPatchHandle !=
AUDIO_PATCH_HANDLE_NONE)) {
clearPatchConnections(removedPatch);
}
+ // 2) if the new patch and old patch source or sink are devices from different
+ // hw modules, clear the audio HAL patches now because they will not be updated
+ // by call to create_audio_patch() below which will happen on a different HW module
+ if (halHandle != AUDIO_PATCH_HANDLE_NONE) {
+ audio_module_handle_t hwModule = AUDIO_MODULE_HANDLE_NONE;
+ if ((removedPatch->mAudioPatch.sources[0].type == AUDIO_PORT_TYPE_DEVICE) &&
+ ((patch->sources[0].type != AUDIO_PORT_TYPE_DEVICE) ||
+ (removedPatch->mAudioPatch.sources[0].ext.device.hw_module !=
+ patch->sources[0].ext.device.hw_module))) {
+ hwModule = removedPatch->mAudioPatch.sources[0].ext.device.hw_module;
+ } else if ((patch->num_sinks == 0) ||
+ ((removedPatch->mAudioPatch.sinks[0].type == AUDIO_PORT_TYPE_DEVICE) &&
+ ((patch->sinks[0].type != AUDIO_PORT_TYPE_DEVICE) ||
+ (removedPatch->mAudioPatch.sinks[0].ext.device.hw_module !=
+ patch->sinks[0].ext.device.hw_module)))) {
+ // Note on (patch->num_sinks == 0): this situation should not happen as
+ // these special patches are only created by the policy manager but just
+ // in case, systematically clear the HAL patch.
+ // Note that removedPatch->mAudioPatch.num_sinks cannot be 0 here because
+ // halHandle would be AUDIO_PATCH_HANDLE_NONE in this case.
+ hwModule = removedPatch->mAudioPatch.sinks[0].ext.device.hw_module;
+ }
+ if (hwModule != AUDIO_MODULE_HANDLE_NONE) {
+ ssize_t index = audioflinger->mAudioHwDevs.indexOfKey(hwModule);
+ if (index >= 0) {
+ audio_hw_device_t *hwDevice =
+ audioflinger->mAudioHwDevs.valueAt(index)->hwDevice();
+ hwDevice->release_audio_patch(hwDevice, halHandle);
+ }
+ }
+ }
mPatches.removeAt(index);
delete removedPatch;
break;
@@ -437,7 +471,7 @@
format,
frameCount,
NULL,
- IAudioFlinger::TRACK_DEFAULT);
+ AUDIO_INPUT_FLAG_NONE);
if (patch->mPatchRecord == 0) {
return NO_MEMORY;
}
@@ -457,7 +491,7 @@
format,
frameCount,
patch->mPatchRecord->buffer(),
- IAudioFlinger::TRACK_DEFAULT);
+ AUDIO_OUTPUT_FLAG_NONE);
if (patch->mPatchTrack == 0) {
return NO_MEMORY;
}
diff --git a/services/audioflinger/PatchPanel.h b/services/audioflinger/PatchPanel.h
index a56932c..d37c0d3 100644
--- a/services/audioflinger/PatchPanel.h
+++ b/services/audioflinger/PatchPanel.h
@@ -62,12 +62,24 @@
struct audio_patch mAudioPatch;
audio_patch_handle_t mHandle;
+ // handle for audio HAL patch handle present only when the audio HAL version is >= 3.0
audio_patch_handle_t mHalHandle;
+ // below members are used by a software audio patch connecting a source device from a
+ // given audio HW module to a sink device on an other audio HW module.
+ // playback thread created by createAudioPatch() and released by clearPatchConnections() if
+ // no existing playback thread can be used by the software patch
sp<PlaybackThread> mPlaybackThread;
+ // audio track created by createPatchConnections() and released by clearPatchConnections()
sp<PlaybackThread::PatchTrack> mPatchTrack;
+ // record thread created by createAudioPatch() and released by clearPatchConnections()
sp<RecordThread> mRecordThread;
+ // audio record created by createPatchConnections() and released by clearPatchConnections()
sp<RecordThread::PatchRecord> mPatchRecord;
+ // handle for audio patch connecting source device to record thread input.
+ // created by createPatchConnections() and released by clearPatchConnections()
audio_patch_handle_t mRecordPatchHandle;
+ // handle for audio patch connecting playback thread output to sink device
+ // created by createPatchConnections() and released by clearPatchConnections()
audio_patch_handle_t mPlaybackPatchHandle;
};
diff --git a/services/audioflinger/PlaybackTracks.h b/services/audioflinger/PlaybackTracks.h
index 270e27f..5601bde 100644
--- a/services/audioflinger/PlaybackTracks.h
+++ b/services/audioflinger/PlaybackTracks.h
@@ -33,7 +33,7 @@
const sp<IMemory>& sharedBuffer,
audio_session_t sessionId,
int uid,
- IAudioFlinger::track_flags_t flags,
+ audio_output_flags_t flags,
track_type type);
virtual ~Track();
virtual status_t initCheck() const;
@@ -55,8 +55,9 @@
audio_stream_type_t streamType() const {
return mStreamType;
}
- bool isOffloaded() const { return (mFlags & IAudioFlinger::TRACK_OFFLOAD) != 0; }
- bool isDirect() const { return (mFlags & IAudioFlinger::TRACK_DIRECT) != 0; }
+ bool isOffloaded() const
+ { return (mFlags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) != 0; }
+ bool isDirect() const { return (mFlags & AUDIO_OUTPUT_FLAG_DIRECT) != 0; }
status_t setParameters(const String8& keyValuePairs);
status_t attachAuxEffect(int EffectId);
void setAuxBuffer(int EffectId, int32_t *buffer);
@@ -72,6 +73,8 @@
virtual status_t setSyncEvent(const sp<SyncEvent>& event);
+ virtual bool isFastTrack() const { return (mFlags & AUDIO_OUTPUT_FLAG_FAST) != 0; }
+
protected:
// for numerous
friend class PlaybackThread;
@@ -166,7 +169,7 @@
AudioTrackServerProxy* mAudioTrackServerProxy;
bool mResumeToStopping; // track was paused in stopping state.
bool mFlushHwPending; // track requests for thread flush
-
+ audio_output_flags_t mFlags;
}; // end of Track
@@ -226,7 +229,7 @@
audio_format_t format,
size_t frameCount,
void *buffer,
- IAudioFlinger::track_flags_t flags);
+ audio_output_flags_t flags);
virtual ~PatchTrack();
virtual status_t start(AudioSystem::sync_event_t event =
diff --git a/services/audioflinger/RecordTracks.h b/services/audioflinger/RecordTracks.h
index 13396a6..123e033 100644
--- a/services/audioflinger/RecordTracks.h
+++ b/services/audioflinger/RecordTracks.h
@@ -31,7 +31,7 @@
void *buffer,
audio_session_t sessionId,
int uid,
- IAudioFlinger::track_flags_t flags,
+ audio_input_flags_t flags,
track_type type);
virtual ~RecordTrack();
virtual status_t initCheck() const;
@@ -58,6 +58,9 @@
int64_t sourceFramesRead,
uint32_t halSampleRate,
const ExtendedTimestamp ×tamp);
+
+ virtual bool isFastTrack() const { return (mFlags & AUDIO_INPUT_FLAG_FAST) != 0; }
+
private:
friend class AudioFlinger; // for mState
@@ -86,6 +89,7 @@
// used by the record thread to convert frames to proper destination format
RecordBufferConverter *mRecordBufferConverter;
+ audio_input_flags_t mFlags;
};
// playback track, used by PatchPanel
@@ -98,7 +102,7 @@
audio_format_t format,
size_t frameCount,
void *buffer,
- IAudioFlinger::track_flags_t flags);
+ audio_input_flags_t flags);
virtual ~PatchRecord();
// AudioBufferProvider interface
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index e028e0e..8a4fbb5 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -1256,6 +1256,144 @@
}
}
+// checkEffectCompatibility_l() must be called with ThreadBase::mLock held
+status_t AudioFlinger::RecordThread::checkEffectCompatibility_l(
+ const effect_descriptor_t *desc, audio_session_t sessionId)
+{
+ // No global effect sessions on record threads
+ if (sessionId == AUDIO_SESSION_OUTPUT_MIX || sessionId == AUDIO_SESSION_OUTPUT_STAGE) {
+ ALOGW("checkEffectCompatibility_l(): global effect %s on record thread %s",
+ desc->name, mThreadName);
+ return BAD_VALUE;
+ }
+ // only pre processing effects on record thread
+ if ((desc->flags & EFFECT_FLAG_TYPE_MASK) != EFFECT_FLAG_TYPE_PRE_PROC) {
+ ALOGW("checkEffectCompatibility_l(): non pre processing effect %s on record thread %s",
+ desc->name, mThreadName);
+ return BAD_VALUE;
+ }
+
+ // always allow effects without processing load or latency
+ if ((desc->flags & EFFECT_FLAG_NO_PROCESS_MASK) == EFFECT_FLAG_NO_PROCESS) {
+ return NO_ERROR;
+ }
+
+ audio_input_flags_t flags = mInput->flags;
+ if (hasFastCapture() || (flags & AUDIO_INPUT_FLAG_FAST)) {
+ if (flags & AUDIO_INPUT_FLAG_RAW) {
+ ALOGW("checkEffectCompatibility_l(): effect %s on record thread %s in raw mode",
+ desc->name, mThreadName);
+ return BAD_VALUE;
+ }
+ if ((desc->flags & EFFECT_FLAG_HW_ACC_TUNNEL) == 0) {
+ ALOGW("checkEffectCompatibility_l(): non HW effect %s on record thread %s in fast mode",
+ desc->name, mThreadName);
+ return BAD_VALUE;
+ }
+ }
+ return NO_ERROR;
+}
+
+// checkEffectCompatibility_l() must be called with ThreadBase::mLock held
+status_t AudioFlinger::PlaybackThread::checkEffectCompatibility_l(
+ const effect_descriptor_t *desc, audio_session_t sessionId)
+{
+ // no preprocessing on playback threads
+ if ((desc->flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_PRE_PROC) {
+ ALOGW("checkEffectCompatibility_l(): pre processing effect %s created on playback"
+ " thread %s", desc->name, mThreadName);
+ return BAD_VALUE;
+ }
+
+ switch (mType) {
+ case MIXER: {
+ // Reject any effect on mixer multichannel sinks.
+ // TODO: fix both format and multichannel issues with effects.
+ if (mChannelCount != FCC_2) {
+ ALOGW("checkEffectCompatibility_l(): effect %s for multichannel(%d) on MIXER"
+ " thread %s", desc->name, mChannelCount, mThreadName);
+ return BAD_VALUE;
+ }
+ audio_output_flags_t flags = mOutput->flags;
+ if (hasFastMixer() || (flags & AUDIO_OUTPUT_FLAG_FAST)) {
+ if (sessionId == AUDIO_SESSION_OUTPUT_MIX) {
+ // global effects are applied only to non fast tracks if they are SW
+ if ((desc->flags & EFFECT_FLAG_HW_ACC_TUNNEL) == 0) {
+ break;
+ }
+ } else if (sessionId == AUDIO_SESSION_OUTPUT_STAGE) {
+ // only post processing on output stage session
+ if ((desc->flags & EFFECT_FLAG_TYPE_MASK) != EFFECT_FLAG_TYPE_POST_PROC) {
+ ALOGW("checkEffectCompatibility_l(): non post processing effect %s not allowed"
+ " on output stage session", desc->name);
+ return BAD_VALUE;
+ }
+ } else {
+ // no restriction on effects applied on non fast tracks
+ if ((hasAudioSession_l(sessionId) & ThreadBase::FAST_SESSION) == 0) {
+ break;
+ }
+ }
+
+ // always allow effects without processing load or latency
+ if ((desc->flags & EFFECT_FLAG_NO_PROCESS_MASK) == EFFECT_FLAG_NO_PROCESS) {
+ break;
+ }
+ if (flags & AUDIO_OUTPUT_FLAG_RAW) {
+ ALOGW("checkEffectCompatibility_l(): effect %s on playback thread in raw mode",
+ desc->name);
+ return BAD_VALUE;
+ }
+ if ((desc->flags & EFFECT_FLAG_HW_ACC_TUNNEL) == 0) {
+ ALOGW("checkEffectCompatibility_l(): non HW effect %s on playback thread"
+ " in fast mode", desc->name);
+ return BAD_VALUE;
+ }
+ }
+ } break;
+ case OFFLOAD:
+ // nothing actionable on offload threads, if the effect:
+ // - is offloadable: the effect can be created
+ // - is NOT offloadable: the effect should still be created, but EffectHandle::enable()
+ // will take care of invalidating the tracks of the thread
+ break;
+ case DIRECT:
+ // Reject any effect on Direct output threads for now, since the format of
+ // mSinkBuffer is not guaranteed to be compatible with effect processing (PCM 16 stereo).
+ ALOGW("checkEffectCompatibility_l(): effect %s on DIRECT output thread %s",
+ desc->name, mThreadName);
+ return BAD_VALUE;
+ case DUPLICATING:
+ // Reject any effect on mixer multichannel sinks.
+ // TODO: fix both format and multichannel issues with effects.
+ if (mChannelCount != FCC_2) {
+ ALOGW("checkEffectCompatibility_l(): effect %s for multichannel(%d)"
+ " on DUPLICATING thread %s", desc->name, mChannelCount, mThreadName);
+ return BAD_VALUE;
+ }
+ if ((sessionId == AUDIO_SESSION_OUTPUT_STAGE) || (sessionId == AUDIO_SESSION_OUTPUT_MIX)) {
+ ALOGW("checkEffectCompatibility_l(): global effect %s on DUPLICATING"
+ " thread %s", desc->name, mThreadName);
+ return BAD_VALUE;
+ }
+ if ((desc->flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_POST_PROC) {
+ ALOGW("checkEffectCompatibility_l(): post processing effect %s on"
+ " DUPLICATING thread %s", desc->name, mThreadName);
+ return BAD_VALUE;
+ }
+ if ((desc->flags & EFFECT_FLAG_HW_ACC_TUNNEL) != 0) {
+ ALOGW("checkEffectCompatibility_l(): HW tunneled effect %s on"
+ " DUPLICATING thread %s", desc->name, mThreadName);
+ return BAD_VALUE;
+ }
+ break;
+ default:
+ LOG_ALWAYS_FATAL("checkEffectCompatibility_l(): wrong thread type %d", mType);
+ }
+
+ return NO_ERROR;
+}
+
// ThreadBase::createEffect_l() must be called with AudioFlinger::mLock held
sp<AudioFlinger::EffectHandle> AudioFlinger::ThreadBase::createEffect_l(
const sp<AudioFlinger::Client>& client,
@@ -1280,54 +1418,16 @@
goto Exit;
}
- // Reject any effect on Direct output threads for now, since the format of
- // mSinkBuffer is not guaranteed to be compatible with effect processing (PCM 16 stereo).
- if (mType == DIRECT) {
- ALOGW("createEffect_l() Cannot add effect %s on Direct output type thread %s",
- desc->name, mThreadName);
- lStatus = BAD_VALUE;
- goto Exit;
- }
-
- // Reject any effect on mixer or duplicating multichannel sinks.
- // TODO: fix both format and multichannel issues with effects.
- if ((mType == MIXER || mType == DUPLICATING) && mChannelCount != FCC_2) {
- ALOGW("createEffect_l() Cannot add effect %s for multichannel(%d) %s threads",
- desc->name, mChannelCount, mType == MIXER ? "MIXER" : "DUPLICATING");
- lStatus = BAD_VALUE;
- goto Exit;
- }
-
- // Allow global effects only on offloaded and mixer threads
- if (sessionId == AUDIO_SESSION_OUTPUT_MIX) {
- switch (mType) {
- case MIXER:
- case OFFLOAD:
- break;
- case DIRECT:
- case DUPLICATING:
- case RECORD:
- default:
- ALOGW("createEffect_l() Cannot add global effect %s on thread %s",
- desc->name, mThreadName);
- lStatus = BAD_VALUE;
- goto Exit;
- }
- }
-
- // Only Pre processor effects are allowed on input threads and only on input threads
- if ((mType == RECORD) != ((desc->flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_PRE_PROC)) {
- ALOGW("createEffect_l() effect %s (flags %08x) created on wrong thread type %d",
- desc->name, desc->flags, mType);
- lStatus = BAD_VALUE;
- goto Exit;
- }
-
ALOGV("createEffect_l() thread %p effect %s on session %d", this, desc->name, sessionId);
{ // scope for mLock
Mutex::Autolock _l(mLock);
+ lStatus = checkEffectCompatibility_l(desc, sessionId);
+ if (lStatus != NO_ERROR) {
+ goto Exit;
+ }
+
// check for existing effect chain with the requested audio session
chain = getEffectChain_l(sessionId);
if (chain == 0) {
@@ -1763,7 +1863,7 @@
size_t *pFrameCount,
const sp<IMemory>& sharedBuffer,
audio_session_t sessionId,
- IAudioFlinger::track_flags_t *flags,
+ audio_output_flags_t *flags,
pid_t tid,
int uid,
status_t *status)
@@ -1771,9 +1871,22 @@
size_t frameCount = *pFrameCount;
sp<Track> track;
status_t lStatus;
+ audio_output_flags_t outputFlags = mOutput->flags;
+
+ // special case for FAST flag considered OK if fast mixer is present
+ if (hasFastMixer()) {
+ outputFlags = (audio_output_flags_t)(outputFlags | AUDIO_OUTPUT_FLAG_FAST);
+ }
+
+ // Check if requested flags are compatible with output stream flags
+ if ((*flags & outputFlags) != *flags) {
+ ALOGW("createTrack_l(): mismatch between requested flags (%08x) and output flags (%08x)",
+ *flags, outputFlags);
+ *flags = (audio_output_flags_t)(*flags & outputFlags);
+ }
// client expresses a preference for FAST, but we get the final say
- if (*flags & IAudioFlinger::TRACK_FAST) {
+ if (*flags & AUDIO_OUTPUT_FLAG_FAST) {
if (
// PCM data
audio_is_linear_pcm(format) &&
@@ -1801,8 +1914,29 @@
}
frameCount = max(frameCount, mFrameCount * sFastTrackMultiplier); // incl framecount 0
}
- ALOGV("AUDIO_OUTPUT_FLAG_FAST accepted: frameCount=%zu mFrameCount=%zu",
- frameCount, mFrameCount);
+
+ // check compatibility with audio effects.
+ { // scope for mLock
+ Mutex::Autolock _l(mLock);
+ for (audio_session_t session : {
+ AUDIO_SESSION_OUTPUT_STAGE,
+ AUDIO_SESSION_OUTPUT_MIX,
+ sessionId,
+ }) {
+ sp<EffectChain> chain = getEffectChain_l(session);
+ if (chain.get() != nullptr) {
+ audio_output_flags_t old = *flags;
+ chain->checkOutputFlagCompatibility(flags);
+ if (old != *flags) {
+ ALOGV("AUDIO_OUTPUT_FLAGS denied by effect, session=%d old=%#x new=%#x",
+ (int)session, (int)old, (int)*flags);
+ }
+ }
+ }
+ }
+ ALOGV_IF((*flags & AUDIO_OUTPUT_FLAG_FAST) != 0,
+ "AUDIO_OUTPUT_FLAG_FAST accepted: frameCount=%zu mFrameCount=%zu",
+ frameCount, mFrameCount);
} else {
ALOGV("AUDIO_OUTPUT_FLAG_FAST denied: sharedBuffer=%p frameCount=%zu "
"mFrameCount=%zu format=%#x mFormat=%#x isLinear=%d channelMask=%#x "
@@ -1811,7 +1945,7 @@
sharedBuffer.get(), frameCount, mFrameCount, format, mFormat,
audio_is_linear_pcm(format),
channelMask, sampleRate, mSampleRate, hasFastMixer(), tid, mFastTrackAvailMask);
- *flags &= ~IAudioFlinger::TRACK_FAST;
+ *flags = (audio_output_flags_t)(*flags & ~AUDIO_OUTPUT_FLAG_FAST);
}
}
// For normal PCM streaming tracks, update minimum frame count.
@@ -1819,7 +1953,7 @@
// to be at least 2 x the normal mixer frame count and cover audio hardware latency.
// This is probably too conservative, but legacy application code may depend on it.
// If you change this calculation, also review the start threshold which is related.
- if (!(*flags & IAudioFlinger::TRACK_FAST)
+ if (!(*flags & AUDIO_OUTPUT_FLAG_FAST)
&& audio_has_proportional_frames(format) && sharedBuffer == 0) {
// this must match AudioTrack.cpp calculateMinFrameCount().
// TODO: Move to a common library
@@ -1927,7 +2061,7 @@
chain->incTrackCnt();
}
- if ((*flags & IAudioFlinger::TRACK_FAST) && (tid != -1)) {
+ if ((*flags & AUDIO_OUTPUT_FLAG_FAST) && (tid != -1)) {
pid_t callingPid = IPCThreadState::self()->getCallingPid();
// we don't have CAP_SYS_NICE, nor do we want to have it as it's too powerful,
// so ask activity manager to do this on our behalf
@@ -2171,6 +2305,12 @@
mCallbackThread->resetDraining();
}
+void AudioFlinger::PlaybackThread::errorCallback()
+{
+ ALOG_ASSERT(mCallbackThread != 0);
+ mCallbackThread->setAsyncError();
+}
+
void AudioFlinger::PlaybackThread::resetWriteBlocked(uint32_t sequence)
{
Mutex::Autolock _l(mLock);
@@ -2205,6 +2345,9 @@
case STREAM_CBK_EVENT_DRAIN_READY:
me->drainCallback();
break;
+ case STREAM_CBK_EVENT_ERROR:
+ me->errorCallback();
+ break;
default:
ALOGW("asyncCallback() unknown event %d", event);
break;
@@ -2295,6 +2438,7 @@
kUseFastMixer == FastMixer_Dynamic)) {
size_t minNormalFrameCount = (kMinNormalSinkBufferSizeMs * mSampleRate) / 1000;
size_t maxNormalFrameCount = (kMaxNormalSinkBufferSizeMs * mSampleRate) / 1000;
+
// round up minimum and round down maximum to nearest 16 frames to satisfy AudioMixer
minNormalFrameCount = (minNormalFrameCount + 15) & ~15;
maxNormalFrameCount = maxNormalFrameCount & ~15;
@@ -2311,18 +2455,7 @@
multiplier = (double) maxNormalFrameCount / (double) mFrameCount;
}
} else {
- // prefer an even multiplier, for compatibility with doubling of fast tracks due to HAL
- // SRC (it would be unusual for the normal sink buffer size to not be a multiple of fast
- // track, but we sometimes have to do this to satisfy the maximum frame count
- // constraint)
- // FIXME this rounding up should not be done if no HAL SRC
- uint32_t truncMult = (uint32_t) multiplier;
- if ((truncMult & 1)) {
- if ((truncMult + 1) * mFrameCount <= maxNormalFrameCount) {
- ++truncMult;
- }
- }
- multiplier = (double) truncMult;
+ multiplier = floor(multiplier);
}
}
mNormalFrameCount = multiplier * mFrameCount;
@@ -2407,9 +2540,9 @@
}
}
-uint32_t AudioFlinger::PlaybackThread::hasAudioSession(audio_session_t sessionId) const
+// hasAudioSession_l() must be called with ThreadBase::mLock held
+uint32_t AudioFlinger::PlaybackThread::hasAudioSession_l(audio_session_t sessionId) const
{
- Mutex::Autolock _l(mLock);
uint32_t result = 0;
if (getEffectChain_l(sessionId) != 0) {
result = EFFECT_SESSION;
@@ -2419,6 +2552,9 @@
sp<Track> track = mTracks[i];
if (sessionId == track->sessionId() && !track->isInvalid()) {
result |= TRACK_SESSION;
+ if (track->isFastTrack()) {
+ result |= FAST_SESSION;
+ }
break;
}
}
@@ -2920,7 +3056,7 @@
if (timestamp.mTimeNs[ExtendedTimestamp::LOCATION_KERNEL] >= 0) {
kernelLocationUpdate = true;
} else {
- ALOGV("getTimestamp error - no valid kernel position");
+ ALOGVV("getTimestamp error - no valid kernel position");
}
// copy over kernel info
@@ -2975,9 +3111,9 @@
if (!keepWakeLock()) {
releaseWakeLock_l();
released = true;
+ mWakeLockUids.clear();
+ mActiveTracksGeneration++;
}
- mWakeLockUids.clear();
- mActiveTracksGeneration++;
ALOGV("wait async completion");
mWaitWorkCV.wait(mLock);
ALOGV("async completion/wake");
@@ -3188,6 +3324,11 @@
// (1) mixer threads without a fast mixer (which has its own warm-up)
// (2) minimum buffer sized tracks (even if the track is full,
// the app won't fill fast enough to handle the sudden draw).
+ //
+ // Total time spent in last processing cycle equals time spent in
+ // 1. threadLoop_write, as well as time spent in
+ // 2. threadLoop_mix (significant for heavy mixing, especially
+ // on low tier processors)
// it's OK if deltaMs is an overestimate.
const int32_t deltaMs =
@@ -3201,6 +3342,9 @@
" ret(%zd) deltaMs(%d) requires sleep %d ms",
this, ret, deltaMs, throttleMs);
mThreadThrottleTimeMs += throttleMs;
+ // Throttle must be attributed to the previous mixer loop's write time
+ // to allow back-to-back throttling.
+ lastWriteFinished += throttleMs * 1000000;
} else {
uint32_t diff = mThreadThrottleTimeMs - mThreadThrottleEndMs;
if (diff > 0) {
@@ -3308,10 +3452,15 @@
status_t AudioFlinger::MixerThread::createAudioPatch_l(const struct audio_patch *patch,
audio_patch_handle_t *handle)
{
- AutoPark<FastMixer> park(mFastMixer);
-
- status_t status = PlaybackThread::createAudioPatch_l(patch, handle);
-
+ status_t status;
+ if (property_get_bool("af.patch_park", false /* default_value */)) {
+ // Park FastMixer to avoid potential DOS issues with writing to the HAL
+ // or if HAL does not properly lock against access.
+ AutoPark<FastMixer> park(mFastMixer);
+ status = PlaybackThread::createAudioPatch_l(patch, handle);
+ } else {
+ status = PlaybackThread::createAudioPatch_l(patch, handle);
+ }
return status;
}
@@ -3393,10 +3542,15 @@
status_t AudioFlinger::MixerThread::releaseAudioPatch_l(const audio_patch_handle_t handle)
{
- AutoPark<FastMixer> park(mFastMixer);
-
- status_t status = PlaybackThread::releaseAudioPatch_l(handle);
-
+ status_t status;
+ if (property_get_bool("af.patch_park", false /* default_value */)) {
+ // Park FastMixer to avoid potential DOS issues with writing to the HAL
+ // or if HAL does not properly lock against access.
+ AutoPark<FastMixer> park(mFastMixer);
+ status = PlaybackThread::releaseAudioPatch_l(handle);
+ } else {
+ status = PlaybackThread::releaseAudioPatch_l(handle);
+ }
return status;
}
@@ -3778,6 +3932,13 @@
broadcast_l();
}
+void AudioFlinger::PlaybackThread::onAsyncError()
+{
+ for (int i = AUDIO_STREAM_SYSTEM; i < (int)AUDIO_STREAM_CNT; i++) {
+ invalidateTracks((audio_stream_type_t)i);
+ }
+}
+
void AudioFlinger::MixerThread::threadLoop_mix()
{
// mix buffers...
@@ -4421,10 +4582,25 @@
return mixerStatus;
}
+// trackCountForUid_l() must be called with ThreadBase::mLock held
+uint32_t AudioFlinger::PlaybackThread::trackCountForUid_l(uid_t uid)
+{
+ uint32_t trackCount = 0;
+ for (size_t i = 0; i < mTracks.size() ; i++) {
+ if (mTracks[i]->uid() == (int)uid) {
+ trackCount++;
+ }
+ }
+ return trackCount;
+}
+
// getTrackName_l() must be called with ThreadBase::mLock held
int AudioFlinger::MixerThread::getTrackName_l(audio_channel_mask_t channelMask,
- audio_format_t format, audio_session_t sessionId)
+ audio_format_t format, audio_session_t sessionId, uid_t uid)
{
+ if (trackCountForUid_l(uid) > (PlaybackThread::kMaxTracksPerUid - 1)) {
+ return -1;
+ }
return mAudioMixer->getTrackName(channelMask, format, sessionId);
}
@@ -4529,7 +4705,7 @@
mAudioMixer = new AudioMixer(mNormalFrameCount, mSampleRate);
for (size_t i = 0; i < mTracks.size() ; i++) {
int name = getTrackName_l(mTracks[i]->mChannelMask,
- mTracks[i]->mFormat, mTracks[i]->mSessionId);
+ mTracks[i]->mFormat, mTracks[i]->mSessionId, mTracks[i]->uid());
if (name < 0) {
break;
}
@@ -4736,9 +4912,12 @@
}
} else if (track->isResumePending()) {
track->resumeAck();
- if (last && mHwPaused) {
- doHwResume = true;
- mHwPaused = false;
+ if (last) {
+ mLeftVolFloat = mRightVolFloat = -1.0;
+ if (mHwPaused) {
+ doHwResume = true;
+ mHwPaused = false;
+ }
}
}
@@ -4764,8 +4943,10 @@
if (track->mFillingUpStatus == Track::FS_FILLED) {
track->mFillingUpStatus = Track::FS_ACTIVE;
- // make sure processVolume_l() will apply new volume even if 0
- mLeftVolFloat = mRightVolFloat = -1.0;
+ if (last) {
+ // make sure processVolume_l() will apply new volume even if 0
+ mLeftVolFloat = mRightVolFloat = -1.0;
+ }
if (!mHwSupportsPause) {
track->resumeAck();
}
@@ -4969,8 +5150,11 @@
// getTrackName_l() must be called with ThreadBase::mLock held
int AudioFlinger::DirectOutputThread::getTrackName_l(audio_channel_mask_t channelMask __unused,
- audio_format_t format __unused, audio_session_t sessionId __unused)
+ audio_format_t format __unused, audio_session_t sessionId __unused, uid_t uid)
{
+ if (trackCountForUid_l(uid) > (PlaybackThread::kMaxTracksPerUid - 1)) {
+ return -1;
+ }
return 0;
}
@@ -5094,7 +5278,8 @@
: Thread(false /*canCallJava*/),
mPlaybackThread(playbackThread),
mWriteAckSequence(0),
- mDrainSequence(0)
+ mDrainSequence(0),
+ mAsyncError(false)
{
}
@@ -5112,11 +5297,13 @@
while (!exitPending()) {
uint32_t writeAckSequence;
uint32_t drainSequence;
+ bool asyncError;
{
Mutex::Autolock _l(mLock);
while (!((mWriteAckSequence & 1) ||
(mDrainSequence & 1) ||
+ mAsyncError ||
exitPending())) {
mWaitWorkCV.wait(mLock);
}
@@ -5130,6 +5317,8 @@
mWriteAckSequence &= ~1;
drainSequence = mDrainSequence;
mDrainSequence &= ~1;
+ asyncError = mAsyncError;
+ mAsyncError = false;
}
{
sp<AudioFlinger::PlaybackThread> playbackThread = mPlaybackThread.promote();
@@ -5140,6 +5329,9 @@
if (drainSequence & 1) {
playbackThread->resetDraining(drainSequence >> 1);
}
+ if (asyncError) {
+ playbackThread->onAsyncError();
+ }
}
}
}
@@ -5188,12 +5380,20 @@
}
}
+void AudioFlinger::AsyncCallbackThread::setAsyncError()
+{
+ Mutex::Autolock _l(mLock);
+ mAsyncError = true;
+ mWaitWorkCV.signal();
+}
+
// ----------------------------------------------------------------------------
AudioFlinger::OffloadThread::OffloadThread(const sp<AudioFlinger>& audioFlinger,
AudioStreamOut* output, audio_io_handle_t id, uint32_t device, bool systemReady)
: DirectOutputThread(audioFlinger, output, id, device, OFFLOAD, systemReady),
- mPausedWriteLength(0), mPausedBytesRemaining(0), mKeepWakeLock(true)
+ mPausedWriteLength(0), mPausedBytesRemaining(0), mKeepWakeLock(true),
+ mOffloadUnderrunPosition(~0LL)
{
//FIXME: mStandby should be set to true by ThreadBase constructor
mStandby = true;
@@ -5303,6 +5503,8 @@
// enable write to audio HAL
mSleepTimeUs = 0;
+ mLeftVolFloat = mRightVolFloat = -1.0;
+
// Do not handle new data in this iteration even if track->framesReady()
mixerStatus = MIXER_TRACKS_ENABLED;
}
@@ -5311,8 +5513,10 @@
ALOGVV("OffloadThread: track %d s=%08x [OK]", track->name(), cblk->mServer);
if (track->mFillingUpStatus == Track::FS_FILLED) {
track->mFillingUpStatus = Track::FS_ACTIVE;
- // make sure processVolume_l() will apply new volume even if 0
- mLeftVolFloat = mRightVolFloat = -1.0;
+ if (last) {
+ // make sure processVolume_l() will apply new volume even if 0
+ mLeftVolFloat = mRightVolFloat = -1.0;
+ }
}
if (last) {
@@ -5399,12 +5603,30 @@
// No buffers for this track. Give it a few chances to
// fill a buffer, then remove it from active list.
if (--(track->mRetryCount) <= 0) {
- ALOGV("OffloadThread: BUFFER TIMEOUT: remove(%d) from active list",
- track->name());
- tracksToRemove->add(track);
- // indicate to client process that the track was disabled because of underrun;
- // it will then automatically call start() when data is available
- track->disable();
+ bool running = false;
+ if (mOutput->stream->get_presentation_position != nullptr) {
+ uint64_t position = 0;
+ struct timespec unused;
+ // The running check restarts the retry counter at least once.
+ int ret = mOutput->stream->get_presentation_position(
+ mOutput->stream, &position, &unused);
+ if (ret == NO_ERROR && position != mOffloadUnderrunPosition) {
+ running = true;
+ mOffloadUnderrunPosition = position;
+ }
+ ALOGVV("underrun counter, running(%d): %lld vs %lld", running,
+ (long long)position, (long long)mOffloadUnderrunPosition);
+ }
+ if (running) { // still running, give us more time.
+ track->mRetryCount = kMaxTrackRetriesOffload;
+ } else {
+ ALOGV("OffloadThread: BUFFER TIMEOUT: remove(%d) from active list",
+ track->name());
+ tracksToRemove->add(track);
+ // indicate to client process that the track was disabled because of underrun;
+ // it will then automatically call start() when data is available
+ track->disable();
+ }
} else if (last){
mixerStatus = MIXER_TRACKS_ENABLED;
}
@@ -5461,6 +5683,7 @@
mPausedBytesRemaining = 0;
// reset bytes written count to reflect that DSP buffers are empty after flush.
mBytesWritten = 0;
+ mOffloadUnderrunPosition = ~0LL;
if (mUseAsyncWrite) {
// discard any pending drain or write ack by incrementing sequence
@@ -5582,12 +5805,15 @@
mChannelMask,
frameCount,
IPCThreadState::self()->getCallingUid());
- if (outputTrack->cblk() != NULL) {
- thread->setStreamVolume(AUDIO_STREAM_PATCH, 1.0f);
- mOutputTracks.add(outputTrack);
- ALOGV("addOutputTrack() track %p, on thread %p", outputTrack.get(), thread);
- updateWaitTime_l();
+ status_t status = outputTrack != 0 ? outputTrack->initCheck() : (status_t) NO_MEMORY;
+ if (status != NO_ERROR) {
+ ALOGE("addOutputTrack() initCheck failed %d", status);
+ return;
}
+ thread->setStreamVolume(AUDIO_STREAM_PATCH, 1.0f);
+ mOutputTracks.add(outputTrack);
+ ALOGV("addOutputTrack() track %p, on thread %p", outputTrack.get(), thread);
+ updateWaitTime_l();
}
void AudioFlinger::DuplicatingThread::removeOutputTrack(MixerThread *thread)
@@ -5857,14 +6083,6 @@
for (;;) {
Vector< sp<EffectChain> > effectChains;
- // sleep with mutex unlocked
- if (sleepUs > 0) {
- ATRACE_BEGIN("sleep");
- usleep(sleepUs);
- ATRACE_END();
- sleepUs = 0;
- }
-
// activeTracks accumulates a copy of a subset of mActiveTracks
Vector< sp<RecordTrack> > activeTracks;
@@ -5885,6 +6103,15 @@
break;
}
+ // sleep with mutex unlocked
+ if (sleepUs > 0) {
+ ATRACE_BEGIN("sleepC");
+ mWaitWorkCV.waitRelative(mLock, microseconds((nsecs_t)sleepUs));
+ ATRACE_END();
+ sleepUs = 0;
+ continue;
+ }
+
// if no active track(s), then standby and release wakelock
size_t size = mActiveTracks.size();
if (size == 0) {
@@ -5908,6 +6135,7 @@
}
bool doBroadcast = false;
+ bool allStopped = true;
for (size_t i = 0; i < size; ) {
activeTrack = mActiveTracks[i];
@@ -5936,15 +6164,18 @@
case TrackBase::STARTING_1:
sleepUs = 10000;
i++;
+ allStopped = false;
continue;
case TrackBase::STARTING_2:
doBroadcast = true;
mStandby = false;
activeTrack->mState = TrackBase::ACTIVE;
+ allStopped = false;
break;
case TrackBase::ACTIVE:
+ allStopped = false;
break;
case TrackBase::IDLE:
@@ -5964,6 +6195,10 @@
fastTrack = activeTrack;
}
}
+
+ if (allStopped) {
+ standbyIfNotAlreadyInStandby();
+ }
if (doBroadcast) {
mStartStopCond.broadcast();
}
@@ -6071,7 +6306,8 @@
mTimestamp.mTimeNs[ExtendedTimestamp::LOCATION_SERVER] = systemTime();
// Update server timestamp with kernel stats
- if (mInput->stream->get_capture_position != nullptr) {
+ if (mInput->stream->get_capture_position != nullptr
+ && mPipeSource.get() == nullptr /* don't obtain for FastCapture, could block */) {
int64_t position, time;
int ret = mInput->stream->get_capture_position(mInput->stream, &position, &time);
if (ret == NO_ERROR) {
@@ -6295,16 +6531,30 @@
audio_session_t sessionId,
size_t *notificationFrames,
int uid,
- IAudioFlinger::track_flags_t *flags,
+ audio_input_flags_t *flags,
pid_t tid,
status_t *status)
{
size_t frameCount = *pFrameCount;
sp<RecordTrack> track;
status_t lStatus;
+ audio_input_flags_t inputFlags = mInput->flags;
+
+ // special case for FAST flag considered OK if fast capture is present
+ if (hasFastCapture()) {
+ inputFlags = (audio_input_flags_t)(inputFlags | AUDIO_INPUT_FLAG_FAST);
+ }
+
+ // Check if requested flags are compatible with output stream flags
+ if ((*flags & inputFlags) != *flags) {
+ ALOGW("createRecordTrack_l(): mismatch between requested flags (%08x) and"
+ " input flags (%08x)",
+ *flags, inputFlags);
+ *flags = (audio_input_flags_t)(*flags & inputFlags);
+ }
// client expresses a preference for FAST, but we get the final say
- if (*flags & IAudioFlinger::TRACK_FAST) {
+ if (*flags & AUDIO_INPUT_FLAG_FAST) {
if (
// we formerly checked for a callback handler (non-0 tid),
// but that is no longer required for TRANSFER_OBTAIN mode
@@ -6324,8 +6574,21 @@
// there are sufficient fast track slots available
mFastTrackAvail
) {
- ALOGV("AUDIO_INPUT_FLAG_FAST accepted: frameCount=%zu mFrameCount=%zu",
- frameCount, mFrameCount);
+ // check compatibility with audio effects.
+ Mutex::Autolock _l(mLock);
+ // Do not accept FAST flag if the session has software effects
+ sp<EffectChain> chain = getEffectChain_l(sessionId);
+ if (chain != 0) {
+ audio_input_flags_t old = *flags;
+ chain->checkInputFlagCompatibility(flags);
+ if (old != *flags) {
+ ALOGV("AUDIO_INPUT_FLAGS denied by effect old=%#x new=%#x",
+ (int)old, (int)*flags);
+ }
+ }
+ ALOGV_IF((*flags & AUDIO_INPUT_FLAG_FAST) != 0,
+ "AUDIO_INPUT_FLAG_FAST accepted: frameCount=%zu mFrameCount=%zu",
+ frameCount, mFrameCount);
} else {
ALOGV("AUDIO_INPUT_FLAG_FAST denied: frameCount=%zu mFrameCount=%zu mPipeFramesP2=%zu "
"format=%#x isLinear=%d channelMask=%#x sampleRate=%u mSampleRate=%u "
@@ -6333,12 +6596,12 @@
frameCount, mFrameCount, mPipeFramesP2,
format, audio_is_linear_pcm(format), channelMask, sampleRate, mSampleRate,
hasFastCapture(), tid, mFastTrackAvail);
- *flags &= ~IAudioFlinger::TRACK_FAST;
+ *flags = (audio_input_flags_t)(*flags & ~AUDIO_INPUT_FLAG_FAST);
}
}
// compute track buffer size in frames, and suggest the notification frame count
- if (*flags & IAudioFlinger::TRACK_FAST) {
+ if (*flags & AUDIO_INPUT_FLAG_FAST) {
// fast track: frame count is exactly the pipe depth
frameCount = mPipeFramesP2;
// ignore requested notificationFrames, and always notify exactly once every HAL buffer
@@ -6394,7 +6657,7 @@
setEffectSuspended_l(FX_IID_AEC, suspend, sessionId);
setEffectSuspended_l(FX_IID_NS, suspend, sessionId);
- if ((*flags & IAudioFlinger::TRACK_FAST) && (tid != -1)) {
+ if ((*flags & AUDIO_INPUT_FLAG_FAST) && (tid != -1)) {
pid_t callingPid = IPCThreadState::self()->getCallingPid();
// we don't have CAP_SYS_NICE, nor do we want to have it as it's too powerful,
// so ask activity manager to do this on our behalf
@@ -6518,6 +6781,8 @@
}
// note that threadLoop may still be processing the track at this point [without lock]
recordTrack->mState = TrackBase::PAUSING;
+ // signal thread to stop
+ mWaitWorkCV.broadcast();
// do not wait for mStartStopCond if exiting
if (exitPending()) {
return true;
@@ -7215,9 +7480,9 @@
return mInput->stream->get_input_frames_lost(mInput->stream);
}
-uint32_t AudioFlinger::RecordThread::hasAudioSession(audio_session_t sessionId) const
+// hasAudioSession_l() must be called with ThreadBase::mLock held
+uint32_t AudioFlinger::RecordThread::hasAudioSession_l(audio_session_t sessionId) const
{
- Mutex::Autolock _l(mLock);
uint32_t result = 0;
if (getEffectChain_l(sessionId) != 0) {
result = EFFECT_SESSION;
@@ -7226,6 +7491,9 @@
for (size_t i = 0; i < mTracks.size(); ++i) {
if (sessionId == mTracks[i]->sessionId()) {
result |= TRACK_SESSION;
+ if (mTracks[i]->isFastTrack()) {
+ result |= FAST_SESSION;
+ }
break;
}
}
diff --git a/services/audioflinger/Threads.h b/services/audioflinger/Threads.h
index c56c74a..a55655f 100644
--- a/services/audioflinger/Threads.h
+++ b/services/audioflinger/Threads.h
@@ -301,8 +301,10 @@
enum effect_state {
EFFECT_SESSION = 0x1, // the audio session corresponds to at least one
// effect
- TRACK_SESSION = 0x2 // the audio session corresponds to at least one
+ TRACK_SESSION = 0x2, // the audio session corresponds to at least one
// track
+ FAST_SESSION = 0x4 // the audio session corresponds to at least one
+ // fast track
};
// get effect chain corresponding to session Id.
@@ -335,9 +337,16 @@
void removeEffect_l(const sp< EffectModule>& effect);
// detach all tracks connected to an auxiliary effect
virtual void detachAuxEffect_l(int effectId __unused) {}
- // returns either EFFECT_SESSION if effects on this audio session exist in one
- // chain, or TRACK_SESSION if tracks on this audio session exist, or both
- virtual uint32_t hasAudioSession(audio_session_t sessionId) const = 0;
+ // returns a combination of:
+ // - EFFECT_SESSION if effects on this audio session exist in one chain
+ // - TRACK_SESSION if tracks on this audio session exist
+ // - FAST_SESSION if fast tracks on this audio session exist
+ virtual uint32_t hasAudioSession_l(audio_session_t sessionId) const = 0;
+ uint32_t hasAudioSession(audio_session_t sessionId) const {
+ Mutex::Autolock _l(mLock);
+ return hasAudioSession_l(sessionId);
+ }
+
// the value returned by default implementation is not important as the
// strategy is only meaningful for PlaybackThread which implements this method
virtual uint32_t getStrategyForSession_l(audio_session_t sessionId __unused)
@@ -374,6 +383,10 @@
void systemReady();
+ // checkEffectCompatibility_l() must be called with ThreadBase::mLock held
+ virtual status_t checkEffectCompatibility_l(const effect_descriptor_t *desc,
+ audio_session_t sessionId) = 0;
+
mutable Mutex mLock;
protected:
@@ -493,6 +506,8 @@
static const int8_t kMaxTrackRetriesOffload = 20;
static const int8_t kMaxTrackStartupRetriesOffload = 100;
static const int8_t kMaxTrackStopRetriesOffload = 2;
+ // 14 tracks max per client allows for 2 misbehaving application leaving 4 available tracks.
+ static const uint32_t kMaxTracksPerUid = 14;
PlaybackThread(const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output,
audio_io_handle_t id, audio_devices_t device, type_t type, bool systemReady);
@@ -506,6 +521,9 @@
// RefBase
virtual void onFirstRef();
+ virtual status_t checkEffectCompatibility_l(const effect_descriptor_t *desc,
+ audio_session_t sessionId);
+
protected:
// Code snippets that were lifted up out of threadLoop()
virtual void threadLoop_mix() = 0;
@@ -527,6 +545,7 @@
void resetWriteBlocked(uint32_t sequence);
void drainCallback();
void resetDraining(uint32_t sequence);
+ void errorCallback();
static int asyncCallback(stream_callback_event_t event, void *param, void *cookie);
@@ -534,6 +553,7 @@
virtual bool waitingAsyncCallback_l();
virtual bool shouldStandby_l();
virtual void onAddNewTrack_l();
+ void onAsyncError(); // error reported by AsyncCallbackThread
// ThreadBase virtuals
virtual void preExit();
@@ -566,7 +586,7 @@
size_t *pFrameCount,
const sp<IMemory>& sharedBuffer,
audio_session_t sessionId,
- IAudioFlinger::track_flags_t *flags,
+ audio_output_flags_t *flags,
pid_t tid,
int uid,
status_t *status /*non-NULL*/);
@@ -605,7 +625,7 @@
virtual status_t addEffectChain_l(const sp<EffectChain>& chain);
virtual size_t removeEffectChain_l(const sp<EffectChain>& chain);
- virtual uint32_t hasAudioSession(audio_session_t sessionId) const;
+ virtual uint32_t hasAudioSession_l(audio_session_t sessionId) const;
virtual uint32_t getStrategyForSession_l(audio_session_t sessionId);
@@ -718,8 +738,8 @@
// Allocate a track name for a given channel mask.
// Returns name >= 0 if successful, -1 on failure.
- virtual int getTrackName_l(audio_channel_mask_t channelMask,
- audio_format_t format, audio_session_t sessionId) = 0;
+ virtual int getTrackName_l(audio_channel_mask_t channelMask, audio_format_t format,
+ audio_session_t sessionId, uid_t uid) = 0;
virtual void deleteTrackName_l(int name) = 0;
// Time to sleep between cycles when:
@@ -749,6 +769,8 @@
&& mHwSupportsPause
&& (mOutput->flags & AUDIO_OUTPUT_FLAG_HW_AV_SYNC); }
+ uint32_t trackCountForUid_l(uid_t uid);
+
private:
friend class AudioFlinger; // for numerous
@@ -871,8 +893,8 @@
protected:
virtual mixer_state prepareTracks_l(Vector< sp<Track> > *tracksToRemove);
- virtual int getTrackName_l(audio_channel_mask_t channelMask,
- audio_format_t format, audio_session_t sessionId);
+ virtual int getTrackName_l(audio_channel_mask_t channelMask, audio_format_t format,
+ audio_session_t sessionId, uid_t uid);
virtual void deleteTrackName_l(int name);
virtual uint32_t idleSleepTimeUs() const;
virtual uint32_t suspendSleepTimeUs() const;
@@ -951,8 +973,8 @@
virtual void flushHw_l();
protected:
- virtual int getTrackName_l(audio_channel_mask_t channelMask,
- audio_format_t format, audio_session_t sessionId);
+ virtual int getTrackName_l(audio_channel_mask_t channelMask, audio_format_t format,
+ audio_session_t sessionId, uid_t uid);
virtual void deleteTrackName_l(int name);
virtual uint32_t activeSleepTimeUs() const;
virtual uint32_t idleSleepTimeUs() const;
@@ -1003,12 +1025,16 @@
virtual bool waitingAsyncCallback_l();
virtual void invalidateTracks(audio_stream_type_t streamType);
- virtual bool keepWakeLock() const { return mKeepWakeLock; }
+ virtual bool keepWakeLock() const { return (mKeepWakeLock || (mDrainSequence & 1)); }
private:
size_t mPausedWriteLength; // length in bytes of write interrupted by pause
size_t mPausedBytesRemaining; // bytes still waiting in mixbuffer after resume
bool mKeepWakeLock; // keep wake lock while waiting for write callback
+ uint64_t mOffloadUnderrunPosition; // Current frame position for offloaded playback
+ // used and valid only during underrun. ~0 if
+ // no underrun has occurred during playback and
+ // is not reset on standby.
};
class AsyncCallbackThread : public Thread {
@@ -1029,6 +1055,7 @@
void resetWriteBlocked();
void setDraining(uint32_t sequence);
void resetDraining();
+ void setAsyncError();
private:
const wp<PlaybackThread> mPlaybackThread;
@@ -1042,6 +1069,7 @@
uint32_t mDrainSequence;
Condition mWaitWorkCV;
Mutex mLock;
+ bool mAsyncError;
};
class DuplicatingThread : public MixerThread {
@@ -1259,7 +1287,7 @@
audio_session_t sessionId,
size_t *notificationFrames,
int uid,
- IAudioFlinger::track_flags_t *flags,
+ audio_input_flags_t *flags,
pid_t tid,
status_t *status /*non-NULL*/);
@@ -1293,7 +1321,7 @@
virtual status_t addEffectChain_l(const sp<EffectChain>& chain);
virtual size_t removeEffectChain_l(const sp<EffectChain>& chain);
- virtual uint32_t hasAudioSession(audio_session_t sessionId) const;
+ virtual uint32_t hasAudioSession_l(audio_session_t sessionId) const;
// Return the set of unique session IDs across all tracks.
// The keys are the session IDs, and the associated values are meaningless.
@@ -1309,6 +1337,9 @@
bool hasFastCapture() const { return mFastCapture != 0; }
virtual void getAudioPortConfig(struct audio_port_config *config);
+ virtual status_t checkEffectCompatibility_l(const effect_descriptor_t *desc,
+ audio_session_t sessionId);
+
private:
// Enter standby if not already in standby, and set mStandby flag
void standbyIfNotAlreadyInStandby();
diff --git a/services/audioflinger/TrackBase.h b/services/audioflinger/TrackBase.h
index 67a5e58..6b97246 100644
--- a/services/audioflinger/TrackBase.h
+++ b/services/audioflinger/TrackBase.h
@@ -63,7 +63,6 @@
void *buffer,
audio_session_t sessionId,
int uid,
- IAudioFlinger::track_flags_t flags,
bool isOut,
alloc_type alloc = ALLOC_CBLK,
track_type type = TYPE_DEFAULT);
@@ -81,7 +80,7 @@
sp<IMemory> getBuffers() const { return mBufferMemory; }
void* buffer() const { return mBuffer; }
- bool isFastTrack() const { return (mFlags & IAudioFlinger::TRACK_FAST) != 0; }
+ virtual bool isFastTrack() const = 0;
bool isOutputTrack() const { return (mType == TYPE_OUTPUT); }
bool isPatchTrack() const { return (mType == TYPE_PATCH); }
bool isExternalTrack() const { return !isOutputTrack() && !isPatchTrack(); }
@@ -156,7 +155,6 @@
const audio_session_t mSessionId;
int mUid;
Vector < sp<SyncEvent> >mSyncEvents;
- const IAudioFlinger::track_flags_t mFlags;
const bool mIsOut;
ServerProxy* mServerProxy;
const int mId;
diff --git a/services/audioflinger/Tracks.cpp b/services/audioflinger/Tracks.cpp
index 364e339..ba6e6e5 100644
--- a/services/audioflinger/Tracks.cpp
+++ b/services/audioflinger/Tracks.cpp
@@ -73,7 +73,6 @@
void *buffer,
audio_session_t sessionId,
int clientUid,
- IAudioFlinger::track_flags_t flags,
bool isOut,
alloc_type alloc,
track_type type)
@@ -93,7 +92,6 @@
mChannelCount * audio_bytes_per_sample(format) : sizeof(int8_t)),
mFrameCount(frameCount),
mSessionId(sessionId),
- mFlags(flags),
mIsOut(isOut),
mServerProxy(NULL),
mId(android_atomic_inc(&nextTrackId)),
@@ -345,11 +343,11 @@
const sp<IMemory>& sharedBuffer,
audio_session_t sessionId,
int uid,
- IAudioFlinger::track_flags_t flags,
+ audio_output_flags_t flags,
track_type type)
: TrackBase(thread, client, sampleRate, format, channelMask, frameCount,
(sharedBuffer != 0) ? sharedBuffer->pointer() : buffer,
- sessionId, uid, flags, true /*isOut*/,
+ sessionId, uid, true /*isOut*/,
(type == TYPE_PATCH) ? ( buffer == NULL ? ALLOC_LOCAL : ALLOC_NONE) : ALLOC_CBLK,
type),
mFillingUpStatus(FS_INVALID),
@@ -368,7 +366,8 @@
mIsInvalid(false),
mAudioTrackServerProxy(NULL),
mResumeToStopping(false),
- mFlushHwPending(false)
+ mFlushHwPending(false),
+ mFlags(flags)
{
// client == 0 implies sharedBuffer == 0
ALOG_ASSERT(!(client == 0 && sharedBuffer != 0));
@@ -389,13 +388,13 @@
}
mServerProxy = mAudioTrackServerProxy;
- mName = thread->getTrackName_l(channelMask, format, sessionId);
+ mName = thread->getTrackName_l(channelMask, format, sessionId, uid);
if (mName < 0) {
ALOGE("no more track names available");
return;
}
// only allocate a fast track index if we were able to allocate a normal track name
- if (flags & IAudioFlinger::TRACK_FAST) {
+ if (flags & AUDIO_OUTPUT_FLAG_FAST) {
// FIXME: Not calling framesReadyIsCalledByMultipleThreads() exposes a potential
// race with setSyncEvent(). However, if we call it, we cannot properly start
// static fast tracks (SoundPool) immediately after stopping.
@@ -778,6 +777,13 @@
Mutex::Autolock _l(thread->mLock);
PlaybackThread *playbackThread = (PlaybackThread *)thread.get();
+ // Flush the ring buffer now if the track is not active in the PlaybackThread.
+ // Otherwise the flush would not be done until the track is resumed.
+ // Requires FastTrack removal be BLOCK_UNTIL_ACKED
+ if (playbackThread->mActiveTracks.indexOf(this) < 0) {
+ (void)mServerProxy->flushBufferIfNeeded();
+ }
+
if (isOffloaded()) {
// If offloaded we allow flush during any state except terminated
// and keep the track active to avoid problems if user is seeking
@@ -829,6 +835,10 @@
if (!isOffloaded() && !isDirect())
return;
+ // Clear the client ring buffer so that the app can prime the buffer while paused.
+ // Otherwise it might not get cleared until playback is resumed and obtainBuffer() is called.
+ mServerProxy->flushBufferIfNeeded();
+
mFlushHwPending = false;
}
@@ -1133,7 +1143,7 @@
int uid)
: Track(playbackThread, NULL, AUDIO_STREAM_PATCH,
sampleRate, format, channelMask, frameCount,
- NULL, 0, AUDIO_SESSION_NONE, uid, IAudioFlinger::TRACK_DEFAULT,
+ NULL, 0, AUDIO_SESSION_NONE, uid, AUDIO_OUTPUT_FLAG_NONE,
TYPE_OUTPUT),
mActive(false), mSourceThread(sourceThread), mClientProxy(NULL)
{
@@ -1329,7 +1339,7 @@
audio_format_t format,
size_t frameCount,
void *buffer,
- IAudioFlinger::track_flags_t flags)
+ audio_output_flags_t flags)
: Track(playbackThread, NULL, streamType,
sampleRate, format, channelMask, frameCount,
buffer, 0, AUDIO_SESSION_NONE, getuid(), flags, TYPE_PATCH),
@@ -1468,19 +1478,19 @@
void *buffer,
audio_session_t sessionId,
int uid,
- IAudioFlinger::track_flags_t flags,
+ audio_input_flags_t flags,
track_type type)
: TrackBase(thread, client, sampleRate, format,
- channelMask, frameCount, buffer, sessionId, uid,
- flags, false /*isOut*/,
+ channelMask, frameCount, buffer, sessionId, uid, false /*isOut*/,
(type == TYPE_DEFAULT) ?
- ((flags & IAudioFlinger::TRACK_FAST) ? ALLOC_PIPE : ALLOC_CBLK) :
+ ((flags & AUDIO_INPUT_FLAG_FAST) ? ALLOC_PIPE : ALLOC_CBLK) :
((buffer == NULL) ? ALLOC_LOCAL : ALLOC_NONE),
type),
mOverflow(false),
mFramesToDrop(0),
mResamplerBufferProvider(NULL), // initialize in case of early constructor exit
- mRecordBufferConverter(NULL)
+ mRecordBufferConverter(NULL),
+ mFlags(flags)
{
if (mCblk == NULL) {
return;
@@ -1505,7 +1515,7 @@
mResamplerBufferProvider = new ResamplerBufferProvider(this);
- if (flags & IAudioFlinger::TRACK_FAST) {
+ if (flags & AUDIO_INPUT_FLAG_FAST) {
ALOG_ASSERT(thread->mFastTrackAvail);
thread->mFastTrackAvail = false;
}
@@ -1664,7 +1674,7 @@
audio_format_t format,
size_t frameCount,
void *buffer,
- IAudioFlinger::track_flags_t flags)
+ audio_input_flags_t flags)
: RecordTrack(recordThread, NULL, sampleRate, format, channelMask, frameCount,
buffer, AUDIO_SESSION_NONE, getuid(), flags, TYPE_PATCH),
mProxy(new ClientProxy(mCblk, mBuffer, frameCount, mFrameSize, false, true))
diff --git a/services/audiopolicy/Android.mk b/services/audiopolicy/Android.mk
index 407a264..69d22c0 100644
--- a/services/audiopolicy/Android.mk
+++ b/services/audiopolicy/Android.mk
@@ -49,6 +49,7 @@
LOCAL_MODULE:= libaudiopolicyservice
LOCAL_CFLAGS += -fvisibility=hidden
+LOCAL_CFLAGS += -Wall -Werror
include $(BUILD_SHARED_LIBRARY)
@@ -101,6 +102,8 @@
LOCAL_CFLAGS += -DUSE_XML_AUDIO_POLICY_CONF
endif #ifeq ($(USE_XML_AUDIO_POLICY_CONF), 1)
+LOCAL_CFLAGS += -Wall -Werror
+
LOCAL_MULTILIB := $(AUDIOSERVER_MULTILIB)
LOCAL_MODULE:= libaudiopolicymanagerdefault
@@ -124,6 +127,8 @@
$(TOPDIR)frameworks/av/services/audiopolicy/common/include \
$(TOPDIR)frameworks/av/services/audiopolicy/engine/interface
+LOCAL_CFLAGS := -Wall -Werror
+
LOCAL_MULTILIB := $(AUDIOSERVER_MULTILIB)
LOCAL_MODULE:= libaudiopolicymanager
diff --git a/services/audiopolicy/common/managerdefinitions/Android.mk b/services/audiopolicy/common/managerdefinitions/Android.mk
index 8ca36ea..02118c4 100644
--- a/services/audiopolicy/common/managerdefinitions/Android.mk
+++ b/services/audiopolicy/common/managerdefinitions/Android.mk
@@ -63,6 +63,8 @@
LOCAL_MULTILIB := $(AUDIOSERVER_MULTILIB)
+LOCAL_CFLAGS := -Wall -Werror
+
LOCAL_MODULE := libaudiopolicycomponents
include $(BUILD_STATIC_LIBRARY)
diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioInputDescriptor.h b/services/audiopolicy/common/managerdefinitions/include/AudioInputDescriptor.h
index ccea023..e689320 100644
--- a/services/audiopolicy/common/managerdefinitions/include/AudioInputDescriptor.h
+++ b/services/audiopolicy/common/managerdefinitions/include/AudioInputDescriptor.h
@@ -93,7 +93,9 @@
sp<AudioInputDescriptor> getInputFromId(audio_port_handle_t id) const;
- uint32_t activeInputsCount() const;
+ // count active capture sessions using one of the specified devices.
+ // ignore devices if AUDIO_DEVICE_IN_DEFAULT is passed
+ uint32_t activeInputsCountOnDevices(audio_devices_t devices = AUDIO_DEVICE_IN_DEFAULT) const;
/**
* return io handle of active input or 0 if no input is active
diff --git a/services/audiopolicy/common/managerdefinitions/include/TypeConverter.h b/services/audiopolicy/common/managerdefinitions/include/TypeConverter.h
index b828f81..1612714 100644
--- a/services/audiopolicy/common/managerdefinitions/include/TypeConverter.h
+++ b/services/audiopolicy/common/managerdefinitions/include/TypeConverter.h
@@ -131,6 +131,7 @@
typedef TypeConverter<StreamTraits> StreamTypeConverter;
typedef TypeConverter<DeviceCategoryTraits> DeviceCategoryConverter;
+inline
static SampleRateTraits::Collection samplingRatesFromString(const std::string &samplingRates,
const char *del = "|")
{
@@ -139,6 +140,7 @@
return samplingRateCollection;
}
+inline
static FormatTraits::Collection formatsFromString(const std::string &formats, const char *del = "|")
{
FormatTraits::Collection formatCollection;
@@ -146,6 +148,7 @@
return formatCollection;
}
+inline
static audio_format_t formatFromString(const std::string &literalFormat)
{
audio_format_t format;
@@ -156,6 +159,7 @@
return format;
}
+inline
static audio_channel_mask_t channelMaskFromString(const std::string &literalChannels)
{
audio_channel_mask_t channels;
@@ -166,6 +170,7 @@
return channels;
}
+inline
static ChannelTraits::Collection channelMasksFromString(const std::string &channels,
const char *del = "|")
{
@@ -176,6 +181,7 @@
return channelMaskCollection;
}
+inline
static InputChannelTraits::Collection inputChannelMasksFromString(const std::string &inChannels,
const char *del = "|")
{
@@ -185,6 +191,7 @@
return inputChannelMaskCollection;
}
+inline
static OutputChannelTraits::Collection outputChannelMasksFromString(const std::string &outChannels,
const char *del = "|")
{
diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioInputDescriptor.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioInputDescriptor.cpp
index 6dacaa4..c7d2ee4 100644
--- a/services/audiopolicy/common/managerdefinitions/src/AudioInputDescriptor.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/AudioInputDescriptor.cpp
@@ -222,12 +222,14 @@
return inputDesc;
}
-uint32_t AudioInputCollection::activeInputsCount() const
+uint32_t AudioInputCollection::activeInputsCountOnDevices(audio_devices_t devices) const
{
uint32_t count = 0;
for (size_t i = 0; i < size(); i++) {
const sp<AudioInputDescriptor> inputDescriptor = valueAt(i);
- if (inputDescriptor->isActive()) {
+ if (inputDescriptor->isActive() &&
+ ((devices == AUDIO_DEVICE_IN_DEFAULT) ||
+ ((inputDescriptor->mDevice & devices & ~AUDIO_DEVICE_BIT_IN) != 0))) {
count++;
}
}
diff --git a/services/audiopolicy/common/managerdefinitions/src/DeviceDescriptor.cpp b/services/audiopolicy/common/managerdefinitions/src/DeviceDescriptor.cpp
index b687f3e..ba2b9e3 100644
--- a/services/audiopolicy/common/managerdefinitions/src/DeviceDescriptor.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/DeviceDescriptor.cpp
@@ -104,7 +104,6 @@
ssize_t DeviceVector::remove(const sp<DeviceDescriptor>& item)
{
- size_t i;
ssize_t ret = indexOf(item);
if (ret < 0) {
diff --git a/services/audiopolicy/common/managerdefinitions/src/TypeConverter.cpp b/services/audiopolicy/common/managerdefinitions/src/TypeConverter.cpp
index f639551..48bfd79 100644
--- a/services/audiopolicy/common/managerdefinitions/src/TypeConverter.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/TypeConverter.cpp
@@ -142,6 +142,7 @@
MAKE_STRING_FROM_ENUM(AUDIO_FORMAT_DTS),
MAKE_STRING_FROM_ENUM(AUDIO_FORMAT_DTS_HD),
MAKE_STRING_FROM_ENUM(AUDIO_FORMAT_IEC61937),
+ MAKE_STRING_FROM_ENUM(AUDIO_FORMAT_DOLBY_TRUEHD),
};
template<>
const size_t FormatConverter::mSize = sizeof(FormatConverter::mTable) /
diff --git a/services/audiopolicy/common/managerdefinitions/src/VolumeCurve.cpp b/services/audiopolicy/common/managerdefinitions/src/VolumeCurve.cpp
index ab2b51f..14caf7c 100644
--- a/services/audiopolicy/common/managerdefinitions/src/VolumeCurve.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/VolumeCurve.cpp
@@ -34,7 +34,8 @@
// Where would this volume index been inserted in the curve point
size_t indexInUiPosition = mCurvePoints.orderOf(CurvePoint(volIdx, 0));
if (indexInUiPosition >= nbCurvePoints) {
- return 0.0f; // out of bounds
+ //use last point of table
+ return mCurvePoints[nbCurvePoints - 1].mAttenuationInMb / 100.0f;
}
if (indexInUiPosition == 0) {
if (indexInUiPosition != mCurvePoints[0].mIndex) {
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
index 696ac5e..a8bdf86 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
@@ -49,6 +49,9 @@
namespace android {
+//FIXME: workaround for truncated touch sounds
+// to be removed when the problem is handled by system UI
+#define TOUCH_SOUND_FIXED_DELAY_MS 100
// ----------------------------------------------------------------------------
// AudioPolicyInterface implementation
// ----------------------------------------------------------------------------
@@ -281,6 +284,9 @@
}
closeAllInputs();
+ // As the input device list can impact the output device selection, update
+ // getDeviceForStrategy() cache
+ updateDevicesAndOutputs();
if (mEngine->getPhoneState() == AUDIO_MODE_IN_CALL && hasPrimaryOutput()) {
audio_devices_t newDevice = getNewOutputDevice(mPrimaryOutput, false /*fromCache*/);
@@ -327,15 +333,16 @@
AUDIO_POLICY_DEVICE_STATE_AVAILABLE : AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE;
}
-void AudioPolicyManager::updateCallRouting(audio_devices_t rxDevice, int delayMs)
+uint32_t AudioPolicyManager::updateCallRouting(audio_devices_t rxDevice, uint32_t delayMs)
{
bool createTxPatch = false;
status_t status;
audio_patch_handle_t afPatchHandle;
DeviceVector deviceList;
+ uint32_t muteWaitMs = 0;
if(!hasPrimaryOutput()) {
- return;
+ return muteWaitMs;
}
audio_devices_t txDevice = getDeviceAndMixForInputSource(AUDIO_SOURCE_VOICE_COMMUNICATION);
ALOGV("updateCallRouting device rxDevice %08x txDevice %08x", rxDevice, txDevice);
@@ -355,7 +362,7 @@
// via setOutputDevice() on primary output.
// Otherwise, create two audio patches for TX and RX path.
if (availablePrimaryOutputDevices() & rxDevice) {
- setOutputDevice(mPrimaryOutput, rxDevice, true, delayMs);
+ muteWaitMs = setOutputDevice(mPrimaryOutput, rxDevice, true, delayMs);
// If the TX device is also on the primary HW module, setOutputDevice() will take care
// of it due to legacy implementation. If not, create a patch.
if ((availablePrimaryInputDevices() & txDevice & ~AUDIO_DEVICE_BIT_IN)
@@ -395,7 +402,7 @@
}
afPatchHandle = AUDIO_PATCH_HANDLE_NONE;
- status = mpClientInterface->createAudioPatch(&patch, &afPatchHandle, 0);
+ status = mpClientInterface->createAudioPatch(&patch, &afPatchHandle, delayMs);
ALOGW_IF(status != NO_ERROR, "updateCallRouting() error %d creating RX audio patch",
status);
if (status == NO_ERROR) {
@@ -454,7 +461,7 @@
}
afPatchHandle = AUDIO_PATCH_HANDLE_NONE;
- status = mpClientInterface->createAudioPatch(&patch, &afPatchHandle, 0);
+ status = mpClientInterface->createAudioPatch(&patch, &afPatchHandle, delayMs);
ALOGW_IF(status != NO_ERROR, "setPhoneState() error %d creating TX audio patch",
status);
if (status == NO_ERROR) {
@@ -463,6 +470,8 @@
mCallTxPatch->mUid = mUidCached;
}
}
+
+ return muteWaitMs;
}
void AudioPolicyManager::setPhoneState(audio_mode_t state)
@@ -595,18 +604,26 @@
checkOutputForAllStrategies();
updateDevicesAndOutputs();
+ //FIXME: workaround for truncated touch sounds
+ // to be removed when the problem is handled by system UI
+ uint32_t delayMs = 0;
+ uint32_t waitMs = 0;
+ if (usage == AUDIO_POLICY_FORCE_FOR_COMMUNICATION) {
+ delayMs = TOUCH_SOUND_FIXED_DELAY_MS;
+ }
if (mEngine->getPhoneState() == AUDIO_MODE_IN_CALL && hasPrimaryOutput()) {
audio_devices_t newDevice = getNewOutputDevice(mPrimaryOutput, true /*fromCache*/);
- updateCallRouting(newDevice);
+ waitMs = updateCallRouting(newDevice, delayMs);
}
for (size_t i = 0; i < mOutputs.size(); i++) {
sp<SwAudioOutputDescriptor> outputDesc = mOutputs.valueAt(i);
audio_devices_t newDevice = getNewOutputDevice(outputDesc, true /*fromCache*/);
if ((mEngine->getPhoneState() != AUDIO_MODE_IN_CALL) || (outputDesc != mPrimaryOutput)) {
- setOutputDevice(outputDesc, newDevice, (newDevice != AUDIO_DEVICE_NONE));
+ waitMs = setOutputDevice(outputDesc, newDevice, (newDevice != AUDIO_DEVICE_NONE),
+ delayMs);
}
if (forceVolumeReeval && (newDevice != AUDIO_DEVICE_NONE)) {
- applyStreamVolumes(outputDesc, newDevice, 0, true);
+ applyStreamVolumes(outputDesc, newDevice, waitMs, true);
}
}
@@ -786,7 +803,6 @@
const audio_offload_info_t *offloadInfo)
{
audio_io_handle_t output = AUDIO_IO_HANDLE_NONE;
- uint32_t latency = 0;
status_t status;
#ifdef AUDIO_POLICY_TEST
@@ -1165,8 +1181,10 @@
beaconMuteLatency = handleEventForBeacon(STARTING_OUTPUT);
}
+ // force device change if the output is inactive and no audio patch is already present.
// check active before incrementing usage count
- bool force = !outputDesc->isActive();
+ bool force = !outputDesc->isActive() &&
+ (outputDesc->getPatchHandle() == AUDIO_PATCH_HANDLE_NONE);
// increment usage count for this stream on the requested output:
// NOTE that the usage count is the same for duplicated output and hardware output which is
@@ -1186,12 +1204,17 @@
for (size_t i = 0; i < mOutputs.size(); i++) {
sp<AudioOutputDescriptor> desc = mOutputs.valueAt(i);
if (desc != outputDesc) {
- // force a device change if any other output is managed by the same hw
- // module and has a current device selection that differs from selected device.
+ // force a device change if any other output is:
+ // - managed by the same hw module
+ // - has a current device selection that differs from selected device.
+ // - supports currently selected device
+ // - has an active audio patch
// In this case, the audio HAL must receive the new device selection so that it can
// change the device currently selected by the other active output.
if (outputDesc->sharesHwModuleWith(desc) &&
- desc->device() != device) {
+ desc->device() != device &&
+ desc->supportedDevices() & device &&
+ desc->getPatchHandle() != AUDIO_PATCH_HANDLE_NONE) {
force = true;
}
// wait for audio on other active outputs to be presented when starting
@@ -1224,7 +1247,12 @@
if (strategy == STRATEGY_SONIFICATION) {
mpClientInterface->invalidateStream(AUDIO_STREAM_ACCESSIBILITY);
}
+
+ if (waitMs > muteWaitMs) {
+ *delayMs = waitMs - muteWaitMs;
+ }
}
+
return NO_ERROR;
}
@@ -1298,17 +1326,23 @@
// force restoring the device selection on other active outputs if it differs from the
// one being selected for this output
+ uint32_t delayMs = outputDesc->latency()*2;
for (size_t i = 0; i < mOutputs.size(); i++) {
- audio_io_handle_t curOutput = mOutputs.keyAt(i);
sp<AudioOutputDescriptor> desc = mOutputs.valueAt(i);
if (desc != outputDesc &&
desc->isActive() &&
outputDesc->sharesHwModuleWith(desc) &&
(newDevice != desc->device())) {
+ audio_devices_t newDevice2 = getNewOutputDevice(desc, false /*fromCache*/);
+ bool force = desc->device() != newDevice2;
setOutputDevice(desc,
- getNewOutputDevice(desc, false /*fromCache*/),
- true,
- outputDesc->latency()*2);
+ newDevice2,
+ force,
+ delayMs);
+ // re-apply device specific volume if not done by setOutputDevice()
+ if (!force) {
+ applyStreamVolumes(desc, newDevice2, delayMs);
+ }
}
}
// update the outputs if stopping one with a stream that can affect notification routing
@@ -1499,6 +1533,8 @@
profileFlags);
if (profile != 0) {
break; // success
+ } else if (profileFlags & AUDIO_INPUT_FLAG_RAW) {
+ profileFlags = (audio_input_flags_t) (profileFlags & ~AUDIO_INPUT_FLAG_RAW); // retry
} else if (profileFlags != AUDIO_INPUT_FLAG_NONE) {
profileFlags = AUDIO_INPUT_FLAG_NONE; // retry
} else { // fail
@@ -1663,10 +1699,15 @@
MIX_STATE_MIXING);
}
- if (mInputs.activeInputsCount() == 0) {
+ // indicate active capture to sound trigger service if starting capture from a mic on
+ // primary HW module
+ audio_devices_t device = getNewInputDevice(input);
+ audio_devices_t primaryInputDevices = availablePrimaryInputDevices();
+ if (((device & primaryInputDevices & ~AUDIO_DEVICE_BIT_IN) != 0) &&
+ mInputs.activeInputsCountOnDevices(primaryInputDevices) == 0) {
SoundTrigger::setCaptureState(true);
}
- setInputDevice(input, getNewInputDevice(input), true /* force */);
+ setInputDevice(input, device, true /* force */);
// automatically enable the remote submix output when input is started if not
// used by a policy mix of type MIX_TYPE_RECORDERS
@@ -1743,9 +1784,14 @@
}
}
+ audio_devices_t device = inputDesc->mDevice;
resetInputDevice(input);
- if (mInputs.activeInputsCount() == 0) {
+ // indicate inactive capture to sound trigger service if stopping capture from a mic on
+ // primary HW module
+ audio_devices_t primaryInputDevices = availablePrimaryInputDevices();
+ if (((device & primaryInputDevices & ~AUDIO_DEVICE_BIT_IN) != 0) &&
+ mInputs.activeInputsCountOnDevices(primaryInputDevices) == 0) {
SoundTrigger::setCaptureState(false);
}
inputDesc->clearPreemptedSessions();
@@ -1804,7 +1850,7 @@
ssize_t patch_index = mAudioPatches.indexOfKey(inputDesc->getPatchHandle());
if (patch_index >= 0) {
sp<AudioPatch> patchDesc = mAudioPatches.valueAt(patch_index);
- status_t status = mpClientInterface->releaseAudioPatch(patchDesc->mAfPatchHandle, 0);
+ (void) /*status_t status*/ mpClientInterface->releaseAudioPatch(patchDesc->mAfPatchHandle, 0);
mAudioPatches.removeItemsAt(patch_index);
patchRemoved = true;
}
@@ -1883,21 +1929,27 @@
continue;
}
routing_strategy curStrategy = getStrategy((audio_stream_type_t)curStream);
- audio_devices_t curStreamDevice = getDeviceForStrategy(curStrategy, true /*fromCache*/);
- if ((curStreamDevice & device) == 0) {
+ audio_devices_t curStreamDevice = getDeviceForStrategy(curStrategy, false /*fromCache*/);
+ if ((device != AUDIO_DEVICE_OUT_DEFAULT_FOR_VOLUME) &&
+ ((curStreamDevice & device) == 0)) {
continue;
}
- bool applyDefault = false;
+ bool applyVolume;
if (device != AUDIO_DEVICE_OUT_DEFAULT_FOR_VOLUME) {
curStreamDevice |= device;
- } else if (!mVolumeCurves->hasVolumeIndexForDevice(
- stream, Volume::getDeviceForVolume(curStreamDevice))) {
- applyDefault = true;
+ applyVolume = (curDevice & curStreamDevice) != 0;
+ } else {
+ applyVolume = !mVolumeCurves->hasVolumeIndexForDevice(
+ stream, Volume::getDeviceForVolume(curStreamDevice));
}
- if (applyDefault || ((curDevice & curStreamDevice) != 0)) {
+ if (applyVolume) {
+ //FIXME: workaround for truncated touch sounds
+ // delayed volume change for system stream to be removed when the problem is
+ // handled by system UI
status_t volStatus =
- checkAndSetVolume((audio_stream_type_t)curStream, index, desc, curDevice);
+ checkAndSetVolume((audio_stream_type_t)curStream, index, desc, curDevice,
+ (stream == AUDIO_STREAM_SYSTEM) ? TOUCH_SOUND_FIXED_DELAY_MS : 0);
if (volStatus != NO_ERROR) {
status = volStatus;
}
@@ -2717,7 +2769,6 @@
true,
NULL);
} else if (patch->sinks[0].type == AUDIO_PORT_TYPE_DEVICE) {
- audio_patch_handle_t afPatchHandle = patchDesc->mAfPatchHandle;
status_t status = mpClientInterface->releaseAudioPatch(patchDesc->mAfPatchHandle, 0);
ALOGV("releaseAudioPatch() patch panel returned %d patchHandle %d",
status, patchDesc->mAfPatchHandle);
@@ -3196,6 +3247,7 @@
}
mEngine->setObserver(this);
status_t status = mEngine->initCheck();
+ (void) status;
ALOG_ASSERT(status == NO_ERROR, "Policy engine not initialized(err=%d)", status);
// mAvailableOutputDevices and mAvailableInputDevices now contain all attached devices
@@ -4075,7 +4127,7 @@
ssize_t index = mAudioPatches.indexOfKey(outputDesc->getPatchHandle());
if (index >= 0) {
sp<AudioPatch> patchDesc = mAudioPatches.valueAt(index);
- status_t status = mpClientInterface->releaseAudioPatch(patchDesc->mAfPatchHandle, 0);
+ (void) /*status_t status*/ mpClientInterface->releaseAudioPatch(patchDesc->mAfPatchHandle, 0);
mAudioPatches.removeItemsAt(index);
mpClientInterface->onAudioPatchListUpdate();
}
@@ -4104,7 +4156,7 @@
ssize_t index = mAudioPatches.indexOfKey(inputDesc->getPatchHandle());
if (index >= 0) {
sp<AudioPatch> patchDesc = mAudioPatches.valueAt(index);
- status_t status = mpClientInterface->releaseAudioPatch(patchDesc->mAfPatchHandle, 0);
+ (void) /*status_t status*/ mpClientInterface->releaseAudioPatch(patchDesc->mAfPatchHandle, 0);
mAudioPatches.removeItemsAt(index);
mpClientInterface->onAudioPatchListUpdate();
}
@@ -4384,7 +4436,7 @@
}
routing_strategy curStrategy = getStrategy((audio_stream_type_t)curStream);
audio_devices_t curDevices =
- getDeviceForStrategy((routing_strategy)curStrategy, true /*fromCache*/);
+ getDeviceForStrategy((routing_strategy)curStrategy, false /*fromCache*/);
SortedVector<audio_io_handle_t> outputs = getOutputsForDevice(curDevices, mOutputs);
for (size_t i = 0; i < outputs.size(); i++) {
sp<AudioOutputDescriptor> outputDesc = mOutputs.valueFor(outputs[i]);
@@ -4584,15 +4636,20 @@
// temporary mute output if device selection changes to avoid volume bursts due to
// different per device volumes
if (outputDesc->isActive() && (device != prevDevice)) {
- if (muteWaitMs < outputDesc->latency() * 2) {
- muteWaitMs = outputDesc->latency() * 2;
+ uint32_t tempMuteWaitMs = outputDesc->latency() * 2;
+ // temporary mute duration is conservatively set to 4 times the reported latency
+ uint32_t tempMuteDurationMs = outputDesc->latency() * 4;
+ if (muteWaitMs < tempMuteWaitMs) {
+ muteWaitMs = tempMuteWaitMs;
}
+
for (size_t i = 0; i < NUM_STRATEGIES; i++) {
if (isStrategyActive(outputDesc, (routing_strategy)i)) {
- setStrategyMute((routing_strategy)i, true, outputDesc);
- // do tempMute unmute after twice the mute wait time
+ // make sure that we do not start the temporary mute period too early in case of
+ // delayed device change
+ setStrategyMute((routing_strategy)i, true, outputDesc, delayMs);
setStrategyMute((routing_strategy)i, false, outputDesc,
- muteWaitMs *2, device);
+ delayMs + tempMuteDurationMs, device);
}
}
}
@@ -4910,6 +4967,18 @@
audio_devices_t device)
{
float volumeDB = mVolumeCurves->volIndexToDb(stream, Volume::getDeviceCategory(device), index);
+
+ // handle the case of accessibility active while a ringtone is playing: if the ringtone is much
+ // louder than the accessibility prompt, the prompt cannot be heard, thus masking the touch
+ // exploration of the dialer UI. In this situation, bring the accessibility volume closer to
+ // the ringtone volume
+ if ((stream == AUDIO_STREAM_ACCESSIBILITY)
+ && (AUDIO_MODE_RINGTONE == mEngine->getPhoneState())
+ && isStreamActive(AUDIO_STREAM_RING, 0)) {
+ const float ringVolumeDB = computeVolume(AUDIO_STREAM_RING, index, device);
+ return ringVolumeDB - 4 > volumeDB ? ringVolumeDB - 4 : volumeDB;
+ }
+
// if a headset is connected, apply the following rules to ring tones and notifications
// to avoid sound level bursts in user's ears:
// - always attenuate notifications volume by 6dB
@@ -5409,7 +5478,6 @@
AudioProfileVector &profiles)
{
String8 reply;
- char *value;
// Format MUST be checked first to update the list of AudioProfile
if (profiles.hasDynamicFormat()) {
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.h b/services/audiopolicy/managerdefault/AudioPolicyManager.h
index 5e801af..5c2b673 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.h
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.h
@@ -477,7 +477,7 @@
return mAvailableInputDevices.getDevicesFromHwModule(mPrimaryOutput->getModuleHandle());
}
- void updateCallRouting(audio_devices_t rxDevice, int delayMs = 0);
+ uint32_t updateCallRouting(audio_devices_t rxDevice, uint32_t delayMs = 0);
// if argument "device" is different from AUDIO_DEVICE_NONE, startSource() will force
// the re-evaluation of the output device.
diff --git a/services/audiopolicy/service/AudioPolicyService.cpp b/services/audiopolicy/service/AudioPolicyService.cpp
index 9cc8449..52ed73e 100644
--- a/services/audiopolicy/service/AudioPolicyService.cpp
+++ b/services/audiopolicy/service/AudioPolicyService.cpp
@@ -52,9 +52,11 @@
static const nsecs_t kAudioCommandTimeoutNs = seconds(3); // 3 seconds
+#ifdef USE_LEGACY_AUDIO_POLICY
namespace {
extern struct audio_policy_service_ops aps_ops;
};
+#endif
// ----------------------------------------------------------------------------
@@ -66,11 +68,6 @@
void AudioPolicyService::onFirstRef()
{
- char value[PROPERTY_VALUE_MAX];
- const struct hw_module_t *module;
- int forced_val;
- int rc;
-
{
Mutex::Autolock _l(mLock);
@@ -85,7 +82,8 @@
ALOGI("AudioPolicyService CSTOR in legacy mode");
/* instantiate the audio policy manager */
- rc = hw_get_module(AUDIO_POLICY_HARDWARE_MODULE_ID, &module);
+ const struct hw_module_t *module;
+ int rc = hw_get_module(AUDIO_POLICY_HARDWARE_MODULE_ID, &module);
if (rc) {
return;
}
@@ -989,6 +987,18 @@
delayMs = 1;
} break;
+ case SET_VOICE_VOLUME: {
+ VoiceVolumeData *data = (VoiceVolumeData *)command->mParam.get();
+ VoiceVolumeData *data2 = (VoiceVolumeData *)command2->mParam.get();
+ ALOGV("Filtering out voice volume command value %f replaced by %f",
+ data2->mVolume, data->mVolume);
+ removedCommands.add(command2);
+ command->mTime = command2->mTime;
+ // force delayMs to non 0 so that code below does not request to wait for
+ // command status as the command is now delayed
+ delayMs = 1;
+ } break;
+
case CREATE_AUDIO_PATCH:
case RELEASE_AUDIO_PATCH: {
audio_patch_handle_t handle;
@@ -1198,6 +1208,7 @@
int aps_set_voice_volume(void *service, float volume, int delay_ms);
};
+#ifdef USE_LEGACY_AUDIO_POLICY
namespace {
struct audio_policy_service_ops aps_ops = {
.open_output = aps_open_output,
@@ -1220,5 +1231,6 @@
.open_input_on_module = aps_open_input_on_module,
};
}; // namespace <unnamed>
+#endif
}; // namespace android
diff --git a/services/camera/libcameraservice/Android.mk b/services/camera/libcameraservice/Android.mk
index 6b5421d..7feed6b 100644
--- a/services/camera/libcameraservice/Android.mk
+++ b/services/camera/libcameraservice/Android.mk
@@ -51,7 +51,8 @@
device3/Camera3BufferManager.cpp \
gui/RingBufferConsumer.cpp \
utils/CameraTraces.cpp \
- utils/AutoConditionLock.cpp
+ utils/AutoConditionLock.cpp \
+ utils/TagMonitor.cpp
LOCAL_SHARED_LIBRARIES:= \
libui \
diff --git a/services/camera/libcameraservice/CameraService.cpp b/services/camera/libcameraservice/CameraService.cpp
index 5062a08..dd9029e 100644
--- a/services/camera/libcameraservice/CameraService.cpp
+++ b/services/camera/libcameraservice/CameraService.cpp
@@ -914,8 +914,9 @@
// Can camera service trust the caller based on the calling UID?
static bool isTrustedCallingUid(uid_t uid) {
switch (uid) {
- case AID_MEDIA: // mediaserver
+ case AID_MEDIA: // mediaserver
case AID_CAMERASERVER: // cameraserver
+ case AID_RADIO: // telephony
return true;
default:
return false;
@@ -2142,6 +2143,8 @@
}
finishCameraOps();
+ // Notify flashlight that a camera device is closed.
+ mCameraService->mFlashlight->deviceClosed(String8::format("%d", mCameraId));
ALOGI("%s: Disconnected client for camera %d for PID %d", __FUNCTION__, mCameraId, mClientPid);
// client shouldn't be able to call into us anymore
@@ -2239,10 +2242,6 @@
// Transition device state to CLOSED
mCameraService->updateProxyDeviceState(ICameraServiceProxy::CAMERA_STATE_CLOSED,
String8::format("%d", mCameraId));
-
- // Notify flashlight that a camera device is closed.
- mCameraService->mFlashlight->deviceClosed(
- String8::format("%d", mCameraId));
}
// Always stop watching, even if no camera op is active
if (mOpsCallback != NULL) {
diff --git a/services/camera/libcameraservice/api1/Camera2Client.cpp b/services/camera/libcameraservice/api1/Camera2Client.cpp
index 7b5b11a..bcd62d6 100644
--- a/services/camera/libcameraservice/api1/Camera2Client.cpp
+++ b/services/camera/libcameraservice/api1/Camera2Client.cpp
@@ -322,6 +322,9 @@
p.fastInfo.bestStillCaptureFpsRange[0],
p.fastInfo.bestStillCaptureFpsRange[1]);
+ result.appendFormat(" Use zero shutter lag: %s\n",
+ p.useZeroShutterLag() ? "yes" : "no");
+
result.append(" Current streams:\n");
result.appendFormat(" Preview stream ID: %d\n",
getPreviewStreamId());
@@ -813,7 +816,7 @@
}
}
- if (params.zslMode && !params.recordingHint &&
+ if (params.useZeroShutterLag() &&
getRecordingStreamId() == NO_STREAM) {
res = updateProcessorStream(mZslProcessor, params);
if (res != OK) {
@@ -1362,7 +1365,7 @@
return OK;
}
- if (l.mParameters.zslMode) {
+ if (l.mParameters.allowZslMode) {
mZslProcessor->clearZslQueue();
}
}
@@ -1460,7 +1463,7 @@
// Clear ZSL buffer queue when Jpeg size is changed.
bool jpegStreamChanged = mJpegProcessor->getStreamId() != lastJpegStreamId;
- if (l.mParameters.zslMode && jpegStreamChanged) {
+ if (l.mParameters.allowZslMode && jpegStreamChanged) {
ALOGV("%s: Camera %d: Clear ZSL buffer queue when Jpeg size is changed",
__FUNCTION__, mCameraId);
mZslProcessor->clearZslQueue();
@@ -1495,7 +1498,7 @@
if (res != OK) return res;
Parameters::focusMode_t focusModeAfter = l.mParameters.focusMode;
- if (l.mParameters.zslMode && focusModeAfter != focusModeBefore) {
+ if (l.mParameters.allowZslMode && focusModeAfter != focusModeBefore) {
mZslProcessor->clearZslQueue();
}
diff --git a/services/camera/libcameraservice/api1/client2/CaptureSequencer.cpp b/services/camera/libcameraservice/api1/client2/CaptureSequencer.cpp
index 9dd1721..b65f1c7 100644
--- a/services/camera/libcameraservice/api1/client2/CaptureSequencer.cpp
+++ b/services/camera/libcameraservice/api1/client2/CaptureSequencer.cpp
@@ -338,7 +338,7 @@
return DONE;
}
- else if (l.mParameters.zslMode &&
+ else if (l.mParameters.useZeroShutterLag() &&
l.mParameters.state == Parameters::STILL_CAPTURE &&
l.mParameters.flashMode != Parameters::FLASH_MODE_ON) {
nextState = ZSL_START;
diff --git a/services/camera/libcameraservice/api1/client2/JpegProcessor.cpp b/services/camera/libcameraservice/api1/client2/JpegProcessor.cpp
index ffe96fc..d6d8dde 100644
--- a/services/camera/libcameraservice/api1/client2/JpegProcessor.cpp
+++ b/services/camera/libcameraservice/api1/client2/JpegProcessor.cpp
@@ -67,10 +67,15 @@
}
void JpegProcessor::onBufferReleased(const BufferInfo& bufferInfo) {
- Mutex::Autolock l(mInputMutex);
ALOGV("%s", __FUNCTION__);
-
if (bufferInfo.mError) {
+ // Only lock in case of error, since we get one of these for each
+ // onFrameAvailable as well, and scheduling may delay this call late
+ // enough to run into later preview restart operations, for non-error
+ // cases.
+ // b/29524651
+ ALOGV("%s: JPEG buffer lost", __FUNCTION__);
+ Mutex::Autolock l(mInputMutex);
mCaptureDone = true;
mCaptureSuccess = false;
mCaptureDoneSignal.signal();
diff --git a/services/camera/libcameraservice/api1/client2/Parameters.cpp b/services/camera/libcameraservice/api1/client2/Parameters.cpp
index 5779176..9d5f33c 100644
--- a/services/camera/libcameraservice/api1/client2/Parameters.cpp
+++ b/services/camera/libcameraservice/api1/client2/Parameters.cpp
@@ -908,12 +908,12 @@
property_get("camera.disable_zsl_mode", value, "0");
if (!strcmp(value,"1") || slowJpegMode) {
ALOGI("Camera %d: Disabling ZSL mode", cameraId);
- zslMode = false;
+ allowZslMode = false;
} else {
- zslMode = true;
+ allowZslMode = true;
}
- ALOGI("%s: zslMode: %d slowJpegMode %d", __FUNCTION__, zslMode, slowJpegMode);
+ ALOGI("%s: allowZslMode: %d slowJpegMode %d", __FUNCTION__, allowZslMode, slowJpegMode);
state = STOPPED;
@@ -1127,6 +1127,8 @@
ALOGV("Camera %d: Flexible YUV %s supported",
cameraId, fastInfo.useFlexibleYuv ? "is" : "is not");
+ fastInfo.maxJpegSize = getMaxSize(getAvailableJpegSizes());
+
return OK;
}
@@ -2231,6 +2233,25 @@
return pictureSizeOverriden;
}
+bool Parameters::useZeroShutterLag() const {
+ // If ZSL mode is disabled, don't use it
+ if (!allowZslMode) return false;
+ // If recording hint is enabled, don't do ZSL
+ if (recordingHint) return false;
+ // If still capture size is no bigger than preview or video size,
+ // don't do ZSL
+ if (pictureWidth <= previewWidth || pictureHeight <= previewHeight ||
+ pictureWidth <= videoWidth || pictureHeight <= videoHeight) {
+ return false;
+ }
+ // If still capture size is less than quarter of max, don't do ZSL
+ if ((pictureWidth * pictureHeight) <
+ (fastInfo.maxJpegSize.width * fastInfo.maxJpegSize.height / 4) ) {
+ return false;
+ }
+ return true;
+}
+
const char* Parameters::getStateName(State state) {
#define CASE_ENUM_TO_CHAR(x) case x: return(#x); break;
switch(state) {
diff --git a/services/camera/libcameraservice/api1/client2/Parameters.h b/services/camera/libcameraservice/api1/client2/Parameters.h
index a6e816c..798aab5 100644
--- a/services/camera/libcameraservice/api1/client2/Parameters.h
+++ b/services/camera/libcameraservice/api1/client2/Parameters.h
@@ -161,9 +161,9 @@
bool previewCallbackOneShot;
bool previewCallbackSurface;
- bool zslMode;
+ bool allowZslMode;
// Whether the jpeg stream is slower than 30FPS and can slow down preview.
- // When slowJpegMode is true, zslMode must be false to avoid slowing down preview.
+ // When slowJpegMode is true, allowZslMode must be false to avoid slowing down preview.
bool slowJpegMode;
// Overall camera state
@@ -219,6 +219,7 @@
DefaultKeyedVector<uint8_t, OverrideModes> sceneModeOverrides;
float minFocalLength;
bool useFlexibleYuv;
+ Size maxJpegSize;
} fastInfo;
// Quirks information; these are short-lived flags to enable workarounds for
@@ -271,6 +272,8 @@
status_t recoverOverriddenJpegSize();
// if video snapshot size is currently overridden
bool isJpegSizeOverridden();
+ // whether zero shutter lag should be used for non-recording operation
+ bool useZeroShutterLag() const;
// Calculate the crop region rectangle, either tightly about the preview
// resolution, or a region just based on the active array; both take
diff --git a/services/camera/libcameraservice/api1/client2/StreamingProcessor.cpp b/services/camera/libcameraservice/api1/client2/StreamingProcessor.cpp
index 4c2d0bd..bf92a2b 100644
--- a/services/camera/libcameraservice/api1/client2/StreamingProcessor.cpp
+++ b/services/camera/libcameraservice/api1/client2/StreamingProcessor.cpp
@@ -115,7 +115,7 @@
// Use CAMERA3_TEMPLATE_ZERO_SHUTTER_LAG for ZSL streaming case.
if (client->getCameraDeviceVersion() >= CAMERA_DEVICE_API_VERSION_3_0) {
- if (params.zslMode && !params.recordingHint) {
+ if (params.useZeroShutterLag() && !params.recordingHint) {
res = device->createDefaultRequest(CAMERA3_TEMPLATE_ZERO_SHUTTER_LAG,
&mPreviewRequest);
} else {
diff --git a/services/camera/libcameraservice/api2/CameraDeviceClient.cpp b/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
index 2ebd5a8..b4f8e21 100644
--- a/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
+++ b/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
@@ -338,13 +338,15 @@
status_t err = mDevice->configureStreams(isConstrainedHighSpeed);
if (err == BAD_VALUE) {
- res = STATUS_ERROR_FMT(CameraService::ERROR_ILLEGAL_ARGUMENT,
- "Camera %d: Unsupported set of inputs/outputs provided",
+ String8 msg = String8::format("Camera %d: Unsupported set of inputs/outputs provided",
mCameraId);
+ ALOGE("%s: %s", __FUNCTION__, msg.string());
+ res = STATUS_ERROR(CameraService::ERROR_ILLEGAL_ARGUMENT, msg.string());
} else if (err != OK) {
- res = STATUS_ERROR_FMT(CameraService::ERROR_INVALID_OPERATION,
- "Camera %d: Error configuring streams: %s (%d)",
+ String8 msg = String8::format("Camera %d: Error configuring streams: %s (%d)",
mCameraId, strerror(-err), err);
+ ALOGE("%s: %s", __FUNCTION__, msg.string());
+ res = STATUS_ERROR(CameraService::ERROR_INVALID_OPERATION, msg.string());
}
return res;
@@ -365,6 +367,7 @@
bool isInput = false;
ssize_t index = NAME_NOT_FOUND;
+ ssize_t dIndex = NAME_NOT_FOUND;
if (mInputStream.configured && mInputStream.id == streamId) {
isInput = true;
@@ -378,10 +381,19 @@
}
if (index == NAME_NOT_FOUND) {
- String8 msg = String8::format("Camera %d: Invalid stream ID (%d) specified, no such "
- "stream created yet", mCameraId, streamId);
- ALOGW("%s: %s", __FUNCTION__, msg.string());
- return STATUS_ERROR(CameraService::ERROR_ILLEGAL_ARGUMENT, msg.string());
+ // See if this stream is one of the deferred streams.
+ for (size_t i = 0; i < mDeferredStreams.size(); ++i) {
+ if (streamId == mDeferredStreams[i]) {
+ dIndex = i;
+ break;
+ }
+ }
+ if (dIndex == NAME_NOT_FOUND) {
+ String8 msg = String8::format("Camera %d: Invalid stream ID (%d) specified, no such"
+ " stream created yet", mCameraId, streamId);
+ ALOGW("%s: %s", __FUNCTION__, msg.string());
+ return STATUS_ERROR(CameraService::ERROR_ILLEGAL_ARGUMENT, msg.string());
+ }
}
}
@@ -396,8 +408,10 @@
} else {
if (isInput) {
mInputStream.configured = false;
- } else {
+ } else if (index != NAME_NOT_FOUND) {
mStreamMap.removeItemsAt(index);
+ } else {
+ mDeferredStreams.removeItemsAt(dIndex);
}
}
@@ -416,14 +430,30 @@
Mutex::Autolock icl(mBinderSerializationLock);
sp<IGraphicBufferProducer> bufferProducer = outputConfiguration.getGraphicBufferProducer();
- if (bufferProducer == NULL) {
- ALOGE("%s: bufferProducer must not be null", __FUNCTION__);
+ bool deferredConsumer = bufferProducer == NULL;
+ int surfaceType = outputConfiguration.getSurfaceType();
+ bool validSurfaceType = ((surfaceType == OutputConfiguration::SURFACE_TYPE_SURFACE_VIEW) ||
+ (surfaceType == OutputConfiguration::SURFACE_TYPE_SURFACE_TEXTURE));
+ if (deferredConsumer && !validSurfaceType) {
+ ALOGE("%s: Target surface is invalid: bufferProducer = %p, surfaceType = %d.",
+ __FUNCTION__, bufferProducer.get(), surfaceType);
return STATUS_ERROR(CameraService::ERROR_ILLEGAL_ARGUMENT, "Target Surface is invalid");
}
+
if (!mDevice.get()) {
return STATUS_ERROR(CameraService::ERROR_DISCONNECTED, "Camera device no longer alive");
}
+ int width, height, format;
+ int32_t consumerUsage;
+ android_dataspace dataSpace;
+ status_t err;
+
+ // Create stream for deferred surface case.
+ if (deferredConsumer) {
+ return createDeferredSurfaceStreamLocked(outputConfiguration, newStreamId);
+ }
+
// Don't create multiple streams for the same target surface
{
ssize_t index = mStreamMap.indexOfKey(IInterface::asBinder(bufferProducer));
@@ -435,13 +465,10 @@
}
}
- status_t err;
-
// HACK b/10949105
// Query consumer usage bits to set async operation mode for
// GLConsumer using controlledByApp parameter.
bool useAsync = false;
- int32_t consumerUsage;
if ((err = bufferProducer->query(NATIVE_WINDOW_CONSUMER_USAGE_BITS,
&consumerUsage)) != OK) {
String8 msg = String8::format("Camera %d: Failed to query Surface consumer usage: %s (%d)",
@@ -450,8 +477,8 @@
return STATUS_ERROR(CameraService::ERROR_INVALID_OPERATION, msg.string());
}
if (consumerUsage & GraphicBuffer::USAGE_HW_TEXTURE) {
- ALOGW("%s: Camera %d: Forcing asynchronous mode for stream",
- __FUNCTION__, mCameraId);
+ ALOGW("%s: Camera %d with consumer usage flag: 0x%x: Forcing asynchronous mode for stream",
+ __FUNCTION__, mCameraId, consumerUsage);
useAsync = true;
}
@@ -467,9 +494,6 @@
sp<Surface> surface = new Surface(bufferProducer, useAsync);
ANativeWindow *anw = surface.get();
- int width, height, format;
- android_dataspace dataSpace;
-
if ((err = anw->query(anw, NATIVE_WINDOW_WIDTH, &width)) != OK) {
String8 msg = String8::format("Camera %d: Failed to query Surface width: %s (%d)",
mCameraId, strerror(-err), err);
@@ -527,29 +551,12 @@
} else {
mStreamMap.add(binder, streamId);
- ALOGV("%s: Camera %d: Successfully created a new stream ID %d",
- __FUNCTION__, mCameraId, streamId);
+ ALOGV("%s: Camera %d: Successfully created a new stream ID %d for output surface"
+ " (%d x %d) with format 0x%x.",
+ __FUNCTION__, mCameraId, streamId, width, height, format);
- /**
- * Set the stream transform flags to automatically
- * rotate the camera stream for preview use cases.
- */
- int32_t transform = 0;
- err = getRotationTransformLocked(&transform);
-
- if (err != OK) {
- // Error logged by getRotationTransformLocked.
- return STATUS_ERROR(CameraService::ERROR_INVALID_OPERATION,
- "Unable to calculate rotation transform for new stream");
- }
-
- err = mDevice->setStreamTransform(streamId, transform);
- if (err != OK) {
- String8 msg = String8::format("Failed to set stream transform (stream id %d)",
- streamId);
- ALOGE("%s: %s", __FUNCTION__, msg.string());
- return STATUS_ERROR(CameraService::ERROR_INVALID_OPERATION, msg.string());
- }
+ // Set transform flags to ensure preview to be rotated correctly.
+ res = setStreamTransformLocked(streamId);
*newStreamId = streamId;
}
@@ -557,6 +564,84 @@
return res;
}
+binder::Status CameraDeviceClient::createDeferredSurfaceStreamLocked(
+ const hardware::camera2::params::OutputConfiguration &outputConfiguration,
+ /*out*/
+ int* newStreamId) {
+ int width, height, format, surfaceType;
+ int32_t consumerUsage;
+ android_dataspace dataSpace;
+ status_t err;
+ binder::Status res;
+
+ if (!mDevice.get()) {
+ return STATUS_ERROR(CameraService::ERROR_DISCONNECTED, "Camera device no longer alive");
+ }
+
+ // Infer the surface info for deferred surface stream creation.
+ width = outputConfiguration.getWidth();
+ height = outputConfiguration.getHeight();
+ surfaceType = outputConfiguration.getSurfaceType();
+ format = HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED;
+ dataSpace = android_dataspace_t::HAL_DATASPACE_UNKNOWN;
+ // Hardcode consumer usage flags: SurfaceView--0x900, SurfaceTexture--0x100.
+ consumerUsage = GraphicBuffer::USAGE_HW_TEXTURE;
+ if (surfaceType == OutputConfiguration::SURFACE_TYPE_SURFACE_VIEW) {
+ consumerUsage |= GraphicBuffer::USAGE_HW_COMPOSER;
+ }
+ int streamId = camera3::CAMERA3_STREAM_ID_INVALID;
+ err = mDevice->createStream(/*surface*/nullptr, width, height, format, dataSpace,
+ static_cast<camera3_stream_rotation_t>(outputConfiguration.getRotation()),
+ &streamId, outputConfiguration.getSurfaceSetID(), consumerUsage);
+
+ if (err != OK) {
+ res = STATUS_ERROR_FMT(CameraService::ERROR_INVALID_OPERATION,
+ "Camera %d: Error creating output stream (%d x %d, fmt %x, dataSpace %x): %s (%d)",
+ mCameraId, width, height, format, dataSpace, strerror(-err), err);
+ } else {
+ // Can not add streamId to mStreamMap here, as the surface is deferred. Add it to
+ // a separate list to track. Once the deferred surface is set, this id will be
+ // relocated to mStreamMap.
+ mDeferredStreams.push_back(streamId);
+
+ ALOGV("%s: Camera %d: Successfully created a new stream ID %d for a deferred surface"
+ " (%d x %d) stream with format 0x%x.",
+ __FUNCTION__, mCameraId, streamId, width, height, format);
+
+ // Set transform flags to ensure preview to be rotated correctly.
+ res = setStreamTransformLocked(streamId);
+
+ *newStreamId = streamId;
+ }
+ return res;
+}
+
+binder::Status CameraDeviceClient::setStreamTransformLocked(int streamId) {
+ int32_t transform = 0;
+ status_t err;
+ binder::Status res;
+
+ if (!mDevice.get()) {
+ return STATUS_ERROR(CameraService::ERROR_DISCONNECTED, "Camera device no longer alive");
+ }
+
+ err = getRotationTransformLocked(&transform);
+ if (err != OK) {
+ // Error logged by getRotationTransformLocked.
+ return STATUS_ERROR(CameraService::ERROR_INVALID_OPERATION,
+ "Unable to calculate rotation transform for new stream");
+ }
+
+ err = mDevice->setStreamTransform(streamId, transform);
+ if (err != OK) {
+ String8 msg = String8::format("Failed to set stream transform (stream id %d)",
+ streamId);
+ ALOGE("%s: %s", __FUNCTION__, msg.string());
+ return STATUS_ERROR(CameraService::ERROR_INVALID_OPERATION, msg.string());
+ }
+
+ return res;
+}
binder::Status CameraDeviceClient::createInputStream(
int width, int height, int format,
@@ -966,6 +1051,76 @@
return res;
}
+binder::Status CameraDeviceClient::setDeferredConfiguration(int32_t streamId,
+ const hardware::camera2::params::OutputConfiguration &outputConfiguration) {
+ ATRACE_CALL();
+
+ binder::Status res;
+ if (!(res = checkPidStatus(__FUNCTION__)).isOk()) return res;
+
+ Mutex::Autolock icl(mBinderSerializationLock);
+
+ sp<IGraphicBufferProducer> bufferProducer = outputConfiguration.getGraphicBufferProducer();
+
+ // Client code should guarantee that the surface is from SurfaceView or SurfaceTexture.
+ if (bufferProducer == NULL) {
+ ALOGE("%s: bufferProducer must not be null", __FUNCTION__);
+ return STATUS_ERROR(CameraService::ERROR_ILLEGAL_ARGUMENT, "Target Surface is invalid");
+ }
+ // Check if this stram id is one of the deferred streams
+ ssize_t index = NAME_NOT_FOUND;
+ for (size_t i = 0; i < mDeferredStreams.size(); i++) {
+ if (streamId == mDeferredStreams[i]) {
+ index = i;
+ break;
+ }
+ }
+ if (index == NAME_NOT_FOUND) {
+ String8 msg = String8::format("Camera %d: deferred surface is set to a unknown stream"
+ "(ID %d)", mCameraId, streamId);
+ ALOGW("%s: %s", __FUNCTION__, msg.string());
+ return STATUS_ERROR(CameraService::ERROR_ILLEGAL_ARGUMENT, msg.string());
+ }
+
+ if (!mDevice.get()) {
+ return STATUS_ERROR(CameraService::ERROR_DISCONNECTED, "Camera device no longer alive");
+ }
+
+ // Don't create multiple streams for the same target surface
+ {
+ ssize_t index = mStreamMap.indexOfKey(IInterface::asBinder(bufferProducer));
+ if (index != NAME_NOT_FOUND) {
+ String8 msg = String8::format("Camera %d: Surface already has a stream created "
+ " for it (ID %zd)", mCameraId, index);
+ ALOGW("%s: %s", __FUNCTION__, msg.string());
+ return STATUS_ERROR(CameraService::ERROR_ALREADY_EXISTS, msg.string());
+ }
+ }
+
+ status_t err;
+
+ // Always set to async, as we know the deferred surface is for preview streaming.
+ sp<Surface> consumerSurface = new Surface(bufferProducer, /*useAsync*/true);
+
+ // Finish the deferred stream configuration with the surface.
+ err = mDevice->setConsumerSurface(streamId, consumerSurface);
+ if (err == OK) {
+ sp<IBinder> binder = IInterface::asBinder(bufferProducer);
+ mStreamMap.add(binder, streamId);
+ mDeferredStreams.removeItemsAt(index);
+ } else if (err == NO_INIT) {
+ res = STATUS_ERROR_FMT(CameraService::ERROR_ILLEGAL_ARGUMENT,
+ "Camera %d: Deferred surface is invalid: %s (%d)",
+ mCameraId, strerror(-err), err);
+ } else {
+ res = STATUS_ERROR_FMT(CameraService::ERROR_INVALID_OPERATION,
+ "Camera %d: Error setting output stream deferred surface: %s (%d)",
+ mCameraId, strerror(-err), err);
+ }
+
+ return res;
+}
+
status_t CameraDeviceClient::dump(int fd, const Vector<String16>& args) {
return BasicClient::dump(fd, args);
}
@@ -991,6 +1146,11 @@
for (size_t i = 0; i < mStreamMap.size(); i++) {
result.appendFormat(" Stream %d\n", mStreamMap.valueAt(i));
}
+ } else if (!mDeferredStreams.isEmpty()) {
+ result.append(" Current deferred surface output stream IDs:\n");
+ for (auto& streamId : mDeferredStreams) {
+ result.appendFormat(" Stream %d\n", streamId);
+ }
} else {
result.append(" No output streams configured.\n");
}
diff --git a/services/camera/libcameraservice/api2/CameraDeviceClient.h b/services/camera/libcameraservice/api2/CameraDeviceClient.h
index 5a9a2d6..de283ea 100644
--- a/services/camera/libcameraservice/api2/CameraDeviceClient.h
+++ b/services/camera/libcameraservice/api2/CameraDeviceClient.h
@@ -131,6 +131,10 @@
// Prepare stream by preallocating up to maxCount of its buffers
virtual binder::Status prepare2(int32_t maxCount, int32_t streamId);
+ // Set the deferred surface for a stream.
+ virtual binder::Status setDeferredConfiguration(int32_t streamId,
+ const hardware::camera2::params::OutputConfiguration &outputConfiguration);
+
/**
* Interface used by CameraService
*/
@@ -188,6 +192,15 @@
// Find the square of the euclidean distance between two points
static int64_t euclidDistSquare(int32_t x0, int32_t y0, int32_t x1, int32_t y1);
+ // Create an output stream with surface deferred for future.
+ binder::Status createDeferredSurfaceStreamLocked(
+ const hardware::camera2::params::OutputConfiguration &outputConfiguration,
+ int* newStreamId = NULL);
+
+ // Set the stream transform flags to automatically rotate the camera stream for preview use
+ // cases.
+ binder::Status setStreamTransformLocked(int streamId);
+
// Find the closest dimensions for a given format in available stream configurations with
// a width <= ROUNDING_WIDTH_CAP
static const int32_t ROUNDING_WIDTH_CAP = 1920;
@@ -216,6 +229,10 @@
int32_t mRequestIdCounter;
+ // The list of output streams whose surfaces are deferred. We have to track them separately
+ // as there are no surfaces available and can not be put into mStreamMap. Once the deferred
+ // Surface is configured, the stream id will be moved to mStreamMap.
+ Vector<int32_t> mDeferredStreams;
};
}; // namespace android
diff --git a/services/camera/libcameraservice/common/Camera2ClientBase.cpp b/services/camera/libcameraservice/common/Camera2ClientBase.cpp
index c0d6da6..ccd1e4d 100644
--- a/services/camera/libcameraservice/common/Camera2ClientBase.cpp
+++ b/services/camera/libcameraservice/common/Camera2ClientBase.cpp
@@ -104,7 +104,8 @@
return res;
}
- res = mDevice->setNotifyCallback(this);
+ wp<CameraDeviceBase::NotificationListener> weakThis(this);
+ res = mDevice->setNotifyCallback(weakThis);
return OK;
}
diff --git a/services/camera/libcameraservice/common/CameraDeviceBase.h b/services/camera/libcameraservice/common/CameraDeviceBase.h
index 35ec531..984d84b 100644
--- a/services/camera/libcameraservice/common/CameraDeviceBase.h
+++ b/services/camera/libcameraservice/common/CameraDeviceBase.h
@@ -110,7 +110,8 @@
virtual status_t createStream(sp<Surface> consumer,
uint32_t width, uint32_t height, int format,
android_dataspace dataSpace, camera3_stream_rotation_t rotation, int *id,
- int streamSetId = camera3::CAMERA3_STREAM_SET_ID_INVALID) = 0;
+ int streamSetId = camera3::CAMERA3_STREAM_SET_ID_INVALID,
+ uint32_t consumerUsage = 0) = 0;
/**
* Create an input stream of width, height, and format.
@@ -189,7 +190,7 @@
/**
* Abstract class for HAL notification listeners
*/
- class NotificationListener {
+ class NotificationListener : public virtual RefBase {
public:
// The set of notifications is a merge of the notifications required for
// API1 and API2.
@@ -218,7 +219,7 @@
* Connect HAL notifications to a listener. Overwrites previous
* listener. Set to NULL to stop receiving notifications.
*/
- virtual status_t setNotifyCallback(NotificationListener *listener) = 0;
+ virtual status_t setNotifyCallback(wp<NotificationListener> listener) = 0;
/**
* Whether the device supports calling notifyAutofocus, notifyAutoExposure,
@@ -312,6 +313,12 @@
* Get the HAL device version.
*/
virtual uint32_t getDeviceVersion() = 0;
+
+ /**
+ * Set the deferred consumer surface and finish the rest of the stream configuration.
+ */
+ virtual status_t setConsumerSurface(int streamId, sp<Surface> consumer) = 0;
+
};
}; // namespace android
diff --git a/services/camera/libcameraservice/device3/Camera3Device.cpp b/services/camera/libcameraservice/device3/Camera3Device.cpp
index 13292aa..94bc303 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.cpp
+++ b/services/camera/libcameraservice/device3/Camera3Device.cpp
@@ -42,6 +42,7 @@
#include <utils/Log.h>
#include <utils/Trace.h>
#include <utils/Timers.h>
+#include <cutils/properties.h>
#include <android/hardware/camera2/ICameraDeviceUser.h>
@@ -169,6 +170,9 @@
return res;
}
+ /** Register in-flight map to the status tracker */
+ mInFlightStatusId = mStatusTracker->addComponent();
+
/** Create buffer manager */
mBufferManager = new Camera3BufferManager();
@@ -256,7 +260,7 @@
ATRACE_CALL();
Mutex::Autolock il(mInterfaceLock);
- ALOGV("%s: E", __FUNCTION__);
+ ALOGI("%s: E", __FUNCTION__);
status_t res = OK;
@@ -333,7 +337,7 @@
internalUpdateStatusLocked(STATUS_UNINITIALIZED);
}
- ALOGV("%s: X", __FUNCTION__);
+ ALOGI("%s: X", __FUNCTION__);
return res;
}
@@ -530,12 +534,26 @@
mId, __FUNCTION__);
bool dumpTemplates = false;
+
String16 templatesOption("-t");
+ String16 monitorOption("-m");
int n = args.size();
for (int i = 0; i < n; i++) {
if (args[i] == templatesOption) {
dumpTemplates = true;
}
+ if (args[i] == monitorOption) {
+ if (i + 1 < n) {
+ String8 monitorTags = String8(args[i + 1]);
+ if (monitorTags == "off") {
+ mTagMonitor.disableMonitoring();
+ } else {
+ mTagMonitor.parseTagsToMonitor(monitorTags);
+ }
+ } else {
+ mTagMonitor.disableMonitoring();
+ }
+ }
}
String8 lines;
@@ -622,6 +640,8 @@
}
}
+ mTagMonitor.dumpMonitoredMetadata(fd);
+
if (mHal3Device != NULL) {
lines = String8(" HAL device dump:\n");
write(fd, lines.string(), lines.size());
@@ -990,12 +1010,13 @@
status_t Camera3Device::createStream(sp<Surface> consumer,
uint32_t width, uint32_t height, int format, android_dataspace dataSpace,
- camera3_stream_rotation_t rotation, int *id, int streamSetId) {
+ camera3_stream_rotation_t rotation, int *id, int streamSetId, uint32_t consumerUsage) {
ATRACE_CALL();
Mutex::Autolock il(mInterfaceLock);
Mutex::Autolock l(mLock);
- ALOGV("Camera %d: Creating new stream %d: %d x %d, format %d, dataspace %d rotation %d",
- mId, mNextStreamId, width, height, format, dataSpace, rotation);
+ ALOGV("Camera %d: Creating new stream %d: %d x %d, format %d, dataspace %d rotation %d"
+ " consumer usage 0x%x", mId, mNextStreamId, width, height, format, dataSpace, rotation,
+ consumerUsage);
status_t res;
bool wasActive = false;
@@ -1032,6 +1053,19 @@
if (mDeviceVersion <= CAMERA_DEVICE_API_VERSION_3_2) {
streamSetId = CAMERA3_STREAM_SET_ID_INVALID;
}
+
+ // HAL3.1 doesn't support deferred consumer stream creation as it requires buffer registration
+ // which requires a consumer surface to be available.
+ if (consumer == nullptr && mDeviceVersion < CAMERA_DEVICE_API_VERSION_3_2) {
+ ALOGE("HAL3.1 doesn't support deferred consumer stream creation");
+ return BAD_VALUE;
+ }
+
+ if (consumer == nullptr && format != HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED) {
+ ALOGE("Deferred consumer stream creation only support IMPLEMENTATION_DEFINED format");
+ return BAD_VALUE;
+ }
+
// Use legacy dataspace values for older HALs
if (mDeviceVersion <= CAMERA_DEVICE_API_VERSION_3_3) {
dataSpace = mapToLegacyDataspace(dataSpace);
@@ -1063,6 +1097,10 @@
newStream = new Camera3OutputStream(mNextStreamId, consumer,
width, height, rawOpaqueBufferSize, format, dataSpace, rotation,
mTimestampOffset, streamSetId);
+ } else if (consumer == nullptr) {
+ newStream = new Camera3OutputStream(mNextStreamId,
+ width, height, format, consumerUsage, dataSpace, rotation,
+ mTimestampOffset, streamSetId);
} else {
newStream = new Camera3OutputStream(mNextStreamId, consumer,
width, height, format, dataSpace, rotation,
@@ -1445,7 +1483,7 @@
}
-status_t Camera3Device::setNotifyCallback(NotificationListener *listener) {
+status_t Camera3Device::setNotifyCallback(wp<NotificationListener> listener) {
ATRACE_CALL();
Mutex::Autolock l(mOutputLock);
@@ -1578,15 +1616,9 @@
ALOGV("%s: Camera %d: Flushing all requests", __FUNCTION__, mId);
Mutex::Autolock il(mInterfaceLock);
- NotificationListener* listener;
- {
- Mutex::Autolock l(mOutputLock);
- listener = mListener;
- }
-
{
Mutex::Autolock l(mLock);
- mRequestThread->clear(listener, /*out*/frameNumber);
+ mRequestThread->clear(/*out*/frameNumber);
}
status_t res;
@@ -1712,16 +1744,55 @@
// state changes
if (mPauseStateNotify) return;
}
- NotificationListener *listener;
+
+ sp<NotificationListener> listener;
{
Mutex::Autolock l(mOutputLock);
- listener = mListener;
+ listener = mListener.promote();
}
if (idle && listener != NULL) {
listener->notifyIdle();
}
}
+status_t Camera3Device::setConsumerSurface(int streamId, sp<Surface> consumer) {
+ ATRACE_CALL();
+ ALOGV("%s: Camera %d: set consumer surface for stream %d", __FUNCTION__, mId, streamId);
+ Mutex::Autolock il(mInterfaceLock);
+ Mutex::Autolock l(mLock);
+
+ if (consumer == nullptr) {
+ CLOGE("Null consumer is passed!");
+ return BAD_VALUE;
+ }
+
+ ssize_t idx = mOutputStreams.indexOfKey(streamId);
+ if (idx == NAME_NOT_FOUND) {
+ CLOGE("Stream %d is unknown", streamId);
+ return idx;
+ }
+ sp<Camera3OutputStreamInterface> stream = mOutputStreams[idx];
+ status_t res = stream->setConsumer(consumer);
+ if (res != OK) {
+ CLOGE("Stream %d set consumer failed (error %d %s) ", streamId, res, strerror(-res));
+ return res;
+ }
+
+ if (!stream->isConfiguring()) {
+ CLOGE("Stream %d was already fully configured.", streamId);
+ return INVALID_OPERATION;
+ }
+
+ res = stream->finishConfiguration(mHal3Device);
+ if (res != OK) {
+ SET_ERR_L("Can't finish configuring output stream %d: %s (%d)",
+ stream->getId(), strerror(-res), res);
+ return res;
+ }
+
+ return OK;
+}
+
/**
* Camera3Device private methods
*/
@@ -1781,6 +1852,13 @@
sp<Camera3OutputStreamInterface> stream =
mOutputStreams.editValueAt(idx);
+ // It is illegal to include a deferred consumer output stream into a request
+ if (stream->isConsumerConfigurationDeferred()) {
+ CLOGE("Stream %d hasn't finished configuration yet due to deferred consumer",
+ stream->getId());
+ return NULL;
+ }
+
// Lazy completion of stream configuration (allocation/registration)
// on first use
if (stream->isConfiguring()) {
@@ -1948,7 +2026,7 @@
for (size_t i = 0; i < mOutputStreams.size(); i++) {
sp<Camera3OutputStreamInterface> outputStream =
mOutputStreams.editValueAt(i);
- if (outputStream->isConfiguring()) {
+ if (outputStream->isConfiguring() && !outputStream->isConsumerConfigurationDeferred()) {
res = outputStream->finishConfiguration(mHal3Device);
if (res != OK) {
CLOGE("Can't finish configuring output stream %d: %s (%d)",
@@ -1963,19 +2041,20 @@
// across configure_streams() calls
mRequestThread->configurationComplete(mIsConstrainedHighSpeedConfiguration);
- // Boost priority of request thread for high speed recording to SCHED_FIFO
- if (mIsConstrainedHighSpeedConfiguration) {
+ char value[PROPERTY_VALUE_MAX];
+ property_get("camera.fifo.disable", value, "0");
+ int32_t disableFifo = atoi(value);
+ if (disableFifo != 1) {
+ // Boost priority of request thread to SCHED_FIFO.
pid_t requestThreadTid = mRequestThread->getTid();
res = requestPriority(getpid(), requestThreadTid,
- kConstrainedHighSpeedThreadPriority, /*asynchronous*/ false);
+ kRequestThreadPriority, /*asynchronous*/ false);
if (res != OK) {
ALOGW("Can't set realtime priority for request processing thread: %s (%d)",
strerror(-res), res);
} else {
ALOGD("Set real time priority for request queue thread (tid %d)", requestThreadTid);
}
- } else {
- // TODO: Set/restore normal priority for normal use cases
}
// Update device state
@@ -2094,8 +2173,9 @@
internalUpdateStatusLocked(STATUS_ERROR);
// Notify upstream about a device error
- if (mListener != NULL) {
- mListener->notifyError(hardware::camera2::ICameraDeviceCallbacks::ERROR_CAMERA_DEVICE,
+ sp<NotificationListener> listener = mListener.promote();
+ if (listener != NULL) {
+ listener->notifyError(hardware::camera2::ICameraDeviceCallbacks::ERROR_CAMERA_DEVICE,
CaptureResultExtras());
}
@@ -2119,6 +2199,10 @@
aeTriggerCancelOverride));
if (res < 0) return res;
+ if (mInFlightMap.size() == 1) {
+ mStatusTracker->markComponentActive(mInFlightStatusId);
+ }
+
return OK;
}
@@ -2175,6 +2259,11 @@
mInFlightMap.removeItemsAt(idx, 1);
+ // Indicate idle inFlightMap to the status tracker
+ if (mInFlightMap.size() == 0) {
+ mStatusTracker->markComponentIdle(mInFlightStatusId, Fence::NO_FENCE);
+ }
+
ALOGVV("%s: removed frame %d from InFlightMap", __FUNCTION__, frameNumber);
}
@@ -2283,13 +2372,16 @@
captureResult.mMetadata.sort();
// Check that there's a timestamp in the result metadata
- camera_metadata_entry entry = captureResult.mMetadata.find(ANDROID_SENSOR_TIMESTAMP);
- if (entry.count == 0) {
+ camera_metadata_entry timestamp = captureResult.mMetadata.find(ANDROID_SENSOR_TIMESTAMP);
+ if (timestamp.count == 0) {
SET_ERR("No timestamp provided by HAL for frame %d!",
frameNumber);
return;
}
+ mTagMonitor.monitorMetadata(TagMonitor::RESULT,
+ frameNumber, timestamp.data.i64[0], captureResult.mMetadata);
+
insertResultLocked(&captureResult, frameNumber, aeTriggerCancelOverride);
}
@@ -2485,10 +2577,10 @@
void Camera3Device::notify(const camera3_notify_msg *msg) {
ATRACE_CALL();
- NotificationListener *listener;
+ sp<NotificationListener> listener;
{
Mutex::Autolock l(mOutputLock);
- listener = mListener;
+ listener = mListener.promote();
}
if (msg == NULL) {
@@ -2512,7 +2604,7 @@
}
void Camera3Device::notifyError(const camera3_error_msg_t &msg,
- NotificationListener *listener) {
+ sp<NotificationListener> listener) {
// Map camera HAL error codes to ICameraDeviceCallback error codes
// Index into this with the HAL error code
@@ -2583,7 +2675,7 @@
}
void Camera3Device::notifyShutter(const camera3_shutter_msg_t &msg,
- NotificationListener *listener) {
+ sp<NotificationListener> listener) {
ssize_t idx;
// Set timestamp for the request in the in-flight tracking
@@ -2658,6 +2750,11 @@
}
+void Camera3Device::monitorMetadata(TagMonitor::eventSource source,
+ int64_t frameNumber, nsecs_t timestamp, const CameraMetadata& metadata) {
+ mTagMonitor.monitorMetadata(source, frameNumber, timestamp, metadata);
+}
+
/**
* RequestThread inner class methods
*/
@@ -2687,7 +2784,7 @@
}
void Camera3Device::RequestThread::setNotificationListener(
- NotificationListener *listener) {
+ wp<NotificationListener> listener) {
Mutex::Autolock l(mRequestLock);
mListener = listener;
}
@@ -2822,7 +2919,6 @@
}
status_t Camera3Device::RequestThread::clear(
- NotificationListener *listener,
/*out*/int64_t *lastFrameNumber) {
Mutex::Autolock l(mRequestLock);
ALOGV("RequestThread::%s:", __FUNCTION__);
@@ -2831,6 +2927,7 @@
// Send errors for all requests pending in the request queue, including
// pending repeating requests
+ sp<NotificationListener> listener = mListener.promote();
if (listener != NULL) {
for (RequestList::iterator it = mRequestQueue.begin();
it != mRequestQueue.end(); ++it) {
@@ -2972,6 +3069,7 @@
void Camera3Device::RequestThread::checkAndStopRepeatingRequest() {
bool surfaceAbandoned = false;
int64_t lastFrameNumber = 0;
+ sp<NotificationListener> listener;
{
Mutex::Autolock l(mRequestLock);
// Check all streams needed by repeating requests are still valid. Otherwise, stop
@@ -2988,9 +3086,11 @@
break;
}
}
+ listener = mListener.promote();
}
- if (surfaceAbandoned) {
- mListener->notifyRepeatingRequestError(lastFrameNumber);
+
+ if (listener != NULL && surfaceAbandoned) {
+ listener->notifyRepeatingRequestError(lastFrameNumber);
}
}
@@ -3086,6 +3186,12 @@
camera_metadata_t* cloned = clone_camera_metadata(nextRequest.halRequest.settings);
mLatestRequest.acquire(cloned);
+
+ sp<Camera3Device> parent = mParent.promote();
+ if (parent != NULL) {
+ parent->monitorMetadata(TagMonitor::REQUEST, nextRequest.halRequest.frame_number,
+ 0, mLatestRequest);
+ }
}
if (nextRequest.halRequest.settings != NULL) {
@@ -3327,8 +3433,9 @@
if (sendRequestError) {
Mutex::Autolock l(mRequestLock);
- if (mListener != NULL) {
- mListener->notifyError(
+ sp<NotificationListener> listener = mListener.promote();
+ if (listener != NULL) {
+ listener->notifyError(
hardware::camera2::ICameraDeviceCallbacks::ERROR_CAMERA_REQUEST,
captureRequest->mResultExtras);
}
@@ -3467,8 +3574,10 @@
// error
ALOGE("%s: Can't get input buffer, skipping request:"
" %s (%d)", __FUNCTION__, strerror(-res), res);
- if (mListener != NULL) {
- mListener->notifyError(
+
+ sp<NotificationListener> listener = mListener.promote();
+ if (listener != NULL) {
+ listener->notifyError(
hardware::camera2::ICameraDeviceCallbacks::ERROR_CAMERA_REQUEST,
nextRequest->mResultExtras);
}
@@ -3748,13 +3857,14 @@
status_t res;
Mutex::Autolock l(mLock);
+ sp<NotificationListener> listener = mListener.promote();
res = stream->startPrepare(maxCount);
if (res == OK) {
// No preparation needed, fire listener right off
ALOGV("%s: Stream %d already prepared", __FUNCTION__, stream->getId());
- if (mListener) {
- mListener->notifyPrepared(stream->getId());
+ if (listener != NULL) {
+ listener->notifyPrepared(stream->getId());
}
return OK;
} else if (res != NOT_ENOUGH_DATA) {
@@ -3769,8 +3879,8 @@
res = Thread::run("C3PrepThread", PRIORITY_BACKGROUND);
if (res != OK) {
ALOGE("%s: Unable to start preparer stream: %d (%s)", __FUNCTION__, res, strerror(-res));
- if (mListener) {
- mListener->notifyPrepared(stream->getId());
+ if (listener != NULL) {
+ listener->notifyPrepared(stream->getId());
}
return res;
}
@@ -3798,7 +3908,7 @@
return OK;
}
-void Camera3Device::PreparerThread::setNotificationListener(NotificationListener *listener) {
+void Camera3Device::PreparerThread::setNotificationListener(wp<NotificationListener> listener) {
Mutex::Autolock l(mLock);
mListener = listener;
}
@@ -3845,10 +3955,11 @@
// This stream has finished, notify listener
Mutex::Autolock l(mLock);
- if (mListener) {
+ sp<NotificationListener> listener = mListener.promote();
+ if (listener != NULL) {
ALOGV("%s: Stream %d prepare done, signaling listener", __FUNCTION__,
mCurrentStream->getId());
- mListener->notifyPrepared(mCurrentStream->getId());
+ listener->notifyPrepared(mCurrentStream->getId());
}
ATRACE_ASYNC_END("stream prepare", mCurrentStream->getId());
diff --git a/services/camera/libcameraservice/device3/Camera3Device.h b/services/camera/libcameraservice/device3/Camera3Device.h
index 6170820..33429a6 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.h
+++ b/services/camera/libcameraservice/device3/Camera3Device.h
@@ -30,6 +30,7 @@
#include "common/CameraDeviceBase.h"
#include "device3/StatusTracker.h"
#include "device3/Camera3BufferManager.h"
+#include "utils/TagMonitor.h"
/**
* Function pointer types with C calling convention to
@@ -95,11 +96,14 @@
// Actual stream creation/deletion is delayed until first request is submitted
// If adding streams while actively capturing, will pause device before adding
- // stream, reconfiguring device, and unpausing.
+ // stream, reconfiguring device, and unpausing. If the client create a stream
+ // with nullptr consumer surface, the client must then call setConsumer()
+ // and finish the stream configuration before starting output streaming.
virtual status_t createStream(sp<Surface> consumer,
uint32_t width, uint32_t height, int format,
android_dataspace dataSpace, camera3_stream_rotation_t rotation, int *id,
- int streamSetId = camera3::CAMERA3_STREAM_SET_ID_INVALID);
+ int streamSetId = camera3::CAMERA3_STREAM_SET_ID_INVALID,
+ uint32_t consumerUsage = 0);
virtual status_t createInputStream(
uint32_t width, uint32_t height, int format,
int *id);
@@ -128,7 +132,7 @@
// Transitions to the idle state on success
virtual status_t waitUntilDrained();
- virtual status_t setNotifyCallback(NotificationListener *listener);
+ virtual status_t setNotifyCallback(wp<NotificationListener> listener);
virtual bool willNotify3A();
virtual status_t waitForNextFrame(nsecs_t timeout);
virtual status_t getNextResult(CaptureResult *frame);
@@ -160,6 +164,12 @@
// Methods called by subclasses
void notifyStatus(bool idle); // updates from StatusTracker
+ /**
+ * Set the deferred consumer surface to the output stream and finish the deferred
+ * consumer configuration.
+ */
+ virtual status_t setConsumerSurface(int streamId, sp<Surface> consumer);
+
private:
static const size_t kDumpLockAttempts = 10;
static const size_t kDumpSleepDuration = 100000; // 0.10 sec
@@ -168,7 +178,7 @@
static const size_t kInFlightWarnLimit = 20;
static const size_t kInFlightWarnLimitHighSpeed = 256; // batch size 32 * pipe depth 8
// SCHED_FIFO priority for request submission thread in HFR mode
- static const int kConstrainedHighSpeedThreadPriority = 1;
+ static const int kRequestThreadPriority = 1;
struct RequestTrigger;
// minimal jpeg buffer size: 256KB + blob header
@@ -450,7 +460,7 @@
camera3_device_t *hal3Device,
bool aeLockAvailable);
- void setNotificationListener(NotificationListener *listener);
+ void setNotificationListener(wp<NotificationListener> listener);
/**
* Call after stream (re)-configuration is completed.
@@ -475,9 +485,7 @@
/**
* Remove all queued and repeating requests, and pending triggers
*/
- status_t clear(NotificationListener *listener,
- /*out*/
- int64_t *lastFrameNumber = NULL);
+ status_t clear(/*out*/int64_t *lastFrameNumber = NULL);
/**
* Flush all pending requests in HAL.
@@ -593,7 +601,7 @@
wp<camera3::StatusTracker> mStatusTracker;
camera3_device_t *mHal3Device;
- NotificationListener *mListener;
+ wp<NotificationListener> mListener;
const int mId; // The camera ID
int mStatusId; // The RequestThread's component ID for
@@ -717,6 +725,7 @@
Mutex mInFlightLock; // Protects mInFlightMap
InFlightMap mInFlightMap;
+ int mInFlightStatusId;
status_t registerInFlight(uint32_t frameNumber,
int32_t numBuffers, CaptureResultExtras resultExtras, bool hasInput,
@@ -749,7 +758,7 @@
PreparerThread();
~PreparerThread();
- void setNotificationListener(NotificationListener *listener);
+ void setNotificationListener(wp<NotificationListener> listener);
/**
* Queue up a stream to be prepared. Streams are processed by a background thread in FIFO
@@ -770,7 +779,7 @@
// Guarded by mLock
- NotificationListener *mListener;
+ wp<NotificationListener> mListener;
List<sp<camera3::Camera3StreamInterface> > mPendingStreams;
bool mActive;
bool mCancelNow;
@@ -799,7 +808,7 @@
uint32_t mNextReprocessShutterFrameNumber;
List<CaptureResult> mResultQueue;
Condition mResultSignal;
- NotificationListener *mListener;
+ wp<NotificationListener> mListener;
/**** End scope for mOutputLock ****/
@@ -812,9 +821,9 @@
// Specific notify handlers
void notifyError(const camera3_error_msg_t &msg,
- NotificationListener *listener);
+ sp<NotificationListener> listener);
void notifyShutter(const camera3_shutter_msg_t &msg,
- NotificationListener *listener);
+ sp<NotificationListener> listener);
// helper function to return the output buffers to the streams.
void returnOutputBuffers(const camera3_stream_buffer_t *outputBuffers,
@@ -846,6 +855,16 @@
/**** End scope for mInFlightLock ****/
+ // Debug tracker for metadata tag value changes
+ // - Enabled with the -m <taglist> option to dumpsys, such as
+ // dumpsys -m android.control.aeState,android.control.aeMode
+ // - Disabled with -m off
+ // - dumpsys -m 3a is a shortcut for ae/af/awbMode, State, and Triggers
+ TagMonitor mTagMonitor;
+
+ void monitorMetadata(TagMonitor::eventSource source, int64_t frameNumber,
+ nsecs_t timestamp, const CameraMetadata& metadata);
+
/**
* Static callback forwarding methods from HAL to instance
*/
diff --git a/services/camera/libcameraservice/device3/Camera3DummyStream.cpp b/services/camera/libcameraservice/device3/Camera3DummyStream.cpp
index 6354ef7..5123785 100644
--- a/services/camera/libcameraservice/device3/Camera3DummyStream.cpp
+++ b/services/camera/libcameraservice/device3/Camera3DummyStream.cpp
@@ -103,6 +103,15 @@
return false;
}
+bool Camera3DummyStream::isConsumerConfigurationDeferred() const {
+ return false;
+}
+
+status_t Camera3DummyStream::setConsumer(sp<Surface> consumer) {
+ ALOGE("%s: Stream %d: Dummy stream doesn't support set consumer surface %p!",
+ __FUNCTION__, mId, consumer.get());
+ return INVALID_OPERATION;
+}
}; // namespace camera3
}; // namespace android
diff --git a/services/camera/libcameraservice/device3/Camera3DummyStream.h b/services/camera/libcameraservice/device3/Camera3DummyStream.h
index 1f37e93..18e8a23 100644
--- a/services/camera/libcameraservice/device3/Camera3DummyStream.h
+++ b/services/camera/libcameraservice/device3/Camera3DummyStream.h
@@ -61,6 +61,16 @@
*/
bool isVideoStream() const;
+ /**
+ * Return if the consumer configuration of this stream is deferred.
+ */
+ virtual bool isConsumerConfigurationDeferred() const;
+
+ /**
+ * Set the consumer surface to the output stream.
+ */
+ virtual status_t setConsumer(sp<Surface> consumer);
+
protected:
/**
diff --git a/services/camera/libcameraservice/device3/Camera3IOStreamBase.cpp b/services/camera/libcameraservice/device3/Camera3IOStreamBase.cpp
index 4824974..cb39244 100644
--- a/services/camera/libcameraservice/device3/Camera3IOStreamBase.cpp
+++ b/services/camera/libcameraservice/device3/Camera3IOStreamBase.cpp
@@ -124,6 +124,7 @@
switch (mState) {
case STATE_IN_RECONFIG:
case STATE_CONFIGURED:
+ case STATE_ABANDONED:
// OK
break;
default:
diff --git a/services/camera/libcameraservice/device3/Camera3OutputStream.cpp b/services/camera/libcameraservice/device3/Camera3OutputStream.cpp
index dff5a49..7229929 100644
--- a/services/camera/libcameraservice/device3/Camera3OutputStream.cpp
+++ b/services/camera/libcameraservice/device3/Camera3OutputStream.cpp
@@ -42,7 +42,8 @@
mTransform(0),
mTraceFirstBuffer(true),
mUseBufferManager(false),
- mTimestampOffset(timestampOffset) {
+ mTimestampOffset(timestampOffset),
+ mConsumerUsage(0) {
if (mConsumer == NULL) {
ALOGE("%s: Consumer is NULL!", __FUNCTION__);
@@ -66,7 +67,8 @@
mTraceFirstBuffer(true),
mUseMonoTimestamp(false),
mUseBufferManager(false),
- mTimestampOffset(timestampOffset) {
+ mTimestampOffset(timestampOffset),
+ mConsumerUsage(0) {
if (format != HAL_PIXEL_FORMAT_BLOB && format != HAL_PIXEL_FORMAT_RAW_OPAQUE) {
ALOGE("%s: Bad format for size-only stream: %d", __FUNCTION__,
@@ -84,6 +86,39 @@
}
}
+Camera3OutputStream::Camera3OutputStream(int id,
+ uint32_t width, uint32_t height, int format,
+ uint32_t consumerUsage, android_dataspace dataSpace,
+ camera3_stream_rotation_t rotation, nsecs_t timestampOffset, int setId) :
+ Camera3IOStreamBase(id, CAMERA3_STREAM_OUTPUT, width, height,
+ /*maxSize*/0, format, dataSpace, rotation, setId),
+ mConsumer(nullptr),
+ mTransform(0),
+ mTraceFirstBuffer(true),
+ mUseBufferManager(false),
+ mTimestampOffset(timestampOffset),
+ mConsumerUsage(consumerUsage) {
+ // Deferred consumer only support preview surface format now.
+ if (format != HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED) {
+ ALOGE("%s: Deferred consumer only supports IMPLEMENTATION_DEFINED format now!",
+ __FUNCTION__);
+ mState = STATE_ERROR;
+ }
+
+ // Sanity check for the consumer usage flag.
+ if ((consumerUsage & GraphicBuffer::USAGE_HW_TEXTURE) == 0 &&
+ (consumerUsage & GraphicBuffer::USAGE_HW_COMPOSER) == 0) {
+ ALOGE("%s: Deferred consumer usage flag is illegal (0x%x)!", __FUNCTION__, consumerUsage);
+ mState = STATE_ERROR;
+ }
+
+ mConsumerName = String8("Deferred");
+ if (setId > CAMERA3_STREAM_SET_ID_INVALID) {
+ mBufferReleasedListener = new BufferReleasedListener(this);
+ }
+
+}
+
Camera3OutputStream::Camera3OutputStream(int id, camera3_stream_type_t type,
uint32_t width, uint32_t height,
int format,
@@ -96,7 +131,8 @@
mTransform(0),
mTraceFirstBuffer(true),
mUseMonoTimestamp(false),
- mUseBufferManager(false) {
+ mUseBufferManager(false),
+ mConsumerUsage(0) {
if (setId > CAMERA3_STREAM_SET_ID_INVALID) {
mBufferReleasedListener = new BufferReleasedListener(this);
@@ -235,6 +271,7 @@
*/
if (buffer.status == CAMERA3_BUFFER_STATUS_ERROR) {
// Cancel buffer
+ ALOGW("A frame is dropped for stream %d", mId);
res = currentConsumer->cancelBuffer(currentConsumer.get(),
container_of(buffer.buffer, ANativeWindowBuffer, handle),
anwReleaseFence);
@@ -481,11 +518,16 @@
return res;
}
+ // Stream configuration was not finished (can only be in STATE_IN_CONFIG or STATE_CONSTRUCTED
+ // state), don't need change the stream state, return OK.
+ if (mConsumer == nullptr) {
+ return OK;
+ }
+
ALOGV("%s: disconnecting stream %d from native window", __FUNCTION__, getId());
res = native_window_api_disconnect(mConsumer.get(),
NATIVE_WINDOW_API_CAMERA);
-
/**
* This is not an error. if client calling process dies, the window will
* also die and all calls to it will return DEAD_OBJECT, thus it's already
@@ -527,6 +569,12 @@
status_t res;
int32_t u = 0;
+ if (mConsumer == nullptr) {
+ // mConsumerUsage was sanitized before the Camera3OutputStream was constructed.
+ *usage = mConsumerUsage;
+ return OK;
+ }
+
res = static_cast<ANativeWindow*>(mConsumer.get())->query(mConsumer.get(),
NATIVE_WINDOW_CONSUMER_USAGE_BITS, &u);
@@ -562,7 +610,7 @@
status_t Camera3OutputStream::setBufferManager(sp<Camera3BufferManager> bufferManager) {
Mutex::Autolock l(mLock);
if (mState != STATE_CONSTRUCTED) {
- ALOGE("%s: this method can only be called when stream in in CONSTRUCTED state.",
+ ALOGE("%s: this method can only be called when stream in CONSTRUCTED state.",
__FUNCTION__);
return INVALID_OPERATION;
}
@@ -611,9 +659,9 @@
*buffer = 0;
ALOGW("%s: the released buffer has already been freed by the buffer queue!", __FUNCTION__);
} else if (res != OK) {
- // Other errors are fatal.
+ // Treat other errors as abandonment
ALOGE("%s: detach next buffer failed: %s (%d).", __FUNCTION__, strerror(-res), res);
- mState = STATE_ERROR;
+ mState = STATE_ABANDONED;
return res;
}
@@ -628,6 +676,26 @@
return OK;
}
+bool Camera3OutputStream::isConsumerConfigurationDeferred() const {
+ Mutex::Autolock l(mLock);
+ return mConsumer == nullptr;
+}
+
+status_t Camera3OutputStream::setConsumer(sp<Surface> consumer) {
+ if (consumer == nullptr) {
+ ALOGE("%s: it's illegal to set a null consumer surface!", __FUNCTION__);
+ return INVALID_OPERATION;
+ }
+
+ if (mConsumer != nullptr) {
+ ALOGE("%s: consumer surface was already set!", __FUNCTION__);
+ return INVALID_OPERATION;
+ }
+
+ mConsumer = consumer;
+ return OK;
+}
+
bool Camera3OutputStream::isConsumedByHWComposer() const {
uint32_t usage = 0;
status_t res = getEndpointUsage(&usage);
diff --git a/services/camera/libcameraservice/device3/Camera3OutputStream.h b/services/camera/libcameraservice/device3/Camera3OutputStream.h
index 46c1465..12d497e 100644
--- a/services/camera/libcameraservice/device3/Camera3OutputStream.h
+++ b/services/camera/libcameraservice/device3/Camera3OutputStream.h
@@ -94,6 +94,16 @@
android_dataspace dataSpace, camera3_stream_rotation_t rotation,
nsecs_t timestampOffset, int setId = CAMERA3_STREAM_SET_ID_INVALID);
+ /**
+ * Set up a stream with deferred consumer for formats that have 2 dimensions, such as
+ * RAW and YUV. The consumer must be set before using this stream for output. A valid
+ * stream set id needs to be set to support buffer sharing between multiple streams.
+ */
+ Camera3OutputStream(int id, uint32_t width, uint32_t height, int format,
+ uint32_t consumerUsage, android_dataspace dataSpace,
+ camera3_stream_rotation_t rotation, nsecs_t timestampOffset,
+ int setId = CAMERA3_STREAM_SET_ID_INVALID);
+
virtual ~Camera3OutputStream();
/**
@@ -122,6 +132,16 @@
*/
bool isConsumedByHWTexture() const;
+ /**
+ * Return if the consumer configuration of this stream is deferred.
+ */
+ virtual bool isConsumerConfigurationDeferred() const;
+
+ /**
+ * Set the consumer surface to the output stream.
+ */
+ virtual status_t setConsumer(sp<Surface> consumer);
+
class BufferReleasedListener : public BnProducerListener {
public:
BufferReleasedListener(wp<Camera3OutputStream> parent) : mParent(parent) {}
@@ -164,6 +184,7 @@
virtual status_t disconnectLocked();
sp<Surface> mConsumer;
+
private:
static const nsecs_t kDequeueBufferTimeout = 1000000000; // 1 sec
@@ -203,6 +224,12 @@
nsecs_t mTimestampOffset;
/**
+ * Consumer end point usage flag set by the constructor for the deferred
+ * consumer case.
+ */
+ uint32_t mConsumerUsage;
+
+ /**
* Internal Camera3Stream interface
*/
virtual status_t getBufferLocked(camera3_stream_buffer *buffer);
diff --git a/services/camera/libcameraservice/device3/Camera3OutputStreamInterface.h b/services/camera/libcameraservice/device3/Camera3OutputStreamInterface.h
index 50dce55..3f83c89 100644
--- a/services/camera/libcameraservice/device3/Camera3OutputStreamInterface.h
+++ b/services/camera/libcameraservice/device3/Camera3OutputStreamInterface.h
@@ -41,6 +41,16 @@
virtual bool isVideoStream() const = 0;
/**
+ * Return if the consumer configuration of this stream is deferred.
+ */
+ virtual bool isConsumerConfigurationDeferred() const = 0;
+
+ /**
+ * Set the consumer surface to the output stream.
+ */
+ virtual status_t setConsumer(sp<Surface> consumer) = 0;
+
+ /**
* Detach an unused buffer from the stream.
*
* buffer must be non-null; fenceFd may null, and if it is non-null, but
@@ -49,7 +59,6 @@
*
*/
virtual status_t detachBuffer(sp<GraphicBuffer>* buffer, int* fenceFd) = 0;
-
};
} // namespace camera3
diff --git a/services/camera/libcameraservice/device3/Camera3Stream.cpp b/services/camera/libcameraservice/device3/Camera3Stream.cpp
index 96d62d4..3ffd9d1 100644
--- a/services/camera/libcameraservice/device3/Camera3Stream.cpp
+++ b/services/camera/libcameraservice/device3/Camera3Stream.cpp
@@ -55,7 +55,7 @@
mMaxSize(maxSize),
mState(STATE_CONSTRUCTED),
mStatusId(StatusTracker::NO_STATUS_ID),
- mStreamUnpreparable(false),
+ mStreamUnpreparable(true),
mOldUsage(0),
mOldMaxBuffers(0),
mPrepared(false),
diff --git a/services/camera/libcameraservice/device3/Camera3Stream.h b/services/camera/libcameraservice/device3/Camera3Stream.h
index 0755700..1ff215d 100644
--- a/services/camera/libcameraservice/device3/Camera3Stream.h
+++ b/services/camera/libcameraservice/device3/Camera3Stream.h
@@ -175,7 +175,8 @@
* OK on a successful configuration
* NO_INIT in case of a serious error from the HAL device
* NO_MEMORY in case of an error registering buffers
- * INVALID_OPERATION in case connecting to the consumer failed
+ * INVALID_OPERATION in case connecting to the consumer failed or consumer
+ * doesn't exist yet.
*/
status_t finishConfiguration(camera3_device *hal3Device);
diff --git a/services/camera/libcameraservice/utils/TagMonitor.cpp b/services/camera/libcameraservice/utils/TagMonitor.cpp
new file mode 100644
index 0000000..f1b65bd
--- /dev/null
+++ b/services/camera/libcameraservice/utils/TagMonitor.cpp
@@ -0,0 +1,281 @@
+/*
+ * Copyright (C) 2016 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 "Camera3-TagMonitor"
+#define ATRACE_TAG ATRACE_TAG_CAMERA
+//#define LOG_NDEBUG 0
+
+#include "TagMonitor.h"
+
+#include <inttypes.h>
+#include <utils/Log.h>
+#include <camera/VendorTagDescriptor.h>
+
+namespace android {
+
+TagMonitor::TagMonitor():
+ mMonitoringEnabled(false),
+ mMonitoringEvents(kMaxMonitorEvents)
+{}
+
+const char* TagMonitor::k3aTags =
+ "android.control.aeMode, android.control.afMode, android.control.awbMode,"
+ "android.control.aeState, android.control.afState, android.control.awbState,"
+ "android.control.aePrecaptureTrigger, android.control.afTrigger,"
+ "android.control.aeRegions, android.control.awbRegions, android.control.afRegions,"
+ "android.control.aeExposureCompensation, android.control.aeLock, android.control.awbLock,"
+ "android.control.aeAntibandingMode, android.control.aeTargetFpsRange,"
+ "android.control.effectMode, android.control.mode, android.control.sceneMode,"
+ "android.control.videoStabilizationMode";
+
+void TagMonitor::parseTagsToMonitor(String8 tagNames) {
+ std::lock_guard<std::mutex> lock(mMonitorMutex);
+
+ // Expand shorthands
+ if (ssize_t idx = tagNames.find("3a") != -1) {
+ ssize_t end = tagNames.find(",", idx);
+ char* start = tagNames.lockBuffer(tagNames.size());
+ start[idx] = '\0';
+ char* rest = (end != -1) ? (start + end) : (start + tagNames.size());
+ tagNames = String8::format("%s%s%s", start, k3aTags, rest);
+ }
+
+ sp<VendorTagDescriptor> vTags =
+ VendorTagDescriptor::getGlobalVendorTagDescriptor();
+
+ bool gotTag = false;
+
+ char *tokenized = tagNames.lockBuffer(tagNames.size());
+ char *savePtr;
+ char *nextTagName = strtok_r(tokenized, ", ", &savePtr);
+ while (nextTagName != nullptr) {
+ uint32_t tag;
+ status_t res = CameraMetadata::getTagFromName(nextTagName, vTags.get(), &tag);
+ if (res != OK) {
+ ALOGW("%s: Unknown tag %s, ignoring", __FUNCTION__, nextTagName);
+ } else {
+ if (!gotTag) {
+ mMonitoredTagList.clear();
+ gotTag = true;
+ }
+ mMonitoredTagList.push_back(tag);
+ }
+ nextTagName = strtok_r(nullptr, ", ", &savePtr);
+ }
+
+ tagNames.unlockBuffer();
+
+ if (gotTag) {
+ // Got at least one new tag
+ mMonitoringEnabled = true;
+ }
+}
+
+void TagMonitor::disableMonitoring() {
+ mMonitoringEnabled = false;
+ mLastMonitoredRequestValues.clear();
+ mLastMonitoredResultValues.clear();
+}
+
+void TagMonitor::monitorMetadata(eventSource source, int64_t frameNumber, nsecs_t timestamp,
+ const CameraMetadata& metadata) {
+ if (!mMonitoringEnabled) return;
+
+ std::lock_guard<std::mutex> lock(mMonitorMutex);
+
+ if (timestamp == 0) {
+ timestamp = systemTime(SYSTEM_TIME_BOOTTIME);
+ }
+
+ for (auto tag : mMonitoredTagList) {
+ camera_metadata_ro_entry entry = metadata.find(tag);
+ CameraMetadata &lastValues = (source == REQUEST) ?
+ mLastMonitoredRequestValues : mLastMonitoredResultValues;
+ camera_metadata_entry lastEntry = lastValues.find(tag);
+
+ if (entry.count > 0) {
+ bool isDifferent = false;
+ if (lastEntry.count > 0) {
+ // Have a last value, compare to see if changed
+ if (lastEntry.type == entry.type &&
+ lastEntry.count == entry.count) {
+ // Same type and count, compare values
+ size_t bytesPerValue = camera_metadata_type_size[lastEntry.type];
+ size_t entryBytes = bytesPerValue * lastEntry.count;
+ int cmp = memcmp(entry.data.u8, lastEntry.data.u8, entryBytes);
+ if (cmp != 0) {
+ isDifferent = true;
+ }
+ } else {
+ // Count or type has changed
+ isDifferent = true;
+ }
+ } else {
+ // No last entry, so always consider to be different
+ isDifferent = true;
+ }
+
+ if (isDifferent) {
+ ALOGV("%s: Tag %s changed", __FUNCTION__, get_camera_metadata_tag_name(tag));
+ lastValues.update(entry);
+ mMonitoringEvents.emplace(source, frameNumber, timestamp, entry);
+ }
+ } else if (lastEntry.count > 0) {
+ // Value has been removed
+ ALOGV("%s: Tag %s removed", __FUNCTION__, get_camera_metadata_tag_name(tag));
+ lastValues.erase(tag);
+ entry.tag = tag;
+ entry.type = get_camera_metadata_tag_type(tag);
+ entry.count = 0;
+ mMonitoringEvents.emplace(source, frameNumber, timestamp, entry);
+ }
+ }
+}
+
+void TagMonitor::dumpMonitoredMetadata(int fd) {
+ std::lock_guard<std::mutex> lock(mMonitorMutex);
+
+ if (mMonitoringEnabled) {
+ dprintf(fd, " Tag monitoring enabled for tags:\n");
+ for (uint32_t tag : mMonitoredTagList) {
+ dprintf(fd, " %s.%s\n",
+ get_camera_metadata_section_name(tag),
+ get_camera_metadata_tag_name(tag));
+ }
+ } else {
+ dprintf(fd, " Tag monitoring disabled (enable with -m <name1,..,nameN>)\n");
+ }
+ if (mMonitoringEvents.size() > 0) {
+ dprintf(fd, " Monitored tag event log:\n");
+ for (const auto& event : mMonitoringEvents) {
+ int indentation = (event.source == REQUEST) ? 15 : 30;
+ dprintf(fd, " f%d:%" PRId64 "ns: %*s%s.%s: ",
+ event.frameNumber, event.timestamp,
+ indentation,
+ event.source == REQUEST ? "REQ:" : "RES:",
+ get_camera_metadata_section_name(event.tag),
+ get_camera_metadata_tag_name(event.tag));
+ if (event.newData.size() == 0) {
+ dprintf(fd, " (Removed)\n");
+ } else {
+ printData(fd, event.newData.data(), event.tag,
+ event.type, event.newData.size() / camera_metadata_type_size[event.type],
+ indentation + 18);
+ }
+ }
+ }
+
+}
+
+// TODO: Consolidate with printData from camera_metadata.h
+
+#define CAMERA_METADATA_ENUM_STRING_MAX_SIZE 29
+
+void TagMonitor::printData(int fd, const uint8_t *data_ptr, uint32_t tag,
+ int type, int count, int indentation) {
+ static int values_per_line[NUM_TYPES] = {
+ [TYPE_BYTE] = 16,
+ [TYPE_INT32] = 8,
+ [TYPE_FLOAT] = 8,
+ [TYPE_INT64] = 4,
+ [TYPE_DOUBLE] = 4,
+ [TYPE_RATIONAL] = 4,
+ };
+ size_t type_size = camera_metadata_type_size[type];
+ char value_string_tmp[CAMERA_METADATA_ENUM_STRING_MAX_SIZE];
+ uint32_t value;
+
+ int lines = count / values_per_line[type];
+ if (count % values_per_line[type] != 0) lines++;
+
+ int index = 0;
+ int j, k;
+ for (j = 0; j < lines; j++) {
+ dprintf(fd, "%*s[", (j != 0) ? indentation + 4 : 0, "");
+ for (k = 0;
+ k < values_per_line[type] && count > 0;
+ k++, count--, index += type_size) {
+
+ switch (type) {
+ case TYPE_BYTE:
+ value = *(data_ptr + index);
+ if (camera_metadata_enum_snprint(tag,
+ value,
+ value_string_tmp,
+ sizeof(value_string_tmp))
+ == OK) {
+ dprintf(fd, "%s ", value_string_tmp);
+ } else {
+ dprintf(fd, "%hhu ",
+ *(data_ptr + index));
+ }
+ break;
+ case TYPE_INT32:
+ value =
+ *(int32_t*)(data_ptr + index);
+ if (camera_metadata_enum_snprint(tag,
+ value,
+ value_string_tmp,
+ sizeof(value_string_tmp))
+ == OK) {
+ dprintf(fd, "%s ", value_string_tmp);
+ } else {
+ dprintf(fd, "%" PRId32 " ",
+ *(int32_t*)(data_ptr + index));
+ }
+ break;
+ case TYPE_FLOAT:
+ dprintf(fd, "%0.8f ",
+ *(float*)(data_ptr + index));
+ break;
+ case TYPE_INT64:
+ dprintf(fd, "%" PRId64 " ",
+ *(int64_t*)(data_ptr + index));
+ break;
+ case TYPE_DOUBLE:
+ dprintf(fd, "%0.8f ",
+ *(double*)(data_ptr + index));
+ break;
+ case TYPE_RATIONAL: {
+ int32_t numerator = *(int32_t*)(data_ptr + index);
+ int32_t denominator = *(int32_t*)(data_ptr + index + 4);
+ dprintf(fd, "(%d / %d) ",
+ numerator, denominator);
+ break;
+ }
+ default:
+ dprintf(fd, "??? ");
+ }
+ }
+ dprintf(fd, "]\n");
+ }
+}
+
+template<typename T>
+TagMonitor::MonitorEvent::MonitorEvent(eventSource src, uint32_t frameNumber, nsecs_t timestamp,
+ const T &value) :
+ source(src),
+ frameNumber(frameNumber),
+ timestamp(timestamp),
+ tag(value.tag),
+ type(value.type),
+ newData(value.data.u8, value.data.u8 + camera_metadata_type_size[value.type] * value.count) {
+}
+
+TagMonitor::MonitorEvent::~MonitorEvent() {
+}
+
+} // namespace android
diff --git a/services/camera/libcameraservice/utils/TagMonitor.h b/services/camera/libcameraservice/utils/TagMonitor.h
new file mode 100644
index 0000000..d7aa419
--- /dev/null
+++ b/services/camera/libcameraservice/utils/TagMonitor.h
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2016 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_SERVERS_CAMERA_TAGMONITOR_H
+#define ANDROID_SERVERS_CAMERA_TAGMONITOR_H
+
+#include <vector>
+#include <atomic>
+#include <mutex>
+
+#include <utils/RefBase.h>
+#include <utils/String8.h>
+#include <utils/Timers.h>
+
+#include <media/RingBuffer.h>
+#include <system/camera_metadata.h>
+#include <camera/CameraMetadata.h>
+
+namespace android {
+
+/**
+ * A monitor for camera metadata values.
+ * Tracks changes to specified metadata values over time, keeping a circular
+ * buffer log that can be dumped at will. */
+class TagMonitor {
+ public:
+ enum eventSource {
+ REQUEST,
+ RESULT
+ };
+
+ TagMonitor();
+
+ // Parse tag name list (comma-separated) and if valid, enable monitoring
+ // If invalid, do nothing.
+ // Recognizes "3a" as a shortcut for enabling tracking 3A state, mode, and
+ // triggers
+ void parseTagsToMonitor(String8 tagNames);
+
+ // Disable monitoring; does not clear the event log
+ void disableMonitoring();
+
+ // Scan through the metadata and update the monitoring information
+ void monitorMetadata(eventSource source, int64_t frameNumber,
+ nsecs_t timestamp, const CameraMetadata& metadata);
+
+ // Dump current event log to the provided fd
+ void dumpMonitoredMetadata(int fd);
+
+ private:
+
+ static void printData(int fd, const uint8_t *data_ptr, uint32_t tag,
+ int type, int count, int indentation);
+
+ std::atomic<bool> mMonitoringEnabled;
+ std::mutex mMonitorMutex;
+
+ // Current tags to monitor and record changes to
+ std::vector<uint32_t> mMonitoredTagList;
+
+ // Latest-seen values of tracked tags
+ CameraMetadata mLastMonitoredRequestValues;
+ CameraMetadata mLastMonitoredResultValues;
+
+ /**
+ * A monitoring event
+ * Stores a new metadata field value and the timestamp at which it changed.
+ * Copies the source metadata value array and frees it on destruct.
+ */
+ struct MonitorEvent {
+ template<typename T>
+ MonitorEvent(eventSource src, uint32_t frameNumber, nsecs_t timestamp,
+ const T &newValue);
+ ~MonitorEvent();
+
+ eventSource source;
+ uint32_t frameNumber;
+ nsecs_t timestamp;
+ uint32_t tag;
+ uint8_t type;
+ std::vector<uint8_t> newData;
+ };
+
+ // A ring buffer for tracking the last kMaxMonitorEvents metadata changes
+ static const int kMaxMonitorEvents = 100;
+ RingBuffer<MonitorEvent> mMonitoringEvents;
+
+ // 3A fields to use with the "3a" option
+ static const char *k3aTags;
+};
+
+} // namespace android
+
+#endif
diff --git a/services/soundtrigger/Android.mk b/services/soundtrigger/Android.mk
index 90bcdbc..0f5bbba 100644
--- a/services/soundtrigger/Android.mk
+++ b/services/soundtrigger/Android.mk
@@ -39,6 +39,8 @@
LOCAL_MULTILIB := $(AUDIOSERVER_MULTILIB)
+LOCAL_CFLAGS := -Wall -Werror
+
LOCAL_MODULE:= libsoundtriggerservice
include $(BUILD_SHARED_LIBRARY)
diff --git a/services/soundtrigger/SoundTriggerHwService.cpp b/services/soundtrigger/SoundTriggerHwService.cpp
index ac379bd..6a52b9c 100644
--- a/services/soundtrigger/SoundTriggerHwService.cpp
+++ b/services/soundtrigger/SoundTriggerHwService.cpp
@@ -542,6 +542,22 @@
struct sound_trigger_sound_model *sound_model =
(struct sound_trigger_sound_model *)modelMemory->pointer();
+ size_t structSize;
+ if (sound_model->type == SOUND_MODEL_TYPE_KEYPHRASE) {
+ structSize = sizeof(struct sound_trigger_phrase_sound_model);
+ } else {
+ structSize = sizeof(struct sound_trigger_sound_model);
+ }
+
+ if (sound_model->data_offset < structSize ||
+ sound_model->data_size > (UINT_MAX - sound_model->data_offset) ||
+ modelMemory->size() < sound_model->data_offset ||
+ sound_model->data_size > (modelMemory->size() - sound_model->data_offset)) {
+ android_errorWriteLog(0x534e4554, "30148546");
+ ALOGE("loadSoundModel() data_size is too big");
+ return BAD_VALUE;
+ }
+
AutoMutex lock(mLock);
if (mModels.size() >= mDescriptor.properties.max_sound_models) {
@@ -607,11 +623,23 @@
return PERMISSION_DENIED;
}
- if (dataMemory != 0 && dataMemory->pointer() == NULL) {
- ALOGE("startRecognition() dataMemory is non-0 but has NULL pointer()");
+ if (dataMemory == 0 || dataMemory->pointer() == NULL) {
+ ALOGE("startRecognition() dataMemory is 0 or has NULL pointer()");
return BAD_VALUE;
}
+
+ struct sound_trigger_recognition_config *config =
+ (struct sound_trigger_recognition_config *)dataMemory->pointer();
+
+ if (config->data_offset < sizeof(struct sound_trigger_recognition_config) ||
+ config->data_size > (UINT_MAX - config->data_offset) ||
+ dataMemory->size() < config->data_offset ||
+ config->data_size > (dataMemory->size() - config->data_offset)) {
+ ALOGE("startRecognition() data_size is too big");
+ return BAD_VALUE;
+ }
+
AutoMutex lock(mLock);
if (mServiceState == SOUND_TRIGGER_STATE_DISABLED) {
return INVALID_OPERATION;
@@ -620,17 +648,11 @@
if (model == 0) {
return BAD_VALUE;
}
- if ((dataMemory == 0) ||
- (dataMemory->size() < sizeof(struct sound_trigger_recognition_config))) {
- return BAD_VALUE;
- }
if (model->mState == Model::STATE_ACTIVE) {
return INVALID_OPERATION;
}
- struct sound_trigger_recognition_config *config =
- (struct sound_trigger_recognition_config *)dataMemory->pointer();
//TODO: get capture handle and device from audio policy service
config->capture_handle = model->mCaptureIOHandle;
diff --git a/soundtrigger/SoundTrigger.cpp b/soundtrigger/SoundTrigger.cpp
index 2138cb7..e959b83 100644
--- a/soundtrigger/SoundTrigger.cpp
+++ b/soundtrigger/SoundTrigger.cpp
@@ -55,7 +55,7 @@
sp<DeathNotifier> gDeathNotifier;
}; // namespace anonymous
-const sp<ISoundTriggerHwService>& SoundTrigger::getSoundTriggerHwService()
+const sp<ISoundTriggerHwService> SoundTrigger::getSoundTriggerHwService()
{
Mutex::Autolock _l(gLock);
if (gSoundTriggerHwService.get() == 0) {
@@ -84,7 +84,7 @@
uint32_t *numModules)
{
ALOGV("listModules()");
- const sp<ISoundTriggerHwService>& service = getSoundTriggerHwService();
+ const sp<ISoundTriggerHwService> service = getSoundTriggerHwService();
if (service == 0) {
return NO_INIT;
}
@@ -96,7 +96,7 @@
{
ALOGV("attach()");
sp<SoundTrigger> soundTrigger;
- const sp<ISoundTriggerHwService>& service = getSoundTriggerHwService();
+ const sp<ISoundTriggerHwService> service = getSoundTriggerHwService();
if (service == 0) {
return soundTrigger;
}
@@ -116,7 +116,7 @@
status_t SoundTrigger::setCaptureState(bool active)
{
ALOGV("setCaptureState(%d)", active);
- const sp<ISoundTriggerHwService>& service = getSoundTriggerHwService();
+ const sp<ISoundTriggerHwService> service = getSoundTriggerHwService();
if (service == 0) {
return NO_INIT;
}