Merge "Revert "Remove IDataSource dependency from DataSource.""
diff --git a/camera/ndk/include/camera/NdkCameraCaptureSession.h b/camera/ndk/include/camera/NdkCameraCaptureSession.h
index 67003c1..51cef8c 100644
--- a/camera/ndk/include/camera/NdkCameraCaptureSession.h
+++ b/camera/ndk/include/camera/NdkCameraCaptureSession.h
@@ -33,6 +33,7 @@
* Do not #include files that aren't part of the NDK.
*/
#include <sys/cdefs.h>
+#include <stdbool.h>
#include <android/native_window.h>
#include "NdkCameraError.h"
diff --git a/camera/ndk/include/camera/NdkCameraDevice.h b/camera/ndk/include/camera/NdkCameraDevice.h
index f425830..b715b12 100644
--- a/camera/ndk/include/camera/NdkCameraDevice.h
+++ b/camera/ndk/include/camera/NdkCameraDevice.h
@@ -153,6 +153,11 @@
} ACameraDevice_StateCallbacks;
/**
+ * For backward compatiblity.
+ */
+typedef ACameraDevice_StateCallbacks ACameraDevice_stateCallbacks;
+
+/**
* Close the connection and free this ACameraDevice synchronously. Access to the ACameraDevice
* after calling this method will cause a crash.
*
diff --git a/camera/ndk/include/camera/NdkCameraMetadataTags.h b/camera/ndk/include/camera/NdkCameraMetadataTags.h
index d35a52b..d88a7a5 100644
--- a/camera/ndk/include/camera/NdkCameraMetadataTags.h
+++ b/camera/ndk/include/camera/NdkCameraMetadataTags.h
@@ -4573,11 +4573,6 @@
* <li>ACaptureRequest</li>
* </ul></p>
*
- * <p>When set to ON,
- * ACAMERA_STATISTICS_OIS_TIMESTAMPS, android.statistics.oisShiftPixelX,
- * and android.statistics.oisShiftPixelY provide OIS data in the output result metadata.</p>
- *
- * @see ACAMERA_STATISTICS_OIS_TIMESTAMPS
*/
ACAMERA_STATISTICS_OIS_DATA_MODE = // byte (acamera_metadata_enum_android_statistics_ois_data_mode_t)
ACAMERA_STATISTICS_START + 17,
@@ -4610,7 +4605,7 @@
*
* <p>The array contains the amount of shifts in x direction, in pixels, based on OIS samples.
* A positive value is a shift from left to right in active array coordinate system. For
- * example, if the optical center is (1000, 500) in active array coordinates, an shift of
+ * example, if the optical center is (1000, 500) in active array coordinates, a shift of
* (3, 0) puts the new optical center at (1003, 500).</p>
* <p>The number of shifts must match the number of timestamps in
* ACAMERA_STATISTICS_OIS_TIMESTAMPS.</p>
@@ -4631,7 +4626,7 @@
*
* <p>The array contains the amount of shifts in y direction, in pixels, based on OIS samples.
* A positive value is a shift from top to bottom in active array coordinate system. For
- * example, if the optical center is (1000, 500) in active array coordinates, an shift of
+ * example, if the optical center is (1000, 500) in active array coordinates, a shift of
* (0, 5) puts the new optical center at (1000, 505).</p>
* <p>The number of shifts must match the number of timestamps in
* ACAMERA_STATISTICS_OIS_TIMESTAMPS.</p>
@@ -6998,19 +6993,24 @@
* <li>ACAMERA_LENS_RADIAL_DISTORTION</li>
* </ul>
* </li>
+ * <li>The SENSOR_INFO_TIMESTAMP_SOURCE of the logical device and physical devices must be
+ * the same.</li>
* <li>The logical camera device must be LIMITED or higher device.</li>
* </ul>
* <p>Both the logical camera device and its underlying physical devices support the
* mandatory stream combinations required for their device levels.</p>
* <p>Additionally, for each guaranteed stream combination, the logical camera supports:</p>
* <ul>
- * <li>Replacing one logical {@link AIMAGE_FORMAT_YUV_420_888 YUV_420_888}
+ * <li>For each guaranteed stream combination, the logical camera supports replacing one
+ * logical {@link AIMAGE_FORMAT_YUV_420_888 YUV_420_888}
* or raw stream with two physical streams of the same size and format, each from a
* separate physical camera, given that the size and format are supported by both
* physical cameras.</li>
- * <li>Adding two raw streams, each from one physical camera, if the logical camera doesn't
- * advertise RAW capability, but the underlying physical cameras do. This is usually
- * the case when the physical cameras have different sensor sizes.</li>
+ * <li>If the logical camera doesn't advertise RAW capability, but the underlying physical
+ * cameras do, the logical camera will support guaranteed stream combinations for RAW
+ * capability, except that the RAW streams will be physical streams, each from a separate
+ * physical camera. This is usually the case when the physical cameras have different
+ * sensor sizes.</li>
* </ul>
* <p>Using physical streams in place of a logical stream of the same size and format will
* not slow down the frame rate of the capture, as long as the minimum frame duration
@@ -7365,6 +7365,12 @@
/**
* <p>Include OIS data in the capture result.</p>
+ * <p>ACAMERA_STATISTICS_OIS_TIMESTAMPS, ACAMERA_STATISTICS_OIS_X_SHIFTS,
+ * and ACAMERA_STATISTICS_OIS_Y_SHIFTS provide OIS data in the output result metadata.</p>
+ *
+ * @see ACAMERA_STATISTICS_OIS_TIMESTAMPS
+ * @see ACAMERA_STATISTICS_OIS_X_SHIFTS
+ * @see ACAMERA_STATISTICS_OIS_Y_SHIFTS
*/
ACAMERA_STATISTICS_OIS_DATA_MODE_ON = 1,
diff --git a/cmds/stagefright/SineSource.cpp b/cmds/stagefright/SineSource.cpp
index cad8caf..0ecc16c 100644
--- a/cmds/stagefright/SineSource.cpp
+++ b/cmds/stagefright/SineSource.cpp
@@ -4,6 +4,7 @@
#include <media/stagefright/MediaBufferGroup.h>
#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/MediaBuffer.h>
#include <media/stagefright/MediaDefs.h>
#include <media/stagefright/MetaData.h>
@@ -59,10 +60,10 @@
}
status_t SineSource::read(
- MediaBuffer **out, const ReadOptions * /* options */) {
+ MediaBufferBase **out, const ReadOptions * /* options */) {
*out = NULL;
- MediaBuffer *buffer;
+ MediaBufferBase *buffer;
status_t err = mGroup->acquire_buffer(&buffer);
if (err != OK) {
@@ -88,7 +89,7 @@
x += k;
}
- buffer->meta_data()->setInt64(
+ buffer->meta_data().setInt64(
kKeyTime, ((int64_t)mPhase * 1000000) / mSampleRate);
mPhase += numFramesPerBuffer;
diff --git a/cmds/stagefright/SineSource.h b/cmds/stagefright/SineSource.h
index f1fb96d..1817291 100644
--- a/cmds/stagefright/SineSource.h
+++ b/cmds/stagefright/SineSource.h
@@ -18,7 +18,7 @@
virtual sp<MetaData> getFormat();
virtual status_t read(
- MediaBuffer **out, const ReadOptions *options = NULL);
+ MediaBufferBase **out, const ReadOptions *options = NULL);
protected:
virtual ~SineSource();
diff --git a/cmds/stagefright/audioloop.cpp b/cmds/stagefright/audioloop.cpp
index fc24646..d4f2e8d 100644
--- a/cmds/stagefright/audioloop.cpp
+++ b/cmds/stagefright/audioloop.cpp
@@ -169,7 +169,7 @@
ALOGI("Line: %d", __LINE__);
} else {
CHECK_EQ(decoder->start(), (status_t)OK);
- MediaBuffer* buffer;
+ MediaBufferBase* buffer;
while (decoder->read(&buffer) == OK) {
// do something with buffer (save it eventually?)
// need to stop after some count though...
diff --git a/cmds/stagefright/record.cpp b/cmds/stagefright/record.cpp
index 073ee6b..44b0015 100644
--- a/cmds/stagefright/record.cpp
+++ b/cmds/stagefright/record.cpp
@@ -328,7 +328,7 @@
int32_t n = 0;
status_t err;
- MediaBuffer *buffer;
+ MediaBufferBase *buffer;
while ((err = encoder->read(&buffer)) == OK) {
printf(".");
fflush(stdout);
diff --git a/cmds/stagefright/recordvideo.cpp b/cmds/stagefright/recordvideo.cpp
index af39d46..a63b9b9 100644
--- a/cmds/stagefright/recordvideo.cpp
+++ b/cmds/stagefright/recordvideo.cpp
@@ -90,7 +90,7 @@
}
virtual status_t read(
- MediaBuffer **buffer, const MediaSource::ReadOptions *options __unused) {
+ MediaBufferBase **buffer, const MediaSource::ReadOptions *options __unused) {
if (mNumFramesOutput % 10 == 0) {
fprintf(stderr, ".");
@@ -114,8 +114,8 @@
x = x >= 0xa0 ? 0x60 : x + 1;
#endif
(*buffer)->set_range(0, mSize);
- (*buffer)->meta_data()->clear();
- (*buffer)->meta_data()->setInt64(
+ (*buffer)->meta_data().clear();
+ (*buffer)->meta_data().setInt64(
kKeyTime, (mNumFramesOutput * 1000000) / mFrameRate);
++mNumFramesOutput;
diff --git a/cmds/stagefright/stagefright.cpp b/cmds/stagefright/stagefright.cpp
index bb517aa..936733d 100644
--- a/cmds/stagefright/stagefright.cpp
+++ b/cmds/stagefright/stagefright.cpp
@@ -150,7 +150,7 @@
status_t err;
for (;;) {
- MediaBuffer *mbuf;
+ MediaBufferBase *mbuf;
err = source->read(&mbuf);
if (err == INFO_FORMAT_CHANGED) {
@@ -234,7 +234,7 @@
CHECK(meta->findInt64(kKeyDuration, &durationUs));
status_t err;
- MediaBuffer *buffer;
+ MediaBufferBase *buffer;
MediaSource::ReadOptions options;
int64_t seekTimeUs = -1;
for (;;) {
@@ -253,7 +253,7 @@
shouldSeek = true;
} else {
int64_t timestampUs;
- CHECK(buffer->meta_data()->findInt64(kKeyTime, ×tampUs));
+ CHECK(buffer->meta_data().findInt64(kKeyTime, ×tampUs));
bool failed = false;
@@ -321,7 +321,7 @@
while (numIterationsLeft-- > 0) {
long numFrames = 0;
- MediaBuffer *buffer;
+ MediaBufferBase *buffer;
for (;;) {
int64_t startDecodeUs = getNowUs();
@@ -416,7 +416,7 @@
virtual sp<MetaData> getFormat();
virtual status_t read(
- MediaBuffer **buffer, const ReadOptions *options);
+ MediaBufferBase **buffer, const ReadOptions *options);
private:
enum StreamType {
@@ -465,7 +465,7 @@
return mSource->getFormat();
}
-static bool isIDRFrame(MediaBuffer *buffer) {
+static bool isIDRFrame(MediaBufferBase *buffer) {
const uint8_t *data =
(const uint8_t *)buffer->data() + buffer->range_offset();
size_t size = buffer->range_length();
@@ -482,7 +482,7 @@
}
status_t DetectSyncSource::read(
- MediaBuffer **buffer, const ReadOptions *options) {
+ MediaBufferBase **buffer, const ReadOptions *options) {
for (;;) {
status_t err = mSource->read(buffer, options);
@@ -492,12 +492,12 @@
if (mStreamType == AVC) {
bool isIDR = isIDRFrame(*buffer);
- (*buffer)->meta_data()->setInt32(kKeyIsSyncFrame, isIDR);
+ (*buffer)->meta_data().setInt32(kKeyIsSyncFrame, isIDR);
if (isIDR) {
mSawFirstIDRFrame = true;
}
} else {
- (*buffer)->meta_data()->setInt32(kKeyIsSyncFrame, true);
+ (*buffer)->meta_data().setInt32(kKeyIsSyncFrame, true);
}
if (mStreamType != AVC || mSawFirstIDRFrame) {
@@ -562,7 +562,7 @@
options.setSeekTo(
seekTimeUs, MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC);
- MediaBuffer *buffer;
+ MediaBufferBase *buffer;
status_t err;
for (;;) {
err = source->read(&buffer, &options);
@@ -591,7 +591,7 @@
if (err == OK) {
int64_t timeUs;
- CHECK(buffer->meta_data()->findInt64(kKeyTime, &timeUs));
+ CHECK(buffer->meta_data().findInt64(kKeyTime, &timeUs));
printf("%" PRId64 "\t%" PRId64 "\t%" PRId64 "\n",
seekTimeUs, timeUs, seekTimeUs - timeUs);
diff --git a/drm/libmediadrm/Android.bp b/drm/libmediadrm/Android.bp
index ea239c5..5ea4614 100644
--- a/drm/libmediadrm/Android.bp
+++ b/drm/libmediadrm/Android.bp
@@ -15,25 +15,20 @@
"IDrm.cpp",
"IDrmClient.cpp",
"IMediaDrmService.cpp",
- "PluginMetricsReporting.cpp",
"SharedLibrary.cpp",
"DrmHal.cpp",
- "DrmMetrics.cpp",
"CryptoHal.cpp",
- "protos/plugin_metrics.proto",
],
- proto: {
- type: "lite",
- },
-
shared_libs: [
"libbinder",
"libcutils",
"libdl",
"liblog",
+ "libmediadrmmetrics_lite",
"libmediametrics",
"libmediautils",
+ "libprotobuf-cpp-lite",
"libstagefright_foundation",
"libutils",
"android.hardware.drm@1.0",
@@ -48,3 +43,66 @@
"-Wall",
],
}
+
+// This is the version of the drm metrics configured for protobuf lite.
+cc_library_shared {
+ name: "libmediadrmmetrics_lite",
+ srcs: [
+ "DrmMetrics.cpp",
+ "PluginMetricsReporting.cpp",
+ "protos/metrics.proto",
+ ],
+
+ proto: {
+ export_proto_headers: true,
+ type: "lite",
+ },
+ shared_libs: [
+ "android.hardware.drm@1.0",
+ "android.hardware.drm@1.1",
+ "libbase",
+ "libbinder",
+ "liblog",
+ "libmediametrics",
+ "libprotobuf-cpp-lite",
+ "libutils",
+ ],
+ cflags: [
+ // Suppress unused parameter and no error options. These cause problems
+ // with the when using the map type in a proto definition.
+ "-Wno-unused-parameter",
+ "-Wno-error",
+ ],
+}
+
+// This is the version of the drm metrics library configured for full protobuf.
+cc_library_shared {
+ name: "libmediadrmmetrics_full",
+ srcs: [
+ "DrmMetrics.cpp",
+ "PluginMetricsReporting.cpp",
+ "protos/metrics.proto",
+ ],
+
+ proto: {
+ export_proto_headers: true,
+ type: "full",
+ },
+ shared_libs: [
+ "android.hardware.drm@1.0",
+ "android.hardware.drm@1.1",
+ "libbase",
+ "libbinder",
+ "liblog",
+ "libmediametrics",
+ "libprotobuf-cpp-full",
+ "libutils",
+ ],
+ cflags: [
+ // Suppress unused parameter and no error options. These cause problems
+ // when using the map type in a proto definition.
+ "-Wno-unused-parameter",
+ "-Wno-error",
+ ],
+}
+
diff --git a/drm/libmediadrm/DrmHal.cpp b/drm/libmediadrm/DrmHal.cpp
index 039e1e9..068a52b 100644
--- a/drm/libmediadrm/DrmHal.cpp
+++ b/drm/libmediadrm/DrmHal.cpp
@@ -16,6 +16,8 @@
//#define LOG_NDEBUG 0
#define LOG_TAG "DrmHal"
+#include <iomanip>
+
#include <utils/Log.h>
#include <binder/IPCThreadState.h>
@@ -52,6 +54,7 @@
using ::android::hardware::Return;
using ::android::hardware::Void;
using ::android::hidl::manager::V1_0::IServiceManager;
+using ::android::os::PersistableBundle;
using ::android::sp;
namespace {
@@ -97,6 +100,15 @@
return hidl_string(string.string());
}
+std::string toHexString(const std::string& str) {
+ std::ostringstream out;
+ out << std::hex << std::setfill('0');
+ for (size_t i = 0; i < str.size(); i++) {
+ out << std::setw(2) << (int)(str[i]);
+ }
+ return out.str();
+}
+
static DrmPlugin::SecurityLevel toSecurityLevel(SecurityLevel level) {
switch(level) {
case SecurityLevel::SW_SECURE_CRYPTO:
@@ -249,20 +261,39 @@
}
void DrmHal::closeOpenSessions() {
- if (mPlugin != NULL) {
- for (size_t i = 0; i < mOpenSessions.size(); i++) {
- mPlugin->closeSession(toHidlVec(mOpenSessions[i]));
- DrmSessionManager::Instance()->removeSession(mOpenSessions[i]);
- }
+ Mutex::Autolock autoLock(mLock);
+ auto openSessions = mOpenSessions;
+ for (size_t i = 0; i < openSessions.size(); i++) {
+ mLock.unlock();
+ closeSession(openSessions[i]);
+ mLock.lock();
}
mOpenSessions.clear();
}
DrmHal::~DrmHal() {
- closeOpenSessions();
DrmSessionManager::Instance()->removeDrm(mDrmSessionClient);
}
+void DrmHal::cleanup() {
+ closeOpenSessions();
+
+ Mutex::Autolock autoLock(mLock);
+ reportPluginMetrics();
+ reportFrameworkMetrics();
+
+ setListener(NULL);
+ mInitCheck = NO_INIT;
+
+ if (mPlugin != NULL) {
+ if (!mPlugin->setListener(NULL).isOk()) {
+ mInitCheck = DEAD_OBJECT;
+ }
+ }
+ mPlugin.clear();
+ mPluginV1_1.clear();
+}
+
Vector<sp<IDrmFactory>> DrmHal::makeDrmFactories() {
Vector<sp<IDrmFactory>> factories;
@@ -308,6 +339,7 @@
sp<IDrmPlugin> DrmHal::makeDrmPlugin(const sp<IDrmFactory>& factory,
const uint8_t uuid[16], const String8& appPackageName) {
+ mMetrics.SetAppPackageName(appPackageName);
sp<IDrmPlugin> plugin;
Return<void> hResult = factory->createPlugin(uuid, appPackageName.string(),
@@ -499,21 +531,7 @@
}
status_t DrmHal::destroyPlugin() {
- Mutex::Autolock autoLock(mLock);
- INIT_CHECK();
-
- closeOpenSessions();
- reportMetrics();
- setListener(NULL);
- mInitCheck = NO_INIT;
-
- if (mPlugin != NULL) {
- if (!mPlugin->setListener(NULL).isOk()) {
- mInitCheck = DEAD_OBJECT;
- }
- }
- mPlugin.clear();
- mPluginV1_1.clear();
+ cleanup();
return OK;
}
@@ -594,6 +612,7 @@
DrmSessionManager::Instance()->addSession(getCallingPid(),
mDrmSessionClient, sessionId);
mOpenSessions.push(sessionId);
+ mMetrics.SetSessionStart(sessionId);
}
mMetrics.mOpenSessionCounter.Increment(err);
@@ -615,8 +634,8 @@
}
}
}
- reportMetrics();
status_t response = toStatusT(status);
+ mMetrics.SetSessionEnd(sessionId);
mMetrics.mCloseSessionCounter.Increment(response);
return response;
}
@@ -631,7 +650,7 @@
String8 &defaultUrl, DrmPlugin::KeyRequestType *keyRequestType) {
Mutex::Autolock autoLock(mLock);
INIT_CHECK();
- EventTimer<status_t> keyRequestTimer(&mMetrics.mGetKeyRequestTiming);
+ EventTimer<status_t> keyRequestTimer(&mMetrics.mGetKeyRequestTimeUs);
DrmSessionManager::Instance()->useSession(sessionId);
@@ -726,7 +745,7 @@
status_t DrmHal::provideKeyResponse(Vector<uint8_t> const &sessionId,
Vector<uint8_t> const &response, Vector<uint8_t> &keySetId) {
Mutex::Autolock autoLock(mLock);
- EventTimer<status_t> keyResponseTimer(&mMetrics.mProvideKeyResponseTiming);
+ EventTimer<status_t> keyResponseTimer(&mMetrics.mProvideKeyResponseTimeUs);
INIT_CHECK();
@@ -1095,7 +1114,7 @@
return toStatusT(status);
}
-status_t DrmHal::getMetrics(MediaAnalyticsItem* item) {
+status_t DrmHal::getMetrics(PersistableBundle* item) {
if (item == nullptr) {
return UNEXPECTED_NULL;
}
@@ -1251,17 +1270,7 @@
void DrmHal::binderDied(const wp<IBinder> &the_late_who __unused)
{
- Mutex::Autolock autoLock(mLock);
- closeOpenSessions();
- setListener(NULL);
- mInitCheck = NO_INIT;
-
- if (mPlugin != NULL) {
- if (!mPlugin->setListener(NULL).isOk()) {
- mInitCheck = DEAD_OBJECT;
- }
- }
- mPlugin.clear();
+ cleanup();
}
void DrmHal::writeByteArray(Parcel &obj, hidl_vec<uint8_t> const &vec)
@@ -1274,8 +1283,41 @@
}
}
+void DrmHal::reportFrameworkMetrics() const
+{
+ MediaAnalyticsItem item("mediadrm");
+ item.generateSessionID();
+ item.setPkgName(mMetrics.GetAppPackageName().c_str());
+ String8 vendor;
+ String8 description;
+ status_t result = getPropertyStringInternal(String8("vendor"), vendor);
+ if (result != OK) {
+ ALOGE("Failed to get vendor from drm plugin: %d", result);
+ } else {
+ item.setCString("vendor", vendor.c_str());
+ }
+ result = getPropertyStringInternal(String8("description"), description);
+ if (result != OK) {
+ ALOGE("Failed to get description from drm plugin: %d", result);
+ } else {
+ item.setCString("description", description.c_str());
+ }
-void DrmHal::reportMetrics() const
+ std::string serializedMetrics;
+ result = mMetrics.GetSerializedMetrics(&serializedMetrics);
+ if (result != OK) {
+ ALOGE("Failed to serialize framework metrics: %d", result);
+ }
+ serializedMetrics = toHexString(serializedMetrics);
+ if (!serializedMetrics.empty()) {
+ item.setCString("serialized_metrics", serializedMetrics.c_str());
+ }
+ if (!item.selfrecord()) {
+ ALOGE("Failed to self record framework metrics");
+ }
+}
+
+void DrmHal::reportPluginMetrics() const
{
Vector<uint8_t> metrics;
String8 vendor;
@@ -1286,7 +1328,7 @@
status_t res = android::reportDrmPluginMetrics(
metrics, vendor, description);
if (res != OK) {
- ALOGE("Metrics were retrieved but could not be reported: %i", res);
+ ALOGE("Metrics were retrieved but could not be reported: %d", res);
}
}
}
diff --git a/drm/libmediadrm/DrmMetrics.cpp b/drm/libmediadrm/DrmMetrics.cpp
index 258c4b0..03bd88a 100644
--- a/drm/libmediadrm/DrmMetrics.cpp
+++ b/drm/libmediadrm/DrmMetrics.cpp
@@ -13,140 +13,330 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+#define LOG_TAG "DrmMetrics"
+#include <iomanip>
+#include <utility>
#include <android-base/macros.h>
#include <media/DrmMetrics.h>
+#include <media/stagefright/foundation/base64.h>
+#include <sys/time.h>
+#include <utils/Log.h>
+#include <utils/Timers.h>
+#include "protos/metrics.pb.h"
+
+using ::android::String16;
+using ::android::String8;
+using ::android::drm_metrics::DrmFrameworkMetrics;
using ::android::hardware::drm::V1_0::EventType;
using ::android::hardware::drm::V1_0::KeyStatusType;
+using ::android::os::PersistableBundle;
namespace {
-template<typename T>
-std::string GetAttributeName(T type);
+template <typename T> std::string GetAttributeName(T type);
-template<>
-std::string GetAttributeName<KeyStatusType>(KeyStatusType type) {
- static const char* type_names[] = {
- "USABLE", "EXPIRED", "OUTPUT_NOT_ALLOWED",
- "STATUS_PENDING", "INTERNAL_ERROR" };
- if (((size_t) type) > arraysize(type_names)) {
- return "UNKNOWN_TYPE";
- }
- return type_names[(size_t) type];
+template <> std::string GetAttributeName<KeyStatusType>(KeyStatusType type) {
+ static const char *type_names[] = {"USABLE", "EXPIRED",
+ "OUTPUT_NOT_ALLOWED", "STATUS_PENDING",
+ "INTERNAL_ERROR"};
+ if (((size_t)type) > arraysize(type_names)) {
+ return "UNKNOWN_TYPE";
+ }
+ return type_names[(size_t)type];
}
-template<>
-std::string GetAttributeName<EventType>(EventType type) {
- static const char* type_names[] = {
- "PROVISION_REQUIRED", "KEY_NEEDED", "KEY_EXPIRED",
- "VENDOR_DEFINED", "SESSION_RECLAIMED" };
- if (((size_t) type) > arraysize(type_names)) {
- return "UNKNOWN_TYPE";
- }
- return type_names[(size_t) type];
+template <> std::string GetAttributeName<EventType>(EventType type) {
+ static const char *type_names[] = {"PROVISION_REQUIRED", "KEY_NEEDED",
+ "KEY_EXPIRED", "VENDOR_DEFINED",
+ "SESSION_RECLAIMED"};
+ if (((size_t)type) > arraysize(type_names)) {
+ return "UNKNOWN_TYPE";
+ }
+ return type_names[(size_t)type];
}
-template<typename T>
-void ExportCounterMetric(const android::CounterMetric<T>& counter,
- android::MediaAnalyticsItem* item) {
- if (!item) {
- ALOGE("item was unexpectedly null.");
- return;
- }
- std::string success_count_name = counter.metric_name() + ".ok.count";
- std::string error_count_name = counter.metric_name() + ".error.count";
- counter.ExportValues(
- [&] (const android::status_t status, const int64_t value) {
- if (status == android::OK) {
- item->setInt64(success_count_name.c_str(), value);
- } else {
- int64_t total_errors(0);
- item->getInt64(error_count_name.c_str(), &total_errors);
- item->setInt64(error_count_name.c_str(), total_errors + value);
- // TODO: Add support for exporting the list of error values.
- // This probably needs to be added to MediaAnalyticsItem.
- }
- });
+template <typename T>
+void ExportCounterMetric(const android::CounterMetric<T> &counter,
+ PersistableBundle *metrics) {
+ if (!metrics) {
+ ALOGE("metrics was unexpectedly null.");
+ return;
+ }
+ std::string success_count_name = counter.metric_name() + ".ok.count";
+ std::string error_count_name = counter.metric_name() + ".error.count";
+ std::vector<int64_t> status_values;
+ counter.ExportValues(
+ [&](const android::status_t status, const int64_t value) {
+ if (status == android::OK) {
+ metrics->putLong(android::String16(success_count_name.c_str()),
+ value);
+ } else {
+ int64_t total_errors(0);
+ metrics->getLong(android::String16(error_count_name.c_str()),
+ &total_errors);
+ metrics->putLong(android::String16(error_count_name.c_str()),
+ total_errors + value);
+ status_values.push_back(status);
+ }
+ });
+ if (!status_values.empty()) {
+ std::string error_list_name = counter.metric_name() + ".error.list";
+ metrics->putLongVector(android::String16(error_list_name.c_str()),
+ status_values);
+ }
}
-template<typename T>
+template <typename T>
void ExportCounterMetricWithAttributeNames(
- const android::CounterMetric<T>& counter,
- android::MediaAnalyticsItem* item) {
- if (!item) {
- ALOGE("item was unexpectedly null.");
- return;
- }
- counter.ExportValues(
- [&] (const T& attribute, const int64_t value) {
- std::string name = counter.metric_name()
- + "." + GetAttributeName(attribute) + ".count";
- item->setInt64(name.c_str(), value);
- });
+ const android::CounterMetric<T> &counter, PersistableBundle *metrics) {
+ if (!metrics) {
+ ALOGE("metrics was unexpectedly null.");
+ return;
+ }
+ counter.ExportValues([&](const T &attribute, const int64_t value) {
+ std::string name = counter.metric_name() + "." +
+ GetAttributeName(attribute) + ".count";
+ metrics->putLong(android::String16(name.c_str()), value);
+ });
}
-template<typename T>
-void ExportEventMetric(const android::EventMetric<T>& event,
- android::MediaAnalyticsItem* item) {
- if (!item) {
- ALOGE("item was unexpectedly null.");
- return;
- }
- std::string success_count_name = event.metric_name() + ".ok.count";
- std::string error_count_name = event.metric_name() + ".error.count";
- std::string timing_name = event.metric_name() + ".ok.average_time_micros";
- event.ExportValues(
- [&] (const android::status_t& status,
- const android::EventStatistics& value) {
- if (status == android::OK) {
- item->setInt64(success_count_name.c_str(), value.count);
- item->setInt64(timing_name.c_str(), value.mean);
- } else {
- int64_t total_errors(0);
- item->getInt64(error_count_name.c_str(), &total_errors);
- item->setInt64(error_count_name.c_str(),
+template <typename T>
+void ExportEventMetric(const android::EventMetric<T> &event,
+ PersistableBundle *metrics) {
+ if (!metrics) {
+ ALOGE("metrics was unexpectedly null.");
+ return;
+ }
+ std::string success_count_name = event.metric_name() + ".ok.count";
+ std::string error_count_name = event.metric_name() + ".error.count";
+ std::string timing_name = event.metric_name() + ".ok.average_time_micros";
+ std::vector<int64_t> status_values;
+ event.ExportValues([&](const android::status_t &status,
+ const android::EventStatistics &value) {
+ if (status == android::OK) {
+ metrics->putLong(android::String16(success_count_name.c_str()),
+ value.count);
+ metrics->putLong(android::String16(timing_name.c_str()),
+ value.mean);
+ } else {
+ int64_t total_errors(0);
+ metrics->getLong(android::String16(error_count_name.c_str()),
+ &total_errors);
+ metrics->putLong(android::String16(error_count_name.c_str()),
total_errors + value.count);
- // TODO: Add support for exporting the list of error values.
- // This probably needs to be added to MediaAnalyticsItem.
- }
- });
+ status_values.push_back(status);
+ }
+ });
+ if (!status_values.empty()) {
+ std::string error_list_name = event.metric_name() + ".error.list";
+ metrics->putLongVector(android::String16(error_list_name.c_str()),
+ status_values);
+ }
}
-} // namespace anonymous
+void ExportSessionLifespans(
+ const std::map<std::string, std::pair<int64_t, int64_t>> &mSessionLifespans,
+ PersistableBundle *metrics) {
+ if (!metrics) {
+ ALOGE("metrics was unexpectedly null.");
+ return;
+ }
+
+ if (mSessionLifespans.empty()) {
+ return;
+ }
+
+ PersistableBundle startTimesBundle;
+ PersistableBundle endTimesBundle;
+ for (auto it = mSessionLifespans.begin(); it != mSessionLifespans.end();
+ it++) {
+ String16 key(it->first.c_str(), it->first.size());
+ startTimesBundle.putLong(key, it->second.first);
+ endTimesBundle.putLong(key, it->second.second);
+ }
+ metrics->putPersistableBundle(
+ android::String16("drm.mediadrm.session_start_times_ms"),
+ startTimesBundle);
+ metrics->putPersistableBundle(
+ android::String16("drm.mediadrm.session_end_times_ms"), endTimesBundle);
+}
+
+std::string ToHexString(const android::Vector<uint8_t> &sessionId) {
+ std::ostringstream out;
+ out << std::hex << std::setfill('0');
+ for (size_t i = 0; i < sessionId.size(); i++) {
+ out << std::setw(2) << (int)(sessionId[i]);
+ }
+ return out.str();
+}
+
+} // namespace
namespace android {
MediaDrmMetrics::MediaDrmMetrics()
: mOpenSessionCounter("drm.mediadrm.open_session", "status"),
mCloseSessionCounter("drm.mediadrm.close_session", "status"),
- mGetKeyRequestTiming("drm.mediadrm.get_key_request", "status"),
- mProvideKeyResponseTiming("drm.mediadrm.provide_key_response", "status"),
- mGetProvisionRequestCounter(
- "drm.mediadrm.get_provision_request", "status"),
+ mGetKeyRequestTimeUs("drm.mediadrm.get_key_request", "status"),
+ mProvideKeyResponseTimeUs("drm.mediadrm.provide_key_response", "status"),
+ mGetProvisionRequestCounter("drm.mediadrm.get_provision_request",
+ "status"),
mProvideProvisionResponseCounter(
"drm.mediadrm.provide_provision_response", "status"),
- mKeyStatusChangeCounter(
- "drm.mediadrm.key_status_change", "key_status_type"),
+ mKeyStatusChangeCounter("drm.mediadrm.key_status_change",
+ "key_status_type"),
mEventCounter("drm.mediadrm.event", "event_type"),
- mGetDeviceUniqueIdCounter(
- "drm.mediadrm.get_device_unique_id", "status") {
+ mGetDeviceUniqueIdCounter("drm.mediadrm.get_device_unique_id", "status") {
}
-void MediaDrmMetrics::Export(MediaAnalyticsItem* item) {
- if (!item) {
- ALOGE("item was unexpectedly null.");
- return;
- }
- ExportCounterMetric(mOpenSessionCounter, item);
- ExportCounterMetric(mCloseSessionCounter, item);
- ExportEventMetric(mGetKeyRequestTiming, item);
- ExportEventMetric(mProvideKeyResponseTiming, item);
- ExportCounterMetric(mGetProvisionRequestCounter, item);
- ExportCounterMetric(mProvideProvisionResponseCounter, item);
- ExportCounterMetricWithAttributeNames(mKeyStatusChangeCounter, item);
- ExportCounterMetricWithAttributeNames(mEventCounter, item);
- ExportCounterMetric(mGetDeviceUniqueIdCounter, item);
+void MediaDrmMetrics::SetSessionStart(
+ const android::Vector<uint8_t> &sessionId) {
+ std::string sessionIdHex = ToHexString(sessionId);
+ mSessionLifespans[sessionIdHex] =
+ std::make_pair(GetCurrentTimeMs(), (int64_t)0);
}
-} // namespace android
+void MediaDrmMetrics::SetSessionEnd(const android::Vector<uint8_t> &sessionId) {
+ std::string sessionIdHex = ToHexString(sessionId);
+ int64_t endTimeMs = GetCurrentTimeMs();
+ if (mSessionLifespans.find(sessionIdHex) != mSessionLifespans.end()) {
+ mSessionLifespans[sessionIdHex] =
+ std::make_pair(mSessionLifespans[sessionIdHex].first, endTimeMs);
+ } else {
+ mSessionLifespans[sessionIdHex] = std::make_pair((int64_t)0, endTimeMs);
+ }
+}
+
+void MediaDrmMetrics::Export(PersistableBundle *metrics) {
+ if (!metrics) {
+ ALOGE("metrics was unexpectedly null.");
+ return;
+ }
+ ExportCounterMetric(mOpenSessionCounter, metrics);
+ ExportCounterMetric(mCloseSessionCounter, metrics);
+ ExportEventMetric(mGetKeyRequestTimeUs, metrics);
+ ExportEventMetric(mProvideKeyResponseTimeUs, metrics);
+ ExportCounterMetric(mGetProvisionRequestCounter, metrics);
+ ExportCounterMetric(mProvideProvisionResponseCounter, metrics);
+ ExportCounterMetricWithAttributeNames(mKeyStatusChangeCounter, metrics);
+ ExportCounterMetricWithAttributeNames(mEventCounter, metrics);
+ ExportCounterMetric(mGetDeviceUniqueIdCounter, metrics);
+ ExportSessionLifespans(mSessionLifespans, metrics);
+}
+
+status_t MediaDrmMetrics::GetSerializedMetrics(std::string *serializedMetrics) {
+
+ if (!serializedMetrics) {
+ ALOGE("serializedMetrics was unexpectedly null.");
+ return UNEXPECTED_NULL;
+ }
+
+ DrmFrameworkMetrics metrics;
+
+ mOpenSessionCounter.ExportValues(
+ [&](const android::status_t status, const int64_t value) {
+ DrmFrameworkMetrics::Counter *counter =
+ metrics.add_open_session_counter();
+ counter->set_count(value);
+ counter->mutable_attributes()->set_error_code(status);
+ });
+
+ mCloseSessionCounter.ExportValues(
+ [&](const android::status_t status, const int64_t value) {
+ DrmFrameworkMetrics::Counter *counter =
+ metrics.add_close_session_counter();
+ counter->set_count(value);
+ counter->mutable_attributes()->set_error_code(status);
+ });
+
+ mGetProvisionRequestCounter.ExportValues(
+ [&](const android::status_t status, const int64_t value) {
+ DrmFrameworkMetrics::Counter *counter =
+ metrics.add_get_provisioning_request_counter();
+ counter->set_count(value);
+ counter->mutable_attributes()->set_error_code(status);
+ });
+
+ mProvideProvisionResponseCounter.ExportValues(
+ [&](const android::status_t status, const int64_t value) {
+ DrmFrameworkMetrics::Counter *counter =
+ metrics.add_provide_provisioning_response_counter();
+ counter->set_count(value);
+ counter->mutable_attributes()->set_error_code(status);
+ });
+
+ mKeyStatusChangeCounter.ExportValues(
+ [&](const KeyStatusType key_status_type, const int64_t value) {
+ DrmFrameworkMetrics::Counter *counter =
+ metrics.add_key_status_change_counter();
+ counter->set_count(value);
+ counter->mutable_attributes()->set_key_status_type(
+ (uint32_t)key_status_type);
+ });
+
+ mEventCounter.ExportValues(
+ [&](const EventType event_type, const int64_t value) {
+ DrmFrameworkMetrics::Counter *counter =
+ metrics.add_event_callback_counter();
+ counter->set_count(value);
+ counter->mutable_attributes()->set_event_type((uint32_t)event_type);
+ });
+
+ mGetDeviceUniqueIdCounter.ExportValues(
+ [&](const status_t status, const int64_t value) {
+ DrmFrameworkMetrics::Counter *counter =
+ metrics.add_get_device_unique_id_counter();
+ counter->set_count(value);
+ counter->mutable_attributes()->set_error_code(status);
+ });
+
+ mGetKeyRequestTimeUs.ExportValues(
+ [&](const status_t status, const EventStatistics &stats) {
+ DrmFrameworkMetrics::DistributionMetric *metric =
+ metrics.add_get_key_request_time_us();
+ metric->set_min(stats.min);
+ metric->set_max(stats.max);
+ metric->set_mean(stats.mean);
+ metric->set_operation_count(stats.count);
+ metric->set_variance(stats.sum_squared_deviation / stats.count);
+ metric->mutable_attributes()->set_error_code(status);
+ });
+
+ mProvideKeyResponseTimeUs.ExportValues(
+ [&](const status_t status, const EventStatistics &stats) {
+ DrmFrameworkMetrics::DistributionMetric *metric =
+ metrics.add_provide_key_response_time_us();
+ metric->set_min(stats.min);
+ metric->set_max(stats.max);
+ metric->set_mean(stats.mean);
+ metric->set_operation_count(stats.count);
+ metric->set_variance(stats.sum_squared_deviation / stats.count);
+ metric->mutable_attributes()->set_error_code(status);
+ });
+
+ for (const auto &sessionLifespan : mSessionLifespans) {
+ auto *map = metrics.mutable_session_lifetimes();
+
+ (*map)[sessionLifespan.first].set_start_time_ms(
+ sessionLifespan.second.first);
+ (*map)[sessionLifespan.first].set_end_time_ms(
+ sessionLifespan.second.second);
+ }
+
+ if (!metrics.SerializeToString(serializedMetrics)) {
+ ALOGE("Failed to serialize metrics.");
+ return UNKNOWN_ERROR;
+ }
+
+ return OK;
+}
+
+int64_t MediaDrmMetrics::GetCurrentTimeMs() {
+ struct timeval tv;
+ gettimeofday(&tv, NULL);
+ return ((int64_t)tv.tv_sec * 1000) + ((int64_t)tv.tv_usec / 1000);
+}
+
+} // namespace android
diff --git a/drm/libmediadrm/IDrm.cpp b/drm/libmediadrm/IDrm.cpp
index 9f54dba..22e4e6c 100644
--- a/drm/libmediadrm/IDrm.cpp
+++ b/drm/libmediadrm/IDrm.cpp
@@ -492,7 +492,10 @@
return reply.readInt32();
}
- virtual status_t getMetrics(MediaAnalyticsItem *item) {
+ virtual status_t getMetrics(os::PersistableBundle *metrics) {
+ if (metrics == NULL) {
+ return BAD_VALUE;
+ }
Parcel data, reply;
data.writeInterfaceToken(IDrm::getInterfaceDescriptor());
@@ -500,9 +503,23 @@
if (status != OK) {
return status;
}
+ // The reply data is ordered as
+ // 1) 32 bit integer reply followed by
+ // 2) Serialized PersistableBundle containing metrics.
+ status_t reply_status;
+ if (reply.readInt32(&reply_status) != OK
+ || reply_status != OK) {
+ ALOGE("Failed to read getMetrics response code from parcel. %d",
+ reply_status);
+ return reply_status;
+ }
- item->readFromParcel(reply);
- return reply.readInt32();
+ status = metrics->readFromParcel(&reply);
+ if (status != OK) {
+ ALOGE("Failed to read metrics from parcel. %d", status);
+ return status;
+ }
+ return reply_status;
}
virtual status_t setCipherAlgorithm(Vector<uint8_t> const &sessionId,
@@ -1008,11 +1025,18 @@
{
CHECK_INTERFACE(IDrm, data, reply);
- MediaAnalyticsItem item;
- status_t result = getMetrics(&item);
- item.writeToParcel(reply);
- reply->writeInt32(result);
- return OK;
+ os::PersistableBundle metrics;
+ status_t result = getMetrics(&metrics);
+ // The reply data is ordered as
+ // 1) 32 bit integer reply followed by
+ // 2) Serialized PersistableBundle containing metrics.
+ // Only write the metrics if the getMetrics result was
+ // OK and we successfully added the status to reply.
+ status_t parcel_result = reply->writeInt32(result);
+ if (result == OK && parcel_result == OK) {
+ parcel_result = metrics.writeToParcel(reply);
+ }
+ return parcel_result;
}
case SET_CIPHER_ALGORITHM:
diff --git a/drm/libmediadrm/PluginMetricsReporting.cpp b/drm/libmediadrm/PluginMetricsReporting.cpp
index 26c8427..6c97f2b 100644
--- a/drm/libmediadrm/PluginMetricsReporting.cpp
+++ b/drm/libmediadrm/PluginMetricsReporting.cpp
@@ -23,7 +23,7 @@
#include <media/MediaAnalyticsItem.h>
-#include "protos/plugin_metrics.pb.h"
+#include "protos/metrics.pb.h"
namespace android {
diff --git a/drm/libmediadrm/protos/metrics.proto b/drm/libmediadrm/protos/metrics.proto
new file mode 100644
index 0000000..aa26f5f
--- /dev/null
+++ b/drm/libmediadrm/protos/metrics.proto
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+syntax = "proto2";
+
+package android.drm_metrics;
+
+// The MetricsGroup is a collection of metric name/value pair instances
+// that can be serialized and provided to a caller.
+message MetricsGroup {
+ message Metric {
+ message MetricValue {
+ // Exactly one of the following values must be set.
+ optional int64 int_value = 1;
+ optional double double_value = 2;
+ optional string string_value = 3;
+ }
+
+ // The name of the metric. Must be valid UTF-8. Required.
+ optional string name = 1;
+
+ // The value of the metric. Required.
+ optional MetricValue value = 2;
+ }
+
+ // The list of name/value pairs of metrics.
+ repeated Metric metric = 1;
+
+ // Allow multiple sub groups of metrics.
+ repeated MetricsGroup metric_sub_group = 2;
+
+ // Name of the application package associated with the metrics.
+ optional string app_package_name = 3;
+}
+
+// This message contains the specific metrics captured by DrmMetrics. It is
+// used for serializing and logging metrics.
+// next id: 11.
+message DrmFrameworkMetrics {
+ // TODO: Consider using extensions.
+
+ // Attributes are associated with a recorded value. E.g. A counter may
+ // represent a count of an operation returning a specific error code. The
+ // error code will be an attribute.
+ message Attributes {
+ // Reserved for compatibility with logging proto.
+ reserved 2 to 13;
+
+ // A general purpose error code where 0 means OK.
+ optional int32 error_code = 1;
+
+ // Defined at ::android::hardware::drm::V1_0::KeyStatusType;
+ optional uint32 key_status_type = 14;
+
+ // Defined at ::android::hardware::drm::V1_0::EventType;
+ optional uint32 event_type = 15;
+ }
+
+ // The Counter message is used to store a count value with an associated
+ // Attribute.
+ message Counter {
+ optional int64 count = 1;
+ // Represents the attributes associated with this counter instance.
+ optional Attributes attributes = 2;
+ }
+
+ // The DistributionMetric is meant to capture the moments of a normally
+ // distributed (or approximately normal) value.
+ message DistributionMetric {
+ optional double min = 1;
+ optional double max = 2;
+ optional double mean = 3;
+ optional double variance = 4;
+ optional double operation_count = 5;
+
+ // Represents the attributes assocated with this distribution metric
+ // instance.
+ optional Attributes attributes = 6;
+ }
+
+ message SessionLifetime {
+ // Start time of the session in milliseconds since epoch.
+ optional int64 start_time_ms = 1;
+ // End time of the session in milliseconds since epoch.
+ optional int64 end_time_ms = 2;
+ }
+
+ // The count of open session operations. Each instance has a specific error
+ // code associated with it.
+ repeated Counter open_session_counter = 1;
+
+ // The count of close session operations. Each instance has a specific error
+ // code associated with it.
+ repeated Counter close_session_counter = 2;
+
+ // Count and execution time of getKeyRequest calls.
+ repeated DistributionMetric get_key_request_time_us = 3;
+
+ // Count and execution time of provideKeyResponse calls.
+ repeated DistributionMetric provide_key_response_time_us = 4;
+
+ // Count of getProvisionRequest calls.
+ repeated Counter get_provisioning_request_counter = 5;
+
+ // Count of provideProvisionResponse calls.
+ repeated Counter provide_provisioning_response_counter = 6;
+
+ // Count of key status events broken out by status type.
+ repeated Counter key_status_change_counter = 7;
+
+ // Count of events broken out by event type
+ repeated Counter event_callback_counter = 8;
+
+ // Count getPropertyByteArray calls to retrieve the device unique id.
+ repeated Counter get_device_unique_id_counter = 9;
+
+ // Session ids to lifetime (start and end time) map.
+ // Session ids are strings of hex-encoded byte arrays.
+ map<string, SessionLifetime> session_lifetimes = 10;
+}
+
diff --git a/drm/libmediadrm/protos/plugin_metrics.proto b/drm/libmediadrm/protos/plugin_metrics.proto
deleted file mode 100644
index 7e3bcf5..0000000
--- a/drm/libmediadrm/protos/plugin_metrics.proto
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Copyright (C) 2017 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.
- */
-
-syntax = "proto2";
-
-package android.drm_metrics;
-
-// need this if we are using libprotobuf-cpp-2.3.0-lite
-option optimize_for = LITE_RUNTIME;
-
-// The MetricsGroup is a collection of metric name/value pair instances
-// that can be serialized and provided to a caller.
-message MetricsGroup {
- message Metric {
- message MetricValue {
- // Exactly one of the following values must be set.
- optional int64 int_value = 1;
- optional double double_value = 2;
- optional string string_value = 3;
- }
-
- // The name of the metric. Must be valid UTF-8. Required.
- optional string name = 1;
-
- // The value of the metric. Required.
- optional MetricValue value = 2;
- }
-
- // The list of name/value pairs of metrics.
- repeated Metric metric = 1;
-
- // Allow multiple sub groups of metrics.
- repeated MetricsGroup metric_sub_group = 2;
-
- // Name of the application package associated with the metrics.
- optional string app_package_name = 3;
-}
diff --git a/drm/libmediadrm/tests/Android.bp b/drm/libmediadrm/tests/Android.bp
index fdc982d..670d3b9 100644
--- a/drm/libmediadrm/tests/Android.bp
+++ b/drm/libmediadrm/tests/Android.bp
@@ -16,16 +16,23 @@
srcs: ["DrmMetrics_test.cpp"],
shared_libs: [
"android.hardware.drm@1.0",
+ "libbinder",
"liblog",
- "libmediadrm",
+ "libmediadrmmetrics_full",
"libmediametrics",
+ "libprotobuf-cpp-full",
"libutils",
],
- include_dirs: ["frameworks/av/include/media"],
- cflags: [
- "-Werror",
- "-Wall",
+ static_libs: ["libgmock"],
+ include_dirs: [
+ "frameworks/av/include/media",
],
+ cflags: [
+ // Suppress unused parameter and no error options. These cause problems
+ // when using the map type in a proto definition.
+ "-Wno-unused-parameter",
+ "-Wno-error",
+ ]
}
cc_test {
diff --git a/drm/libmediadrm/tests/DrmMetrics_test.cpp b/drm/libmediadrm/tests/DrmMetrics_test.cpp
index d1948b4..fe762c9 100644
--- a/drm/libmediadrm/tests/DrmMetrics_test.cpp
+++ b/drm/libmediadrm/tests/DrmMetrics_test.cpp
@@ -14,27 +14,48 @@
* limitations under the License.
*/
-#include <gtest/gtest.h>
-
+#define LOG_TAG "DrmMetricsTest"
#include "DrmMetrics.h"
+#include <binder/PersistableBundle.h>
+#include <google/protobuf/text_format.h>
+#include <google/protobuf/util/message_differencer.h>
+#include <gtest/gtest.h>
+#include <utils/Log.h>
+
+#include "protos/metrics.pb.h"
+
+using ::android::drm_metrics::DrmFrameworkMetrics;
using ::android::hardware::drm::V1_0::EventType;
using ::android::hardware::drm::V1_0::KeyStatusType;
+using ::android::os::PersistableBundle;
+using ::google::protobuf::util::MessageDifferencer;
+using ::google::protobuf::TextFormat;
namespace android {
/**
* Unit tests for the MediaDrmMetrics class.
*/
-class MediaDrmMetricsTest : public ::testing::Test {
+class MediaDrmMetricsTest : public ::testing::Test {};
+
+/**
+ * This derived class mocks the clock for testing purposes.
+ */
+class FakeMediaDrmMetrics : public MediaDrmMetrics {
+ public:
+ FakeMediaDrmMetrics() : MediaDrmMetrics(), time_(0) {};
+
+ int64_t GetCurrentTimeMs() { return time_++; }
+ int64_t time_;
};
TEST_F(MediaDrmMetricsTest, EmptySuccess) {
MediaDrmMetrics metrics;
- MediaAnalyticsItem item;
+ PersistableBundle bundle;
- metrics.Export(&item);
- EXPECT_EQ(0, item.count());
+ metrics.Export(&bundle);
+ EXPECT_TRUE(bundle.empty());
}
TEST_F(MediaDrmMetricsTest, AllValuesSuccessCounts) {
@@ -44,9 +65,9 @@
metrics.mCloseSessionCounter.Increment(OK);
{
- EventTimer<status_t> get_key_request_timer(&metrics.mGetKeyRequestTiming);
+ EventTimer<status_t> get_key_request_timer(&metrics.mGetKeyRequestTimeUs);
EventTimer<status_t> provide_key_response_timer(
- &metrics.mProvideKeyResponseTiming);
+ &metrics.mProvideKeyResponseTimeUs);
get_key_request_timer.SetAttribute(OK);
provide_key_response_timer.SetAttribute(OK);
}
@@ -58,10 +79,10 @@
metrics.mKeyStatusChangeCounter.Increment(KeyStatusType::USABLE);
metrics.mEventCounter.Increment(EventType::PROVISION_REQUIRED);
- MediaAnalyticsItem item;
+ PersistableBundle bundle;
- metrics.Export(&item);
- EXPECT_EQ(11, item.count());
+ metrics.Export(&bundle);
+ EXPECT_EQ(11U, bundle.size());
// Verify the list of pairs of int64 metrics.
std::vector<std::pair<std::string, int64_t>> expected_values = {
@@ -75,24 +96,24 @@
{ "drm.mediadrm.event.PROVISION_REQUIRED.count", 1 },
{ "drm.mediadrm.get_device_unique_id.ok.count", 1 }};
for (const auto& expected_pair : expected_values) {
+ String16 key(expected_pair.first.c_str());
int64_t value = -1;
- EXPECT_TRUE(item.getInt64(expected_pair.first.c_str(), &value))
- << "Failed to get " << expected_pair.first;
+ EXPECT_TRUE(bundle.getLong(key, &value))
+ << "Unexpected error retrieviing key: " << key;
EXPECT_EQ(expected_pair.second, value)
- << "Unexpected value for " << expected_pair.first;
+ << "Unexpected value for " << expected_pair.first << ". " << value;
}
// Validate timing values exist.
+ String16 get_key_request_key(
+ "drm.mediadrm.get_key_request.ok.average_time_micros");
+ String16 provide_key_response_key(
+ "drm.mediadrm.provide_key_response.ok.average_time_micros");
int64_t value = -1;
- EXPECT_TRUE(
- item.getInt64("drm.mediadrm.get_key_request.ok.average_time_micros",
- &value));
+ EXPECT_TRUE(bundle.getLong(get_key_request_key, &value));
EXPECT_GE(value, 0);
-
value = -1;
- EXPECT_TRUE(
- item.getInt64("drm.mediadrm.provide_key_response.ok.average_time_micros",
- &value));
+ EXPECT_TRUE(bundle.getLong(provide_key_response_key, &value));
EXPECT_GE(value, 0);
}
@@ -107,9 +128,9 @@
for (status_t s : {OK, UNEXPECTED_NULL}) {
{
- EventTimer<status_t> get_key_request_timer(&metrics.mGetKeyRequestTiming);
+ EventTimer<status_t> get_key_request_timer(&metrics.mGetKeyRequestTimeUs);
EventTimer<status_t> provide_key_response_timer(
- &metrics.mProvideKeyResponseTiming);
+ &metrics.mProvideKeyResponseTimeUs);
get_key_request_timer.SetAttribute(s);
provide_key_response_timer.SetAttribute(s);
}
@@ -133,10 +154,23 @@
metrics.mEventCounter.Increment(EventType::VENDOR_DEFINED);
metrics.mEventCounter.Increment(EventType::SESSION_RECLAIMED);
- MediaAnalyticsItem item;
+ android::Vector<uint8_t> sessionId1;
+ sessionId1.push_back(1);
+ sessionId1.push_back(2);
+ android::Vector<uint8_t> sessionId2;
+ sessionId2.push_back(3);
+ sessionId2.push_back(4);
+ String16 hexSessionId1("0102");
+ String16 hexSessionId2("0304");
- metrics.Export(&item);
- EXPECT_EQ(26, item.count());
+ metrics.SetSessionStart(sessionId1);
+ metrics.SetSessionStart(sessionId2);
+ metrics.SetSessionEnd(sessionId2);
+ metrics.SetSessionEnd(sessionId1);
+
+ PersistableBundle bundle;
+ metrics.Export(&bundle);
+ EXPECT_EQ(35U, bundle.size());
// Verify the list of pairs of int64 metrics.
std::vector<std::pair<std::string, int64_t>> expected_values = {
@@ -165,25 +199,217 @@
{ "drm.mediadrm.event.VENDOR_DEFINED.count", 1 },
{ "drm.mediadrm.event.SESSION_RECLAIMED.count", 1 }};
for (const auto& expected_pair : expected_values) {
+ String16 key(expected_pair.first.c_str());
int64_t value = -1;
- EXPECT_TRUE(item.getInt64(expected_pair.first.c_str(), &value))
- << "Failed to get " << expected_pair.first;
+ EXPECT_TRUE(bundle.getLong(key, &value))
+ << "Unexpected error retrieviing key: " << key;
EXPECT_EQ(expected_pair.second, value)
- << "Unexpected value for " << expected_pair.first;
+ << "Unexpected value for " << expected_pair.first << ". " << value;
+ }
+
+ // Verify the error lists
+ std::vector<std::pair<std::string, std::vector<int64_t>>> expected_vector_values = {
+ { "drm.mediadrm.close_session.error.list", { UNEXPECTED_NULL } },
+ { "drm.mediadrm.get_device_unique_id.error.list", { UNEXPECTED_NULL } },
+ { "drm.mediadrm.get_key_request.error.list", { UNEXPECTED_NULL } },
+ { "drm.mediadrm.get_provision_request.error.list", { UNEXPECTED_NULL } },
+ { "drm.mediadrm.open_session.error.list", { UNEXPECTED_NULL } },
+ { "drm.mediadrm.provide_key_response.error.list", { UNEXPECTED_NULL } },
+ { "drm.mediadrm.provide_provision_response.error.list", { UNEXPECTED_NULL } }};
+ for (const auto& expected_pair : expected_vector_values) {
+ String16 key(expected_pair.first.c_str());
+ std::vector<int64_t> values;
+ EXPECT_TRUE(bundle.getLongVector(key, &values))
+ << "Unexpected error retrieviing key: " << key;
+ for (auto expected : expected_pair.second) {
+ EXPECT_TRUE(std::find(values.begin(), values.end(), expected) != values.end())
+ << "Could not find " << expected << " for key " << expected_pair.first;
+ }
+ }
+
+ // Verify the lifespans
+ PersistableBundle start_times;
+ PersistableBundle end_times;
+ String16 start_time_key("drm.mediadrm.session_start_times_ms");
+ String16 end_time_key("drm.mediadrm.session_end_times_ms");
+ ASSERT_TRUE(bundle.getPersistableBundle(start_time_key, &start_times));
+ ASSERT_TRUE(bundle.getPersistableBundle(end_time_key, &end_times));
+ EXPECT_EQ(2U, start_times.size());
+ EXPECT_EQ(2U, end_times.size());
+ int64_t start_time, end_time;
+ for (const auto& sid : { hexSessionId1, hexSessionId2 }) {
+ start_time = -1;
+ end_time = -1;
+ EXPECT_TRUE(start_times.getLong(sid, &start_time));
+ EXPECT_TRUE(end_times.getLong(sid, &end_time));
+ EXPECT_GT(start_time, 0);
+ EXPECT_GE(end_time, start_time);
}
// Validate timing values exist.
+ String16 get_key_request_key(
+ "drm.mediadrm.get_key_request.ok.average_time_micros");
+ String16 provide_key_response_key(
+ "drm.mediadrm.provide_key_response.ok.average_time_micros");
int64_t value = -1;
- EXPECT_TRUE(
- item.getInt64("drm.mediadrm.get_key_request.ok.average_time_micros",
- &value));
+ EXPECT_TRUE(bundle.getLong(get_key_request_key, &value));
EXPECT_GE(value, 0);
-
value = -1;
- EXPECT_TRUE(
- item.getInt64("drm.mediadrm.provide_key_response.ok.average_time_micros",
- &value));
+ EXPECT_TRUE(bundle.getLong(provide_key_response_key, &value));
EXPECT_GE(value, 0);
}
+
+TEST_F(MediaDrmMetricsTest, CounterValuesProtoSerialization) {
+ MediaDrmMetrics metrics;
+
+ metrics.mOpenSessionCounter.Increment(OK);
+ metrics.mOpenSessionCounter.Increment(UNEXPECTED_NULL);
+ metrics.mCloseSessionCounter.Increment(OK);
+ metrics.mCloseSessionCounter.Increment(UNEXPECTED_NULL);
+
+ metrics.mGetProvisionRequestCounter.Increment(OK);
+ metrics.mGetProvisionRequestCounter.Increment(UNEXPECTED_NULL);
+ metrics.mProvideProvisionResponseCounter.Increment(OK);
+ metrics.mProvideProvisionResponseCounter.Increment(UNEXPECTED_NULL);
+ metrics.mGetDeviceUniqueIdCounter.Increment(OK);
+ metrics.mGetDeviceUniqueIdCounter.Increment(UNEXPECTED_NULL);
+
+ metrics.mKeyStatusChangeCounter.Increment(KeyStatusType::USABLE);
+ metrics.mKeyStatusChangeCounter.Increment(KeyStatusType::EXPIRED);
+ metrics.mKeyStatusChangeCounter.Increment(KeyStatusType::OUTPUTNOTALLOWED);
+ metrics.mKeyStatusChangeCounter.Increment(KeyStatusType::STATUSPENDING);
+ metrics.mKeyStatusChangeCounter.Increment(KeyStatusType::INTERNALERROR);
+ metrics.mEventCounter.Increment(EventType::PROVISION_REQUIRED);
+ metrics.mEventCounter.Increment(EventType::KEY_NEEDED);
+ metrics.mEventCounter.Increment(EventType::KEY_EXPIRED);
+ metrics.mEventCounter.Increment(EventType::VENDOR_DEFINED);
+ metrics.mEventCounter.Increment(EventType::SESSION_RECLAIMED);
+
+ std::string serializedMetrics;
+ ASSERT_EQ(OK, metrics.GetSerializedMetrics(&serializedMetrics));
+
+ DrmFrameworkMetrics metricsProto;
+ ASSERT_TRUE(metricsProto.ParseFromString(serializedMetrics));
+
+ std::string expectedMetrics =
+ "open_session_counter { count: 1 attributes { error_code: -0x7FFFFFF8 } } "
+ "open_session_counter { count: 1 attributes { error_code: 0 } } "
+ "close_session_counter { count: 1 attributes { error_code: -0x7FFFFFF8 } } "
+ "close_session_counter { count: 1 attributes { error_code: 0 } } "
+ "get_provisioning_request_counter { count: 1 attributes { error_code: -0x7FFFFFF8 } } "
+ "get_provisioning_request_counter { count: 1 attributes { error_code: 0 } } "
+ "provide_provisioning_response_counter { count: 1 attributes { error_code: -0x7ffffff8 } } "
+ "provide_provisioning_response_counter { count: 1 attributes { error_code: 0 } } "
+ "get_device_unique_id_counter { count: 1 attributes { error_code: -0x7ffffff8 } } "
+ "get_device_unique_id_counter { count: 1 attributes { error_code: 0 } } "
+ "key_status_change_counter { count: 1 attributes { key_status_type: 0 } } "
+ "key_status_change_counter { count: 1 attributes { key_status_type: 1 } } "
+ "key_status_change_counter { count: 1 attributes { key_status_type: 2 } } "
+ "key_status_change_counter { count: 1 attributes { key_status_type: 3 } } "
+ "key_status_change_counter { count: 1 attributes { key_status_type: 4 } } "
+ "event_callback_counter { count: 1 attributes { event_type: 0 } } "
+ "event_callback_counter { count: 1 attributes { event_type: 1 } } "
+ "event_callback_counter { count: 1 attributes { event_type: 2 } } "
+ "event_callback_counter { count: 1 attributes { event_type: 3 } } "
+ "event_callback_counter { count: 1 attributes { event_type: 4 } } ";
+
+ DrmFrameworkMetrics expectedMetricsProto;
+ ASSERT_TRUE(TextFormat::MergeFromString(expectedMetrics, &expectedMetricsProto));
+
+ std::string diffString;
+ MessageDifferencer differ;
+ differ.ReportDifferencesToString(&diffString);
+ ASSERT_TRUE(differ.Compare(expectedMetricsProto, metricsProto))
+ << diffString;
+}
+
+TEST_F(MediaDrmMetricsTest, TimeMetricsProtoSerialization) {
+ MediaDrmMetrics metrics;
+
+ for (status_t s : {OK, UNEXPECTED_NULL}) {
+ double time = 0;
+ for (int i = 0; i < 5; i++) {
+ time += 1.0;
+ metrics.mGetKeyRequestTimeUs.Record(time, s);
+ metrics.mProvideKeyResponseTimeUs.Record(time, s);
+ }
+ }
+
+ std::string serializedMetrics;
+ ASSERT_EQ(OK, metrics.GetSerializedMetrics(&serializedMetrics));
+
+ DrmFrameworkMetrics metricsProto;
+ ASSERT_TRUE(metricsProto.ParseFromString(serializedMetrics));
+
+ std::string expectedMetrics =
+ "get_key_request_timing { "
+ " min: 1 max: 5 mean: 3.5 variance: 1 operation_count: 5 "
+ " attributes { error_code: -0x7FFFFFF8 } "
+ "} "
+ "get_key_request_timing { "
+ " min: 1 max: 5 mean: 3.5 variance: 1 operation_count: 5 "
+ " attributes { error_code: 0 } "
+ "} "
+ "provide_key_response_timing { "
+ " min: 1 max: 5 mean: 3.5 variance: 1 operation_count: 5 "
+ " attributes { error_code: -0x7FFFFFF8 } "
+ "} "
+ "provide_key_response_timing { "
+ " min: 1 max: 5 mean: 3.5 variance: 1 operation_count: 5 "
+ " attributes { error_code: 0 } "
+ "} ";
+
+ DrmFrameworkMetrics expectedMetricsProto;
+ ASSERT_TRUE(TextFormat::MergeFromString(expectedMetrics, &expectedMetricsProto));
+
+ std::string diffString;
+ MessageDifferencer differ;
+ differ.ReportDifferencesToString(&diffString);
+ ASSERT_TRUE(differ.Compare(expectedMetricsProto, metricsProto))
+ << diffString;
+}
+
+TEST_F(MediaDrmMetricsTest, SessionLifetimeProtoSerialization) {
+ // Use the fake so the clock is predictable;
+ FakeMediaDrmMetrics metrics;
+
+ android::Vector<uint8_t> sessionId1;
+ sessionId1.push_back(1);
+ sessionId1.push_back(2);
+ android::Vector<uint8_t> sessionId2;
+ sessionId2.push_back(3);
+ sessionId2.push_back(4);
+
+ metrics.SetSessionStart(sessionId1);
+ metrics.SetSessionStart(sessionId2);
+ metrics.SetSessionEnd(sessionId2);
+ metrics.SetSessionEnd(sessionId1);
+
+ std::string serializedMetrics;
+ ASSERT_EQ(OK, metrics.GetSerializedMetrics(&serializedMetrics));
+
+ DrmFrameworkMetrics metricsProto;
+ ASSERT_TRUE(metricsProto.ParseFromString(serializedMetrics));
+
+ std::string expectedMetrics =
+ "session_lifetimes: { "
+ " key: '0102' "
+ " value { start_time_ms: 0 end_time_ms: 3 } "
+ "} "
+ "session_lifetimes: { "
+ " key: '0304' "
+ " value { start_time_ms: 1 end_time_ms: 2 } "
+ "} ";
+
+ DrmFrameworkMetrics expectedMetricsProto;
+ ASSERT_TRUE(TextFormat::MergeFromString(expectedMetrics, &expectedMetricsProto));
+
+ std::string diffString;
+ MessageDifferencer differ;
+ differ.ReportDifferencesToString(&diffString);
+ ASSERT_TRUE(differ.Compare(expectedMetricsProto, metricsProto))
+ << diffString;
+}
+
} // namespace android
diff --git a/drm/mediadrm/plugins/clearkey/hidl/DrmPlugin.cpp b/drm/mediadrm/plugins/clearkey/hidl/DrmPlugin.cpp
index d921d9e..6b0201a 100644
--- a/drm/mediadrm/plugins/clearkey/hidl/DrmPlugin.cpp
+++ b/drm/mediadrm/plugins/clearkey/hidl/DrmPlugin.cpp
@@ -110,6 +110,38 @@
return Status::ERROR_DRM_SESSION_NOT_OPENED;
}
+Status DrmPlugin::getKeyRequestCommon(const hidl_vec<uint8_t>& scope,
+ const hidl_vec<uint8_t>& initData,
+ const hidl_string& mimeType,
+ KeyType keyType,
+ const hidl_vec<KeyValue>& optionalParameters,
+ std::vector<uint8_t> *request,
+ KeyRequestType *keyRequestType,
+ std::string *defaultUrl) {
+ UNUSED(optionalParameters);
+
+ *defaultUrl = "";
+ *keyRequestType = KeyRequestType::UNKNOWN;
+ *request = std::vector<uint8_t>();
+
+ if (scope.size() == 0) {
+ return Status::BAD_VALUE;
+ }
+
+ if (keyType != KeyType::STREAMING) {
+ return Status::ERROR_DRM_CANNOT_HANDLE;
+ }
+
+ sp<Session> session = mSessionLibrary->findSession(toVector(scope));
+ if (!session.get()) {
+ return Status::ERROR_DRM_SESSION_NOT_OPENED;
+ }
+
+ Status status = session->getKeyRequest(initData, mimeType, request);
+ *keyRequestType = KeyRequestType::INITIAL;
+ return status;
+}
+
Return<void> DrmPlugin::getKeyRequest(
const hidl_vec<uint8_t>& scope,
const hidl_vec<uint8_t>& initData,
@@ -119,29 +151,16 @@
getKeyRequest_cb _hidl_cb) {
UNUSED(optionalParameters);
- if (scope.size() == 0) {
- // Returns empty keyRequest, unknown keyType and empty defaultUrl
- _hidl_cb(Status::BAD_VALUE, hidl_vec<uint8_t>(),
- KeyRequestType::UNKNOWN, "");
- return Void();
- }
-
- if (keyType != KeyType::STREAMING) {
- _hidl_cb(Status::ERROR_DRM_CANNOT_HANDLE, hidl_vec<uint8_t>(),
- KeyRequestType::UNKNOWN, "");
- return Void();
- }
-
- sp<Session> session = mSessionLibrary->findSession(toVector(scope));
- if (!session.get()) {
- _hidl_cb(Status::ERROR_DRM_SESSION_NOT_OPENED, hidl_vec<uint8_t>(),
- KeyRequestType::UNKNOWN, "");
- return Void();
- }
-
+ KeyRequestType keyRequestType = KeyRequestType::UNKNOWN;
+ std::string defaultUrl("");
std::vector<uint8_t> request;
- Status status = session->getKeyRequest(initData, mimeType, &request);
- _hidl_cb(status, toHidlVec(request), KeyRequestType::INITIAL, "");
+ Status status = getKeyRequestCommon(
+ scope, initData, mimeType, keyType, optionalParameters,
+ &request, &keyRequestType, &defaultUrl);
+
+ _hidl_cb(status, toHidlVec(request),
+ static_cast<drm::V1_0::KeyRequestType>(keyRequestType),
+ hidl_string(defaultUrl));
return Void();
}
@@ -152,23 +171,16 @@
KeyType keyType,
const hidl_vec<KeyValue>& optionalParameters,
getKeyRequest_1_1_cb _hidl_cb) {
- hidl_string defaultUrl;
- hidl_vec<uint8_t> request;
- ::android::hardware::drm::V1_1::KeyRequestType requestType =
- static_cast<::android::hardware::drm::V1_1::KeyRequestType>(KeyRequestType::UNKNOWN);
- Status status = Status::OK;
+ UNUSED(optionalParameters);
- defaultUrl.clear();
- getKeyRequest(scope, initData, mimeType, keyType, optionalParameters,
- [&](Status statusCode, const hidl_vec<uint8_t>& hResult,
- KeyRequestType hKeyRequestType,
- const hidl_string& hDefaultUrl) {
- defaultUrl = hDefaultUrl;
- request = hResult;
- requestType = static_cast<::android::hardware::drm::V1_1::KeyRequestType>(hKeyRequestType);
- status = statusCode;
- });
- _hidl_cb(status, request, requestType, defaultUrl);
+ KeyRequestType keyRequestType = KeyRequestType::UNKNOWN;
+ std::string defaultUrl("");
+ std::vector<uint8_t> request;
+ Status status = getKeyRequestCommon(
+ scope, initData, mimeType, keyType, optionalParameters,
+ &request, &keyRequestType, &defaultUrl);
+
+ _hidl_cb(status, toHidlVec(request), keyRequestType, hidl_string(defaultUrl));
return Void();
}
diff --git a/drm/mediadrm/plugins/clearkey/hidl/include/DrmPlugin.h b/drm/mediadrm/plugins/clearkey/hidl/include/DrmPlugin.h
index 5d12598..19baf0b 100644
--- a/drm/mediadrm/plugins/clearkey/hidl/include/DrmPlugin.h
+++ b/drm/mediadrm/plugins/clearkey/hidl/include/DrmPlugin.h
@@ -31,7 +31,6 @@
using ::android::hardware::drm::V1_0::EventType;
using ::android::hardware::drm::V1_0::IDrmPluginListener;
-using ::android::hardware::drm::V1_0::KeyRequestType;
using ::android::hardware::drm::V1_0::KeyStatus;
using ::android::hardware::drm::V1_0::KeyType;
using ::android::hardware::drm::V1_0::KeyValue;
@@ -39,6 +38,8 @@
using ::android::hardware::drm::V1_0::SecureStopId;
using ::android::hardware::drm::V1_0::Status;
using ::android::hardware::drm::V1_1::DrmMetricGroup;
+using ::android::hardware::drm::V1_1::IDrmPlugin;
+using ::android::hardware::drm::V1_1::KeyRequestType;
using ::android::hardware::hidl_string;
using ::android::hardware::hidl_vec;
@@ -46,7 +47,6 @@
using ::android::hardware::Void;
using ::android::sp;
-
struct DrmPlugin : public IDrmPlugin {
explicit DrmPlugin(SessionLibrary* sessionLibrary);
@@ -335,6 +335,15 @@
Return<Status> setSecurityLevel(const hidl_vec<uint8_t>& sessionId,
SecurityLevel level);
+ Status getKeyRequestCommon(const hidl_vec<uint8_t>& scope,
+ const hidl_vec<uint8_t>& initData,
+ const hidl_string& mimeType,
+ KeyType keyType,
+ const hidl_vec<KeyValue>& optionalParameters,
+ std::vector<uint8_t> *request,
+ KeyRequestType *getKeyRequestType,
+ std::string *defaultUrl);
+
std::vector<KeyValue> mPlayPolicy;
std::map<std::string, std::string> mStringProperties;
std::map<std::string, std::vector<uint8_t> > mByteArrayProperties;
diff --git a/include/media/MediaSourceBase.h b/include/media/MediaSourceBase.h
deleted file mode 120000
index fe227b1..0000000
--- a/include/media/MediaSourceBase.h
+++ /dev/null
@@ -1 +0,0 @@
-../../media/libmediaextractor/include/media/MediaSourceBase.h
\ No newline at end of file
diff --git a/include/media/MediaTrack.h b/include/media/MediaTrack.h
new file mode 120000
index 0000000..5a63287a
--- /dev/null
+++ b/include/media/MediaTrack.h
@@ -0,0 +1 @@
+../../media/libmediaextractor/include/media/MediaTrack.h
\ No newline at end of file
diff --git a/include/media/VorbisComment.h b/include/media/VorbisComment.h
new file mode 120000
index 0000000..adaa489
--- /dev/null
+++ b/include/media/VorbisComment.h
@@ -0,0 +1 @@
+../../media/libmediaextractor/include/media/VorbisComment.h
\ No newline at end of file
diff --git a/include/private/media/AudioTrackShared.h b/include/private/media/AudioTrackShared.h
index ff440bc..b4fa3c5 100644
--- a/include/private/media/AudioTrackShared.h
+++ b/include/private/media/AudioTrackShared.h
@@ -438,7 +438,11 @@
return 0;
}
- virtual uint32_t getUnderrunFrames() const {
+ virtual uint32_t getUnderrunFrames() const override {
+ return 0;
+ }
+
+ virtual uint32_t getUnderrunCount() const override {
return 0;
}
diff --git a/media/extractors/aac/AACExtractor.cpp b/media/extractors/aac/AACExtractor.cpp
index 9df0aaa..bbc1ff8 100644
--- a/media/extractors/aac/AACExtractor.cpp
+++ b/media/extractors/aac/AACExtractor.cpp
@@ -20,7 +20,7 @@
#include "AACExtractor.h"
#include <media/DataSourceBase.h>
-#include <media/MediaSourceBase.h>
+#include <media/MediaTrack.h>
#include <media/stagefright/foundation/ABuffer.h>
#include <media/stagefright/foundation/AMessage.h>
#include <media/stagefright/foundation/ADebug.h>
@@ -33,20 +33,21 @@
namespace android {
-class AACSource : public MediaSourceBase {
+class AACSource : public MediaTrack {
public:
- AACSource(DataSourceBase *source,
- const sp<MetaData> &meta,
- const Vector<uint64_t> &offset_vector,
- int64_t frame_duration_us);
+ AACSource(
+ DataSourceBase *source,
+ MetaDataBase &meta,
+ const Vector<uint64_t> &offset_vector,
+ int64_t frame_duration_us);
- virtual status_t start(MetaData *params = NULL);
+ virtual status_t start(MetaDataBase *params = NULL);
virtual status_t stop();
- virtual sp<MetaData> getFormat();
+ virtual status_t getFormat(MetaDataBase&);
virtual status_t read(
- MediaBuffer **buffer, const ReadOptions *options = NULL);
+ MediaBufferBase **buffer, const ReadOptions *options = NULL);
protected:
virtual ~AACSource();
@@ -54,7 +55,7 @@
private:
static const size_t kMaxFrameSize;
DataSourceBase *mDataSource;
- sp<MetaData> mMeta;
+ MetaDataBase mMeta;
off64_t mOffset;
int64_t mCurrentTimeUs;
@@ -132,19 +133,10 @@
}
AACExtractor::AACExtractor(
- DataSourceBase *source, const sp<AMessage> &_meta)
+ DataSourceBase *source, off64_t offset)
: mDataSource(source),
mInitCheck(NO_INIT),
mFrameDurationUs(0) {
- sp<AMessage> meta = _meta;
-
- if (meta == NULL) {
- ALOGE("no metadata specified");
- return;
- }
-
- int64_t offset;
- CHECK(meta->findInt64("offset", &offset));
uint8_t profile, sf_index, channel, header[2];
if (mDataSource->readAt(offset + 2, &header, 2) < 2) {
@@ -159,7 +151,7 @@
}
channel = (header[0] & 0x1) << 2 | (header[1] >> 6);
- mMeta = MakeAACCodecSpecificData(profile, sf_index, channel);
+ MakeAACCodecSpecificData(mMeta, profile, sf_index, channel);
off64_t streamSize, numFrames = 0;
size_t frameSize = 0;
@@ -182,7 +174,7 @@
// Round up and get the duration
mFrameDurationUs = (1024 * 1000000ll + (sr - 1)) / sr;
duration = numFrames * mFrameDurationUs;
- mMeta->setInt64(kKeyDuration, duration);
+ mMeta.setInt64(kKeyDuration, duration);
}
mInitCheck = OK;
@@ -191,23 +183,20 @@
AACExtractor::~AACExtractor() {
}
-sp<MetaData> AACExtractor::getMetaData() {
- sp<MetaData> meta = new MetaData;
-
- if (mInitCheck != OK) {
- return meta;
+status_t AACExtractor::getMetaData(MetaDataBase &meta) {
+ meta.clear();
+ if (mInitCheck == OK) {
+ meta.setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_AAC_ADTS);
}
- meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_AAC_ADTS);
-
- return meta;
+ return OK;
}
size_t AACExtractor::countTracks() {
return mInitCheck == OK ? 1 : 0;
}
-MediaSourceBase *AACExtractor::getTrack(size_t index) {
+MediaTrack *AACExtractor::getTrack(size_t index) {
if (mInitCheck != OK || index != 0) {
return NULL;
}
@@ -215,12 +204,13 @@
return new AACSource(mDataSource, mMeta, mOffsetVector, mFrameDurationUs);
}
-sp<MetaData> AACExtractor::getTrackMetaData(size_t index, uint32_t /* flags */) {
+status_t AACExtractor::getTrackMetaData(MetaDataBase &meta, size_t index, uint32_t /* flags */) {
if (mInitCheck != OK || index != 0) {
- return NULL;
+ return UNKNOWN_ERROR;
}
- return mMeta;
+ meta = mMeta;
+ return OK;
}
////////////////////////////////////////////////////////////////////////////////
@@ -229,7 +219,8 @@
const size_t AACSource::kMaxFrameSize = 8192;
AACSource::AACSource(
- DataSourceBase *source, const sp<MetaData> &meta,
+ DataSourceBase *source,
+ MetaDataBase &meta,
const Vector<uint64_t> &offset_vector,
int64_t frame_duration_us)
: mDataSource(source),
@@ -248,7 +239,7 @@
}
}
-status_t AACSource::start(MetaData * /* params */) {
+status_t AACSource::start(MetaDataBase * /* params */) {
CHECK(!mStarted);
if (mOffsetVector.empty()) {
@@ -259,7 +250,7 @@
mCurrentTimeUs = 0;
mGroup = new MediaBufferGroup;
- mGroup->add_buffer(new MediaBuffer(kMaxFrameSize));
+ mGroup->add_buffer(MediaBufferBase::Create(kMaxFrameSize));
mStarted = true;
return OK;
@@ -275,12 +266,13 @@
return OK;
}
-sp<MetaData> AACSource::getFormat() {
- return mMeta;
+status_t AACSource::getFormat(MetaDataBase &meta) {
+ meta = mMeta;
+ return OK;
}
status_t AACSource::read(
- MediaBuffer **out, const ReadOptions *options) {
+ MediaBufferBase **out, const ReadOptions *options) {
*out = NULL;
int64_t seekTimeUs;
@@ -303,7 +295,7 @@
return ERROR_END_OF_STREAM;
}
- MediaBuffer *buffer;
+ MediaBufferBase *buffer;
status_t err = mGroup->acquire_buffer(&buffer);
if (err != OK) {
return err;
@@ -319,8 +311,8 @@
}
buffer->set_range(0, frameSizeWithoutHeader);
- buffer->meta_data()->setInt64(kKeyTime, mCurrentTimeUs);
- buffer->meta_data()->setInt32(kKeyIsSyncFrame, 1);
+ buffer->meta_data().setInt64(kKeyTime, mCurrentTimeUs);
+ buffer->meta_data().setInt32(kKeyIsSyncFrame, 1);
mOffset += frameSize;
mCurrentTimeUs += mFrameDurationUs;
@@ -334,14 +326,8 @@
static MediaExtractor* CreateExtractor(
DataSourceBase *source,
void *meta) {
- sp<AMessage> metaData = static_cast<AMessage *>(meta);
- return new AACExtractor(source, metaData);
-}
-
-static void FreeMeta(void *meta) {
- if (meta != nullptr) {
- static_cast<AMessage *>(meta)->decStrong(nullptr);
- }
+ off64_t offset = *static_cast<off64_t*>(meta);
+ return new AACExtractor(source, offset);
}
static MediaExtractor::CreatorFunc Sniff(
@@ -386,12 +372,9 @@
if ((header[0] == 0xff) && ((header[1] & 0xf6) == 0xf0)) {
*confidence = 0.2;
- AMessage *msg = new AMessage;
- msg->setInt64("offset", pos);
- *meta = msg;
- *freeMeta = &FreeMeta;
- // ref count will be decreased in FreeMeta.
- msg->incStrong(nullptr);
+ off64_t *offPtr = (off64_t*) malloc(sizeof(off64_t));
+ *meta = offPtr;
+ *freeMeta = ::free;
return CreateExtractor;
}
diff --git a/media/extractors/aac/AACExtractor.h b/media/extractors/aac/AACExtractor.h
index e99699c..9dadbed 100644
--- a/media/extractors/aac/AACExtractor.h
+++ b/media/extractors/aac/AACExtractor.h
@@ -19,6 +19,7 @@
#define AAC_EXTRACTOR_H_
#include <media/MediaExtractor.h>
+#include <media/stagefright/MetaDataBase.h>
#include <utils/Vector.h>
@@ -29,13 +30,13 @@
class AACExtractor : public MediaExtractor {
public:
- AACExtractor(DataSourceBase *source, const sp<AMessage> &meta);
+ AACExtractor(DataSourceBase *source, off64_t offset);
virtual size_t countTracks();
- virtual MediaSourceBase *getTrack(size_t index);
- virtual sp<MetaData> getTrackMetaData(size_t index, uint32_t flags);
+ virtual MediaTrack *getTrack(size_t index);
+ virtual status_t getTrackMetaData(MetaDataBase& meta, size_t index, uint32_t flags);
- virtual sp<MetaData> getMetaData();
+ virtual status_t getMetaData(MetaDataBase& meta);
virtual const char * name() { return "AACExtractor"; }
protected:
@@ -43,7 +44,7 @@
private:
DataSourceBase *mDataSource;
- sp<MetaData> mMeta;
+ MetaDataBase mMeta;
status_t mInitCheck;
Vector<uint64_t> mOffsetVector;
@@ -54,8 +55,7 @@
};
bool SniffAAC(
- DataSourceBase *source, String8 *mimeType, float *confidence,
- sp<AMessage> *);
+ DataSourceBase *source, String8 *mimeType, float *confidence, off64_t *offset);
} // namespace android
diff --git a/media/extractors/amr/AMRExtractor.cpp b/media/extractors/amr/AMRExtractor.cpp
index eec18b5..f56d5ef 100644
--- a/media/extractors/amr/AMRExtractor.cpp
+++ b/media/extractors/amr/AMRExtractor.cpp
@@ -21,7 +21,7 @@
#include "AMRExtractor.h"
#include <media/DataSourceBase.h>
-#include <media/MediaSourceBase.h>
+#include <media/MediaTrack.h>
#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/MediaBufferGroup.h>
#include <media/stagefright/MediaDefs.h>
@@ -31,28 +31,29 @@
namespace android {
-class AMRSource : public MediaSourceBase {
+class AMRSource : public MediaTrack {
public:
- AMRSource(DataSourceBase *source,
- const sp<MetaData> &meta,
- bool isWide,
- const off64_t *offset_table,
- size_t offset_table_length);
+ AMRSource(
+ DataSourceBase *source,
+ MetaDataBase &meta,
+ bool isWide,
+ const off64_t *offset_table,
+ size_t offset_table_length);
- virtual status_t start(MetaData *params = NULL);
+ virtual status_t start(MetaDataBase *params = NULL);
virtual status_t stop();
- virtual sp<MetaData> getFormat();
+ virtual status_t getFormat(MetaDataBase &);
virtual status_t read(
- MediaBuffer **buffer, const ReadOptions *options = NULL);
+ MediaBufferBase **buffer, const ReadOptions *options = NULL);
protected:
virtual ~AMRSource();
private:
DataSourceBase *mDataSource;
- sp<MetaData> mMeta;
+ MetaDataBase mMeta;
bool mIsWide;
off64_t mOffset;
@@ -116,25 +117,48 @@
return OK;
}
+static bool SniffAMR(
+ DataSourceBase *source, bool *isWide, float *confidence) {
+ char header[9];
+
+ if (source->readAt(0, header, sizeof(header)) != sizeof(header)) {
+ return false;
+ }
+
+ if (!memcmp(header, "#!AMR\n", 6)) {
+ if (isWide != nullptr) {
+ *isWide = false;
+ }
+ *confidence = 0.5;
+
+ return true;
+ } else if (!memcmp(header, "#!AMR-WB\n", 9)) {
+ if (isWide != nullptr) {
+ *isWide = true;
+ }
+ *confidence = 0.5;
+
+ return true;
+ }
+
+ return false;
+}
+
AMRExtractor::AMRExtractor(DataSourceBase *source)
: mDataSource(source),
mInitCheck(NO_INIT),
mOffsetTableLength(0) {
- String8 mimeType;
float confidence;
- if (!SniffAMR(mDataSource, &mimeType, &confidence)) {
+ if (!SniffAMR(mDataSource, &mIsWide, &confidence)) {
return;
}
- mIsWide = (mimeType == MEDIA_MIMETYPE_AUDIO_AMR_WB);
-
- mMeta = new MetaData;
- mMeta->setCString(
+ mMeta.setCString(
kKeyMIMEType, mIsWide ? MEDIA_MIMETYPE_AUDIO_AMR_WB
: MEDIA_MIMETYPE_AUDIO_AMR_NB);
- mMeta->setInt32(kKeyChannelCount, 1);
- mMeta->setInt32(kKeySampleRate, mIsWide ? 16000 : 8000);
+ mMeta.setInt32(kKeyChannelCount, 1);
+ mMeta.setInt32(kKeySampleRate, mIsWide ? 16000 : 8000);
off64_t offset = mIsWide ? 9 : 6;
off64_t streamSize;
@@ -161,7 +185,7 @@
numFrames ++;
}
- mMeta->setInt64(kKeyDuration, duration);
+ mMeta.setInt64(kKeyDuration, duration);
}
mInitCheck = OK;
@@ -170,23 +194,21 @@
AMRExtractor::~AMRExtractor() {
}
-sp<MetaData> AMRExtractor::getMetaData() {
- sp<MetaData> meta = new MetaData;
+status_t AMRExtractor::getMetaData(MetaDataBase &meta) {
+ meta.clear();
- if (mInitCheck != OK) {
- return meta;
+ if (mInitCheck == OK) {
+ meta.setCString(kKeyMIMEType, mIsWide ? "audio/amr-wb" : "audio/amr");
}
- meta->setCString(kKeyMIMEType, mIsWide ? "audio/amr-wb" : "audio/amr");
-
- return meta;
+ return OK;
}
size_t AMRExtractor::countTracks() {
return mInitCheck == OK ? 1 : 0;
}
-MediaSourceBase *AMRExtractor::getTrack(size_t index) {
+MediaTrack *AMRExtractor::getTrack(size_t index) {
if (mInitCheck != OK || index != 0) {
return NULL;
}
@@ -195,18 +217,19 @@
mOffsetTable, mOffsetTableLength);
}
-sp<MetaData> AMRExtractor::getTrackMetaData(size_t index, uint32_t /* flags */) {
+status_t AMRExtractor::getTrackMetaData(MetaDataBase &meta, size_t index, uint32_t /* flags */) {
if (mInitCheck != OK || index != 0) {
- return NULL;
+ return UNKNOWN_ERROR;
}
- return mMeta;
+ meta = mMeta;
+ return OK;
}
////////////////////////////////////////////////////////////////////////////////
AMRSource::AMRSource(
- DataSourceBase *source, const sp<MetaData> &meta,
+ DataSourceBase *source, MetaDataBase &meta,
bool isWide, const off64_t *offset_table, size_t offset_table_length)
: mDataSource(source),
mMeta(meta),
@@ -227,13 +250,13 @@
}
}
-status_t AMRSource::start(MetaData * /* params */) {
+status_t AMRSource::start(MetaDataBase * /* params */) {
CHECK(!mStarted);
mOffset = mIsWide ? 9 : 6;
mCurrentTimeUs = 0;
mGroup = new MediaBufferGroup;
- mGroup->add_buffer(new MediaBuffer(128));
+ mGroup->add_buffer(MediaBufferBase::Create(128));
mStarted = true;
return OK;
@@ -249,12 +272,13 @@
return OK;
}
-sp<MetaData> AMRSource::getFormat() {
- return mMeta;
+status_t AMRSource::getFormat(MetaDataBase &meta) {
+ meta = mMeta;
+ return OK;
}
status_t AMRSource::read(
- MediaBuffer **out, const ReadOptions *options) {
+ MediaBufferBase **out, const ReadOptions *options) {
*out = NULL;
int64_t seekTimeUs;
@@ -303,7 +327,7 @@
return ERROR_MALFORMED;
}
- MediaBuffer *buffer;
+ MediaBufferBase *buffer;
status_t err = mGroup->acquire_buffer(&buffer);
if (err != OK) {
return err;
@@ -325,8 +349,8 @@
}
buffer->set_range(0, frameSize);
- buffer->meta_data()->setInt64(kKeyTime, mCurrentTimeUs);
- buffer->meta_data()->setInt32(kKeyIsSyncFrame, 1);
+ buffer->meta_data().setInt64(kKeyTime, mCurrentTimeUs);
+ buffer->meta_data().setInt32(kKeyIsSyncFrame, 1);
mOffset += frameSize;
mCurrentTimeUs += 20000; // Each frame is 20ms
@@ -338,33 +362,6 @@
////////////////////////////////////////////////////////////////////////////////
-bool SniffAMR(
- DataSourceBase *source, String8 *mimeType, float *confidence) {
- char header[9];
-
- if (source->readAt(0, header, sizeof(header)) != sizeof(header)) {
- return false;
- }
-
- if (!memcmp(header, "#!AMR\n", 6)) {
- if (mimeType != nullptr) {
- *mimeType = MEDIA_MIMETYPE_AUDIO_AMR_NB;
- }
- *confidence = 0.5;
-
- return true;
- } else if (!memcmp(header, "#!AMR-WB\n", 9)) {
- if (mimeType != nullptr) {
- *mimeType = MEDIA_MIMETYPE_AUDIO_AMR_WB;
- }
- *confidence = 0.5;
-
- return true;
- }
-
- return false;
-}
-
extern "C" {
// This is the only symbol that needs to be exported
__attribute__ ((visibility ("default")))
diff --git a/media/extractors/amr/AMRExtractor.h b/media/extractors/amr/AMRExtractor.h
index b8b44ea..c90b325 100644
--- a/media/extractors/amr/AMRExtractor.h
+++ b/media/extractors/amr/AMRExtractor.h
@@ -20,6 +20,7 @@
#include <utils/Errors.h>
#include <media/MediaExtractor.h>
+#include <media/stagefright/MetaDataBase.h>
namespace android {
@@ -32,10 +33,10 @@
explicit AMRExtractor(DataSourceBase *source);
virtual size_t countTracks();
- virtual MediaSourceBase *getTrack(size_t index);
- virtual sp<MetaData> getTrackMetaData(size_t index, uint32_t flags);
+ virtual MediaTrack *getTrack(size_t index);
+ virtual status_t getTrackMetaData(MetaDataBase& meta, size_t index, uint32_t flags);
- virtual sp<MetaData> getMetaData();
+ virtual status_t getMetaData(MetaDataBase& meta);
virtual const char * name() { return "AMRExtractor"; }
protected:
@@ -43,7 +44,7 @@
private:
DataSourceBase *mDataSource;
- sp<MetaData> mMeta;
+ MetaDataBase mMeta;
status_t mInitCheck;
bool mIsWide;
@@ -54,9 +55,6 @@
AMRExtractor &operator=(const AMRExtractor &);
};
-bool SniffAMR(
- DataSourceBase *source, String8 *mimeType, float *confidence);
-
} // namespace android
#endif // AMR_EXTRACTOR_H_
diff --git a/media/extractors/amr/Android.bp b/media/extractors/amr/Android.bp
index e5bbe31..bd8a00c 100644
--- a/media/extractors/amr/Android.bp
+++ b/media/extractors/amr/Android.bp
@@ -10,7 +10,6 @@
"liblog",
"libmediaextractor",
"libstagefright_foundation",
- "libutils",
],
name: "libamrextractor",
diff --git a/media/extractors/flac/Android.bp b/media/extractors/flac/Android.bp
index 84ba6d3..0160ca4 100644
--- a/media/extractors/flac/Android.bp
+++ b/media/extractors/flac/Android.bp
@@ -11,7 +11,6 @@
"liblog",
"libmediaextractor",
"libstagefright_foundation",
- "libutils",
],
static_libs: [
diff --git a/media/extractors/flac/FLACExtractor.cpp b/media/extractors/flac/FLACExtractor.cpp
index 2ce20db..e3da259 100644
--- a/media/extractors/flac/FLACExtractor.cpp
+++ b/media/extractors/flac/FLACExtractor.cpp
@@ -23,170 +23,44 @@
#include "FLAC/stream_decoder.h"
#include <media/DataSourceBase.h>
-#include <media/MediaSourceBase.h>
+#include <media/MediaTrack.h>
+#include <media/VorbisComment.h>
#include <media/stagefright/foundation/ABuffer.h>
#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/foundation/base64.h>
-#include <media/stagefright/foundation/ByteUtils.h>
#include <media/stagefright/MediaBufferGroup.h>
#include <media/stagefright/MediaDefs.h>
#include <media/stagefright/MetaData.h>
-#include <media/stagefright/MediaBuffer.h>
+#include <media/stagefright/MediaBufferBase.h>
namespace android {
-// also exists in OggExtractor, candidate for moving to utility/support library?
-static void extractAlbumArt(
- const sp<MetaData> &fileMeta, const void *data, size_t size) {
- ALOGV("extractAlbumArt from '%s'", (const char *)data);
-
- sp<ABuffer> flacBuffer = decodeBase64(AString((const char *)data, size));
- if (flacBuffer == NULL) {
- ALOGE("malformed base64 encoded data.");
- return;
- }
-
- size_t flacSize = flacBuffer->size();
- uint8_t *flac = flacBuffer->data();
- ALOGV("got flac of size %zu", flacSize);
-
- uint32_t picType;
- uint32_t typeLen;
- uint32_t descLen;
- uint32_t dataLen;
- char type[128];
-
- if (flacSize < 8) {
- return;
- }
-
- picType = U32_AT(flac);
-
- if (picType != 3) {
- // This is not a front cover.
- return;
- }
-
- typeLen = U32_AT(&flac[4]);
- if (typeLen > sizeof(type) - 1) {
- return;
- }
-
- // we've already checked above that flacSize >= 8
- if (flacSize - 8 < typeLen) {
- return;
- }
-
- memcpy(type, &flac[8], typeLen);
- type[typeLen] = '\0';
-
- ALOGV("picType = %d, type = '%s'", picType, type);
-
- if (!strcmp(type, "-->")) {
- // This is not inline cover art, but an external url instead.
- return;
- }
-
- if (flacSize < 32 || flacSize - 32 < typeLen) {
- return;
- }
-
- descLen = U32_AT(&flac[8 + typeLen]);
- if (flacSize - 32 - typeLen < descLen) {
- return;
- }
-
- dataLen = U32_AT(&flac[8 + typeLen + 4 + descLen + 16]);
-
- // we've already checked above that (flacSize - 32 - typeLen - descLen) >= 0
- if (flacSize - 32 - typeLen - descLen < dataLen) {
- return;
- }
-
- ALOGV("got image data, %zu trailing bytes",
- flacSize - 32 - typeLen - descLen - dataLen);
-
- fileMeta->setData(
- kKeyAlbumArt, 0, &flac[8 + typeLen + 4 + descLen + 20], dataLen);
-
- fileMeta->setCString(kKeyAlbumArtMIME, type);
-}
-
-// also exists in OggExtractor, candidate for moving to utility/support library?
-static void parseVorbisComment(
- const sp<MetaData> &fileMeta, const char *comment, size_t commentLength)
-{
- struct {
- const char *const mTag;
- uint32_t mKey;
- } kMap[] = {
- { "TITLE", kKeyTitle },
- { "ARTIST", kKeyArtist },
- { "ALBUMARTIST", kKeyAlbumArtist },
- { "ALBUM ARTIST", kKeyAlbumArtist },
- { "COMPILATION", kKeyCompilation },
- { "ALBUM", kKeyAlbum },
- { "COMPOSER", kKeyComposer },
- { "GENRE", kKeyGenre },
- { "AUTHOR", kKeyAuthor },
- { "TRACKNUMBER", kKeyCDTrackNumber },
- { "DISCNUMBER", kKeyDiscNumber },
- { "DATE", kKeyDate },
- { "YEAR", kKeyYear },
- { "LYRICIST", kKeyWriter },
- { "METADATA_BLOCK_PICTURE", kKeyAlbumArt },
- { "ANDROID_LOOP", kKeyAutoLoop },
- };
-
- for (size_t j = 0; j < sizeof(kMap) / sizeof(kMap[0]); ++j) {
- size_t tagLen = strlen(kMap[j].mTag);
- if (!strncasecmp(kMap[j].mTag, comment, tagLen)
- && comment[tagLen] == '=') {
- if (kMap[j].mKey == kKeyAlbumArt) {
- extractAlbumArt(
- fileMeta,
- &comment[tagLen + 1],
- commentLength - tagLen - 1);
- } else if (kMap[j].mKey == kKeyAutoLoop) {
- if (!strcasecmp(&comment[tagLen + 1], "true")) {
- fileMeta->setInt32(kKeyAutoLoop, true);
- }
- } else {
- fileMeta->setCString(kMap[j].mKey, &comment[tagLen + 1]);
- }
- }
- }
-
-}
-
class FLACParser;
-class FLACSource : public MediaSourceBase {
+class FLACSource : public MediaTrack {
public:
FLACSource(
DataSourceBase *dataSource,
- const sp<MetaData> &trackMetadata);
+ MetaDataBase &meta);
- virtual status_t start(MetaData *params);
+ virtual status_t start(MetaDataBase *params);
virtual status_t stop();
- virtual sp<MetaData> getFormat();
+ virtual status_t getFormat(MetaDataBase &meta);
virtual status_t read(
- MediaBuffer **buffer, const ReadOptions *options = NULL);
+ MediaBufferBase **buffer, const ReadOptions *options = NULL);
protected:
virtual ~FLACSource();
private:
DataSourceBase *mDataSource;
- sp<MetaData> mTrackMetadata;
- sp<FLACParser> mParser;
+ MetaDataBase mTrackMetadata;
+ FLACParser *mParser;
bool mInitCheck;
bool mStarted;
- status_t init();
-
// no copy constructor or assignment
FLACSource(const FLACSource &);
FLACSource &operator=(const FLACSource &);
@@ -195,7 +69,7 @@
// FLACParser wraps a C libFLAC parser aka stream decoder
-class FLACParser : public RefBase {
+class FLACParser {
public:
enum {
@@ -205,8 +79,10 @@
explicit FLACParser(
DataSourceBase *dataSource,
// If metadata pointers aren't provided, we don't fill them
- const sp<MetaData> &fileMetadata = 0,
- const sp<MetaData> &trackMetadata = 0);
+ MetaDataBase *fileMetadata = 0,
+ MetaDataBase *trackMetadata = 0);
+
+ virtual ~FLACParser();
status_t initCheck() const {
return mInitCheck;
@@ -232,20 +108,17 @@
// media buffers
void allocateBuffers();
void releaseBuffers();
- MediaBuffer *readBuffer() {
+ MediaBufferBase *readBuffer() {
return readBuffer(false, 0LL);
}
- MediaBuffer *readBuffer(FLAC__uint64 sample) {
+ MediaBufferBase *readBuffer(FLAC__uint64 sample) {
return readBuffer(true, sample);
}
-protected:
- virtual ~FLACParser();
-
private:
DataSourceBase *mDataSource;
- sp<MetaData> mFileMetadata;
- sp<MetaData> mTrackMetadata;
+ MetaDataBase *mFileMetadata;
+ MetaDataBase *mTrackMetadata;
bool mInitCheck;
// media buffers
@@ -274,7 +147,7 @@
FLAC__StreamDecoderErrorStatus mErrorStatus;
status_t init();
- MediaBuffer *readBuffer(bool doSeek, FLAC__uint64 sample);
+ MediaBufferBase *readBuffer(bool doSeek, FLAC__uint64 sample);
// no copy constructor or assignment
FLACParser(const FLACParser &);
@@ -613,8 +486,8 @@
FLACParser::FLACParser(
DataSourceBase *dataSource,
- const sp<MetaData> &fileMetadata,
- const sp<MetaData> &trackMetadata)
+ MetaDataBase *fileMetadata,
+ MetaDataBase *trackMetadata)
: mDataSource(dataSource),
mFileMetadata(fileMetadata),
mTrackMetadata(trackMetadata),
@@ -763,7 +636,7 @@
CHECK(mGroup == NULL);
mGroup = new MediaBufferGroup;
mMaxBufferSize = getMaxBlockSize() * getChannels() * sizeof(short);
- mGroup->add_buffer(new MediaBuffer(mMaxBufferSize));
+ mGroup->add_buffer(MediaBufferBase::Create(mMaxBufferSize));
}
void FLACParser::releaseBuffers()
@@ -773,7 +646,7 @@
mGroup = NULL;
}
-MediaBuffer *FLACParser::readBuffer(bool doSeek, FLAC__uint64 sample)
+MediaBufferBase *FLACParser::readBuffer(bool doSeek, FLAC__uint64 sample)
{
mWriteRequested = true;
mWriteCompleted = false;
@@ -810,7 +683,7 @@
}
// acquire a media buffer
CHECK(mGroup != NULL);
- MediaBuffer *buffer;
+ MediaBufferBase *buffer;
status_t err = mGroup->acquire_buffer(&buffer);
if (err != OK) {
return NULL;
@@ -825,8 +698,8 @@
CHECK(mWriteHeader.number_type == FLAC__FRAME_NUMBER_TYPE_SAMPLE_NUMBER);
FLAC__uint64 sampleNumber = mWriteHeader.number.sample_number;
int64_t timeUs = (1000000LL * sampleNumber) / getSampleRate();
- buffer->meta_data()->setInt64(kKeyTime, timeUs);
- buffer->meta_data()->setInt32(kKeyIsSyncFrame, 1);
+ buffer->meta_data().setInt64(kKeyTime, timeUs);
+ buffer->meta_data().setInt32(kKeyIsSyncFrame, 1);
return buffer;
}
@@ -834,7 +707,7 @@
FLACSource::FLACSource(
DataSourceBase *dataSource,
- const sp<MetaData> &trackMetadata)
+ MetaDataBase &trackMetadata)
: mDataSource(dataSource),
mTrackMetadata(trackMetadata),
mParser(0),
@@ -842,7 +715,9 @@
mStarted(false)
{
ALOGV("FLACSource::FLACSource");
- mInitCheck = init();
+ // re-use the same track metadata passed into constructor from FLACExtractor
+ mParser = new FLACParser(mDataSource);
+ mInitCheck = mParser->initCheck();
}
FLACSource::~FLACSource()
@@ -851,9 +726,10 @@
if (mStarted) {
stop();
}
+ delete mParser;
}
-status_t FLACSource::start(MetaData * /* params */)
+status_t FLACSource::start(MetaDataBase * /* params */)
{
ALOGV("FLACSource::start");
@@ -875,15 +751,16 @@
return OK;
}
-sp<MetaData> FLACSource::getFormat()
+status_t FLACSource::getFormat(MetaDataBase &meta)
{
- return mTrackMetadata;
+ meta = mTrackMetadata;
+ return OK;
}
status_t FLACSource::read(
- MediaBuffer **outBuffer, const ReadOptions *options)
+ MediaBufferBase **outBuffer, const ReadOptions *options)
{
- MediaBuffer *buffer;
+ MediaBufferBase *buffer;
// process an optional seek request
int64_t seekTimeUs;
ReadOptions::SeekMode mode;
@@ -907,28 +784,24 @@
return buffer != NULL ? (status_t) OK : (status_t) ERROR_END_OF_STREAM;
}
-status_t FLACSource::init()
-{
- ALOGV("FLACSource::init");
- // re-use the same track metadata passed into constructor from FLACExtractor
- mParser = new FLACParser(mDataSource);
- return mParser->initCheck();
-}
-
// FLACExtractor
FLACExtractor::FLACExtractor(
DataSourceBase *dataSource)
: mDataSource(dataSource),
+ mParser(nullptr),
mInitCheck(false)
{
ALOGV("FLACExtractor::FLACExtractor");
- mInitCheck = init();
+ // FLACParser will fill in the metadata for us
+ mParser = new FLACParser(mDataSource, &mFileMetadata, &mTrackMetadata);
+ mInitCheck = mParser->initCheck();
}
FLACExtractor::~FLACExtractor()
{
ALOGV("~FLACExtractor::FLACExtractor");
+ delete mParser;
}
size_t FLACExtractor::countTracks()
@@ -936,7 +809,7 @@
return mInitCheck == OK ? 1 : 0;
}
-MediaSourceBase *FLACExtractor::getTrack(size_t index)
+MediaTrack *FLACExtractor::getTrack(size_t index)
{
if (mInitCheck != OK || index > 0) {
return NULL;
@@ -944,26 +817,20 @@
return new FLACSource(mDataSource, mTrackMetadata);
}
-sp<MetaData> FLACExtractor::getTrackMetaData(
+status_t FLACExtractor::getTrackMetaData(
+ MetaDataBase &meta,
size_t index, uint32_t /* flags */) {
if (mInitCheck != OK || index > 0) {
- return NULL;
+ return UNKNOWN_ERROR;
}
- return mTrackMetadata;
+ meta = mTrackMetadata;
+ return OK;
}
-status_t FLACExtractor::init()
+status_t FLACExtractor::getMetaData(MetaDataBase &meta)
{
- mFileMetadata = new MetaData;
- mTrackMetadata = new MetaData;
- // FLACParser will fill in the metadata for us
- mParser = new FLACParser(mDataSource, mFileMetadata, mTrackMetadata);
- return mParser->initCheck();
-}
-
-sp<MetaData> FLACExtractor::getMetaData()
-{
- return mFileMetadata;
+ meta = mFileMetadata;
+ return OK;
}
// Sniffer
diff --git a/media/extractors/flac/FLACExtractor.h b/media/extractors/flac/FLACExtractor.h
index f41d878..7fb6ec6 100644
--- a/media/extractors/flac/FLACExtractor.h
+++ b/media/extractors/flac/FLACExtractor.h
@@ -19,6 +19,7 @@
#include <media/DataSourceBase.h>
#include <media/MediaExtractor.h>
+#include <media/stagefright/MetaDataBase.h>
#include <utils/String8.h>
namespace android {
@@ -31,10 +32,10 @@
explicit FLACExtractor(DataSourceBase *source);
virtual size_t countTracks();
- virtual MediaSourceBase *getTrack(size_t index);
- virtual sp<MetaData> getTrackMetaData(size_t index, uint32_t flags);
+ virtual MediaTrack *getTrack(size_t index);
+ virtual status_t getTrackMetaData(MetaDataBase& meta, size_t index, uint32_t flags);
- virtual sp<MetaData> getMetaData();
+ virtual status_t getMetaData(MetaDataBase& meta);
virtual const char * name() { return "FLACExtractor"; }
protected:
@@ -42,14 +43,12 @@
private:
DataSourceBase *mDataSource;
- sp<FLACParser> mParser;
+ FLACParser *mParser;
status_t mInitCheck;
- sp<MetaData> mFileMetadata;
+ MetaDataBase mFileMetadata;
// There is only one track
- sp<MetaData> mTrackMetadata;
-
- status_t init();
+ MetaDataBase mTrackMetadata;
FLACExtractor(const FLACExtractor &);
FLACExtractor &operator=(const FLACExtractor &);
diff --git a/media/extractors/midi/Android.bp b/media/extractors/midi/Android.bp
index 9af128e..5412e99 100644
--- a/media/extractors/midi/Android.bp
+++ b/media/extractors/midi/Android.bp
@@ -9,8 +9,7 @@
shared_libs: [
"liblog",
"libmediaextractor",
- "libstagefright_foundation",
- "libutils",
+ "libstagefright_foundation"
],
static_libs: [
diff --git a/media/extractors/midi/MidiExtractor.cpp b/media/extractors/midi/MidiExtractor.cpp
index 1e38194..949fbe0 100644
--- a/media/extractors/midi/MidiExtractor.cpp
+++ b/media/extractors/midi/MidiExtractor.cpp
@@ -25,34 +25,34 @@
#include <media/stagefright/MediaBufferGroup.h>
#include <media/stagefright/MediaDefs.h>
#include <media/stagefright/MetaData.h>
-#include <media/MediaSourceBase.h>
+#include <media/MediaTrack.h>
#include <libsonivox/eas_reverb.h>
namespace android {
-// how many Sonivox output buffers to aggregate into one MediaBuffer
+// how many Sonivox output buffers to aggregate into one MediaBufferBase
static const int NUM_COMBINE_BUFFERS = 4;
-class MidiSource : public MediaSourceBase {
+class MidiSource : public MediaTrack {
public:
MidiSource(
- const sp<MidiEngine> &engine,
- const sp<MetaData> &trackMetadata);
+ MidiEngine &engine,
+ MetaDataBase &trackMetadata);
- virtual status_t start(MetaData *params);
+ virtual status_t start(MetaDataBase *params);
virtual status_t stop();
- virtual sp<MetaData> getFormat();
+ virtual status_t getFormat(MetaDataBase&);
virtual status_t read(
- MediaBuffer **buffer, const ReadOptions *options = NULL);
+ MediaBufferBase **buffer, const ReadOptions *options = NULL);
protected:
virtual ~MidiSource();
private:
- sp<MidiEngine> mEngine;
- sp<MetaData> mTrackMetadata;
+ MidiEngine &mEngine;
+ MetaDataBase &mTrackMetadata;
bool mInitCheck;
bool mStarted;
@@ -68,8 +68,8 @@
// Midisource
MidiSource::MidiSource(
- const sp<MidiEngine> &engine,
- const sp<MetaData> &trackMetadata)
+ MidiEngine &engine,
+ MetaDataBase &trackMetadata)
: mEngine(engine),
mTrackMetadata(trackMetadata),
mInitCheck(false),
@@ -87,13 +87,13 @@
}
}
-status_t MidiSource::start(MetaData * /* params */)
+status_t MidiSource::start(MetaDataBase * /* params */)
{
ALOGV("MidiSource::start");
CHECK(!mStarted);
mStarted = true;
- mEngine->allocateBuffers();
+ mEngine.allocateBuffers();
return OK;
}
@@ -103,21 +103,22 @@
CHECK(mStarted);
mStarted = false;
- mEngine->releaseBuffers();
+ mEngine.releaseBuffers();
return OK;
}
-sp<MetaData> MidiSource::getFormat()
+status_t MidiSource::getFormat(MetaDataBase &meta)
{
- return mTrackMetadata;
+ meta = mTrackMetadata;
+ return OK;
}
status_t MidiSource::read(
- MediaBuffer **outBuffer, const ReadOptions *options)
+ MediaBufferBase **outBuffer, const ReadOptions *options)
{
ALOGV("MidiSource::read");
- MediaBuffer *buffer;
+ MediaBufferBase *buffer;
// process an optional seek request
int64_t seekTimeUs;
ReadOptions::SeekMode mode;
@@ -125,9 +126,9 @@
if (seekTimeUs <= 0LL) {
seekTimeUs = 0LL;
}
- mEngine->seekTo(seekTimeUs);
+ mEngine.seekTo(seekTimeUs);
}
- buffer = mEngine->readBuffer();
+ buffer = mEngine.readBuffer();
*outBuffer = buffer;
ALOGV("MidiSource::read %p done", this);
return buffer != NULL ? (status_t) OK : (status_t) ERROR_END_OF_STREAM;
@@ -142,8 +143,8 @@
// MidiEngine
MidiEngine::MidiEngine(DataSourceBase *dataSource,
- const sp<MetaData> &fileMetadata,
- const sp<MetaData> &trackMetadata) :
+ MetaDataBase *fileMetadata,
+ MetaDataBase *trackMetadata) :
mGroup(NULL),
mEasData(NULL),
mEasHandle(NULL),
@@ -191,7 +192,7 @@
EAS_Shutdown(mEasData);
}
delete mGroup;
-
+ delete mIoWrapper;
}
status_t MidiEngine::initCheck() {
@@ -207,7 +208,7 @@
int bufsize = sizeof(EAS_PCM)
* mEasConfig->mixBufferSize * mEasConfig->numChannels * NUM_COMBINE_BUFFERS;
ALOGV("using %d byte buffer", bufsize);
- mGroup->add_buffer(new MediaBuffer(bufsize));
+ mGroup->add_buffer(MediaBufferBase::Create(bufsize));
return OK;
}
@@ -223,13 +224,13 @@
return result == EAS_SUCCESS ? OK : UNKNOWN_ERROR;
}
-MediaBuffer* MidiEngine::readBuffer() {
+MediaBufferBase* MidiEngine::readBuffer() {
EAS_STATE state;
EAS_State(mEasData, mEasHandle, &state);
if ((state == EAS_STATE_STOPPED) || (state == EAS_STATE_ERROR)) {
return NULL;
}
- MediaBuffer *buffer;
+ MediaBufferBase *buffer;
status_t err = mGroup->acquire_buffer(&buffer);
if (err != OK) {
ALOGE("readBuffer: no buffer");
@@ -238,7 +239,7 @@
EAS_I32 timeMs;
EAS_GetLocation(mEasData, mEasHandle, &timeMs);
int64_t timeUs = 1000ll * timeMs;
- buffer->meta_data()->setInt64(kKeyTime, timeUs);
+ buffer->meta_data().setInt64(kKeyTime, timeUs);
EAS_PCM* p = (EAS_PCM*) buffer->data();
int numBytesOutput = 0;
@@ -266,9 +267,7 @@
mInitCheck(false)
{
ALOGV("MidiExtractor ctor");
- mFileMetadata = new MetaData;
- mTrackMetadata = new MetaData;
- mEngine = new MidiEngine(mDataSource, mFileMetadata, mTrackMetadata);
+ mEngine = new MidiEngine(mDataSource, &mFileMetadata, &mTrackMetadata);
mInitCheck = mEngine->initCheck();
}
@@ -282,35 +281,38 @@
return mInitCheck == OK ? 1 : 0;
}
-MediaSourceBase *MidiExtractor::getTrack(size_t index)
+MediaTrack *MidiExtractor::getTrack(size_t index)
{
if (mInitCheck != OK || index > 0) {
return NULL;
}
- return new MidiSource(mEngine, mTrackMetadata);
+ return new MidiSource(*mEngine, mTrackMetadata);
}
-sp<MetaData> MidiExtractor::getTrackMetaData(
+status_t MidiExtractor::getTrackMetaData(
+ MetaDataBase &meta,
size_t index, uint32_t /* flags */) {
ALOGV("MidiExtractor::getTrackMetaData");
if (mInitCheck != OK || index > 0) {
- return NULL;
+ return UNKNOWN_ERROR;
}
- return mTrackMetadata;
+ meta = mTrackMetadata;
+ return OK;
}
-sp<MetaData> MidiExtractor::getMetaData()
+status_t MidiExtractor::getMetaData(MetaDataBase &meta)
{
ALOGV("MidiExtractor::getMetaData");
- return mFileMetadata;
+ meta = mFileMetadata;
+ return OK;
}
// Sniffer
bool SniffMidi(DataSourceBase *source, float *confidence)
{
- sp<MidiEngine> p = new MidiEngine(source, NULL, NULL);
- if (p->initCheck() == OK) {
+ MidiEngine p(source, NULL, NULL);
+ if (p.initCheck() == OK) {
*confidence = 0.8;
ALOGV("SniffMidi: yes");
return true;
diff --git a/media/extractors/midi/MidiExtractor.h b/media/extractors/midi/MidiExtractor.h
index 173a814..244dd0f 100644
--- a/media/extractors/midi/MidiExtractor.h
+++ b/media/extractors/midi/MidiExtractor.h
@@ -19,19 +19,20 @@
#include <media/DataSourceBase.h>
#include <media/MediaExtractor.h>
-#include <media/stagefright/MediaBuffer.h>
+#include <media/stagefright/MediaBufferBase.h>
#include <media/stagefright/MediaBufferGroup.h>
+#include <media/stagefright/MetaDataBase.h>
#include <media/MidiIoWrapper.h>
#include <utils/String8.h>
#include <libsonivox/eas.h>
namespace android {
-class MidiEngine : public RefBase {
+class MidiEngine {
public:
- MidiEngine(DataSourceBase *dataSource,
- const sp<MetaData> &fileMetadata,
- const sp<MetaData> &trackMetadata);
+ explicit MidiEngine(DataSourceBase *dataSource,
+ MetaDataBase *fileMetadata,
+ MetaDataBase *trackMetadata);
~MidiEngine();
status_t initCheck();
@@ -39,9 +40,9 @@
status_t allocateBuffers();
status_t releaseBuffers();
status_t seekTo(int64_t positionUs);
- MediaBuffer* readBuffer();
+ MediaBufferBase* readBuffer();
private:
- sp<MidiIoWrapper> mIoWrapper;
+ MidiIoWrapper *mIoWrapper;
MediaBufferGroup *mGroup;
EAS_DATA_HANDLE mEasData;
EAS_HANDLE mEasHandle;
@@ -55,10 +56,10 @@
explicit MidiExtractor(DataSourceBase *source);
virtual size_t countTracks();
- virtual MediaSourceBase *getTrack(size_t index);
- virtual sp<MetaData> getTrackMetaData(size_t index, uint32_t flags);
+ virtual MediaTrack *getTrack(size_t index);
+ virtual status_t getTrackMetaData(MetaDataBase& meta, size_t index, uint32_t flags);
- virtual sp<MetaData> getMetaData();
+ virtual status_t getMetaData(MetaDataBase& meta);
virtual const char * name() { return "MidiExtractor"; }
protected:
@@ -67,12 +68,12 @@
private:
DataSourceBase *mDataSource;
status_t mInitCheck;
- sp<MetaData> mFileMetadata;
+ MetaDataBase mFileMetadata;
// There is only one track
- sp<MetaData> mTrackMetadata;
+ MetaDataBase mTrackMetadata;
- sp<MidiEngine> mEngine;
+ MidiEngine *mEngine;
EAS_DATA_HANDLE mEasData;
EAS_HANDLE mEasHandle;
diff --git a/media/extractors/mkv/MatroskaExtractor.cpp b/media/extractors/mkv/MatroskaExtractor.cpp
index 7396113..5592a88 100644
--- a/media/extractors/mkv/MatroskaExtractor.cpp
+++ b/media/extractors/mkv/MatroskaExtractor.cpp
@@ -22,14 +22,14 @@
#include "MatroskaExtractor.h"
#include <media/DataSourceBase.h>
-#include <media/MediaSourceBase.h>
+#include <media/MediaTrack.h>
#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/foundation/AUtils.h>
#include <media/stagefright/foundation/ABuffer.h>
#include <media/stagefright/foundation/ByteUtils.h>
#include <media/stagefright/foundation/ColorUtils.h>
#include <media/stagefright/foundation/hexdump.h>
-#include <media/stagefright/MediaBuffer.h>
+#include <media/stagefright/MediaBufferBase.h>
#include <media/stagefright/MediaDefs.h>
#include <media/stagefright/MediaErrors.h>
#include <media/stagefright/MetaData.h>
@@ -121,16 +121,16 @@
BlockIterator &operator=(const BlockIterator &);
};
-struct MatroskaSource : public MediaSourceBase {
+struct MatroskaSource : public MediaTrack {
MatroskaSource(MatroskaExtractor *extractor, size_t index);
- virtual status_t start(MetaData *params);
+ virtual status_t start(MetaDataBase *params);
virtual status_t stop();
- virtual sp<MetaData> getFormat();
+ virtual status_t getFormat(MetaDataBase &);
virtual status_t read(
- MediaBuffer **buffer, const ReadOptions *options);
+ MediaBufferBase **buffer, const ReadOptions *options);
protected:
virtual ~MatroskaSource();
@@ -150,11 +150,11 @@
BlockIterator mBlockIter;
ssize_t mNALSizeLen; // for type AVC or HEVC
- List<MediaBuffer *> mPendingFrames;
+ List<MediaBufferBase *> mPendingFrames;
status_t advance();
- status_t setWebmBlockCryptoInfo(MediaBuffer *mbuf);
+ status_t setWebmBlockCryptoInfo(MediaBufferBase *mbuf);
status_t readBlock();
void clearPendingFrames();
@@ -219,10 +219,10 @@
mExtractor->mTracks.itemAt(index).mTrackNum,
index),
mNALSizeLen(-1) {
- sp<MetaData> meta = mExtractor->mTracks.itemAt(index).mMeta;
+ MetaDataBase &meta = mExtractor->mTracks.editItemAt(index).mMeta;
const char *mime;
- CHECK(meta->findCString(kKeyMIMEType, &mime));
+ CHECK(meta.findCString(kKeyMIMEType, &mime));
mIsAudio = !strncasecmp("audio/", mime, 6);
@@ -233,11 +233,11 @@
const uint8_t *avcc;
size_t avccSize;
int32_t nalSizeLen = 0;
- if (meta->findInt32(kKeyNalLengthSize, &nalSizeLen)) {
+ if (meta.findInt32(kKeyNalLengthSize, &nalSizeLen)) {
if (nalSizeLen >= 0 && nalSizeLen <= 4) {
mNALSizeLen = nalSizeLen;
}
- } else if (meta->findData(kKeyAVCC, &dummy, (const void **)&avcc, &avccSize)
+ } else if (meta.findData(kKeyAVCC, &dummy, (const void **)&avcc, &avccSize)
&& avccSize >= 5u) {
mNALSizeLen = 1 + (avcc[4] & 3);
ALOGV("mNALSizeLen = %zd", mNALSizeLen);
@@ -250,7 +250,7 @@
uint32_t dummy;
const uint8_t *hvcc;
size_t hvccSize;
- if (meta->findData(kKeyHVCC, &dummy, (const void **)&hvcc, &hvccSize)
+ if (meta.findData(kKeyHVCC, &dummy, (const void **)&hvcc, &hvccSize)
&& hvccSize >= 22u) {
mNALSizeLen = 1 + (hvcc[14+7] & 3);
ALOGV("mNALSizeLen = %zu", mNALSizeLen);
@@ -266,7 +266,7 @@
clearPendingFrames();
}
-status_t MatroskaSource::start(MetaData * /* params */) {
+status_t MatroskaSource::start(MetaDataBase * /* params */) {
if (mType == AVC && mNALSizeLen < 0) {
return ERROR_MALFORMED;
}
@@ -282,8 +282,9 @@
return OK;
}
-sp<MetaData> MatroskaSource::getFormat() {
- return mExtractor->mTracks.itemAt(mTrackIndex).mMeta;
+status_t MatroskaSource::getFormat(MetaDataBase &meta) {
+ meta = mExtractor->mTracks.itemAt(mTrackIndex).mMeta;
+ return OK;
}
////////////////////////////////////////////////////////////////////////////////
@@ -568,7 +569,7 @@
void MatroskaSource::clearPendingFrames() {
while (!mPendingFrames.empty()) {
- MediaBuffer *frame = *mPendingFrames.begin();
+ MediaBufferBase *frame = *mPendingFrames.begin();
mPendingFrames.erase(mPendingFrames.begin());
frame->release();
@@ -576,7 +577,7 @@
}
}
-status_t MatroskaSource::setWebmBlockCryptoInfo(MediaBuffer *mbuf) {
+status_t MatroskaSource::setWebmBlockCryptoInfo(MediaBufferBase *mbuf) {
if (mbuf->range_length() < 1 || mbuf->range_length() - 1 > INT32_MAX) {
// 1-byte signal
return ERROR_MALFORMED;
@@ -589,7 +590,7 @@
return ERROR_MALFORMED;
}
- sp<MetaData> meta = mbuf->meta_data();
+ MetaDataBase &meta = mbuf->meta_data();
if (blockEncrypted) {
/*
* 0 1 2 3
@@ -612,13 +613,13 @@
uint32_t type;
const uint8_t *keyId;
size_t keyIdSize;
- sp<MetaData> trackMeta = mExtractor->mTracks.itemAt(mTrackIndex).mMeta;
- CHECK(trackMeta->findData(kKeyCryptoKey, &type, (const void **)&keyId, &keyIdSize));
- meta->setData(kKeyCryptoKey, 0, keyId, keyIdSize);
+ const MetaDataBase &trackMeta = mExtractor->mTracks.itemAt(mTrackIndex).mMeta;
+ CHECK(trackMeta.findData(kKeyCryptoKey, &type, (const void **)&keyId, &keyIdSize));
+ meta.setData(kKeyCryptoKey, 0, keyId, keyIdSize);
memcpy(ctrCounter, data + 1, 8);
- meta->setData(kKeyCryptoIV, 0, ctrCounter, 16);
- meta->setData(kKeyPlainSizes, 0, plainSizes, sizeof(plainSizes));
- meta->setData(kKeyEncryptedSizes, 0, encryptedSizes, sizeof(encryptedSizes));
+ meta.setData(kKeyCryptoIV, 0, ctrCounter, 16);
+ meta.setData(kKeyPlainSizes, 0, plainSizes, sizeof(plainSizes));
+ meta.setData(kKeyEncryptedSizes, 0, encryptedSizes, sizeof(encryptedSizes));
mbuf->set_range(9, mbuf->range_length() - 9);
} else {
/*
@@ -634,8 +635,8 @@
*/
int32_t plainSizes[] = { static_cast<int32_t>(mbuf->range_length() - 1) };
int32_t encryptedSizes[] = { 0 };
- meta->setData(kKeyPlainSizes, 0, plainSizes, sizeof(plainSizes));
- meta->setData(kKeyEncryptedSizes, 0, encryptedSizes, sizeof(encryptedSizes));
+ meta.setData(kKeyPlainSizes, 0, plainSizes, sizeof(plainSizes));
+ meta.setData(kKeyEncryptedSizes, 0, encryptedSizes, sizeof(encryptedSizes));
mbuf->set_range(1, mbuf->range_length() - 1);
}
@@ -662,14 +663,14 @@
}
len += trackInfo->mHeaderLen;
- MediaBuffer *mbuf = new MediaBuffer(len);
+ MediaBufferBase *mbuf = MediaBufferBase::Create(len);
uint8_t *data = static_cast<uint8_t *>(mbuf->data());
if (trackInfo->mHeader) {
memcpy(data, trackInfo->mHeader, trackInfo->mHeaderLen);
}
- mbuf->meta_data()->setInt64(kKeyTime, timeUs);
- mbuf->meta_data()->setInt32(kKeyIsSyncFrame, block->IsKey());
+ mbuf->meta_data().setInt64(kKeyTime, timeUs);
+ mbuf->meta_data().setInt32(kKeyIsSyncFrame, block->IsKey());
status_t err = frame.Read(mExtractor->mReader, data + trackInfo->mHeaderLen);
if (err == OK
@@ -695,7 +696,7 @@
}
status_t MatroskaSource::read(
- MediaBuffer **out, const ReadOptions *options) {
+ MediaBufferBase **out, const ReadOptions *options) {
*out = NULL;
int64_t targetSampleTimeUs = -1ll;
@@ -731,12 +732,12 @@
}
}
- MediaBuffer *frame = *mPendingFrames.begin();
+ MediaBufferBase *frame = *mPendingFrames.begin();
mPendingFrames.erase(mPendingFrames.begin());
if ((mType != AVC && mType != HEVC) || mNALSizeLen == 0) {
if (targetSampleTimeUs >= 0ll) {
- frame->meta_data()->setInt64(
+ frame->meta_data().setInt64(
kKeyTargetTime, targetSampleTimeUs);
}
@@ -760,7 +761,7 @@
size_t srcSize = frame->range_length();
size_t dstSize = 0;
- MediaBuffer *buffer = NULL;
+ MediaBufferBase *buffer = NULL;
uint8_t *dstPtr = NULL;
for (int32_t pass = 0; pass < 2; ++pass) {
@@ -820,16 +821,16 @@
// each 4-byte nal size with a 4-byte start code
buffer = frame;
} else {
- buffer = new MediaBuffer(dstSize);
+ buffer = MediaBufferBase::Create(dstSize);
}
int64_t timeUs;
- CHECK(frame->meta_data()->findInt64(kKeyTime, &timeUs));
+ CHECK(frame->meta_data().findInt64(kKeyTime, &timeUs));
int32_t isSync;
- CHECK(frame->meta_data()->findInt32(kKeyIsSyncFrame, &isSync));
+ CHECK(frame->meta_data().findInt32(kKeyIsSyncFrame, &isSync));
- buffer->meta_data()->setInt64(kKeyTime, timeUs);
- buffer->meta_data()->setInt32(kKeyIsSyncFrame, isSync);
+ buffer->meta_data().setInt64(kKeyTime, timeUs);
+ buffer->meta_data().setInt32(kKeyIsSyncFrame, isSync);
dstPtr = (uint8_t *)buffer->data();
}
@@ -841,7 +842,7 @@
}
if (targetSampleTimeUs >= 0ll) {
- buffer->meta_data()->setInt64(
+ buffer->meta_data().setInt64(
kKeyTargetTime, targetSampleTimeUs);
}
@@ -927,7 +928,7 @@
return mTracks.size();
}
-MediaSourceBase *MatroskaExtractor::getTrack(size_t index) {
+MediaTrack *MatroskaExtractor::getTrack(size_t index) {
if (index >= mTracks.size()) {
return NULL;
}
@@ -935,10 +936,11 @@
return new MatroskaSource(this, index);
}
-sp<MetaData> MatroskaExtractor::getTrackMetaData(
+status_t MatroskaExtractor::getTrackMetaData(
+ MetaDataBase &meta,
size_t index, uint32_t flags) {
if (index >= mTracks.size()) {
- return NULL;
+ return UNKNOWN_ERROR;
}
if ((flags & kIncludeExtensiveMetaData) && !mExtractedThumbnails
@@ -947,7 +949,8 @@
mExtractedThumbnails = true;
}
- return mTracks.itemAt(index).mMeta;
+ meta = mTracks.itemAt(index).mMeta;
+ return OK;
}
bool MatroskaExtractor::isLiveStreaming() const {
@@ -982,7 +985,7 @@
}
static void addESDSFromCodecPrivate(
- const sp<MetaData> &meta,
+ MetaDataBase &meta,
bool isAudio, const void *priv, size_t privSize) {
int privSizeBytesRequired = bytesForSize(privSize);
@@ -1010,14 +1013,14 @@
storeSize(esds, idx, privSize);
memcpy(esds + idx, priv, privSize);
- meta->setData(kKeyESDS, 0, esds, esdsSize);
+ meta.setData(kKeyESDS, 0, esds, esdsSize);
delete[] esds;
esds = NULL;
}
status_t addVorbisCodecInfo(
- const sp<MetaData> &meta,
+ MetaDataBase &meta,
const void *_codecPrivate, size_t codecPrivateSize) {
// hexdump(_codecPrivate, codecPrivateSize);
@@ -1075,7 +1078,7 @@
if (codecPrivate[offset] != 0x01) {
return ERROR_MALFORMED;
}
- meta->setData(kKeyVorbisInfo, 0, &codecPrivate[offset], len1);
+ meta.setData(kKeyVorbisInfo, 0, &codecPrivate[offset], len1);
offset += len1;
if (codecPrivate[offset] != 0x03) {
@@ -1087,7 +1090,7 @@
return ERROR_MALFORMED;
}
- meta->setData(
+ meta.setData(
kKeyVorbisBooks, 0, &codecPrivate[offset],
codecPrivateSize - offset);
@@ -1095,11 +1098,11 @@
}
static status_t addFlacMetadata(
- const sp<MetaData> &meta,
+ MetaDataBase &meta,
const void *codecPrivate, size_t codecPrivateSize) {
// hexdump(codecPrivate, codecPrivateSize);
- meta->setData(kKeyFlacMetadata, 0, codecPrivate, codecPrivateSize);
+ meta.setData(kKeyFlacMetadata, 0, codecPrivate, codecPrivateSize);
int32_t maxInputSize = 64 << 10;
sp<FLACDecoder> flacDecoder = FLACDecoder::Create();
@@ -1120,7 +1123,7 @@
* streamInfo.max_blocksize * streamInfo.channels;
}
}
- meta->setInt32(kKeyMaxInputSize, maxInputSize);
+ meta.setInt32(kKeyMaxInputSize, maxInputSize);
return OK;
}
@@ -1143,14 +1146,12 @@
return ERROR_MALFORMED;
}
- sp<MetaData> avcMeta = MakeAVCCodecSpecificData(abuf);
- if (avcMeta == NULL) {
+ if (!MakeAVCCodecSpecificData(trackInfo->mMeta, abuf)) {
return ERROR_MALFORMED;
}
// Override the synthesized nal length size, which is arbitrary
- avcMeta->setInt32(kKeyNalLengthSize, 0);
- trackInfo->mMeta = avcMeta;
+ trackInfo->mMeta.setInt32(kKeyNalLengthSize, 0);
return OK;
}
@@ -1172,7 +1173,7 @@
}
void MatroskaExtractor::getColorInformation(
- const mkvparser::VideoTrack *vtrack, sp<MetaData> &meta) {
+ const mkvparser::VideoTrack *vtrack, MetaDataBase &meta) {
const mkvparser::Colour *color = vtrack->GetColour();
if (color == NULL) {
return;
@@ -1206,10 +1207,10 @@
ColorAspects aspects;
ColorUtils::convertIsoColorAspectsToCodecAspects(
primaries, transfer, coeffs, fullRange, aspects);
- meta->setInt32(kKeyColorPrimaries, aspects.mPrimaries);
- meta->setInt32(kKeyTransferFunction, aspects.mTransfer);
- meta->setInt32(kKeyColorMatrix, aspects.mMatrixCoeffs);
- meta->setInt32(
+ meta.setInt32(kKeyColorPrimaries, aspects.mPrimaries);
+ meta.setInt32(kKeyTransferFunction, aspects.mTransfer);
+ meta.setInt32(kKeyColorMatrix, aspects.mMatrixCoeffs);
+ meta.setInt32(
kKeyColorRange, rangeSpecified ? aspects.mRange : ColorAspects::RangeUnspecified);
}
@@ -1254,13 +1255,13 @@
// Only advertise static info if at least one of the groups have been specified.
if (memcmp(&info, &nullInfo, sizeof(info)) != 0) {
info.mID = HDRStaticInfo::kType1;
- meta->setData(kKeyHdrStaticInfo, 'hdrS', &info, sizeof(info));
+ meta.setData(kKeyHdrStaticInfo, 'hdrS', &info, sizeof(info));
}
}
}
status_t MatroskaExtractor::initTrackInfo(
- const mkvparser::Track *track, const sp<MetaData> &meta, TrackInfo *trackInfo) {
+ const mkvparser::Track *track, MetaDataBase &meta, TrackInfo *trackInfo) {
trackInfo->mTrackNum = track->GetNumber();
trackInfo->mMeta = meta;
trackInfo->mExtractor = this;
@@ -1273,7 +1274,7 @@
for(size_t j = 0; j < encoding->GetEncryptionCount(); j++) {
const mkvparser::ContentEncoding::ContentEncryption *encryption;
encryption = encoding->GetEncryptionByIndex(j);
- trackInfo->mMeta->setData(kKeyCryptoKey, 0, encryption->key_id, encryption->key_id_len);
+ trackInfo->mMeta.setData(kKeyCryptoKey, 0, encryption->key_id, encryption->key_id_len);
trackInfo->mEncrypted = true;
break;
}
@@ -1322,7 +1323,7 @@
enum { VIDEO_TRACK = 1, AUDIO_TRACK = 2 };
- sp<MetaData> meta = new MetaData;
+ MetaDataBase meta;
status_t err = OK;
@@ -1333,19 +1334,19 @@
static_cast<const mkvparser::VideoTrack *>(track);
if (!strcmp("V_MPEG4/ISO/AVC", codecID)) {
- meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_AVC);
- meta->setData(kKeyAVCC, 0, codecPrivate, codecPrivateSize);
+ meta.setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_AVC);
+ meta.setData(kKeyAVCC, 0, codecPrivate, codecPrivateSize);
} else if (!strcmp("V_MPEGH/ISO/HEVC", codecID)) {
- meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_HEVC);
+ meta.setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_HEVC);
if (codecPrivateSize > 0) {
- meta->setData(kKeyHVCC, kTypeHVCC, codecPrivate, codecPrivateSize);
+ meta.setData(kKeyHVCC, kTypeHVCC, codecPrivate, codecPrivateSize);
} else {
ALOGW("HEVC is detected, but does not have configuration.");
continue;
}
} else if (!strcmp("V_MPEG4/ISO/ASP", codecID)) {
if (codecPrivateSize > 0) {
- meta->setCString(
+ meta.setCString(
kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_MPEG4);
addESDSFromCodecPrivate(
meta, false, codecPrivate, codecPrivateSize);
@@ -1355,13 +1356,13 @@
continue;
}
} else if (!strcmp("V_VP8", codecID)) {
- meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_VP8);
+ meta.setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_VP8);
} else if (!strcmp("V_VP9", codecID)) {
- meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_VP9);
+ meta.setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_VP9);
if (codecPrivateSize > 0) {
// 'csd-0' for VP9 is the Blob of Codec Private data as
// specified in http://www.webmproject.org/vp9/profiles/.
- meta->setData(
+ meta.setData(
kKeyVp9CodecPrivate, 0, codecPrivate,
codecPrivateSize);
}
@@ -1380,8 +1381,8 @@
ALOGW("track height exceeds int32_t, %lld", height);
continue;
}
- meta->setInt32(kKeyWidth, (int32_t)width);
- meta->setInt32(kKeyHeight, (int32_t)height);
+ meta.setInt32(kKeyWidth, (int32_t)width);
+ meta.setInt32(kKeyHeight, (int32_t)height);
// setting display width/height is optional
const long long displayUnit = vtrack->GetDisplayUnit();
@@ -1391,8 +1392,8 @@
&& displayHeight > 0 && displayHeight <= INT32_MAX) {
switch (displayUnit) {
case 0: // pixels
- meta->setInt32(kKeyDisplayWidth, (int32_t)displayWidth);
- meta->setInt32(kKeyDisplayHeight, (int32_t)displayHeight);
+ meta.setInt32(kKeyDisplayWidth, (int32_t)displayWidth);
+ meta.setInt32(kKeyDisplayHeight, (int32_t)displayHeight);
break;
case 1: // centimeters
case 2: // inches
@@ -1406,8 +1407,8 @@
const long long computedHeight =
std::max(height, width * displayHeight / displayWidth);
if (computedWidth <= INT32_MAX && computedHeight <= INT32_MAX) {
- meta->setInt32(kKeyDisplayWidth, (int32_t)computedWidth);
- meta->setInt32(kKeyDisplayHeight, (int32_t)computedHeight);
+ meta.setInt32(kKeyDisplayWidth, (int32_t)computedWidth);
+ meta.setInt32(kKeyDisplayHeight, (int32_t)computedHeight);
}
break;
}
@@ -1427,34 +1428,34 @@
static_cast<const mkvparser::AudioTrack *>(track);
if (!strcmp("A_AAC", codecID)) {
- meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_AAC);
+ meta.setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_AAC);
CHECK(codecPrivateSize >= 2);
addESDSFromCodecPrivate(
meta, true, codecPrivate, codecPrivateSize);
} else if (!strcmp("A_VORBIS", codecID)) {
- meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_VORBIS);
+ meta.setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_VORBIS);
err = addVorbisCodecInfo(
meta, codecPrivate, codecPrivateSize);
} else if (!strcmp("A_OPUS", codecID)) {
- meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_OPUS);
- meta->setData(kKeyOpusHeader, 0, codecPrivate, codecPrivateSize);
- meta->setInt64(kKeyOpusCodecDelay, track->GetCodecDelay());
- meta->setInt64(kKeyOpusSeekPreRoll, track->GetSeekPreRoll());
+ meta.setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_OPUS);
+ meta.setData(kKeyOpusHeader, 0, codecPrivate, codecPrivateSize);
+ meta.setInt64(kKeyOpusCodecDelay, track->GetCodecDelay());
+ meta.setInt64(kKeyOpusSeekPreRoll, track->GetSeekPreRoll());
mSeekPreRollNs = track->GetSeekPreRoll();
} else if (!strcmp("A_MPEG/L3", codecID)) {
- meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_MPEG);
+ meta.setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_MPEG);
} else if (!strcmp("A_FLAC", codecID)) {
- meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_FLAC);
+ meta.setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_FLAC);
err = addFlacMetadata(meta, codecPrivate, codecPrivateSize);
} else {
ALOGW("%s is not supported.", codecID);
continue;
}
- meta->setInt32(kKeySampleRate, atrack->GetSamplingRate());
- meta->setInt32(kKeyChannelCount, atrack->GetChannels());
+ meta.setInt32(kKeySampleRate, atrack->GetSamplingRate());
+ meta.setInt32(kKeyChannelCount, atrack->GetChannels());
break;
}
@@ -1467,7 +1468,7 @@
char lang[4];
strncpy(lang, language, 3);
lang[3] = '\0';
- meta->setCString(kKeyMediaLanguage, lang);
+ meta.setCString(kKeyMediaLanguage, lang);
}
if (err != OK) {
@@ -1476,7 +1477,7 @@
}
long long durationNs = mSegment->GetDuration();
- meta->setInt64(kKeyDuration, (durationNs + 500) / 1000);
+ meta.setInt64(kKeyDuration, (durationNs + 500) / 1000);
mTracks.push();
size_t n = mTracks.size() - 1;
@@ -1498,7 +1499,7 @@
TrackInfo *info = &mTracks.editItemAt(i);
const char *mime;
- CHECK(info->mMeta->findCString(kKeyMIMEType, &mime));
+ CHECK(info->mMeta.findCString(kKeyMIMEType, &mime));
if (strncasecmp(mime, "video/", 6)) {
continue;
@@ -1524,18 +1525,16 @@
}
iter.advance();
}
- info->mMeta->setInt64(kKeyThumbnailTime, thumbnailTimeUs);
+ info->mMeta.setInt64(kKeyThumbnailTime, thumbnailTimeUs);
}
}
-sp<MetaData> MatroskaExtractor::getMetaData() {
- sp<MetaData> meta = new MetaData;
-
- meta->setCString(
+status_t MatroskaExtractor::getMetaData(MetaDataBase &meta) {
+ meta.setCString(
kKeyMIMEType,
mIsWebm ? "video/webm" : MEDIA_MIMETYPE_CONTAINER_MATROSKA);
- return meta;
+ return OK;
}
uint32_t MatroskaExtractor::flags() const {
diff --git a/media/extractors/mkv/MatroskaExtractor.h b/media/extractors/mkv/MatroskaExtractor.h
index 095452b..3568ea1 100644
--- a/media/extractors/mkv/MatroskaExtractor.h
+++ b/media/extractors/mkv/MatroskaExtractor.h
@@ -21,6 +21,7 @@
#include "mkvparser/mkvparser.h"
#include <media/MediaExtractor.h>
+#include <media/stagefright/MetaDataBase.h>
#include <utils/Vector.h>
#include <utils/threads.h>
@@ -38,12 +39,11 @@
virtual size_t countTracks();
- virtual MediaSourceBase *getTrack(size_t index);
+ virtual MediaTrack *getTrack(size_t index);
- virtual sp<MetaData> getTrackMetaData(
- size_t index, uint32_t flags);
+ virtual status_t getTrackMetaData(MetaDataBase& meta, size_t index, uint32_t flags);
- virtual sp<MetaData> getMetaData();
+ virtual status_t getMetaData(MetaDataBase& meta);
virtual uint32_t flags() const;
@@ -59,7 +59,7 @@
struct TrackInfo {
unsigned long mTrackNum;
bool mEncrypted;
- sp<MetaData> mMeta;
+ MetaDataBase mMeta;
const MatroskaExtractor *mExtractor;
Vector<const mkvparser::CuePoint*> mCuePoints;
@@ -85,20 +85,21 @@
int64_t mSeekPreRollNs;
status_t synthesizeAVCC(TrackInfo *trackInfo, size_t index);
- status_t initTrackInfo(const mkvparser::Track *track, const sp<MetaData> &meta, TrackInfo *trackInfo);
+ status_t initTrackInfo(
+ const mkvparser::Track *track,
+ MetaDataBase &meta,
+ TrackInfo *trackInfo);
void addTracks();
void findThumbnails();
- void getColorInformation(const mkvparser::VideoTrack *vtrack, sp<MetaData> &meta);
+ void getColorInformation(
+ const mkvparser::VideoTrack *vtrack,
+ MetaDataBase &meta);
bool isLiveStreaming() const;
MatroskaExtractor(const MatroskaExtractor &);
MatroskaExtractor &operator=(const MatroskaExtractor &);
};
-bool SniffMatroska(
- DataSourceBase *source, String8 *mimeType, float *confidence,
- sp<AMessage> *);
-
} // namespace android
#endif // MATROSKA_EXTRACTOR_H_
diff --git a/media/extractors/mp3/MP3Extractor.cpp b/media/extractors/mp3/MP3Extractor.cpp
index e55084e..33cff96 100644
--- a/media/extractors/mp3/MP3Extractor.cpp
+++ b/media/extractors/mp3/MP3Extractor.cpp
@@ -25,12 +25,12 @@
#include "XINGSeeker.h"
#include <media/DataSourceBase.h>
-#include <media/MediaSourceBase.h>
+#include <media/MediaTrack.h>
#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/foundation/AMessage.h>
#include <media/stagefright/foundation/avc_utils.h>
#include <media/stagefright/foundation/ByteUtils.h>
-#include <media/stagefright/MediaBuffer.h>
+#include <media/stagefright/MediaBufferBase.h>
#include <media/stagefright/MediaBufferGroup.h>
#include <media/stagefright/MediaDefs.h>
#include <media/stagefright/MediaErrors.h>
@@ -209,34 +209,34 @@
return valid;
}
-class MP3Source : public MediaSourceBase {
+class MP3Source : public MediaTrack {
public:
MP3Source(
- const sp<MetaData> &meta, DataSourceBase *source,
+ MetaDataBase &meta, DataSourceBase *source,
off64_t first_frame_pos, uint32_t fixed_header,
- const sp<MP3Seeker> &seeker);
+ MP3Seeker *seeker);
- virtual status_t start(MetaData *params = NULL);
+ virtual status_t start(MetaDataBase *params = NULL);
virtual status_t stop();
- virtual sp<MetaData> getFormat();
+ virtual status_t getFormat(MetaDataBase &meta);
virtual status_t read(
- MediaBuffer **buffer, const ReadOptions *options = NULL);
+ MediaBufferBase **buffer, const ReadOptions *options = NULL);
protected:
virtual ~MP3Source();
private:
static const size_t kMaxFrameSize;
- sp<MetaData> mMeta;
+ MetaDataBase &mMeta;
DataSourceBase *mDataSource;
off64_t mFirstFramePos;
uint32_t mFixedHeader;
off64_t mCurrentPos;
int64_t mCurrentTimeUs;
bool mStarted;
- sp<MP3Seeker> mSeeker;
+ MP3Seeker *mSeeker;
MediaBufferGroup *mGroup;
int64_t mBasisTimeUs;
@@ -246,31 +246,31 @@
MP3Source &operator=(const MP3Source &);
};
+struct Mp3Meta {
+ off64_t pos;
+ off64_t post_id3_pos;
+ uint32_t header;
+};
+
MP3Extractor::MP3Extractor(
- DataSourceBase *source, const sp<AMessage> &meta)
+ DataSourceBase *source, Mp3Meta *meta)
: mInitCheck(NO_INIT),
mDataSource(source),
mFirstFramePos(-1),
- mFixedHeader(0) {
+ mFixedHeader(0),
+ mSeeker(NULL) {
off64_t pos = 0;
off64_t post_id3_pos;
uint32_t header;
bool success;
- int64_t meta_offset;
- uint32_t meta_header;
- int64_t meta_post_id3_offset;
- if (meta != NULL
- && meta->findInt64("offset", &meta_offset)
- && meta->findInt32("header", (int32_t *)&meta_header)
- && meta->findInt64("post-id3-offset", &meta_post_id3_offset)) {
+ if (meta != NULL) {
// The sniffer has already done all the hard work for us, simply
// accept its judgement.
- pos = (off64_t)meta_offset;
- header = meta_header;
- post_id3_pos = (off64_t)meta_post_id3_offset;
-
+ pos = meta->pos;
+ header = meta->header;
+ post_id3_pos = meta->post_id3_pos;
success = true;
} else {
success = Resync(mDataSource, 0, &pos, &post_id3_pos, &header);
@@ -283,8 +283,7 @@
mFirstFramePos = pos;
mFixedHeader = header;
- mMeta = new MetaData;
- sp<XINGSeeker> seeker = XINGSeeker::CreateFromSource(mDataSource, mFirstFramePos);
+ XINGSeeker *seeker = XINGSeeker::CreateFromSource(mDataSource, mFirstFramePos);
if (seeker == NULL) {
mSeeker = VBRISeeker::CreateFromSource(mDataSource, post_id3_pos);
@@ -293,8 +292,8 @@
int encd = seeker->getEncoderDelay();
int encp = seeker->getEncoderPadding();
if (encd != 0 || encp != 0) {
- mMeta->setInt32(kKeyEncoderDelay, encd);
- mMeta->setInt32(kKeyEncoderPadding, encp);
+ mMeta.setInt32(kKeyEncoderDelay, encd);
+ mMeta.setInt32(kKeyEncoderPadding, encp);
}
}
@@ -330,21 +329,21 @@
switch (layer) {
case 1:
- mMeta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_MPEG_LAYER_I);
+ mMeta.setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_MPEG_LAYER_I);
break;
case 2:
- mMeta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_MPEG_LAYER_II);
+ mMeta.setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_MPEG_LAYER_II);
break;
case 3:
- mMeta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_MPEG);
+ mMeta.setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_MPEG);
break;
default:
TRESPASS();
}
- mMeta->setInt32(kKeySampleRate, sample_rate);
- mMeta->setInt32(kKeyBitRate, bitrate * 1000);
- mMeta->setInt32(kKeyChannelCount, num_channels);
+ mMeta.setInt32(kKeySampleRate, sample_rate);
+ mMeta.setInt32(kKeyBitRate, bitrate * 1000);
+ mMeta.setInt32(kKeyChannelCount, num_channels);
int64_t durationUs;
@@ -364,7 +363,7 @@
}
if (durationUs >= 0) {
- mMeta->setInt64(kKeyDuration, durationUs);
+ mMeta.setInt64(kKeyDuration, durationUs);
}
mInitCheck = OK;
@@ -391,8 +390,8 @@
int32_t delay, padding;
if (sscanf(value, " %*x %x %x %*x", &delay, &padding) == 2) {
- mMeta->setInt32(kKeyEncoderDelay, delay);
- mMeta->setInt32(kKeyEncoderPadding, padding);
+ mMeta.setInt32(kKeyEncoderDelay, delay);
+ mMeta.setInt32(kKeyEncoderPadding, padding);
}
break;
}
@@ -403,11 +402,15 @@
}
}
+MP3Extractor::~MP3Extractor() {
+ delete mSeeker;
+}
+
size_t MP3Extractor::countTracks() {
return mInitCheck != OK ? 0 : 1;
}
-MediaSourceBase *MP3Extractor::getTrack(size_t index) {
+MediaTrack *MP3Extractor::getTrack(size_t index) {
if (mInitCheck != OK || index != 0) {
return NULL;
}
@@ -417,13 +420,14 @@
mSeeker);
}
-sp<MetaData> MP3Extractor::getTrackMetaData(
+status_t MP3Extractor::getTrackMetaData(
+ MetaDataBase &meta,
size_t index, uint32_t /* flags */) {
if (mInitCheck != OK || index != 0) {
- return NULL;
+ return UNKNOWN_ERROR;
}
-
- return mMeta;
+ meta = mMeta;
+ return OK;
}
////////////////////////////////////////////////////////////////////////////////
@@ -436,9 +440,9 @@
// Set our max frame size to the nearest power of 2 above this size (aka, 4kB)
const size_t MP3Source::kMaxFrameSize = (1 << 12); /* 4096 bytes */
MP3Source::MP3Source(
- const sp<MetaData> &meta, DataSourceBase *source,
+ MetaDataBase &meta, DataSourceBase *source,
off64_t first_frame_pos, uint32_t fixed_header,
- const sp<MP3Seeker> &seeker)
+ MP3Seeker *seeker)
: mMeta(meta),
mDataSource(source),
mFirstFramePos(first_frame_pos),
@@ -458,12 +462,12 @@
}
}
-status_t MP3Source::start(MetaData *) {
+status_t MP3Source::start(MetaDataBase *) {
CHECK(!mStarted);
mGroup = new MediaBufferGroup;
- mGroup->add_buffer(new MediaBuffer(kMaxFrameSize));
+ mGroup->add_buffer(MediaBufferBase::Create(kMaxFrameSize));
mCurrentPos = mFirstFramePos;
mCurrentTimeUs = 0;
@@ -487,12 +491,13 @@
return OK;
}
-sp<MetaData> MP3Source::getFormat() {
- return mMeta;
+status_t MP3Source::getFormat(MetaDataBase &meta) {
+ meta = mMeta;
+ return OK;
}
status_t MP3Source::read(
- MediaBuffer **out, const ReadOptions *options) {
+ MediaBufferBase **out, const ReadOptions *options) {
*out = NULL;
int64_t seekTimeUs;
@@ -504,7 +509,7 @@
if (mSeeker == NULL
|| !mSeeker->getOffsetForTime(&actualSeekTimeUs, &mCurrentPos)) {
int32_t bitrate;
- if (!mMeta->findInt32(kKeyBitRate, &bitrate)) {
+ if (!mMeta.findInt32(kKeyBitRate, &bitrate)) {
// bitrate is in bits/sec.
ALOGI("no bitrate");
@@ -522,7 +527,7 @@
mSamplesRead = 0;
}
- MediaBuffer *buffer;
+ MediaBufferBase *buffer;
status_t err = mGroup->acquire_buffer(&buffer);
if (err != OK) {
return err;
@@ -587,8 +592,8 @@
buffer->set_range(0, frame_size);
- buffer->meta_data()->setInt64(kKeyTime, mCurrentTimeUs);
- buffer->meta_data()->setInt32(kKeyIsSyncFrame, 1);
+ buffer->meta_data().setInt64(kKeyTime, mCurrentTimeUs);
+ buffer->meta_data().setInt32(kKeyIsSyncFrame, 1);
mCurrentPos += frame_size;
@@ -600,19 +605,17 @@
return OK;
}
-sp<MetaData> MP3Extractor::getMetaData() {
- sp<MetaData> meta = new MetaData;
-
+status_t MP3Extractor::getMetaData(MetaDataBase &meta) {
+ meta.clear();
if (mInitCheck != OK) {
- return meta;
+ return UNKNOWN_ERROR;
}
-
- meta->setCString(kKeyMIMEType, "audio/mpeg");
+ meta.setCString(kKeyMIMEType, "audio/mpeg");
ID3 id3(mDataSource);
if (!id3.isValid()) {
- return meta;
+ return OK;
}
struct Map {
@@ -651,7 +654,7 @@
it->getString(&s);
delete it;
- meta->setCString(kMap[i].key, s);
+ meta.setCString(kMap[i].key, s);
}
size_t dataSize;
@@ -659,26 +662,20 @@
const void *data = id3.getAlbumArt(&dataSize, &mime);
if (data) {
- meta->setData(kKeyAlbumArt, MetaData::TYPE_NONE, data, dataSize);
- meta->setCString(kKeyAlbumArtMIME, mime.string());
+ meta.setData(kKeyAlbumArt, MetaData::TYPE_NONE, data, dataSize);
+ meta.setCString(kKeyAlbumArtMIME, mime.string());
}
- return meta;
+ return OK;
}
static MediaExtractor* CreateExtractor(
DataSourceBase *source,
void *meta) {
- sp<AMessage> metaData = static_cast<AMessage *>(meta);
+ Mp3Meta *metaData = static_cast<Mp3Meta *>(meta);
return new MP3Extractor(source, metaData);
}
-static void FreeMeta(void *meta) {
- if (meta != nullptr) {
- static_cast<AMessage *>(meta)->decStrong(nullptr);
- }
-}
-
static MediaExtractor::CreatorFunc Sniff(
DataSourceBase *source, float *confidence, void **meta,
MediaExtractor::FreeMetaFunc *freeMeta) {
@@ -698,14 +695,12 @@
return NULL;
}
- AMessage *msg = new AMessage;
- msg->setInt64("offset", pos);
- msg->setInt32("header", header);
- msg->setInt64("post-id3-offset", post_id3_pos);
- *meta = msg;
- *freeMeta = &FreeMeta;
- // ref count will be decreased in FreeMeta.
- msg->incStrong(nullptr);
+ Mp3Meta *mp3Meta = new Mp3Meta;
+ mp3Meta->pos = pos;
+ mp3Meta->header = header;
+ mp3Meta->post_id3_pos = post_id3_pos;
+ *meta = mp3Meta;
+ *freeMeta = ::free;
*confidence = 0.2f;
diff --git a/media/extractors/mp3/MP3Extractor.h b/media/extractors/mp3/MP3Extractor.h
index 6257112..485b0ca 100644
--- a/media/extractors/mp3/MP3Extractor.h
+++ b/media/extractors/mp3/MP3Extractor.h
@@ -20,6 +20,7 @@
#include <utils/Errors.h>
#include <media/MediaExtractor.h>
+#include <media/stagefright/MetaDataBase.h>
namespace android {
@@ -27,16 +28,18 @@
class DataSourceBase;
struct MP3Seeker;
class String8;
+struct Mp3Meta;
class MP3Extractor : public MediaExtractor {
public:
- MP3Extractor(DataSourceBase *source, const sp<AMessage> &meta);
+ MP3Extractor(DataSourceBase *source, Mp3Meta *meta);
+ ~MP3Extractor();
virtual size_t countTracks();
- virtual MediaSourceBase *getTrack(size_t index);
- virtual sp<MetaData> getTrackMetaData(size_t index, uint32_t flags);
+ virtual MediaTrack *getTrack(size_t index);
+ virtual status_t getTrackMetaData(MetaDataBase& meta, size_t index, uint32_t flags);
- virtual sp<MetaData> getMetaData();
+ virtual status_t getMetaData(MetaDataBase& meta);
virtual const char * name() { return "MP3Extractor"; }
private:
@@ -44,18 +47,14 @@
DataSourceBase *mDataSource;
off64_t mFirstFramePos;
- sp<MetaData> mMeta;
+ MetaDataBase mMeta;
uint32_t mFixedHeader;
- sp<MP3Seeker> mSeeker;
+ MP3Seeker *mSeeker;
MP3Extractor(const MP3Extractor &);
MP3Extractor &operator=(const MP3Extractor &);
};
-bool SniffMP3(
- DataSourceBase *source, String8 *mimeType, float *confidence,
- sp<AMessage> *meta);
-
} // namespace android
#endif // MP3_EXTRACTOR_H_
diff --git a/media/extractors/mp3/MP3Seeker.h b/media/extractors/mp3/MP3Seeker.h
index 599542e..0e3af25 100644
--- a/media/extractors/mp3/MP3Seeker.h
+++ b/media/extractors/mp3/MP3Seeker.h
@@ -23,7 +23,7 @@
namespace android {
-struct MP3Seeker : public RefBase {
+struct MP3Seeker {
MP3Seeker() {}
virtual bool getDuration(int64_t *durationUs) = 0;
@@ -33,7 +33,6 @@
// the actual time that seekpoint represents.
virtual bool getOffsetForTime(int64_t *timeUs, off64_t *pos) = 0;
-protected:
virtual ~MP3Seeker() {}
private:
diff --git a/media/extractors/mp3/VBRISeeker.cpp b/media/extractors/mp3/VBRISeeker.cpp
index 51c5d1f..523f14c 100644
--- a/media/extractors/mp3/VBRISeeker.cpp
+++ b/media/extractors/mp3/VBRISeeker.cpp
@@ -36,7 +36,7 @@
}
// static
-sp<VBRISeeker> VBRISeeker::CreateFromSource(
+VBRISeeker *VBRISeeker::CreateFromSource(
DataSourceBase *source, off64_t post_id3_pos) {
off64_t pos = post_id3_pos;
@@ -87,7 +87,7 @@
return NULL;
}
- sp<VBRISeeker> seeker = new (std::nothrow) VBRISeeker;
+ VBRISeeker *seeker = new (std::nothrow) VBRISeeker;
if (seeker == NULL) {
ALOGW("Couldn't allocate VBRISeeker");
return NULL;
@@ -97,6 +97,7 @@
uint8_t *buffer = new (std::nothrow) uint8_t[totalEntrySize];
if (!buffer) {
ALOGW("Couldn't allocate %zu bytes", totalEntrySize);
+ delete seeker;
return NULL;
}
@@ -104,7 +105,7 @@
if (n < (ssize_t)totalEntrySize) {
delete[] buffer;
buffer = NULL;
-
+ delete seeker;
return NULL;
}
diff --git a/media/extractors/mp3/VBRISeeker.h b/media/extractors/mp3/VBRISeeker.h
index e46af36..9213f6e 100644
--- a/media/extractors/mp3/VBRISeeker.h
+++ b/media/extractors/mp3/VBRISeeker.h
@@ -27,7 +27,7 @@
class DataSourceBase;
struct VBRISeeker : public MP3Seeker {
- static sp<VBRISeeker> CreateFromSource(
+ static VBRISeeker *CreateFromSource(
DataSourceBase *source, off64_t post_id3_pos);
virtual bool getDuration(int64_t *durationUs);
diff --git a/media/extractors/mp3/XINGSeeker.cpp b/media/extractors/mp3/XINGSeeker.cpp
index adfa8d2..95ca556 100644
--- a/media/extractors/mp3/XINGSeeker.cpp
+++ b/media/extractors/mp3/XINGSeeker.cpp
@@ -76,11 +76,8 @@
}
// static
-sp<XINGSeeker> XINGSeeker::CreateFromSource(
+XINGSeeker *XINGSeeker::CreateFromSource(
DataSourceBase *source, off64_t first_frame_pos) {
- sp<XINGSeeker> seeker = new XINGSeeker;
-
- seeker->mFirstFramePos = first_frame_pos;
uint8_t buffer[4];
int offset = first_frame_pos;
@@ -98,8 +95,6 @@
NULL, &samples_per_frame)) {
return NULL;
}
- seeker->mFirstFramePos += xingframesize;
-
uint8_t version = (buffer[1] >> 3) & 3;
// determine offset of XING header
@@ -132,9 +127,13 @@
offset += 4;
uint32_t flags = U32_AT(buffer);
+ XINGSeeker *seeker = new XINGSeeker;
+ seeker->mFirstFramePos = first_frame_pos + xingframesize;
+
if (flags & 0x0001) { // Frames field is present
if (source->readAt(offset, buffer, 4) < 4) {
- return NULL;
+ delete seeker;
+ return NULL;
}
int32_t frames = U32_AT(buffer);
// only update mDurationUs if the calculated duration is valid (non zero)
@@ -148,6 +147,7 @@
}
if (flags & 0x0002) { // Bytes field is present
if (source->readAt(offset, buffer, 4) < 4) {
+ delete seeker;
return NULL;
}
seeker->mSizeBytes = U32_AT(buffer);
@@ -155,6 +155,7 @@
}
if (flags & 0x0004) { // TOC field is present
if (source->readAt(offset + 1, seeker->mTOC, 99) < 99) {
+ delete seeker;
return NULL;
}
seeker->mTOCValid = true;
@@ -164,6 +165,7 @@
#if 0
if (flags & 0x0008) { // Quality indicator field is present
if (source->readAt(offset, buffer, 4) < 4) {
+ delete seeker;
return NULL;
}
// do something with the quality indicator
@@ -171,6 +173,7 @@
}
if (source->readAt(xingbase + 0xaf - 0x24, &buffer, 1) < 1) { // encoding flags
+ delete seeker;
return false;
}
diff --git a/media/extractors/mp3/XINGSeeker.h b/media/extractors/mp3/XINGSeeker.h
index db847bc..5867eae 100644
--- a/media/extractors/mp3/XINGSeeker.h
+++ b/media/extractors/mp3/XINGSeeker.h
@@ -25,7 +25,7 @@
class DataSourceBase;
struct XINGSeeker : public MP3Seeker {
- static sp<XINGSeeker> CreateFromSource(
+ static XINGSeeker *CreateFromSource(
DataSourceBase *source, off64_t first_frame_pos);
virtual bool getDuration(int64_t *durationUs);
diff --git a/media/extractors/mp4/MPEG4Extractor.cpp b/media/extractors/mp4/MPEG4Extractor.cpp
index 3d7e45c..78d2ac6 100644
--- a/media/extractors/mp4/MPEG4Extractor.cpp
+++ b/media/extractors/mp4/MPEG4Extractor.cpp
@@ -31,7 +31,7 @@
#include "ItemTable.h"
#include "include/ESDS.h"
-#include <media/MediaSourceBase.h>
+#include <media/MediaTrack.h>
#include <media/stagefright/foundation/ABitReader.h>
#include <media/stagefright/foundation/ABuffer.h>
#include <media/stagefright/foundation/ADebug.h>
@@ -41,7 +41,7 @@
#include <media/stagefright/foundation/ColorUtils.h>
#include <media/stagefright/foundation/avc_utils.h>
#include <media/stagefright/foundation/hexdump.h>
-#include <media/stagefright/MediaBuffer.h>
+#include <media/stagefright/MediaBufferBase.h>
#include <media/stagefright/MediaBufferGroup.h>
#include <media/stagefright/MediaDefs.h>
#include <media/stagefright/MetaData.h>
@@ -65,10 +65,10 @@
kMaxAtomSize = 64 * 1024 * 1024,
};
-class MPEG4Source : public MediaSourceBase {
+class MPEG4Source : public MediaTrack {
public:
// Caller retains ownership of both "dataSource" and "sampleTable".
- MPEG4Source(const sp<MetaData> &format,
+ MPEG4Source(MetaDataBase &format,
DataSourceBase *dataSource,
int32_t timeScale,
const sp<SampleTable> &sampleTable,
@@ -78,21 +78,21 @@
const sp<ItemTable> &itemTable);
virtual status_t init();
- virtual status_t start(MetaData *params = NULL);
+ virtual status_t start(MetaDataBase *params = NULL);
virtual status_t stop();
- virtual sp<MetaData> getFormat();
+ virtual status_t getFormat(MetaDataBase &);
- virtual status_t read(MediaBuffer **buffer, const ReadOptions *options = NULL);
+ virtual status_t read(MediaBufferBase **buffer, const ReadOptions *options = NULL);
virtual bool supportNonblockingRead() { return true; }
- virtual status_t fragmentedRead(MediaBuffer **buffer, const ReadOptions *options = NULL);
+ virtual status_t fragmentedRead(MediaBufferBase **buffer, const ReadOptions *options = NULL);
virtual ~MPEG4Source();
private:
Mutex mLock;
- sp<MetaData> mFormat;
+ MetaDataBase &mFormat;
DataSourceBase *mDataSource;
int32_t mTimescale;
sp<SampleTable> mSampleTable;
@@ -128,7 +128,7 @@
MediaBufferGroup *mGroup;
- MediaBuffer *mBuffer;
+ MediaBufferBase *mBuffer;
bool mWantsNALFragments;
@@ -353,10 +353,7 @@
mHasMoovBox(false),
mPreferHeif(mime != NULL && !strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_HEIF)),
mFirstTrack(NULL),
- mLastTrack(NULL),
- mFileMetaData(new MetaData),
- mFirstSINF(NULL),
- mIsDrm(false) {
+ mLastTrack(NULL) {
ALOGV("mime=%s, mPreferHeif=%d", mime, mPreferHeif);
}
@@ -370,15 +367,6 @@
}
mFirstTrack = mLastTrack = NULL;
- SINF *sinf = mFirstSINF;
- while (sinf) {
- SINF *next = sinf->next;
- delete[] sinf->IPMPData;
- delete sinf;
- sinf = next;
- }
- mFirstSINF = NULL;
-
for (size_t i = 0; i < mPssh.size(); i++) {
delete [] mPssh[i].data;
}
@@ -393,13 +381,13 @@
(CAN_SEEK_BACKWARD | CAN_SEEK_FORWARD | CAN_SEEK) : 0);
}
-sp<MetaData> MPEG4Extractor::getMetaData() {
+status_t MPEG4Extractor::getMetaData(MetaDataBase &meta) {
status_t err;
if ((err = readMetaData()) != OK) {
- return new MetaData;
+ return UNKNOWN_ERROR;
}
-
- return mFileMetaData;
+ meta = mFileMetaData;
+ return OK;
}
size_t MPEG4Extractor::countTracks() {
@@ -420,17 +408,18 @@
return n;
}
-sp<MetaData> MPEG4Extractor::getTrackMetaData(
+status_t MPEG4Extractor::getTrackMetaData(
+ MetaDataBase &meta,
size_t index, uint32_t flags) {
status_t err;
if ((err = readMetaData()) != OK) {
- return NULL;
+ return UNKNOWN_ERROR;
}
Track *track = mFirstTrack;
while (index > 0) {
if (track == NULL) {
- return NULL;
+ return UNKNOWN_ERROR;
}
track = track->next;
@@ -438,15 +427,15 @@
}
if (track == NULL) {
- return NULL;
+ return UNKNOWN_ERROR;
}
[=] {
int64_t duration;
int32_t samplerate;
if (track->has_elst && mHeaderTimescale != 0 &&
- track->meta->findInt64(kKeyDuration, &duration) &&
- track->meta->findInt32(kKeySampleRate, &samplerate)) {
+ track->meta.findInt64(kKeyDuration, &duration) &&
+ track->meta.findInt32(kKeySampleRate, &samplerate)) {
track->has_elst = false;
@@ -473,7 +462,7 @@
return;
}
ALOGV("delay = %" PRId64, delay);
- track->meta->setInt32(kKeyEncoderDelay, delay);
+ track->meta.setInt32(kKeyEncoderDelay, delay);
int64_t scaled_duration;
// scaled_duration = duration * mHeaderTimescale;
@@ -513,7 +502,7 @@
return;
}
ALOGV("paddingsamples = %" PRId64, paddingsamples);
- track->meta->setInt32(kKeyEncoderPadding, paddingsamples);
+ track->meta.setInt32(kKeyEncoderPadding, paddingsamples);
}
}();
@@ -522,7 +511,7 @@
track->includes_expensive_metadata = true;
const char *mime;
- CHECK(track->meta->findCString(kKeyMIMEType, &mime));
+ CHECK(track->meta.findCString(kKeyMIMEType, &mime));
if (!strncasecmp("video/", mime, 6)) {
// MPEG2 tracks do not provide CSD, so read the stream header
if (!strcmp(mime, MEDIA_MIMETYPE_VIDEO_MPEG2)) {
@@ -535,16 +524,16 @@
}
uint8_t header[kMaxTrackHeaderSize];
if (mDataSource->readAt(offset, &header, size) == (ssize_t)size) {
- track->meta->setData(kKeyStreamHeader, 'mdat', header, size);
+ track->meta.setData(kKeyStreamHeader, 'mdat', header, size);
}
}
}
if (mMoofOffset > 0) {
int64_t duration;
- if (track->meta->findInt64(kKeyDuration, &duration)) {
+ if (track->meta.findInt64(kKeyDuration, &duration)) {
// nothing fancy, just pick a frame near 1/4th of the duration
- track->meta->setInt64(
+ track->meta.setInt64(
kKeyThumbnailTime, duration / 4);
}
} else {
@@ -555,7 +544,7 @@
&& track->sampleTable->getMetaDataForSample(
sampleIndex, NULL /* offset */, NULL /* size */,
&sampleTime) == OK) {
- track->meta->setInt64(
+ track->meta.setInt64(
kKeyThumbnailTime,
((int64_t)sampleTime * 1000000) / track->timescale);
}
@@ -563,7 +552,8 @@
}
}
- return track->meta;
+ meta = track->meta;
+ return OK;
}
status_t MPEG4Extractor::readMetaData() {
@@ -621,8 +611,8 @@
}
mLastTrack = track;
- track->meta = meta;
- track->meta->setInt32(kKeyTrackID, imageIndex);
+ track->meta = *(meta.get());
+ track->meta.setInt32(kKeyTrackID, imageIndex);
track->includes_expensive_metadata = false;
track->skipTrack = false;
track->timescale = 0;
@@ -631,16 +621,16 @@
if (mInitCheck == OK) {
if (findTrackByMimePrefix("video/") != NULL) {
- mFileMetaData->setCString(
+ mFileMetaData.setCString(
kKeyMIMEType, MEDIA_MIMETYPE_CONTAINER_MPEG4);
} else if (findTrackByMimePrefix("audio/") != NULL) {
- mFileMetaData->setCString(kKeyMIMEType, "audio/mp4");
+ mFileMetaData.setCString(kKeyMIMEType, "audio/mp4");
} else if (findTrackByMimePrefix(
MEDIA_MIMETYPE_IMAGE_ANDROID_HEIC) != NULL) {
- mFileMetaData->setCString(
+ mFileMetaData.setCString(
kKeyMIMEType, MEDIA_MIMETYPE_CONTAINER_HEIF);
} else {
- mFileMetaData->setCString(kKeyMIMEType, "application/octet-stream");
+ mFileMetaData.setCString(kKeyMIMEType, "application/octet-stream");
}
} else {
mInitCheck = err;
@@ -665,184 +655,13 @@
memcpy(ptr + 20, mPssh[i].data, mPssh[i].datalen);
ptr += (20 + mPssh[i].datalen);
}
- mFileMetaData->setData(kKeyPssh, 'pssh', buf, psshsize);
+ mFileMetaData.setData(kKeyPssh, 'pssh', buf, psshsize);
free(buf);
}
return mInitCheck;
}
-char* MPEG4Extractor::getDrmTrackInfo(size_t trackID, int *len) {
- if (mFirstSINF == NULL) {
- return NULL;
- }
-
- SINF *sinf = mFirstSINF;
- while (sinf && (trackID != sinf->trackID)) {
- sinf = sinf->next;
- }
-
- if (sinf == NULL) {
- return NULL;
- }
-
- *len = sinf->len;
- return sinf->IPMPData;
-}
-
-// Reads an encoded integer 7 bits at a time until it encounters the high bit clear.
-static int32_t readSize(off64_t offset,
- DataSourceBase *DataSourceBase, uint8_t *numOfBytes) {
- uint32_t size = 0;
- uint8_t data;
- bool moreData = true;
- *numOfBytes = 0;
-
- while (moreData) {
- if (DataSourceBase->readAt(offset, &data, 1) < 1) {
- return -1;
- }
- offset ++;
- moreData = (data >= 128) ? true : false;
- size = (size << 7) | (data & 0x7f); // Take last 7 bits
- (*numOfBytes) ++;
- }
-
- return size;
-}
-
-status_t MPEG4Extractor::parseDrmSINF(
- off64_t * /* offset */, off64_t data_offset) {
- uint8_t updateIdTag;
- if (mDataSource->readAt(data_offset, &updateIdTag, 1) < 1) {
- return ERROR_IO;
- }
- data_offset ++;
-
- if (0x01/*OBJECT_DESCRIPTOR_UPDATE_ID_TAG*/ != updateIdTag) {
- return ERROR_MALFORMED;
- }
-
- uint8_t numOfBytes;
- int32_t size = readSize(data_offset, mDataSource, &numOfBytes);
- if (size < 0) {
- return ERROR_IO;
- }
- data_offset += numOfBytes;
-
- while(size >= 11 ) {
- uint8_t descriptorTag;
- if (mDataSource->readAt(data_offset, &descriptorTag, 1) < 1) {
- return ERROR_IO;
- }
- data_offset ++;
-
- if (0x11/*OBJECT_DESCRIPTOR_ID_TAG*/ != descriptorTag) {
- return ERROR_MALFORMED;
- }
-
- uint8_t buffer[8];
- //ObjectDescriptorID and ObjectDescriptor url flag
- if (mDataSource->readAt(data_offset, buffer, 2) < 2) {
- return ERROR_IO;
- }
- data_offset += 2;
-
- if ((buffer[1] >> 5) & 0x0001) { //url flag is set
- return ERROR_MALFORMED;
- }
-
- if (mDataSource->readAt(data_offset, buffer, 8) < 8) {
- return ERROR_IO;
- }
- data_offset += 8;
-
- if ((0x0F/*ES_ID_REF_TAG*/ != buffer[1])
- || ( 0x0A/*IPMP_DESCRIPTOR_POINTER_ID_TAG*/ != buffer[5])) {
- return ERROR_MALFORMED;
- }
-
- SINF *sinf = new SINF;
- sinf->trackID = U16_AT(&buffer[3]);
- sinf->IPMPDescriptorID = buffer[7];
- sinf->next = mFirstSINF;
- mFirstSINF = sinf;
-
- size -= (8 + 2 + 1);
- }
-
- if (size != 0) {
- return ERROR_MALFORMED;
- }
-
- if (mDataSource->readAt(data_offset, &updateIdTag, 1) < 1) {
- return ERROR_IO;
- }
- data_offset ++;
-
- if(0x05/*IPMP_DESCRIPTOR_UPDATE_ID_TAG*/ != updateIdTag) {
- return ERROR_MALFORMED;
- }
-
- size = readSize(data_offset, mDataSource, &numOfBytes);
- if (size < 0) {
- return ERROR_IO;
- }
- data_offset += numOfBytes;
-
- while (size > 0) {
- uint8_t tag;
- int32_t dataLen;
- if (mDataSource->readAt(data_offset, &tag, 1) < 1) {
- return ERROR_IO;
- }
- data_offset ++;
-
- if (0x0B/*IPMP_DESCRIPTOR_ID_TAG*/ == tag) {
- uint8_t id;
- dataLen = readSize(data_offset, mDataSource, &numOfBytes);
- if (dataLen < 0) {
- return ERROR_IO;
- } else if (dataLen < 4) {
- return ERROR_MALFORMED;
- }
- data_offset += numOfBytes;
-
- if (mDataSource->readAt(data_offset, &id, 1) < 1) {
- return ERROR_IO;
- }
- data_offset ++;
-
- SINF *sinf = mFirstSINF;
- while (sinf && (sinf->IPMPDescriptorID != id)) {
- sinf = sinf->next;
- }
- if (sinf == NULL) {
- return ERROR_MALFORMED;
- }
- sinf->len = dataLen - 3;
- sinf->IPMPData = new (std::nothrow) char[sinf->len];
- if (sinf->IPMPData == NULL) {
- return ERROR_MALFORMED;
- }
- data_offset += 2;
-
- if (mDataSource->readAt(data_offset, sinf->IPMPData, sinf->len) < sinf->len) {
- return ERROR_IO;
- }
- data_offset += sinf->len;
-
- size -= (dataLen + numOfBytes + 1);
- }
- }
-
- if (size != 0) {
- return ERROR_MALFORMED;
- }
-
- return UNKNOWN_ERROR; // Return a dummy error.
-}
-
struct PathAdder {
PathAdder(Vector<uint32_t> *path, uint32_t chunkType)
: mPath(path) {
@@ -1084,11 +903,10 @@
}
mLastTrack = track;
- track->meta = new MetaData;
track->includes_expensive_metadata = false;
track->skipTrack = false;
track->timescale = 0;
- track->meta->setCString(kKeyMIMEType, "application/octet-stream");
+ track->meta.setCString(kKeyMIMEType, "application/octet-stream");
track->has_elst = false;
}
@@ -1112,7 +930,7 @@
if (isTrack) {
int32_t trackId;
// There must be exact one track header per track.
- if (!mLastTrack->meta->findInt32(kKeyTrackID, &trackId)) {
+ if (!mLastTrack->meta.findInt32(kKeyTrackID, &trackId)) {
mLastTrack->skipTrack = true;
}
@@ -1144,11 +962,7 @@
} else if (chunk_type == FOURCC('m', 'o', 'o', 'v')) {
mInitCheck = OK;
- if (!mIsDrm) {
- return UNKNOWN_ERROR; // Return a dummy error.
- } else {
- return OK;
- }
+ return UNKNOWN_ERROR; // Return a dummy error.
}
break;
}
@@ -1223,12 +1037,12 @@
return ERROR_MALFORMED;
}
- mLastTrack->meta->setCString(kKeyMIMEType, FourCC2MIME(original_fourcc));
+ mLastTrack->meta.setCString(kKeyMIMEType, FourCC2MIME(original_fourcc));
uint32_t num_channels = 0;
uint32_t sample_rate = 0;
if (AdjustChannelsAndRate(original_fourcc, &num_channels, &sample_rate)) {
- mLastTrack->meta->setInt32(kKeyChannelCount, num_channels);
- mLastTrack->meta->setInt32(kKeySampleRate, sample_rate);
+ mLastTrack->meta.setInt32(kKeyChannelCount, num_channels);
+ mLastTrack->meta.setInt32(kKeySampleRate, sample_rate);
}
break;
}
@@ -1281,9 +1095,9 @@
if (mLastTrack == NULL)
return ERROR_MALFORMED;
- mLastTrack->meta->setInt32(kKeyCryptoMode, defaultAlgorithmId);
- mLastTrack->meta->setInt32(kKeyCryptoDefaultIVSize, defaultIVSize);
- mLastTrack->meta->setData(kKeyCryptoKey, 'tenc', defaultKeyId, 16);
+ mLastTrack->meta.setInt32(kKeyCryptoMode, defaultAlgorithmId);
+ mLastTrack->meta.setInt32(kKeyCryptoDefaultIVSize, defaultIVSize);
+ mLastTrack->meta.setData(kKeyCryptoKey, 'tenc', defaultKeyId, 16);
break;
}
@@ -1417,7 +1231,7 @@
}
}
if (duration != 0 && mLastTrack->timescale != 0) {
- mLastTrack->meta->setInt64(
+ mLastTrack->meta.setInt64(
kKeyDuration, (duration * 1000000) / mLastTrack->timescale);
}
@@ -1445,7 +1259,7 @@
lang_code[2] = (lang[1] & 0x1f) + 0x60;
lang_code[3] = '\0';
- mLastTrack->meta->setCString(
+ mLastTrack->meta.setCString(
kKeyMediaLanguage, lang_code);
break;
@@ -1480,7 +1294,7 @@
if (mLastTrack == NULL)
return ERROR_MALFORMED;
- CHECK(mLastTrack->meta->findCString(kKeyMIMEType, &mime));
+ CHECK(mLastTrack->meta.findCString(kKeyMIMEType, &mime));
if (strcasecmp(mime, MEDIA_MIMETYPE_TEXT_3GPP) &&
strcasecmp(mime, "application/octet-stream")) {
// For now we only support a single type of media per track.
@@ -1521,7 +1335,7 @@
}
String8 mimeFormat((const char *)(buffer->data()), chunk_data_size);
- mLastTrack->meta->setCString(kKeyMIMEType, mimeFormat.string());
+ mLastTrack->meta.setCString(kKeyMIMEType, mimeFormat.string());
break;
}
@@ -1596,14 +1410,14 @@
}
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));
+ // if the chunk type is enca, we'll get the type from the frma box later
+ mLastTrack->meta.setCString(kKeyMIMEType, FourCC2MIME(chunk_type));
AdjustChannelsAndRate(chunk_type, &num_channels, &sample_rate);
}
ALOGV("*** coding='%s' %d channels, size %d, rate %d\n",
chunk, num_channels, sample_size, sample_rate);
- mLastTrack->meta->setInt32(kKeyChannelCount, num_channels);
- mLastTrack->meta->setInt32(kKeySampleRate, sample_rate);
+ mLastTrack->meta.setInt32(kKeyChannelCount, num_channels);
+ mLastTrack->meta.setInt32(kKeySampleRate, sample_rate);
while (*offset < stop_offset) {
status_t err = parseChunk(offset, depth + 1);
@@ -1656,11 +1470,11 @@
return ERROR_MALFORMED;
if (chunk_type != FOURCC('e', 'n', 'c', 'v')) {
- // if the chunk type is encv, we'll get the type from the sinf/frma box later
- mLastTrack->meta->setCString(kKeyMIMEType, FourCC2MIME(chunk_type));
+ // if the chunk type is encv, we'll get the type from the frma box later
+ mLastTrack->meta.setCString(kKeyMIMEType, FourCC2MIME(chunk_type));
}
- mLastTrack->meta->setInt32(kKeyWidth, width);
- mLastTrack->meta->setInt32(kKeyHeight, height);
+ mLastTrack->meta.setInt32(kKeyWidth, width);
+ mLastTrack->meta.setInt32(kKeyHeight, height);
off64_t stop_offset = *offset + chunk_size;
*offset = data_offset + sizeof(buffer);
@@ -1748,12 +1562,12 @@
ALOGE("max sample size too big: %zu", max_size);
return ERROR_MALFORMED;
}
- mLastTrack->meta->setInt32(kKeyMaxInputSize, max_size + 10 * 2);
+ mLastTrack->meta.setInt32(kKeyMaxInputSize, max_size + 10 * 2);
} else {
// No size was specified. Pick a conservatively large size.
uint32_t width, height;
- if (!mLastTrack->meta->findInt32(kKeyWidth, (int32_t*)&width) ||
- !mLastTrack->meta->findInt32(kKeyHeight,(int32_t*) &height)) {
+ if (!mLastTrack->meta.findInt32(kKeyWidth, (int32_t*)&width) ||
+ !mLastTrack->meta.findInt32(kKeyHeight,(int32_t*) &height)) {
ALOGE("No width or height, assuming worst case 1080p");
width = 1920;
height = 1080;
@@ -1768,7 +1582,7 @@
}
const char *mime;
- CHECK(mLastTrack->meta->findCString(kKeyMIMEType, &mime));
+ CHECK(mLastTrack->meta.findCString(kKeyMIMEType, &mime));
if (!strcmp(mime, MEDIA_MIMETYPE_VIDEO_AVC)
|| !strcmp(mime, MEDIA_MIMETYPE_VIDEO_HEVC)) {
// AVC & HEVC requires compression ratio of at least 2, and uses
@@ -1782,26 +1596,26 @@
// HACK: allow 10% overhead
// TODO: read sample size from traf atom for fragmented MPEG4.
max_size += max_size / 10;
- mLastTrack->meta->setInt32(kKeyMaxInputSize, max_size);
+ mLastTrack->meta.setInt32(kKeyMaxInputSize, max_size);
}
// NOTE: setting another piece of metadata invalidates any pointers (such as the
// mimetype) previously obtained, so don't cache them.
const char *mime;
- CHECK(mLastTrack->meta->findCString(kKeyMIMEType, &mime));
+ CHECK(mLastTrack->meta.findCString(kKeyMIMEType, &mime));
// Calculate average frame rate.
if (!strncasecmp("video/", mime, 6)) {
size_t nSamples = mLastTrack->sampleTable->countSamples();
if (nSamples == 0) {
int32_t trackId;
- if (mLastTrack->meta->findInt32(kKeyTrackID, &trackId)) {
+ if (mLastTrack->meta.findInt32(kKeyTrackID, &trackId)) {
for (size_t i = 0; i < mTrex.size(); i++) {
Trex *t = &mTrex.editItemAt(i);
if (t->track_ID == (uint32_t) trackId) {
if (t->default_sample_duration > 0) {
int32_t frameRate =
mLastTrack->timescale / t->default_sample_duration;
- mLastTrack->meta->setInt32(kKeyFrameRate, frameRate);
+ mLastTrack->meta.setInt32(kKeyFrameRate, frameRate);
}
break;
}
@@ -1809,15 +1623,15 @@
}
} else {
int64_t durationUs;
- if (mLastTrack->meta->findInt64(kKeyDuration, &durationUs)) {
+ if (mLastTrack->meta.findInt64(kKeyDuration, &durationUs)) {
if (durationUs > 0) {
int32_t frameRate = (nSamples * 1000000LL +
(durationUs >> 1)) / durationUs;
- mLastTrack->meta->setInt32(kKeyFrameRate, frameRate);
+ mLastTrack->meta.setInt32(kKeyFrameRate, frameRate);
}
}
ALOGV("setting frame count %zu", nSamples);
- mLastTrack->meta->setInt32(kKeyFrameCount, nSamples);
+ mLastTrack->meta.setInt32(kKeyFrameCount, nSamples);
}
}
@@ -1925,7 +1739,7 @@
if (buffer[len - 1] != '/') {
buffer[len] = '/';
}
- mFileMetaData->setCString(kKeyLocation, &buffer[0]);
+ mFileMetaData.setCString(kKeyLocation, &buffer[0]);
break;
}
@@ -1955,7 +1769,7 @@
if (mLastTrack == NULL)
return ERROR_MALFORMED;
- mLastTrack->meta->setData(
+ mLastTrack->meta.setData(
kKeyESDS, kTypeESDS, &buffer[4], chunk_data_size - 4);
if (mPath.size() >= 2
@@ -1980,7 +1794,7 @@
uint8_t objectTypeIndication;
if (esds.getObjectTypeIndication(&objectTypeIndication) == OK) {
if (objectTypeIndication >= 0x60 && objectTypeIndication <= 0x65) {
- mLastTrack->meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_MPEG2);
+ mLastTrack->meta.setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_MPEG2);
}
}
}
@@ -2007,10 +1821,10 @@
uint32_t maxBitrate = U32_AT(&buffer[4]);
uint32_t avgBitrate = U32_AT(&buffer[8]);
if (maxBitrate > 0 && maxBitrate < INT32_MAX) {
- mLastTrack->meta->setInt32(kKeyMaxBitRate, (int32_t)maxBitrate);
+ mLastTrack->meta.setInt32(kKeyMaxBitRate, (int32_t)maxBitrate);
}
if (avgBitrate > 0 && avgBitrate < INT32_MAX) {
- mLastTrack->meta->setInt32(kKeyBitRate, (int32_t)avgBitrate);
+ mLastTrack->meta.setInt32(kKeyBitRate, (int32_t)avgBitrate);
}
break;
}
@@ -2034,7 +1848,7 @@
if (mLastTrack == NULL)
return ERROR_MALFORMED;
- mLastTrack->meta->setData(
+ mLastTrack->meta.setData(
kKeyAVCC, kTypeAVCC, buffer->data(), chunk_data_size);
break;
@@ -2056,7 +1870,7 @@
if (mLastTrack == NULL)
return ERROR_MALFORMED;
- mLastTrack->meta->setData(
+ mLastTrack->meta.setData(
kKeyHVCC, kTypeHVCC, buffer->data(), chunk_data_size);
*offset += chunk_size;
@@ -2092,7 +1906,7 @@
if (mLastTrack == NULL)
return ERROR_MALFORMED;
- mLastTrack->meta->setData(kKeyD263, kTypeD263, buffer, chunk_data_size);
+ mLastTrack->meta.setData(kKeyD263, kTypeD263, buffer, chunk_data_size);
break;
}
@@ -2220,12 +2034,12 @@
duration = d32;
}
if (duration != 0 && mHeaderTimescale != 0 && duration < UINT64_MAX / 1000000) {
- mFileMetaData->setInt64(kKeyDuration, duration * 1000000 / mHeaderTimescale);
+ mFileMetaData.setInt64(kKeyDuration, duration * 1000000 / mHeaderTimescale);
}
String8 s;
if (convertTimeToDate(creationTime, &s)) {
- mFileMetaData->setCString(kKeyDate, s.string());
+ mFileMetaData.setCString(kKeyDate, s.string());
}
@@ -2270,7 +2084,7 @@
}
if (duration != 0 && mHeaderTimescale != 0) {
- mFileMetaData->setInt64(kKeyDuration, duration * 1000000 / mHeaderTimescale);
+ mFileMetaData.setInt64(kKeyDuration, duration * 1000000 / mHeaderTimescale);
}
break;
@@ -2278,20 +2092,10 @@
case FOURCC('m', 'd', 'a', 't'):
{
- ALOGV("mdat chunk, drm: %d", mIsDrm);
-
mMdatFound = true;
- if (!mIsDrm) {
- *offset += chunk_size;
- break;
- }
-
- if (chunk_size < 8) {
- return ERROR_MALFORMED;
- }
-
- return parseDrmSINF(offset, data_offset);
+ *offset += chunk_size;
+ break;
}
case FOURCC('h', 'd', 'l', 'r'):
@@ -2314,7 +2118,7 @@
// for a practical reason as various MPEG4 containers use it.
if (type == FOURCC('t', 'e', 'x', 't') || type == FOURCC('s', 'b', 't', 'l')) {
if (mLastTrack != NULL) {
- mLastTrack->meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_TEXT_3GPP);
+ mLastTrack->meta.setCString(kKeyMIMEType, MEDIA_MIMETYPE_TEXT_3GPP);
}
}
@@ -2361,7 +2165,7 @@
uint32_t type;
const void *data;
size_t size = 0;
- if (!mLastTrack->meta->findData(
+ if (!mLastTrack->meta.findData(
kKeyTextFormatData, &type, &data, &size)) {
size = 0;
}
@@ -2389,7 +2193,7 @@
return ERROR_IO;
}
- mLastTrack->meta->setData(
+ mLastTrack->meta.setData(
kKeyTextFormatData, 0, buffer, size + chunk_size);
delete[] buffer;
@@ -2402,31 +2206,29 @@
{
*offset += chunk_size;
- if (mFileMetaData != NULL) {
- ALOGV("chunk_data_size = %" PRId64 " and data_offset = %" PRId64,
- chunk_data_size, data_offset);
+ ALOGV("chunk_data_size = %" PRId64 " and data_offset = %" PRId64,
+ chunk_data_size, data_offset);
- if (chunk_data_size < 0 || static_cast<uint64_t>(chunk_data_size) >= SIZE_MAX - 1) {
- return ERROR_MALFORMED;
- }
- sp<ABuffer> buffer = new ABuffer(chunk_data_size + 1);
- if (buffer->data() == NULL) {
- ALOGE("b/28471206");
- return NO_MEMORY;
- }
- if (mDataSource->readAt(
- data_offset, buffer->data(), chunk_data_size) != (ssize_t)chunk_data_size) {
- return ERROR_IO;
- }
- const int kSkipBytesOfDataBox = 16;
- if (chunk_data_size <= kSkipBytesOfDataBox) {
- return ERROR_MALFORMED;
- }
-
- mFileMetaData->setData(
- kKeyAlbumArt, MetaData::TYPE_NONE,
- buffer->data() + kSkipBytesOfDataBox, chunk_data_size - kSkipBytesOfDataBox);
+ if (chunk_data_size < 0 || static_cast<uint64_t>(chunk_data_size) >= SIZE_MAX - 1) {
+ return ERROR_MALFORMED;
}
+ sp<ABuffer> buffer = new ABuffer(chunk_data_size + 1);
+ if (buffer->data() == NULL) {
+ ALOGE("b/28471206");
+ return NO_MEMORY;
+ }
+ if (mDataSource->readAt(
+ data_offset, buffer->data(), chunk_data_size) != (ssize_t)chunk_data_size) {
+ return ERROR_IO;
+ }
+ const int kSkipBytesOfDataBox = 16;
+ if (chunk_data_size <= kSkipBytesOfDataBox) {
+ return ERROR_MALFORMED;
+ }
+
+ mFileMetaData.setData(
+ kKeyAlbumArt, MetaData::TYPE_NONE,
+ buffer->data() + kSkipBytesOfDataBox, chunk_data_size - kSkipBytesOfDataBox);
break;
}
@@ -2658,9 +2460,9 @@
if (mLastTrack == NULL) {
return ERROR_MALFORMED;
}
- mLastTrack->meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_AC3);
- mLastTrack->meta->setInt32(kKeyChannelCount, channelCount);
- mLastTrack->meta->setInt32(kKeySampleRate, sampleRate);
+ mLastTrack->meta.setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_AC3);
+ mLastTrack->meta.setInt32(kKeyChannelCount, channelCount);
+ mLastTrack->meta.setInt32(kKeySampleRate, sampleRate);
return OK;
}
@@ -2780,8 +2582,8 @@
return ERROR_MALFORMED;
int64_t metaDuration;
- if (!mLastTrack->meta->findInt64(kKeyDuration, &metaDuration) || metaDuration == 0) {
- mLastTrack->meta->setInt64(kKeyDuration, sidxDuration);
+ if (!mLastTrack->meta.findInt64(kKeyDuration, &metaDuration) || metaDuration == 0) {
+ mLastTrack->meta.setInt64(kKeyDuration, sidxDuration);
}
return OK;
}
@@ -2879,7 +2681,7 @@
return ERROR_MALFORMED;
}
if (!strcasecmp(mMetaKeyMap[index].c_str(), "com.android.capture.fps")) {
- mFileMetaData->setFloat(kKeyCaptureFramerate, *(float *)&val);
+ mFileMetaData.setFloat(kKeyCaptureFramerate, *(float *)&val);
}
} else if (dataType == 67 && dataSize >= 4) {
// BE signed int32
@@ -2888,7 +2690,7 @@
return ERROR_MALFORMED;
}
if (!strcasecmp(mMetaKeyMap[index].c_str(), "com.android.video.temporal_layers_count")) {
- mFileMetaData->setInt32(kKeyTemporalLayerCount, val);
+ mFileMetaData.setInt32(kKeyTemporalLayerCount, val);
}
} else {
// add more keys if needed
@@ -2942,7 +2744,7 @@
if (mLastTrack == NULL)
return ERROR_MALFORMED;
- mLastTrack->meta->setInt32(kKeyTrackID, id);
+ mLastTrack->meta.setInt32(kKeyTrackID, id);
size_t matrixOffset = dynSize + 16;
int32_t a00 = U32_AT(&buffer[matrixOffset]);
@@ -2978,15 +2780,15 @@
}
if (rotationDegrees != 0) {
- mLastTrack->meta->setInt32(kKeyRotation, rotationDegrees);
+ mLastTrack->meta.setInt32(kKeyRotation, rotationDegrees);
}
// Handle presentation display size, which could be different
// from the image size indicated by kKeyWidth and kKeyHeight.
uint32_t width = U32_AT(&buffer[dynSize + 52]);
uint32_t height = U32_AT(&buffer[dynSize + 56]);
- mLastTrack->meta->setInt32(kKeyDisplayWidth, width >> 16);
- mLastTrack->meta->setInt32(kKeyDisplayHeight, height >> 16);
+ mLastTrack->meta.setInt32(kKeyDisplayWidth, width >> 16);
+ mLastTrack->meta.setInt32(kKeyDisplayHeight, height >> 16);
return OK;
}
@@ -3071,7 +2873,7 @@
sprintf(tmp, "%d",
(int)buffer[size - 1]);
- mFileMetaData->setCString(kKeyCompilation, tmp);
+ mFileMetaData.setCString(kKeyCompilation, tmp);
}
break;
}
@@ -3083,7 +2885,7 @@
uint16_t* pTotalTracks = (uint16_t*)&buffer[12];
sprintf(tmp, "%d/%d", ntohs(*pTrack), ntohs(*pTotalTracks));
- mFileMetaData->setCString(kKeyCDTrackNumber, tmp);
+ mFileMetaData.setCString(kKeyCDTrackNumber, tmp);
}
break;
}
@@ -3095,7 +2897,7 @@
uint16_t* pTotalDiscs = (uint16_t*)&buffer[12];
sprintf(tmp, "%d/%d", ntohs(*pDisc), ntohs(*pTotalDiscs));
- mFileMetaData->setCString(kKeyDiscNumber, tmp);
+ mFileMetaData.setCString(kKeyDiscNumber, tmp);
}
break;
}
@@ -3139,8 +2941,8 @@
return ERROR_MALFORMED;
}
- mLastTrack->meta->setInt32(kKeyEncoderDelay, delay);
- mLastTrack->meta->setInt32(kKeyEncoderPadding, padding);
+ mLastTrack->meta.setInt32(kKeyEncoderDelay, delay);
+ mLastTrack->meta.setInt32(kKeyEncoderPadding, padding);
}
}
@@ -3155,9 +2957,9 @@
break;
}
- if (size >= 8 && metadataKey && !mFileMetaData->hasData(metadataKey)) {
+ if (size >= 8 && metadataKey && !mFileMetaData.hasData(metadataKey)) {
if (metadataKey == kKeyAlbumArt) {
- mFileMetaData->setData(
+ mFileMetaData.setData(
kKeyAlbumArt, MetaData::TYPE_NONE,
buffer + 8, size - 8);
} else if (metadataKey == kKeyGenre) {
@@ -3174,18 +2976,18 @@
char genre[10];
sprintf(genre, "%d", genrecode);
- mFileMetaData->setCString(metadataKey, genre);
+ mFileMetaData.setCString(metadataKey, genre);
} else if (flags == 1) {
// custom genre string
buffer[size] = '\0';
- mFileMetaData->setCString(
+ mFileMetaData.setCString(
metadataKey, (const char *)buffer + 8);
}
} else {
buffer[size] = '\0';
- mFileMetaData->setCString(
+ mFileMetaData.setCString(
metadataKey, (const char *)buffer + 8);
}
}
@@ -3225,11 +3027,11 @@
primaries, transfer, coeffs, fullRange, aspects);
// only store the first color specification
- if (!mLastTrack->meta->hasData(kKeyColorPrimaries)) {
- mLastTrack->meta->setInt32(kKeyColorPrimaries, aspects.mPrimaries);
- mLastTrack->meta->setInt32(kKeyTransferFunction, aspects.mTransfer);
- mLastTrack->meta->setInt32(kKeyColorMatrix, aspects.mMatrixCoeffs);
- mLastTrack->meta->setInt32(kKeyColorRange, aspects.mRange);
+ if (!mLastTrack->meta.hasData(kKeyColorPrimaries)) {
+ mLastTrack->meta.setInt32(kKeyColorPrimaries, aspects.mPrimaries);
+ mLastTrack->meta.setInt32(kKeyTransferFunction, aspects.mTransfer);
+ mLastTrack->meta.setInt32(kKeyColorMatrix, aspects.mMatrixCoeffs);
+ mLastTrack->meta.setInt32(kKeyColorRange, aspects.mRange);
}
}
@@ -3284,7 +3086,7 @@
char tmp[4];
sprintf(tmp, "%u", buffer[size - 1]);
- mFileMetaData->setCString(kKeyCDTrackNumber, tmp);
+ mFileMetaData.setCString(kKeyCDTrackNumber, tmp);
}
metadataKey = kKeyAlbum;
@@ -3305,7 +3107,7 @@
if (year < 10000) {
sprintf(tmp, "%u", year);
- mFileMetaData->setCString(kKeyYear, tmp);
+ mFileMetaData.setCString(kKeyYear, tmp);
}
break;
}
@@ -3350,11 +3152,11 @@
if (isUTF8) {
buffer[size] = 0;
- mFileMetaData->setCString(metadataKey, (const char *)buffer + 6);
+ mFileMetaData.setCString(metadataKey, (const char *)buffer + 6);
} else {
// Convert from UTF-16 string to UTF-8 string.
String8 tmpUTF8str(framedata, len16);
- mFileMetaData->setCString(metadataKey, tmpUTF8str.string());
+ mFileMetaData.setCString(metadataKey, tmpUTF8str.string());
}
}
@@ -3389,7 +3191,7 @@
static const size_t kNumMapEntries = sizeof(kMap) / sizeof(kMap[0]);
for (size_t i = 0; i < kNumMapEntries; ++i) {
- if (!mFileMetaData->hasData(kMap[i].key)) {
+ if (!mFileMetaData.hasData(kMap[i].key)) {
ID3::Iterator *it = new ID3::Iterator(id3, kMap[i].tag1);
if (it->done()) {
delete it;
@@ -3405,7 +3207,7 @@
it->getString(&s);
delete it;
- mFileMetaData->setCString(kMap[i].key, s);
+ mFileMetaData.setCString(kMap[i].key, s);
}
}
@@ -3414,13 +3216,13 @@
const void *data = id3.getAlbumArt(&dataSize, &mime);
if (data) {
- mFileMetaData->setData(kKeyAlbumArt, MetaData::TYPE_NONE, data, dataSize);
- mFileMetaData->setCString(kKeyAlbumArtMIME, mime.string());
+ mFileMetaData.setData(kKeyAlbumArt, MetaData::TYPE_NONE, data, dataSize);
+ mFileMetaData.setCString(kKeyAlbumArtMIME, mime.string());
}
}
}
-MediaSourceBase *MPEG4Extractor::getTrack(size_t index) {
+MediaTrack *MPEG4Extractor::getTrack(size_t index) {
status_t err;
if ((err = readMetaData()) != OK) {
return NULL;
@@ -3443,7 +3245,7 @@
Trex *trex = NULL;
int32_t trackId;
- if (track->meta->findInt32(kKeyTrackID, &trackId)) {
+ if (track->meta.findInt32(kKeyTrackID, &trackId)) {
for (size_t i = 0; i < mTrex.size(); i++) {
Trex *t = &mTrex.editItemAt(i);
if (t->track_ID == (uint32_t) trackId) {
@@ -3459,7 +3261,7 @@
ALOGV("getTrack called, pssh: %zu", mPssh.size());
const char *mime;
- if (!track->meta->findCString(kKeyMIMEType, &mime)) {
+ if (!track->meta.findCString(kKeyMIMEType, &mime)) {
return NULL;
}
@@ -3468,7 +3270,7 @@
uint32_t type;
const void *data;
size_t size;
- if (!track->meta->findData(kKeyAVCC, &type, &data, &size)) {
+ if (!track->meta.findData(kKeyAVCC, &type, &data, &size)) {
return NULL;
}
@@ -3482,7 +3284,7 @@
uint32_t type;
const void *data;
size_t size;
- if (!track->meta->findData(kKeyHVCC, &type, &data, &size)) {
+ if (!track->meta.findData(kKeyHVCC, &type, &data, &size)) {
return NULL;
}
@@ -3509,25 +3311,25 @@
// static
status_t MPEG4Extractor::verifyTrack(Track *track) {
const char *mime;
- CHECK(track->meta->findCString(kKeyMIMEType, &mime));
+ CHECK(track->meta.findCString(kKeyMIMEType, &mime));
uint32_t type;
const void *data;
size_t size;
if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AVC)) {
- if (!track->meta->findData(kKeyAVCC, &type, &data, &size)
+ if (!track->meta.findData(kKeyAVCC, &type, &data, &size)
|| type != kTypeAVCC) {
return ERROR_MALFORMED;
}
} else if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_HEVC)) {
- if (!track->meta->findData(kKeyHVCC, &type, &data, &size)
+ if (!track->meta.findData(kKeyHVCC, &type, &data, &size)
|| type != kTypeHVCC) {
return ERROR_MALFORMED;
}
} else if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_MPEG4)
|| !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_MPEG2)
|| !strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AAC)) {
- if (!track->meta->findData(kKeyESDS, &type, &data, &size)
+ if (!track->meta.findData(kKeyESDS, &type, &data, &size)
|| type != kTypeESDS) {
return ERROR_MALFORMED;
}
@@ -3613,7 +3415,7 @@
if (mLastTrack == NULL)
return ERROR_MALFORMED;
- mLastTrack->meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_QCELP);
+ mLastTrack->meta.setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_QCELP);
return OK;
}
@@ -3630,10 +3432,10 @@
uint32_t avgBitrate = 0;
esds.getBitRate(&maxBitrate, &avgBitrate);
if (maxBitrate > 0 && maxBitrate < INT32_MAX) {
- mLastTrack->meta->setInt32(kKeyMaxBitRate, (int32_t)maxBitrate);
+ mLastTrack->meta.setInt32(kKeyMaxBitRate, (int32_t)maxBitrate);
}
if (avgBitrate > 0 && avgBitrate < INT32_MAX) {
- mLastTrack->meta->setInt32(kKeyBitRate, (int32_t)avgBitrate);
+ mLastTrack->meta.setInt32(kKeyBitRate, (int32_t)avgBitrate);
}
}
@@ -3677,7 +3479,7 @@
return ERROR_MALFORMED;
//keep AOT type
- mLastTrack->meta->setInt32(kKeyAACAOT, objectType);
+ mLastTrack->meta.setInt32(kKeyAACAOT, objectType);
uint32_t freqIndex = br.getBits(4);
@@ -3715,7 +3517,7 @@
extSampleRate = kSamplingRate[extFreqIndex];
}
//TODO: save the extension sampling rate value in meta data =>
- // mLastTrack->meta->setInt32(kKeyExtSampleRate, extSampleRate);
+ // mLastTrack->meta.setInt32(kKeyExtSampleRate, extSampleRate);
}
switch (numChannels) {
@@ -3868,24 +3670,24 @@
return ERROR_MALFORMED;
int32_t prevSampleRate;
- CHECK(mLastTrack->meta->findInt32(kKeySampleRate, &prevSampleRate));
+ CHECK(mLastTrack->meta.findInt32(kKeySampleRate, &prevSampleRate));
if (prevSampleRate != sampleRate) {
ALOGV("mpeg4 audio sample rate different from previous setting. "
"was: %d, now: %d", prevSampleRate, sampleRate);
}
- mLastTrack->meta->setInt32(kKeySampleRate, sampleRate);
+ mLastTrack->meta.setInt32(kKeySampleRate, sampleRate);
int32_t prevChannelCount;
- CHECK(mLastTrack->meta->findInt32(kKeyChannelCount, &prevChannelCount));
+ CHECK(mLastTrack->meta.findInt32(kKeyChannelCount, &prevChannelCount));
if (prevChannelCount != numChannels) {
ALOGV("mpeg4 audio channel count different from previous setting. "
"was: %d, now: %d", prevChannelCount, numChannels);
}
- mLastTrack->meta->setInt32(kKeyChannelCount, numChannels);
+ mLastTrack->meta.setInt32(kKeyChannelCount, numChannels);
return OK;
}
@@ -3893,7 +3695,7 @@
////////////////////////////////////////////////////////////////////////////////
MPEG4Source::MPEG4Source(
- const sp<MetaData> &format,
+ MetaDataBase &format,
DataSourceBase *dataSource,
int32_t timeScale,
const sp<SampleTable> &sampleTable,
@@ -3930,20 +3732,20 @@
memset(&mTrackFragmentHeaderInfo, 0, sizeof(mTrackFragmentHeaderInfo));
- mFormat->findInt32(kKeyCryptoMode, &mCryptoMode);
+ mFormat.findInt32(kKeyCryptoMode, &mCryptoMode);
mDefaultIVSize = 0;
- mFormat->findInt32(kKeyCryptoDefaultIVSize, &mDefaultIVSize);
+ mFormat.findInt32(kKeyCryptoDefaultIVSize, &mDefaultIVSize);
uint32_t keytype;
const void *key;
size_t keysize;
- if (mFormat->findData(kKeyCryptoKey, &keytype, &key, &keysize)) {
+ if (mFormat.findData(kKeyCryptoKey, &keytype, &key, &keysize)) {
CHECK(keysize <= 16);
memset(mCryptoKey, 0, 16);
memcpy(mCryptoKey, key, keysize);
}
const char *mime;
- bool success = mFormat->findCString(kKeyMIMEType, &mime);
+ bool success = mFormat.findCString(kKeyMIMEType, &mime);
CHECK(success);
mIsAVC = !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AVC);
@@ -3954,7 +3756,7 @@
uint32_t type;
const void *data;
size_t size;
- CHECK(format->findData(kKeyAVCC, &type, &data, &size));
+ CHECK(format.findData(kKeyAVCC, &type, &data, &size));
const uint8_t *ptr = (const uint8_t *)data;
@@ -3967,7 +3769,7 @@
uint32_t type;
const void *data;
size_t size;
- CHECK(format->findData(kKeyHVCC, &type, &data, &size));
+ CHECK(format.findData(kKeyHVCC, &type, &data, &size));
const uint8_t *ptr = (const uint8_t *)data;
@@ -3977,7 +3779,7 @@
mNALLengthSize = 1 + (ptr[14 + 7] & 3);
}
- CHECK(format->findInt32(kKeyTrackID, &mTrackId));
+ CHECK(format.findInt32(kKeyTrackID, &mTrackId));
}
@@ -3997,7 +3799,7 @@
free(mCurrentSampleInfoOffsets);
}
-status_t MPEG4Source::start(MetaData *params) {
+status_t MPEG4Source::start(MetaDataBase *params) {
Mutex::Autolock autoLock(mLock);
CHECK(!mStarted);
@@ -4011,7 +3813,7 @@
}
int32_t tmp;
- CHECK(mFormat->findInt32(kKeyMaxInputSize, &tmp));
+ CHECK(mFormat.findInt32(kKeyMaxInputSize, &tmp));
size_t max_size = tmp;
// A somewhat arbitrary limit that should be sufficient for 8k video frames
@@ -4338,7 +4140,7 @@
drmoffset += mCurrentMoofOffset;
int ivlength;
- CHECK(mFormat->findInt32(kKeyCryptoDefaultIVSize, &ivlength));
+ CHECK(mFormat.findInt32(kKeyCryptoDefaultIVSize, &ivlength));
// only 0, 8 and 16 byte initialization vectors are supported
if (ivlength != 0 && ivlength != 8 && ivlength != 16) {
@@ -4655,10 +4457,10 @@
return OK;
}
-sp<MetaData> MPEG4Source::getFormat() {
+status_t MPEG4Source::getFormat(MetaDataBase &meta) {
Mutex::Autolock autoLock(mLock);
-
- return mFormat;
+ meta = mFormat;
+ return OK;
}
size_t MPEG4Source::parseNALSize(const uint8_t *data) const {
@@ -4681,7 +4483,7 @@
}
status_t MPEG4Source::read(
- MediaBuffer **out, const ReadOptions *options) {
+ MediaBufferBase **out, const ReadOptions *options) {
Mutex::Autolock autoLock(mLock);
CHECK(mStarted);
@@ -4706,7 +4508,7 @@
CHECK(mSampleTable == NULL);
CHECK(mItemTable != NULL);
int32_t imageIndex;
- if (!mFormat->findInt32(kKeyTrackID, &imageIndex)) {
+ if (!mFormat.findInt32(kKeyTrackID, &imageIndex)) {
return ERROR_MALFORMED;
}
@@ -4860,19 +4662,19 @@
CHECK(mBuffer != NULL);
mBuffer->set_range(0, size);
- mBuffer->meta_data()->clear();
- mBuffer->meta_data()->setInt64(
+ mBuffer->meta_data().clear();
+ mBuffer->meta_data().setInt64(
kKeyTime, ((int64_t)cts * 1000000) / mTimescale);
- mBuffer->meta_data()->setInt64(
+ mBuffer->meta_data().setInt64(
kKeyDuration, ((int64_t)stts * 1000000) / mTimescale);
if (targetSampleTimeUs >= 0) {
- mBuffer->meta_data()->setInt64(
+ mBuffer->meta_data().setInt64(
kKeyTargetTime, targetSampleTimeUs);
}
if (isSyncSample) {
- mBuffer->meta_data()->setInt32(kKeyIsSyncFrame, 1);
+ mBuffer->meta_data().setInt32(kKeyIsSyncFrame, 1);
}
++mCurrentSampleIndex;
@@ -4906,7 +4708,7 @@
return ERROR_MALFORMED;
}
- MediaBuffer *clone = mBuffer->clone();
+ MediaBufferBase *clone = mBuffer->clone();
CHECK(clone != NULL);
clone->set_range(mBuffer->range_offset() + mNALLengthSize, nal_size);
@@ -4928,7 +4730,7 @@
// the start code (0x00 00 00 01).
ssize_t num_bytes_read = 0;
int32_t drm = 0;
- bool usesDRM = (mFormat->findInt32(kKeyIsDRM, &drm) && drm != 0);
+ bool usesDRM = (mFormat.findInt32(kKeyIsDRM, &drm) && drm != 0);
if (usesDRM) {
num_bytes_read =
mDataSource->readAt(offset, (uint8_t*)mBuffer->data(), size);
@@ -4995,25 +4797,25 @@
mBuffer->set_range(0, dstOffset);
}
- mBuffer->meta_data()->clear();
- mBuffer->meta_data()->setInt64(
+ mBuffer->meta_data().clear();
+ mBuffer->meta_data().setInt64(
kKeyTime, ((int64_t)cts * 1000000) / mTimescale);
- mBuffer->meta_data()->setInt64(
+ mBuffer->meta_data().setInt64(
kKeyDuration, ((int64_t)stts * 1000000) / mTimescale);
if (targetSampleTimeUs >= 0) {
- mBuffer->meta_data()->setInt64(
+ mBuffer->meta_data().setInt64(
kKeyTargetTime, targetSampleTimeUs);
}
if (mIsAVC) {
uint32_t layerId = FindAVCLayerId(
(const uint8_t *)mBuffer->data(), mBuffer->range_length());
- mBuffer->meta_data()->setInt32(kKeyTemporalLayerId, layerId);
+ mBuffer->meta_data().setInt32(kKeyTemporalLayerId, layerId);
}
if (isSyncSample) {
- mBuffer->meta_data()->setInt32(kKeyIsSyncFrame, 1);
+ mBuffer->meta_data().setInt32(kKeyIsSyncFrame, 1);
}
++mCurrentSampleIndex;
@@ -5026,7 +4828,7 @@
}
status_t MPEG4Source::fragmentedRead(
- MediaBuffer **out, const ReadOptions *options) {
+ MediaBufferBase **out, const ReadOptions *options) {
ALOGV("MPEG4Source::fragmentedRead");
@@ -5141,18 +4943,18 @@
}
const Sample *smpl = &mCurrentSamples[mCurrentSampleIndex];
- const sp<MetaData> bufmeta = mBuffer->meta_data();
- bufmeta->clear();
+ MetaDataBase &bufmeta = mBuffer->meta_data();
+ bufmeta.clear();
if (smpl->encryptedsizes.size()) {
// store clear/encrypted lengths in metadata
- bufmeta->setData(kKeyPlainSizes, 0,
+ bufmeta.setData(kKeyPlainSizes, 0,
smpl->clearsizes.array(), smpl->clearsizes.size() * 4);
- bufmeta->setData(kKeyEncryptedSizes, 0,
+ bufmeta.setData(kKeyEncryptedSizes, 0,
smpl->encryptedsizes.array(), smpl->encryptedsizes.size() * 4);
- bufmeta->setData(kKeyCryptoIV, 0, smpl->iv, 16); // use 16 or the actual size?
- bufmeta->setInt32(kKeyCryptoDefaultIVSize, mDefaultIVSize);
- bufmeta->setInt32(kKeyCryptoMode, mCryptoMode);
- bufmeta->setData(kKeyCryptoKey, 0, mCryptoKey, 16);
+ bufmeta.setData(kKeyCryptoIV, 0, smpl->iv, 16); // use 16 or the actual size?
+ bufmeta.setInt32(kKeyCryptoDefaultIVSize, mDefaultIVSize);
+ bufmeta.setInt32(kKeyCryptoMode, mCryptoMode);
+ bufmeta.setData(kKeyCryptoKey, 0, mCryptoKey, 16);
}
if ((!mIsAVC && !mIsHEVC)|| mWantsNALFragments) {
@@ -5178,24 +4980,24 @@
CHECK(mBuffer != NULL);
mBuffer->set_range(0, size);
- mBuffer->meta_data()->setInt64(
+ mBuffer->meta_data().setInt64(
kKeyTime, ((int64_t)cts * 1000000) / mTimescale);
- mBuffer->meta_data()->setInt64(
+ mBuffer->meta_data().setInt64(
kKeyDuration, ((int64_t)smpl->duration * 1000000) / mTimescale);
if (targetSampleTimeUs >= 0) {
- mBuffer->meta_data()->setInt64(
+ mBuffer->meta_data().setInt64(
kKeyTargetTime, targetSampleTimeUs);
}
if (mIsAVC) {
uint32_t layerId = FindAVCLayerId(
(const uint8_t *)mBuffer->data(), mBuffer->range_length());
- mBuffer->meta_data()->setInt32(kKeyTemporalLayerId, layerId);
+ mBuffer->meta_data().setInt32(kKeyTemporalLayerId, layerId);
}
if (isSyncSample) {
- mBuffer->meta_data()->setInt32(kKeyIsSyncFrame, 1);
+ mBuffer->meta_data().setInt32(kKeyIsSyncFrame, 1);
}
++mCurrentSampleIndex;
@@ -5230,7 +5032,7 @@
return ERROR_MALFORMED;
}
- MediaBuffer *clone = mBuffer->clone();
+ MediaBufferBase *clone = mBuffer->clone();
CHECK(clone != NULL);
clone->set_range(mBuffer->range_offset() + mNALLengthSize, nal_size);
@@ -5253,7 +5055,7 @@
// the start code (0x00 00 00 01).
ssize_t num_bytes_read = 0;
int32_t drm = 0;
- bool usesDRM = (mFormat->findInt32(kKeyIsDRM, &drm) && drm != 0);
+ bool usesDRM = (mFormat.findInt32(kKeyIsDRM, &drm) && drm != 0);
void *data = NULL;
bool isMalFormed = false;
if (usesDRM) {
@@ -5264,8 +5066,7 @@
}
} else {
int32_t max_size;
- if (mFormat == NULL
- || !mFormat->findInt32(kKeyMaxInputSize, &max_size)
+ if (!mFormat.findInt32(kKeyMaxInputSize, &max_size)
|| !isInRange((size_t)0u, (size_t)max_size, size)) {
isMalFormed = true;
} else {
@@ -5345,18 +5146,18 @@
mBuffer->set_range(0, dstOffset);
}
- mBuffer->meta_data()->setInt64(
+ mBuffer->meta_data().setInt64(
kKeyTime, ((int64_t)cts * 1000000) / mTimescale);
- mBuffer->meta_data()->setInt64(
+ mBuffer->meta_data().setInt64(
kKeyDuration, ((int64_t)smpl->duration * 1000000) / mTimescale);
if (targetSampleTimeUs >= 0) {
- mBuffer->meta_data()->setInt64(
+ mBuffer->meta_data().setInt64(
kKeyTargetTime, targetSampleTimeUs);
}
if (isSyncSample) {
- mBuffer->meta_data()->setInt32(kKeyIsSyncFrame, 1);
+ mBuffer->meta_data().setInt32(kKeyIsSyncFrame, 1);
}
++mCurrentSampleIndex;
@@ -5372,8 +5173,7 @@
const char *mimePrefix) {
for (Track *track = mFirstTrack; track != NULL; track = track->next) {
const char *mime;
- if (track->meta != NULL
- && track->meta->findCString(kKeyMIMEType, &mime)
+ if (track->meta.findCString(kKeyMIMEType, &mime)
&& !strncasecmp(mime, mimePrefix, strlen(mimePrefix))) {
return track;
}
diff --git a/media/extractors/mp4/MPEG4Extractor.h b/media/extractors/mp4/MPEG4Extractor.h
index 644c430..9a9f0d1 100644
--- a/media/extractors/mp4/MPEG4Extractor.h
+++ b/media/extractors/mp4/MPEG4Extractor.h
@@ -22,6 +22,7 @@
#include <media/DataSourceBase.h>
#include <media/MediaExtractor.h>
+#include <media/stagefright/MetaDataBase.h>
#include <media/stagefright/foundation/AString.h>
#include <utils/List.h>
#include <utils/Vector.h>
@@ -56,16 +57,13 @@
explicit MPEG4Extractor(DataSourceBase *source, const char *mime = NULL);
virtual size_t countTracks();
- virtual MediaSourceBase *getTrack(size_t index);
- virtual sp<MetaData> getTrackMetaData(size_t index, uint32_t flags);
+ virtual MediaTrack *getTrack(size_t index);
+ virtual status_t getTrackMetaData(MetaDataBase& meta, size_t index, uint32_t flags);
- virtual sp<MetaData> getMetaData();
+ virtual status_t getMetaData(MetaDataBase& meta);
virtual uint32_t flags() const;
virtual const char * name() { return "MPEG4Extractor"; }
- // for DRM
- virtual char* getDrmTrackInfo(size_t trackID, int *len);
-
protected:
virtual ~MPEG4Extractor();
@@ -78,7 +76,7 @@
};
struct Track {
Track *next;
- sp<MetaData> meta;
+ MetaDataBase meta;
uint32_t timescale;
sp<SampleTable> sampleTable;
bool includes_expensive_metadata;
@@ -108,7 +106,7 @@
Track *mFirstTrack, *mLastTrack;
- sp<MetaData> mFileMetaData;
+ MetaDataBase mFileMetaData;
Vector<uint32_t> mPath;
String8 mLastCommentMean;
@@ -131,21 +129,8 @@
static status_t verifyTrack(Track *track);
- struct SINF {
- SINF *next;
- uint16_t trackID;
- uint8_t IPMPDescriptorID;
- ssize_t len;
- char *IPMPData;
- };
-
- SINF *mFirstSINF;
-
- bool mIsDrm;
sp<ItemTable> mItemTable;
- status_t parseDrmSINF(off64_t *offset, off64_t data_offset);
-
status_t parseTrackHeader(off64_t data_offset, off64_t data_size);
status_t parseSegmentIndex(off64_t data_offset, size_t data_size);
diff --git a/media/extractors/mpeg2/Android.bp b/media/extractors/mpeg2/Android.bp
index 818f9b0..b012b5d 100644
--- a/media/extractors/mpeg2/Android.bp
+++ b/media/extractors/mpeg2/Android.bp
@@ -18,6 +18,7 @@
"libbinder",
"libcrypto",
"libcutils",
+ "libhidlallocatorutils",
"libhidlbase",
"liblog",
"libmediaextractor",
diff --git a/media/extractors/mpeg2/MPEG2PSExtractor.cpp b/media/extractors/mpeg2/MPEG2PSExtractor.cpp
index b4d0ee5..6980b82 100644
--- a/media/extractors/mpeg2/MPEG2PSExtractor.cpp
+++ b/media/extractors/mpeg2/MPEG2PSExtractor.cpp
@@ -24,7 +24,7 @@
#include "mpeg2ts/ESQueue.h"
#include <media/DataSourceBase.h>
-#include <media/MediaSourceBase.h>
+#include <media/MediaTrack.h>
#include <media/stagefright/foundation/ABitReader.h>
#include <media/stagefright/foundation/ABuffer.h>
#include <media/stagefright/foundation/ADebug.h>
@@ -40,16 +40,16 @@
namespace android {
-struct MPEG2PSExtractor::Track : public MediaSourceBase, public RefBase {
+struct MPEG2PSExtractor::Track : public MediaTrack, public RefBase {
Track(MPEG2PSExtractor *extractor,
unsigned stream_id, unsigned stream_type);
- virtual status_t start(MetaData *params);
+ virtual status_t start(MetaDataBase *params);
virtual status_t stop();
- virtual sp<MetaData> getFormat();
+ virtual status_t getFormat(MetaDataBase &);
virtual status_t read(
- MediaBuffer **buffer, const ReadOptions *options);
+ MediaBufferBase **buffer, const ReadOptions *options);
protected:
virtual ~Track();
@@ -72,15 +72,15 @@
DISALLOW_EVIL_CONSTRUCTORS(Track);
};
-struct MPEG2PSExtractor::WrappedTrack : public MediaSourceBase {
+struct MPEG2PSExtractor::WrappedTrack : public MediaTrack {
WrappedTrack(MPEG2PSExtractor *extractor, const sp<Track> &track);
- virtual status_t start(MetaData *params);
+ virtual status_t start(MetaDataBase *params);
virtual status_t stop();
- virtual sp<MetaData> getFormat();
+ virtual status_t getFormat(MetaDataBase &);
virtual status_t read(
- MediaBuffer **buffer, const ReadOptions *options);
+ MediaBufferBase **buffer, const ReadOptions *options);
protected:
virtual ~WrappedTrack();
@@ -108,9 +108,10 @@
}
// Remove all tracks that were unable to determine their format.
+ MetaDataBase meta;
for (size_t i = mTracks.size(); i > 0;) {
i--;
- if (mTracks.valueAt(i)->getFormat() == NULL) {
+ if (mTracks.valueAt(i)->getFormat(meta) != OK) {
mTracks.removeItemsAt(i);
}
}
@@ -125,7 +126,7 @@
return mTracks.size();
}
-MediaSourceBase *MPEG2PSExtractor::getTrack(size_t index) {
+MediaTrack *MPEG2PSExtractor::getTrack(size_t index) {
if (index >= mTracks.size()) {
return NULL;
}
@@ -133,20 +134,20 @@
return new WrappedTrack(this, mTracks.valueAt(index));
}
-sp<MetaData> MPEG2PSExtractor::getTrackMetaData(
+status_t MPEG2PSExtractor::getTrackMetaData(
+ MetaDataBase &meta,
size_t index, uint32_t /* flags */) {
if (index >= mTracks.size()) {
- return NULL;
+ return UNKNOWN_ERROR;
}
- return mTracks.valueAt(index)->getFormat();
+ return mTracks.valueAt(index)->getFormat(meta);
}
-sp<MetaData> MPEG2PSExtractor::getMetaData() {
- sp<MetaData> meta = new MetaData;
- meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_CONTAINER_MPEG2PS);
+status_t MPEG2PSExtractor::getMetaData(MetaDataBase &meta) {
+ meta.setCString(kKeyMIMEType, MEDIA_MIMETYPE_CONTAINER_MPEG2PS);
- return meta;
+ return OK;
}
uint32_t MPEG2PSExtractor::flags() const {
@@ -634,12 +635,12 @@
mQueue = NULL;
}
-status_t MPEG2PSExtractor::Track::start(MetaData *params) {
+status_t MPEG2PSExtractor::Track::start(MetaDataBase *) {
if (mSource == NULL) {
return NO_INIT;
}
- return mSource->start(params);
+ return mSource->start(NULL); // AnotherPacketSource::start doesn't use its argument
}
status_t MPEG2PSExtractor::Track::stop() {
@@ -650,16 +651,18 @@
return mSource->stop();
}
-sp<MetaData> MPEG2PSExtractor::Track::getFormat() {
+status_t MPEG2PSExtractor::Track::getFormat(MetaDataBase &meta) {
if (mSource == NULL) {
- return NULL;
+ return NO_INIT;
}
- return mSource->getFormat();
+ sp<MetaData> sourceMeta = mSource->getFormat();
+ meta = *sourceMeta;
+ return OK;
}
status_t MPEG2PSExtractor::Track::read(
- MediaBuffer **buffer, const ReadOptions *options) {
+ MediaBufferBase **buffer, const ReadOptions *options) {
if (mSource == NULL) {
return NO_INIT;
}
@@ -731,7 +734,7 @@
MPEG2PSExtractor::WrappedTrack::~WrappedTrack() {
}
-status_t MPEG2PSExtractor::WrappedTrack::start(MetaData *params) {
+status_t MPEG2PSExtractor::WrappedTrack::start(MetaDataBase *params) {
return mTrack->start(params);
}
@@ -739,12 +742,12 @@
return mTrack->stop();
}
-sp<MetaData> MPEG2PSExtractor::WrappedTrack::getFormat() {
- return mTrack->getFormat();
+status_t MPEG2PSExtractor::WrappedTrack::getFormat(MetaDataBase &meta) {
+ return mTrack->getFormat(meta);
}
status_t MPEG2PSExtractor::WrappedTrack::read(
- MediaBuffer **buffer, const ReadOptions *options) {
+ MediaBufferBase **buffer, const ReadOptions *options) {
return mTrack->read(buffer, options);
}
diff --git a/media/extractors/mpeg2/MPEG2PSExtractor.h b/media/extractors/mpeg2/MPEG2PSExtractor.h
index 2541f4d..8b9dad9 100644
--- a/media/extractors/mpeg2/MPEG2PSExtractor.h
+++ b/media/extractors/mpeg2/MPEG2PSExtractor.h
@@ -20,6 +20,7 @@
#include <media/stagefright/foundation/ABase.h>
#include <media/MediaExtractor.h>
+#include <media/stagefright/MetaDataBase.h>
#include <utils/threads.h>
#include <utils/KeyedVector.h>
@@ -34,10 +35,10 @@
explicit MPEG2PSExtractor(DataSourceBase *source);
virtual size_t countTracks();
- virtual MediaSourceBase *getTrack(size_t index);
- virtual sp<MetaData> getTrackMetaData(size_t index, uint32_t flags);
+ virtual MediaTrack *getTrack(size_t index);
+ virtual status_t getTrackMetaData(MetaDataBase& meta, size_t index, uint32_t flags);
- virtual sp<MetaData> getMetaData();
+ virtual status_t getMetaData(MetaDataBase& meta);
virtual uint32_t flags() const;
virtual const char * name() { return "MPEG2PSExtractor"; }
diff --git a/media/extractors/mpeg2/MPEG2TSExtractor.cpp b/media/extractors/mpeg2/MPEG2TSExtractor.cpp
index 3183064..c83f7ce 100644
--- a/media/extractors/mpeg2/MPEG2TSExtractor.cpp
+++ b/media/extractors/mpeg2/MPEG2TSExtractor.cpp
@@ -24,7 +24,7 @@
#include <media/DataSourceBase.h>
#include <media/IStreamSource.h>
-#include <media/MediaSourceBase.h>
+#include <media/MediaTrack.h>
#include <media/stagefright/foundation/ABuffer.h>
#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/foundation/ALooper.h>
@@ -49,19 +49,19 @@
static const int kMaxDurationReadSize = 250000LL;
static const int kMaxDurationRetry = 6;
-struct MPEG2TSSource : public MediaSourceBase {
+struct MPEG2TSSource : public MediaTrack {
MPEG2TSSource(
MPEG2TSExtractor *extractor,
const sp<AnotherPacketSource> &impl,
bool doesSeek);
virtual ~MPEG2TSSource();
- virtual status_t start(MetaData *params = NULL);
+ virtual status_t start(MetaDataBase *params = NULL);
virtual status_t stop();
- virtual sp<MetaData> getFormat();
+ virtual status_t getFormat(MetaDataBase &);
virtual status_t read(
- MediaBuffer **buffer, const ReadOptions *options = NULL);
+ MediaBufferBase **buffer, const ReadOptions *options = NULL);
private:
MPEG2TSExtractor *mExtractor;
@@ -86,20 +86,22 @@
MPEG2TSSource::~MPEG2TSSource() {
}
-status_t MPEG2TSSource::start(MetaData *params) {
- return mImpl->start(params);
+status_t MPEG2TSSource::start(MetaDataBase *) {
+ return mImpl->start(NULL); // AnotherPacketSource::start() doesn't use its argument
}
status_t MPEG2TSSource::stop() {
return mImpl->stop();
}
-sp<MetaData> MPEG2TSSource::getFormat() {
- return mImpl->getFormat();
+status_t MPEG2TSSource::getFormat(MetaDataBase &meta) {
+ sp<MetaData> implMeta = mImpl->getFormat();
+ meta = *implMeta;
+ return OK;
}
status_t MPEG2TSSource::read(
- MediaBuffer **out, const ReadOptions *options) {
+ MediaBufferBase **out, const ReadOptions *options) {
*out = NULL;
int64_t seekTimeUs;
@@ -133,7 +135,7 @@
return mSourceImpls.size();
}
-MediaSourceBase *MPEG2TSExtractor::getTrack(size_t index) {
+MediaTrack *MPEG2TSExtractor::getTrack(size_t index) {
if (index >= mSourceImpls.size()) {
return NULL;
}
@@ -144,23 +146,28 @@
(mSeekSyncPoints == &mSyncPoints.editItemAt(index)));
}
-sp<MetaData> MPEG2TSExtractor::getTrackMetaData(
+status_t MPEG2TSExtractor::getTrackMetaData(
+ MetaDataBase &meta,
size_t index, uint32_t /* flags */) {
- return index < mSourceImpls.size()
+ sp<MetaData> implMeta = index < mSourceImpls.size()
? mSourceImpls.editItemAt(index)->getFormat() : NULL;
+ if (implMeta == NULL) {
+ return UNKNOWN_ERROR;
+ }
+ meta = *implMeta;
+ return OK;
}
-sp<MetaData> MPEG2TSExtractor::getMetaData() {
- sp<MetaData> meta = new MetaData;
- meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_CONTAINER_MPEG2TS);
+status_t MPEG2TSExtractor::getMetaData(MetaDataBase &meta) {
+ meta.setCString(kKeyMIMEType, MEDIA_MIMETYPE_CONTAINER_MPEG2TS);
- return meta;
+ return OK;
}
//static
-bool MPEG2TSExtractor::isScrambledFormat(const sp<MetaData> &format) {
+bool MPEG2TSExtractor::isScrambledFormat(MetaDataBase &format) {
const char *mime;
- return format->findCString(kKeyMIMEType, &mime)
+ return format.findCString(kKeyMIMEType, &mime)
&& (!strcasecmp(MEDIA_MIMETYPE_VIDEO_SCRAMBLED, mime)
|| !strcasecmp(MEDIA_MIMETYPE_AUDIO_SCRAMBLED, mime));
}
@@ -213,7 +220,7 @@
if (format != NULL) {
haveVideo = true;
addSource(impl);
- if (!isScrambledFormat(format)) {
+ if (!isScrambledFormat(*(format.get()))) {
mSyncPoints.push();
mSeekSyncPoints = &mSyncPoints.editTop();
}
@@ -229,7 +236,7 @@
if (format != NULL) {
haveAudio = true;
addSource(impl);
- if (!isScrambledFormat(format)) {
+ if (!isScrambledFormat(*(format.get()))) {
mSyncPoints.push();
if (!haveVideo) {
mSeekSyncPoints = &mSyncPoints.editTop();
@@ -470,7 +477,7 @@
}
status_t MPEG2TSExtractor::seek(int64_t seekTimeUs,
- const MediaSourceBase::ReadOptions::SeekMode &seekMode) {
+ const MediaTrack::ReadOptions::SeekMode &seekMode) {
if (mSeekSyncPoints == NULL || mSeekSyncPoints->isEmpty()) {
ALOGW("No sync point to seek to.");
// ... and therefore we have nothing useful to do here.
@@ -491,18 +498,18 @@
}
switch (seekMode) {
- case MediaSourceBase::ReadOptions::SEEK_NEXT_SYNC:
+ case MediaTrack::ReadOptions::SEEK_NEXT_SYNC:
if (index == mSeekSyncPoints->size()) {
ALOGW("Next sync not found; starting from the latest sync.");
--index;
}
break;
- case MediaSourceBase::ReadOptions::SEEK_CLOSEST_SYNC:
- case MediaSourceBase::ReadOptions::SEEK_CLOSEST:
+ case MediaTrack::ReadOptions::SEEK_CLOSEST_SYNC:
+ case MediaTrack::ReadOptions::SEEK_CLOSEST:
ALOGW("seekMode not supported: %d; falling back to PREVIOUS_SYNC",
seekMode);
// fall-through
- case MediaSourceBase::ReadOptions::SEEK_PREVIOUS_SYNC:
+ case MediaTrack::ReadOptions::SEEK_PREVIOUS_SYNC:
if (index == 0) {
ALOGW("Previous sync not found; starting from the earliest "
"sync.");
diff --git a/media/extractors/mpeg2/MPEG2TSExtractor.h b/media/extractors/mpeg2/MPEG2TSExtractor.h
index df07fac..cbdd3cb 100644
--- a/media/extractors/mpeg2/MPEG2TSExtractor.h
+++ b/media/extractors/mpeg2/MPEG2TSExtractor.h
@@ -1,4 +1,5 @@
/*
+
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -20,7 +21,8 @@
#include <media/stagefright/foundation/ABase.h>
#include <media/MediaExtractor.h>
-#include <media/MediaSourceBase.h>
+#include <media/MediaTrack.h>
+#include <media/stagefright/MetaDataBase.h>
#include <utils/threads.h>
#include <utils/KeyedVector.h>
#include <utils/Vector.h>
@@ -40,10 +42,10 @@
explicit MPEG2TSExtractor(DataSourceBase *source);
virtual size_t countTracks();
- virtual MediaSourceBase *getTrack(size_t index);
- virtual sp<MetaData> getTrackMetaData(size_t index, uint32_t flags);
+ virtual MediaTrack *getTrack(size_t index);
+ virtual status_t getTrackMetaData(MetaDataBase &meta, size_t index, uint32_t flags);
- virtual sp<MetaData> getMetaData();
+ virtual status_t getMetaData(MetaDataBase& meta);
virtual status_t setMediaCas(const uint8_t* /*casToken*/, size_t /*size*/) override;
@@ -72,7 +74,7 @@
off64_t mOffset;
- static bool isScrambledFormat(const sp<MetaData> &format);
+ static bool isScrambledFormat(MetaDataBase &format);
void init();
void addSource(const sp<AnotherPacketSource> &impl);
diff --git a/media/extractors/ogg/OggExtractor.cpp b/media/extractors/ogg/OggExtractor.cpp
index ab51e5e..4d49013 100644
--- a/media/extractors/ogg/OggExtractor.cpp
+++ b/media/extractors/ogg/OggExtractor.cpp
@@ -22,16 +22,17 @@
#include <cutils/properties.h>
#include <media/DataSourceBase.h>
-#include <media/MediaSourceBase.h>
+#include <media/MediaTrack.h>
+#include <media/VorbisComment.h>
#include <media/stagefright/foundation/ABuffer.h>
#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/foundation/base64.h>
#include <media/stagefright/foundation/ByteUtils.h>
-#include <media/stagefright/MediaBuffer.h>
+#include <media/stagefright/MediaBufferBase.h>
#include <media/stagefright/MediaBufferGroup.h>
#include <media/stagefright/MediaDefs.h>
#include <media/stagefright/MediaErrors.h>
-#include <media/stagefright/MetaData.h>
+#include <media/stagefright/MetaDataBase.h>
#include <utils/String8.h>
extern "C" {
@@ -45,16 +46,16 @@
namespace android {
-struct OggSource : public MediaSourceBase {
+struct OggSource : public MediaTrack {
explicit OggSource(OggExtractor *extractor);
- virtual sp<MetaData> getFormat();
+ virtual status_t getFormat(MetaDataBase &);
- virtual status_t start(MetaData *params = NULL);
+ virtual status_t start(MetaDataBase *params = NULL);
virtual status_t stop();
virtual status_t read(
- MediaBuffer **buffer, const ReadOptions *options = NULL);
+ MediaBufferBase **buffer, const ReadOptions *options = NULL);
protected:
virtual ~OggSource();
@@ -75,18 +76,21 @@
int64_t seekPreRollUs);
virtual ~MyOggExtractor();
- sp<MetaData> getFormat() const;
+ status_t getFormat(MetaDataBase &) const;
// Returns an approximate bitrate in bits per second.
virtual uint64_t approxBitrate() const = 0;
status_t seekToTime(int64_t timeUs);
status_t seekToOffset(off64_t offset);
- virtual status_t readNextPacket(MediaBuffer **buffer) = 0;
+ virtual status_t readNextPacket(MediaBufferBase **buffer) = 0;
status_t init();
- sp<MetaData> getFileMetaData() { return mFileMeta; }
+ status_t getFileMetaData(MetaDataBase &meta) {
+ meta = mFileMeta;
+ return OK;
+ }
protected:
struct Page {
@@ -124,8 +128,8 @@
vorbis_info mVi;
vorbis_comment mVc;
- sp<MetaData> mMeta;
- sp<MetaData> mFileMeta;
+ MetaDataBase mMeta;
+ MetaDataBase mFileMeta;
Vector<TOCEntry> mTableOfContents;
@@ -141,7 +145,7 @@
// 1 - bitstream identification header
// 3 - comment header
// 5 - codec setup header (Vorbis only)
- virtual status_t verifyHeader(MediaBuffer *buffer, uint8_t type) = 0;
+ virtual status_t verifyHeader(MediaBufferBase *buffer, uint8_t type) = 0;
// Read the next ogg packet from the underlying data source; optionally
// calculate the timestamp for the output packet whilst pretending
@@ -149,9 +153,9 @@
//
// *buffer is NULL'ed out immediately upon entry, and if successful a new buffer is allocated;
// clients are responsible for releasing the original buffer.
- status_t _readNextPacket(MediaBuffer **buffer, bool calcVorbisTimestamp);
+ status_t _readNextPacket(MediaBufferBase **buffer, bool calcVorbisTimestamp);
- int32_t getPacketBlockSize(MediaBuffer *buffer);
+ int32_t getPacketBlockSize(MediaBufferBase *buffer);
void parseFileMetaData();
@@ -173,7 +177,7 @@
virtual uint64_t approxBitrate() const;
- virtual status_t readNextPacket(MediaBuffer **buffer) {
+ virtual status_t readNextPacket(MediaBufferBase **buffer) {
return _readNextPacket(buffer, /* calcVorbisTimestamp = */ true);
}
@@ -185,7 +189,7 @@
return granulePos * 1000000ll / mVi.rate;
}
- virtual status_t verifyHeader(MediaBuffer *buffer, uint8_t type);
+ virtual status_t verifyHeader(MediaBufferBase *buffer, uint8_t type);
};
struct MyOpusExtractor : public MyOggExtractor {
@@ -203,25 +207,22 @@
return 0;
}
- virtual status_t readNextPacket(MediaBuffer **buffer);
+ virtual status_t readNextPacket(MediaBufferBase **buffer);
protected:
virtual int64_t getTimeUsOfGranule(uint64_t granulePos) const;
- virtual status_t verifyHeader(MediaBuffer *buffer, uint8_t type);
+ virtual status_t verifyHeader(MediaBufferBase *buffer, uint8_t type);
private:
- status_t verifyOpusHeader(MediaBuffer *buffer);
- status_t verifyOpusComments(MediaBuffer *buffer);
- uint32_t getNumSamplesInPacket(MediaBuffer *buffer) const;
+ status_t verifyOpusHeader(MediaBufferBase *buffer);
+ status_t verifyOpusComments(MediaBufferBase *buffer);
+ uint32_t getNumSamplesInPacket(MediaBufferBase *buffer) const;
uint8_t mChannelCount;
uint16_t mCodecDelay;
int64_t mStartGranulePosition;
};
-static void extractAlbumArt(
- const sp<MetaData> &fileMeta, const void *data, size_t size);
-
////////////////////////////////////////////////////////////////////////////////
OggSource::OggSource(OggExtractor *extractor)
@@ -235,11 +236,11 @@
}
}
-sp<MetaData> OggSource::getFormat() {
- return mExtractor->mImpl->getFormat();
+status_t OggSource::getFormat(MetaDataBase &meta) {
+ return mExtractor->mImpl->getFormat(meta);
}
-status_t OggSource::start(MetaData * /* params */) {
+status_t OggSource::start(MetaDataBase * /* params */) {
if (mStarted) {
return INVALID_OPERATION;
}
@@ -256,7 +257,7 @@
}
status_t OggSource::read(
- MediaBuffer **out, const ReadOptions *options) {
+ MediaBufferBase **out, const ReadOptions *options) {
*out = NULL;
int64_t seekTimeUs;
@@ -268,7 +269,7 @@
}
}
- MediaBuffer *packet;
+ MediaBufferBase *packet;
status_t err = mExtractor->mImpl->readNextPacket(&packet);
if (err != OK) {
@@ -277,14 +278,14 @@
#if 0
int64_t timeUs;
- if (packet->meta_data()->findInt64(kKeyTime, &timeUs)) {
+ if (packet->meta_data().findInt64(kKeyTime, &timeUs)) {
ALOGI("found time = %lld us", timeUs);
} else {
ALOGI("NO time");
}
#endif
- packet->meta_data()->setInt32(kKeyIsSyncFrame, 1);
+ packet->meta_data().setInt32(kKeyIsSyncFrame, 1);
*out = packet;
@@ -321,8 +322,9 @@
vorbis_info_clear(&mVi);
}
-sp<MetaData> MyOggExtractor::getFormat() const {
- return mMeta;
+status_t MyOggExtractor::getFormat(MetaDataBase &meta) const {
+ meta = mMeta;
+ return OK;
}
status_t MyOggExtractor::findNextPage(
@@ -562,13 +564,13 @@
return sizeof(header) + page->mNumSegments + totalSize;
}
-status_t MyOpusExtractor::readNextPacket(MediaBuffer **out) {
+status_t MyOpusExtractor::readNextPacket(MediaBufferBase **out) {
if (mOffset <= mFirstDataOffset && mStartGranulePosition < 0) {
// The first sample might not start at time 0; find out where by subtracting
// the number of samples on the first page from the granule position
// (position of last complete sample) of the first page. This happens
// the first time before we attempt to read a packet from the first page.
- MediaBuffer *mBuf;
+ MediaBufferBase *mBuf;
uint32_t numSamples = 0;
uint64_t curGranulePosition = 0;
while (true) {
@@ -606,24 +608,24 @@
int32_t currentPageSamples;
// Calculate timestamps by accumulating durations starting from the first sample of a page;
// We assume that we only seek to page boundaries.
- if ((*out)->meta_data()->findInt32(kKeyValidSamples, ¤tPageSamples)) {
+ if ((*out)->meta_data().findInt32(kKeyValidSamples, ¤tPageSamples)) {
// first packet in page
if (mOffset == mFirstDataOffset) {
currentPageSamples -= mStartGranulePosition;
- (*out)->meta_data()->setInt32(kKeyValidSamples, currentPageSamples);
+ (*out)->meta_data().setInt32(kKeyValidSamples, currentPageSamples);
}
mCurGranulePosition = mCurrentPage.mGranulePosition - currentPageSamples;
}
int64_t timeUs = getTimeUsOfGranule(mCurGranulePosition);
- (*out)->meta_data()->setInt64(kKeyTime, timeUs);
+ (*out)->meta_data().setInt64(kKeyTime, timeUs);
uint32_t frames = getNumSamplesInPacket(*out);
mCurGranulePosition += frames;
return OK;
}
-uint32_t MyOpusExtractor::getNumSamplesInPacket(MediaBuffer *buffer) const {
+uint32_t MyOpusExtractor::getNumSamplesInPacket(MediaBufferBase *buffer) const {
if (buffer == NULL || buffer->range_length() < 1) {
return 0;
}
@@ -669,10 +671,10 @@
return numSamples;
}
-status_t MyOggExtractor::_readNextPacket(MediaBuffer **out, bool calcVorbisTimestamp) {
+status_t MyOggExtractor::_readNextPacket(MediaBufferBase **out, bool calcVorbisTimestamp) {
*out = NULL;
- MediaBuffer *buffer = NULL;
+ MediaBufferBase *buffer = NULL;
int64_t timeUs = -1;
for (;;) {
@@ -708,7 +710,7 @@
ALOGE("b/36592202");
return ERROR_MALFORMED;
}
- MediaBuffer *tmp = new (std::nothrow) MediaBuffer(fullSize);
+ MediaBufferBase *tmp = MediaBufferBase::Create(fullSize);
if (tmp == NULL) {
if (buffer != NULL) {
buffer->release();
@@ -745,7 +747,7 @@
// We've just read the entire packet.
if (mFirstPacketInPage) {
- buffer->meta_data()->setInt32(
+ buffer->meta_data().setInt32(
kKeyValidSamples, mCurrentPageSamples);
mFirstPacketInPage = false;
}
@@ -767,7 +769,7 @@
mCurrentPage.mPrevPacketPos += actualBlockSize / 2;
mCurrentPage.mPrevPacketSize = curBlockSize;
}
- buffer->meta_data()->setInt64(kKeyTime, timeUs);
+ buffer->meta_data().setInt64(kKeyTime, timeUs);
}
*out = buffer;
@@ -813,10 +815,10 @@
// is already complete.
if (timeUs >= 0) {
- buffer->meta_data()->setInt64(kKeyTime, timeUs);
+ buffer->meta_data().setInt64(kKeyTime, timeUs);
}
- buffer->meta_data()->setInt32(
+ buffer->meta_data().setInt32(
kKeyValidSamples, mCurrentPageSamples);
mFirstPacketInPage = false;
@@ -829,11 +831,10 @@
}
status_t MyOggExtractor::init() {
- mMeta = new MetaData;
- mMeta->setCString(kKeyMIMEType, mMimeType);
+ mMeta.setCString(kKeyMIMEType, mMimeType);
status_t err;
- MediaBuffer *packet;
+ MediaBufferBase *packet;
for (size_t i = 0; i < mNumHeaders; ++i) {
// ignore timestamp for configuration packets
if ((err = _readNextPacket(&packet, /* calcVorbisTimestamp = */ false)) != OK) {
@@ -863,7 +864,7 @@
int64_t durationUs = getTimeUsOfGranule(lastGranulePosition);
- mMeta->setInt64(kKeyDuration, durationUs);
+ mMeta.setInt64(kKeyDuration, durationUs);
buildTableOfContents();
}
@@ -910,7 +911,7 @@
}
}
-int32_t MyOggExtractor::getPacketBlockSize(MediaBuffer *buffer) {
+int32_t MyOggExtractor::getPacketBlockSize(MediaBufferBase *buffer) {
const uint8_t *data =
(const uint8_t *)buffer->data() + buffer->range_offset();
@@ -950,7 +951,7 @@
return pcmSamplePosition * 1000000ll / kOpusSampleRate;
}
-status_t MyOpusExtractor::verifyHeader(MediaBuffer *buffer, uint8_t type) {
+status_t MyOpusExtractor::verifyHeader(MediaBufferBase *buffer, uint8_t type) {
switch (type) {
// there are actually no header types defined in the Opus spec; we choose 1 and 3 to mean
// header and comments such that we can share code with MyVorbisExtractor.
@@ -963,7 +964,7 @@
}
}
-status_t MyOpusExtractor::verifyOpusHeader(MediaBuffer *buffer) {
+status_t MyOpusExtractor::verifyOpusHeader(MediaBufferBase *buffer) {
const size_t kOpusHeaderSize = 19;
const uint8_t *data =
(const uint8_t *)buffer->data() + buffer->range_offset();
@@ -979,25 +980,35 @@
mChannelCount = data[9];
mCodecDelay = U16LE_AT(&data[10]);
- mMeta->setData(kKeyOpusHeader, 0, data, size);
- mMeta->setInt32(kKeySampleRate, kOpusSampleRate);
- mMeta->setInt32(kKeyChannelCount, mChannelCount);
- mMeta->setInt64(kKeyOpusSeekPreRoll /* ns */, kOpusSeekPreRollUs * 1000 /* = 80 ms*/);
- mMeta->setInt64(kKeyOpusCodecDelay /* ns */,
+ mMeta.setData(kKeyOpusHeader, 0, data, size);
+ mMeta.setInt32(kKeySampleRate, kOpusSampleRate);
+ mMeta.setInt32(kKeyChannelCount, mChannelCount);
+ mMeta.setInt64(kKeyOpusSeekPreRoll /* ns */, kOpusSeekPreRollUs * 1000 /* = 80 ms*/);
+ mMeta.setInt64(kKeyOpusCodecDelay /* ns */,
mCodecDelay /* sample/s */ * 1000000000ll / kOpusSampleRate);
return OK;
}
-status_t MyOpusExtractor::verifyOpusComments(MediaBuffer *buffer) {
+struct TmpData {
+ uint8_t *data;
+ TmpData(size_t size) {
+ data = (uint8_t*) malloc(size);
+ }
+ ~TmpData() {
+ free(data);
+ }
+};
+
+status_t MyOpusExtractor::verifyOpusComments(MediaBufferBase *buffer) {
// add artificial framing bit so we can reuse _vorbis_unpack_comment
int32_t commentSize = buffer->range_length() + 1;
- sp<ABuffer> aBuf = new ABuffer(commentSize);
- if (aBuf->capacity() <= buffer->range_length()) {
+ TmpData commentDataHolder(commentSize);
+ uint8_t *commentData = commentDataHolder.data;
+ if (commentData == nullptr) {
return ERROR_MALFORMED;
}
- uint8_t* commentData = aBuf->data();
memcpy(commentData,
(uint8_t *)buffer->data() + buffer->range_offset(),
buffer->range_length());
@@ -1081,7 +1092,7 @@
}
status_t MyVorbisExtractor::verifyHeader(
- MediaBuffer *buffer, uint8_t type) {
+ MediaBufferBase *buffer, uint8_t type) {
const uint8_t *data =
(const uint8_t *)buffer->data() + buffer->range_offset();
@@ -1120,10 +1131,10 @@
return ERROR_MALFORMED;
}
- mMeta->setData(kKeyVorbisInfo, 0, data, size);
- mMeta->setInt32(kKeySampleRate, mVi.rate);
- mMeta->setInt32(kKeyChannelCount, mVi.channels);
- mMeta->setInt32(kKeyBitRate, mVi.bitrate_nominal);
+ mMeta.setData(kKeyVorbisInfo, 0, data, size);
+ mMeta.setInt32(kKeySampleRate, mVi.rate);
+ mMeta.setInt32(kKeyChannelCount, mVi.channels);
+ mMeta.setInt32(kKeyBitRate, mVi.bitrate_nominal);
ALOGV("lower-bitrate = %ld", mVi.bitrate_lower);
ALOGV("upper-bitrate = %ld", mVi.bitrate_upper);
@@ -1138,7 +1149,7 @@
if (mSource->getSize(&size) == OK) {
uint64_t bps = approxBitrate();
if (bps != 0) {
- mMeta->setInt64(kKeyDuration, size * 8000000ll / bps);
+ mMeta.setInt64(kKeyDuration, size * 8000000ll / bps);
}
}
break;
@@ -1160,7 +1171,7 @@
return ERROR_MALFORMED;
}
- mMeta->setData(kKeyVorbisBooks, 0, data, size);
+ mMeta.setData(kKeyVorbisBooks, 0, data, size);
break;
}
}
@@ -1176,138 +1187,14 @@
return (mVi.bitrate_lower + mVi.bitrate_upper) / 2;
}
-// also exists in FLACExtractor, candidate for moving to utility/support library?
-static void parseVorbisComment(
- const sp<MetaData> &fileMeta, const char *comment, size_t commentLength)
-{
- struct {
- const char *const mTag;
- uint32_t mKey;
- } kMap[] = {
- { "TITLE", kKeyTitle },
- { "ARTIST", kKeyArtist },
- { "ALBUMARTIST", kKeyAlbumArtist },
- { "ALBUM ARTIST", kKeyAlbumArtist },
- { "COMPILATION", kKeyCompilation },
- { "ALBUM", kKeyAlbum },
- { "COMPOSER", kKeyComposer },
- { "GENRE", kKeyGenre },
- { "AUTHOR", kKeyAuthor },
- { "TRACKNUMBER", kKeyCDTrackNumber },
- { "DISCNUMBER", kKeyDiscNumber },
- { "DATE", kKeyDate },
- { "YEAR", kKeyYear },
- { "LYRICIST", kKeyWriter },
- { "METADATA_BLOCK_PICTURE", kKeyAlbumArt },
- { "ANDROID_LOOP", kKeyAutoLoop },
- };
-
- for (size_t j = 0; j < sizeof(kMap) / sizeof(kMap[0]); ++j) {
- size_t tagLen = strlen(kMap[j].mTag);
- if (!strncasecmp(kMap[j].mTag, comment, tagLen)
- && comment[tagLen] == '=') {
- if (kMap[j].mKey == kKeyAlbumArt) {
- extractAlbumArt(
- fileMeta,
- &comment[tagLen + 1],
- commentLength - tagLen - 1);
- } else if (kMap[j].mKey == kKeyAutoLoop) {
- if (!strcasecmp(&comment[tagLen + 1], "true")) {
- fileMeta->setInt32(kKeyAutoLoop, true);
- }
- } else {
- fileMeta->setCString(kMap[j].mKey, &comment[tagLen + 1]);
- }
- }
- }
-
-}
-
-// also exists in FLACExtractor, candidate for moving to utility/support library?
-static void extractAlbumArt(
- const sp<MetaData> &fileMeta, const void *data, size_t size) {
- ALOGV("extractAlbumArt from '%s'", (const char *)data);
-
- sp<ABuffer> flacBuffer = decodeBase64(AString((const char *)data, size));
- if (flacBuffer == NULL) {
- ALOGE("malformed base64 encoded data.");
- return;
- }
-
- size_t flacSize = flacBuffer->size();
- uint8_t *flac = flacBuffer->data();
- ALOGV("got flac of size %zu", flacSize);
-
- uint32_t picType;
- uint32_t typeLen;
- uint32_t descLen;
- uint32_t dataLen;
- char type[128];
-
- if (flacSize < 8) {
- return;
- }
-
- picType = U32_AT(flac);
-
- if (picType != 3) {
- // This is not a front cover.
- return;
- }
-
- typeLen = U32_AT(&flac[4]);
- if (typeLen > sizeof(type) - 1) {
- return;
- }
-
- // we've already checked above that flacSize >= 8
- if (flacSize - 8 < typeLen) {
- return;
- }
-
- memcpy(type, &flac[8], typeLen);
- type[typeLen] = '\0';
-
- ALOGV("picType = %d, type = '%s'", picType, type);
-
- if (!strcmp(type, "-->")) {
- // This is not inline cover art, but an external url instead.
- return;
- }
-
- if (flacSize < 32 || flacSize - 32 < typeLen) {
- return;
- }
-
- descLen = U32_AT(&flac[8 + typeLen]);
- if (flacSize - 32 - typeLen < descLen) {
- return;
- }
-
- dataLen = U32_AT(&flac[8 + typeLen + 4 + descLen + 16]);
-
- // we've already checked above that (flacSize - 32 - typeLen - descLen) >= 0
- if (flacSize - 32 - typeLen - descLen < dataLen) {
- return;
- }
-
- ALOGV("got image data, %zu trailing bytes",
- flacSize - 32 - typeLen - descLen - dataLen);
-
- fileMeta->setData(
- kKeyAlbumArt, 0, &flac[8 + typeLen + 4 + descLen + 20], dataLen);
-
- fileMeta->setCString(kKeyAlbumArtMIME, type);
-}
void MyOggExtractor::parseFileMetaData() {
- mFileMeta = new MetaData;
- mFileMeta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_CONTAINER_OGG);
+ mFileMeta.setCString(kKeyMIMEType, MEDIA_MIMETYPE_CONTAINER_OGG);
for (int i = 0; i < mVc.comments; ++i) {
const char *comment = mVc.user_comments[i];
size_t commentLength = mVc.comment_lengths[i];
- parseVorbisComment(mFileMeta, comment, commentLength);
+ parseVorbisComment(&mFileMeta, comment, commentLength);
//ALOGI("comment #%d: '%s'", i + 1, mVc.user_comments[i]);
}
}
@@ -1348,7 +1235,7 @@
return mInitCheck != OK ? 0 : 1;
}
-MediaSourceBase *OggExtractor::getTrack(size_t index) {
+MediaTrack *OggExtractor::getTrack(size_t index) {
if (index >= 1) {
return NULL;
}
@@ -1356,17 +1243,18 @@
return new OggSource(this);
}
-sp<MetaData> OggExtractor::getTrackMetaData(
+status_t OggExtractor::getTrackMetaData(
+ MetaDataBase &meta,
size_t index, uint32_t /* flags */) {
if (index >= 1) {
- return NULL;
+ return UNKNOWN_ERROR;
}
- return mImpl->getFormat();
+ return mImpl->getFormat(meta);
}
-sp<MetaData> OggExtractor::getMetaData() {
- return mImpl->getFileMetaData();
+status_t OggExtractor::getMetaData(MetaDataBase &meta) {
+ return mImpl->getFileMetaData(meta);
}
static MediaExtractor* CreateExtractor(
diff --git a/media/extractors/ogg/OggExtractor.h b/media/extractors/ogg/OggExtractor.h
index c9c37eb..9fe2944 100644
--- a/media/extractors/ogg/OggExtractor.h
+++ b/media/extractors/ogg/OggExtractor.h
@@ -34,10 +34,10 @@
explicit OggExtractor(DataSourceBase *source);
virtual size_t countTracks();
- virtual MediaSourceBase *getTrack(size_t index);
- virtual sp<MetaData> getTrackMetaData(size_t index, uint32_t flags);
+ virtual MediaTrack *getTrack(size_t index);
+ virtual status_t getTrackMetaData(MetaDataBase& meta, size_t index, uint32_t flags);
- virtual sp<MetaData> getMetaData();
+ virtual status_t getMetaData(MetaDataBase& meta);
virtual const char * name() { return "OggExtractor"; }
protected:
@@ -55,10 +55,6 @@
OggExtractor &operator=(const OggExtractor &);
};
-bool SniffOgg(
- DataSourceBase *source, String8 *mimeType, float *confidence,
- sp<AMessage> *);
-
} // namespace android
#endif // OGG_EXTRACTOR_H_
diff --git a/media/extractors/wav/Android.bp b/media/extractors/wav/Android.bp
index 65c71ef..17836bb 100644
--- a/media/extractors/wav/Android.bp
+++ b/media/extractors/wav/Android.bp
@@ -10,7 +10,6 @@
"liblog",
"libmediaextractor",
"libstagefright_foundation",
- "libutils",
],
static_libs: [
diff --git a/media/extractors/wav/WAVExtractor.cpp b/media/extractors/wav/WAVExtractor.cpp
index 2c991a7..f5a1b01 100644
--- a/media/extractors/wav/WAVExtractor.cpp
+++ b/media/extractors/wav/WAVExtractor.cpp
@@ -22,7 +22,7 @@
#include <audio_utils/primitives.h>
#include <media/DataSourceBase.h>
-#include <media/MediaSourceBase.h>
+#include <media/MediaTrack.h>
#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/MediaBufferGroup.h>
#include <media/stagefright/MediaDefs.h>
@@ -55,20 +55,20 @@
return ptr[1] << 8 | ptr[0];
}
-struct WAVSource : public MediaSourceBase {
+struct WAVSource : public MediaTrack {
WAVSource(
DataSourceBase *dataSource,
- const sp<MetaData> &meta,
+ MetaDataBase &meta,
uint16_t waveFormat,
int32_t bitsPerSample,
off64_t offset, size_t size);
- virtual status_t start(MetaData *params = NULL);
+ virtual status_t start(MetaDataBase *params = NULL);
virtual status_t stop();
- virtual sp<MetaData> getFormat();
+ virtual status_t getFormat(MetaDataBase &meta);
virtual status_t read(
- MediaBuffer **buffer, const ReadOptions *options = NULL);
+ MediaBufferBase **buffer, const ReadOptions *options = NULL);
virtual bool supportNonblockingRead() { return true; }
@@ -79,7 +79,7 @@
static const size_t kMaxFrameSize;
DataSourceBase *mDataSource;
- sp<MetaData> mMeta;
+ MetaDataBase &mMeta;
uint16_t mWaveFormat;
int32_t mSampleRate;
int32_t mNumChannels;
@@ -104,23 +104,20 @@
WAVExtractor::~WAVExtractor() {
}
-sp<MetaData> WAVExtractor::getMetaData() {
- sp<MetaData> meta = new MetaData;
-
- if (mInitCheck != OK) {
- return meta;
+status_t WAVExtractor::getMetaData(MetaDataBase &meta) {
+ meta.clear();
+ if (mInitCheck == OK) {
+ meta.setCString(kKeyMIMEType, MEDIA_MIMETYPE_CONTAINER_WAV);
}
- meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_CONTAINER_WAV);
-
- return meta;
+ return OK;
}
size_t WAVExtractor::countTracks() {
return mInitCheck == OK ? 1 : 0;
}
-MediaSourceBase *WAVExtractor::getTrack(size_t index) {
+MediaTrack *WAVExtractor::getTrack(size_t index) {
if (mInitCheck != OK || index > 0) {
return NULL;
}
@@ -130,13 +127,15 @@
mWaveFormat, mBitsPerSample, mDataOffset, mDataSize);
}
-sp<MetaData> WAVExtractor::getTrackMetaData(
+status_t WAVExtractor::getTrackMetaData(
+ MetaDataBase &meta,
size_t index, uint32_t /* flags */) {
if (mInitCheck != OK || index > 0) {
- return NULL;
+ return UNKNOWN_ERROR;
}
- return mTrackMeta;
+ meta = mTrackMeta;
+ return OK;
}
status_t WAVExtractor::init() {
@@ -285,33 +284,33 @@
mDataOffset = offset;
mDataSize = chunkSize;
- mTrackMeta = new MetaData;
+ mTrackMeta.clear();
switch (mWaveFormat) {
case WAVE_FORMAT_PCM:
case WAVE_FORMAT_IEEE_FLOAT:
- mTrackMeta->setCString(
+ mTrackMeta.setCString(
kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_RAW);
break;
case WAVE_FORMAT_ALAW:
- mTrackMeta->setCString(
+ mTrackMeta.setCString(
kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_G711_ALAW);
break;
case WAVE_FORMAT_MSGSM:
- mTrackMeta->setCString(
+ mTrackMeta.setCString(
kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_MSGSM);
break;
default:
CHECK_EQ(mWaveFormat, (uint16_t)WAVE_FORMAT_MULAW);
- mTrackMeta->setCString(
+ mTrackMeta.setCString(
kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_G711_MLAW);
break;
}
- mTrackMeta->setInt32(kKeyChannelCount, mNumChannels);
- mTrackMeta->setInt32(kKeyChannelMask, mChannelMask);
- mTrackMeta->setInt32(kKeySampleRate, mSampleRate);
- mTrackMeta->setInt32(kKeyPcmEncoding, kAudioEncodingPcm16bit);
+ mTrackMeta.setInt32(kKeyChannelCount, mNumChannels);
+ mTrackMeta.setInt32(kKeyChannelMask, mChannelMask);
+ mTrackMeta.setInt32(kKeySampleRate, mSampleRate);
+ mTrackMeta.setInt32(kKeyPcmEncoding, kAudioEncodingPcm16bit);
int64_t durationUs = 0;
if (mWaveFormat == WAVE_FORMAT_MSGSM) {
@@ -333,7 +332,7 @@
1000000LL * num_samples / mSampleRate;
}
- mTrackMeta->setInt64(kKeyDuration, durationUs);
+ mTrackMeta.setInt64(kKeyDuration, durationUs);
return OK;
}
@@ -349,7 +348,7 @@
WAVSource::WAVSource(
DataSourceBase *dataSource,
- const sp<MetaData> &meta,
+ MetaDataBase &meta,
uint16_t waveFormat,
int32_t bitsPerSample,
off64_t offset, size_t size)
@@ -363,10 +362,10 @@
mSize(size),
mStarted(false),
mGroup(NULL) {
- CHECK(mMeta->findInt32(kKeySampleRate, &mSampleRate));
- CHECK(mMeta->findInt32(kKeyChannelCount, &mNumChannels));
+ CHECK(mMeta.findInt32(kKeySampleRate, &mSampleRate));
+ CHECK(mMeta.findInt32(kKeyChannelCount, &mNumChannels));
- mMeta->setInt32(kKeyMaxInputSize, kMaxFrameSize);
+ mMeta.setInt32(kKeyMaxInputSize, kMaxFrameSize);
}
WAVSource::~WAVSource() {
@@ -375,7 +374,7 @@
}
}
-status_t WAVSource::start(MetaData * /* params */) {
+status_t WAVSource::start(MetaDataBase * /* params */) {
ALOGV("WAVSource::start");
CHECK(!mStarted);
@@ -385,7 +384,7 @@
if (mBitsPerSample == 8) {
// As a temporary buffer for 8->16 bit conversion.
- mGroup->add_buffer(new MediaBuffer(kMaxFrameSize));
+ mGroup->add_buffer(MediaBufferBase::Create(kMaxFrameSize));
}
mCurrentPos = mOffset;
@@ -408,14 +407,15 @@
return OK;
}
-sp<MetaData> WAVSource::getFormat() {
+status_t WAVSource::getFormat(MetaDataBase &meta) {
ALOGV("WAVSource::getFormat");
- return mMeta;
+ meta = mMeta;
+ return OK;
}
status_t WAVSource::read(
- MediaBuffer **out, const ReadOptions *options) {
+ MediaBufferBase **out, const ReadOptions *options) {
*out = NULL;
if (options != nullptr && options->getNonBlocking() && !mGroup->has_buffers()) {
@@ -441,7 +441,7 @@
mCurrentPos = pos + mOffset;
}
- MediaBuffer *buffer;
+ MediaBufferBase *buffer;
status_t err = mGroup->acquire_buffer(&buffer);
if (err != OK) {
return err;
@@ -492,7 +492,7 @@
// Convert 8-bit unsigned samples to 16-bit signed.
// Create new buffer with 2 byte wide samples
- MediaBuffer *tmp;
+ MediaBufferBase *tmp;
CHECK_EQ(mGroup->acquire_buffer(&tmp), (status_t)OK);
tmp->set_range(0, 2 * n);
@@ -532,9 +532,9 @@
/ (mNumChannels * bytesPerSample) / mSampleRate;
}
- buffer->meta_data()->setInt64(kKeyTime, timeStampUs);
+ buffer->meta_data().setInt64(kKeyTime, timeStampUs);
- buffer->meta_data()->setInt32(kKeyIsSyncFrame, 1);
+ buffer->meta_data().setInt32(kKeyIsSyncFrame, 1);
mCurrentPos += n;
*out = buffer;
diff --git a/media/extractors/wav/WAVExtractor.h b/media/extractors/wav/WAVExtractor.h
index 67661ed..467d0b7 100644
--- a/media/extractors/wav/WAVExtractor.h
+++ b/media/extractors/wav/WAVExtractor.h
@@ -20,6 +20,7 @@
#include <utils/Errors.h>
#include <media/MediaExtractor.h>
+#include <media/stagefright/MetaDataBase.h>
namespace android {
@@ -32,10 +33,10 @@
explicit WAVExtractor(DataSourceBase *source);
virtual size_t countTracks();
- virtual MediaSourceBase *getTrack(size_t index);
- virtual sp<MetaData> getTrackMetaData(size_t index, uint32_t flags);
+ virtual MediaTrack *getTrack(size_t index);
+ virtual status_t getTrackMetaData(MetaDataBase& meta, size_t index, uint32_t flags);
- virtual sp<MetaData> getMetaData();
+ virtual status_t getMetaData(MetaDataBase& meta);
virtual const char * name() { return "WAVExtractor"; }
virtual ~WAVExtractor();
@@ -51,7 +52,7 @@
uint16_t mBitsPerSample;
off64_t mDataOffset;
size_t mDataSize;
- sp<MetaData> mTrackMeta;
+ MetaDataBase mTrackMeta;
status_t init();
diff --git a/media/libaudioclient/include/media/AudioMixer.h b/media/libaudioclient/include/media/AudioMixer.h
index 2e29316..cf7d90f 100644
--- a/media/libaudioclient/include/media/AudioMixer.h
+++ b/media/libaudioclient/include/media/AudioMixer.h
@@ -46,10 +46,6 @@
class AudioMixer
{
public:
- // This mixer has a hard-coded upper limit of active track inputs;
- // the value is arbitrary but should be less than TRACK0 to avoid confusion.
- static constexpr int32_t MAX_NUM_TRACKS = 256;
-
// Do not change these unless underlying code changes.
// This mixer has a hard-coded upper limit of 8 channels for output.
static constexpr uint32_t MAX_NUM_CHANNELS = FCC_8;
@@ -61,12 +57,6 @@
static const CONSTEXPR float UNITY_GAIN_FLOAT = 1.0f;
enum { // names
-
- // track names (MAX_NUM_TRACKS units)
- TRACK0 = 0x1000,
-
- // 0x2000 is unused
-
// setParameter targets
TRACK = 0x3000,
RESAMPLE = 0x3001,
@@ -105,23 +95,33 @@
// parameter 'value' is a pointer to the new playback rate.
};
- AudioMixer(size_t frameCount, uint32_t sampleRate, int32_t maxNumTracks = MAX_NUM_TRACKS)
- : mMaxNumTracks(maxNumTracks)
- , mSampleRate(sampleRate)
+ AudioMixer(size_t frameCount, uint32_t sampleRate)
+ : mSampleRate(sampleRate)
, mFrameCount(frameCount) {
pthread_once(&sOnceControl, &sInitRoutine);
}
- // For all APIs with "name": TRACK0 <= name < TRACK0 + MAX_NUM_TRACKS
+ // Create a new track in the mixer.
+ //
+ // \param name a unique user-provided integer associated with the track.
+ // If name already exists, the function will abort.
+ // \param channelMask output channel mask.
+ // \param format PCM format
+ // \param sessionId Session id for the track. Tracks with the same
+ // session id will be submixed together.
+ //
+ // \return OK on success.
+ // BAD_VALUE if the format does not satisfy isValidFormat()
+ // or the channelMask does not satisfy isValidChannelMask().
+ status_t create(
+ int name, audio_channel_mask_t channelMask, audio_format_t format, int sessionId);
- // Allocate a track name. Returns new track name if successful, -1 on failure.
- // The failure could be because of an invalid channelMask or format, or that
- // the track capacity of the mixer is exceeded.
- int getTrackName(audio_channel_mask_t channelMask,
- audio_format_t format, int sessionId);
+ bool exists(int name) const {
+ return mTracks.count(name) > 0;
+ }
- // Free an allocated track by name
- void deleteTrackName(int name);
+ // Free an allocated track by name.
+ void destroy(int name);
// Enable or disable an allocated track by name
void enable(int name);
@@ -149,6 +149,23 @@
mNBLogWriter = logWriter;
}
+ static inline bool isValidFormat(audio_format_t format) {
+ switch (format) {
+ case AUDIO_FORMAT_PCM_8_BIT:
+ case AUDIO_FORMAT_PCM_16_BIT:
+ case AUDIO_FORMAT_PCM_24_BIT_PACKED:
+ case AUDIO_FORMAT_PCM_32_BIT:
+ case AUDIO_FORMAT_PCM_FLOAT:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ static inline bool isValidChannelMask(audio_channel_mask_t channelMask) {
+ return audio_channel_mask_is_valid(channelMask); // the RemixBufferProvider is flexible.
+ }
+
private:
/* For multi-format functions (calls template functions
@@ -361,23 +378,9 @@
static void convertMixerFormat(void *out, audio_format_t mixerOutFormat,
void *in, audio_format_t mixerInFormat, size_t sampleCount);
- static inline bool isValidPcmTrackFormat(audio_format_t format) {
- switch (format) {
- case AUDIO_FORMAT_PCM_8_BIT:
- case AUDIO_FORMAT_PCM_16_BIT:
- case AUDIO_FORMAT_PCM_24_BIT_PACKED:
- case AUDIO_FORMAT_PCM_32_BIT:
- case AUDIO_FORMAT_PCM_FLOAT:
- return true;
- default:
- return false;
- }
- }
-
static void sInitRoutine();
// initialization constants
- const int mMaxNumTracks;
const uint32_t mSampleRate;
const size_t mFrameCount;
@@ -390,12 +393,6 @@
std::unique_ptr<int32_t[]> mOutputTemp;
std::unique_ptr<int32_t[]> mResampleTemp;
- // fast lookup of previously deleted track names for reuse.
- // the AudioMixer tries to return the smallest unused name -
- // this is an arbitrary decision (actually any non-negative
- // integer that isn't in mTracks could be used).
- std::set<int /* name */> mUnusedNames; // set of unused track names (may be empty)
-
// track names grouped by main buffer, in no particular order of main buffer.
// however names for a particular main buffer are in order (by construction).
std::unordered_map<void * /* mainBuffer */, std::vector<int /* name */>> mGroups;
diff --git a/media/libaudioprocessing/AudioMixer.cpp b/media/libaudioprocessing/AudioMixer.cpp
index f1daeb4..93ed5f2 100644
--- a/media/libaudioprocessing/AudioMixer.cpp
+++ b/media/libaudioprocessing/AudioMixer.cpp
@@ -90,34 +90,21 @@
return kUseFloat && kUseNewMixer ? AUDIO_FORMAT_PCM_FLOAT : AUDIO_FORMAT_PCM_16_BIT;
}
-int AudioMixer::getTrackName(
- audio_channel_mask_t channelMask, audio_format_t format, int sessionId)
+status_t AudioMixer::create(
+ int name, audio_channel_mask_t channelMask, audio_format_t format, int sessionId)
{
- if (!isValidPcmTrackFormat(format)) {
- ALOGE("AudioMixer::getTrackName invalid format (%#x)", format);
- return -1;
- }
- if (mTracks.size() >= (size_t)mMaxNumTracks) {
- ALOGE("%s: out of track names (max = %d)", __func__, mMaxNumTracks);
- return -1;
- }
+ LOG_ALWAYS_FATAL_IF(exists(name), "name %d already exists", name);
- // get a new name for the track.
- int name;
- if (mUnusedNames.size() != 0) {
- // reuse first name for deleted track.
- auto it = mUnusedNames.begin();
- name = *it;
- (void)mUnusedNames.erase(it);
- } else {
- // we're fully populated, so create a new name.
- name = mTracks.size();
+ if (!isValidChannelMask(channelMask)) {
+ ALOGE("%s invalid channelMask: %#x", __func__, channelMask);
+ return BAD_VALUE;
}
- ALOGV("add track (%d)", name);
+ if (!isValidFormat(format)) {
+ ALOGE("%s invalid format: %#x", __func__, format);
+ return BAD_VALUE;
+ }
auto t = std::make_shared<Track>();
- mTracks[name] = t;
-
{
// TODO: move initialization to the Track constructor.
// assume default parameters for the track, except where noted below
@@ -179,12 +166,14 @@
status_t status = t->prepareForDownmix();
if (status != OK) {
ALOGE("AudioMixer::getTrackName invalid channelMask (%#x)", channelMask);
- return -1;
+ return BAD_VALUE;
}
// prepareForDownmix() may change mDownmixRequiresFormat
ALOGVV("mMixerFormat:%#x mMixerInFormat:%#x\n", t->mMixerFormat, t->mMixerInFormat);
t->prepareForReformat();
- return TRACK0 + name;
+
+ mTracks[name] = t;
+ return OK;
}
}
@@ -193,7 +182,7 @@
// which will simplify this logic.
bool AudioMixer::setChannelMasks(int name,
audio_channel_mask_t trackChannelMask, audio_channel_mask_t mixerChannelMask) {
- LOG_ALWAYS_FATAL_IF(mTracks.find(name) == mTracks.end(), "invalid name: %d", name);
+ LOG_ALWAYS_FATAL_IF(!exists(name), "invalid name: %d", name);
const std::shared_ptr<Track> &track = mTracks[name];
if (trackChannelMask == track->channelMask
@@ -361,23 +350,20 @@
}
}
-void AudioMixer::deleteTrackName(int name)
+void AudioMixer::destroy(int name)
{
- name -= TRACK0;
- LOG_ALWAYS_FATAL_IF(mTracks.find(name) == mTracks.end(), "invalid name: %d", name);
+ LOG_ALWAYS_FATAL_IF(!exists(name), "invalid name: %d", name);
ALOGV("deleteTrackName(%d)", name);
if (mTracks[name]->enabled) {
invalidate();
}
mTracks.erase(name); // deallocate track
- mUnusedNames.emplace(name); // recycle name
}
void AudioMixer::enable(int name)
{
- name -= TRACK0;
- LOG_ALWAYS_FATAL_IF(mTracks.find(name) == mTracks.end(), "invalid name: %d", name);
+ LOG_ALWAYS_FATAL_IF(!exists(name), "invalid name: %d", name);
const std::shared_ptr<Track> &track = mTracks[name];
if (!track->enabled) {
@@ -389,8 +375,7 @@
void AudioMixer::disable(int name)
{
- name -= TRACK0;
- LOG_ALWAYS_FATAL_IF(mTracks.find(name) == mTracks.end(), "invalid name: %d", name);
+ LOG_ALWAYS_FATAL_IF(!exists(name), "invalid name: %d", name);
const std::shared_ptr<Track> &track = mTracks[name];
if (track->enabled) {
@@ -528,8 +513,7 @@
void AudioMixer::setParameter(int name, int target, int param, void *value)
{
- name -= TRACK0;
- LOG_ALWAYS_FATAL_IF(mTracks.find(name) == mTracks.end(), "invalid name: %d", name);
+ LOG_ALWAYS_FATAL_IF(!exists(name), "invalid name: %d", name);
const std::shared_ptr<Track> &track = mTracks[name];
int valueInt = static_cast<int>(reinterpret_cast<uintptr_t>(value));
@@ -808,7 +792,6 @@
size_t AudioMixer::getUnreleasedFrames(int name) const
{
- name -= TRACK0;
const auto it = mTracks.find(name);
if (it != mTracks.end()) {
return it->second->getUnreleasedFrames();
@@ -818,7 +801,7 @@
void AudioMixer::setBufferProvider(int name, AudioBufferProvider* bufferProvider)
{
- name -= TRACK0;
+ LOG_ALWAYS_FATAL_IF(!exists(name), "invalid name: %d", name);
const std::shared_ptr<Track> &track = mTracks[name];
if (track->mInputBufferProvider == bufferProvider) {
@@ -1410,13 +1393,12 @@
// been enabled for mixing.
if (t->mIn == nullptr) break;
- if (CC_UNLIKELY(aux != NULL)) {
- aux += outFrames;
- }
(t.get()->*t->hook)(
outTemp + outFrames * t->mMixerChannelCount, t->buffer.frameCount,
- mResampleTemp.get() /* naked ptr */, aux);
+ mResampleTemp.get() /* naked ptr */,
+ aux != nullptr ? aux + outFrames : nullptr);
outFrames += t->buffer.frameCount;
+
t->bufferProvider->releaseBuffer(&t->buffer);
}
}
@@ -1704,7 +1686,7 @@
out += outFrames * channels;
if (aux != NULL) {
- aux += channels;
+ aux += outFrames;
}
numFrames -= b.frameCount;
diff --git a/media/libaudioprocessing/tests/test-mixer.cpp b/media/libaudioprocessing/tests/test-mixer.cpp
index b67810d..bc9d2a6 100644
--- a/media/libaudioprocessing/tests/test-mixer.cpp
+++ b/media/libaudioprocessing/tests/test-mixer.cpp
@@ -143,10 +143,6 @@
usage(progname);
return EXIT_FAILURE;
}
- if ((unsigned)argc > AudioMixer::MAX_NUM_TRACKS) {
- fprintf(stderr, "too many tracks: %d > %u", argc, AudioMixer::MAX_NUM_TRACKS);
- return EXIT_FAILURE;
- }
size_t outputFrames = 0;
@@ -246,9 +242,10 @@
for (size_t i = 0; i < providers.size(); ++i) {
//printf("track %d out of %d\n", i, providers.size());
uint32_t channelMask = audio_channel_out_mask_from_count(providers[i].getNumChannels());
- int32_t name = mixer->getTrackName(channelMask,
- formats[i], AUDIO_SESSION_OUTPUT_MIX);
- ALOG_ASSERT(name >= 0);
+ const int name = i;
+ const status_t status = mixer->create(
+ name, channelMask, formats[i], AUDIO_SESSION_OUTPUT_MIX);
+ LOG_ALWAYS_FATAL_IF(status != OK);
names[i] = name;
mixer->setBufferProvider(name, &providers[i]);
mixer->setParameter(name, AudioMixer::TRACK, AudioMixer::MAIN_BUFFER,
@@ -315,8 +312,10 @@
writeFile(outputFilename, outputAddr,
outputSampleRate, outputChannels, outputFrames, useMixerFloat);
if (auxFilename) {
- // Aux buffer is always in q4_27 format for now.
- memcpy_to_i16_from_q4_27((int16_t*)auxAddr, (const int32_t*)auxAddr, outputFrames);
+ // Aux buffer is always in q4_27 format for O and earlier.
+ // memcpy_to_i16_from_q4_27((int16_t*)auxAddr, (const int32_t*)auxAddr, outputFrames);
+ // Aux buffer is always in float format for P.
+ memcpy_to_i16_from_float((int16_t*)auxAddr, (const float*)auxAddr, outputFrames);
writeFile(auxFilename, auxAddr, outputSampleRate, 1, outputFrames, false);
}
diff --git a/media/libmedia/IMediaExtractor.cpp b/media/libmedia/IMediaExtractor.cpp
index 51ccb5a..e9a6230 100644
--- a/media/libmedia/IMediaExtractor.cpp
+++ b/media/libmedia/IMediaExtractor.cpp
@@ -35,9 +35,7 @@
GETTRACKMETADATA,
GETMETADATA,
FLAGS,
- GETDRMTRACKINFO,
SETMEDIACAS,
- SETUID,
NAME,
GETMETRICS
};
@@ -112,11 +110,6 @@
return 0;
}
- virtual char* getDrmTrackInfo(size_t trackID __unused, int *len __unused) {
- ALOGV("getDrmTrackInfo NOT IMPLEMENTED");
- return NULL;
- }
-
virtual status_t setMediaCas(const HInterfaceToken &casToken) {
ALOGV("setMediaCas");
@@ -131,10 +124,6 @@
return reply.readInt32();
}
- virtual void setUID(uid_t uid __unused) {
- ALOGV("setUID NOT IMPLEMENTED");
- }
-
virtual const char * name() {
ALOGV("name NOT IMPLEMENTED");
return NULL;
diff --git a/media/libmedia/IMediaSource.cpp b/media/libmedia/IMediaSource.cpp
index 0d5127c..f185fd4 100644
--- a/media/libmedia/IMediaSource.cpp
+++ b/media/libmedia/IMediaSource.cpp
@@ -113,9 +113,9 @@
return NULL;
}
- virtual status_t read(MediaBuffer **buffer,
+ virtual status_t read(MediaBufferBase **buffer,
const MediaSource::ReadOptions *options) {
- Vector<MediaBuffer *> buffers;
+ Vector<MediaBufferBase *> buffers;
status_t ret = readMultiple(&buffers, 1 /* maxNumBuffers */, options);
*buffer = buffers.size() == 0 ? nullptr : buffers[0];
ALOGV("read status %d, bufferCount %u, sinceStop %u",
@@ -124,7 +124,7 @@
}
virtual status_t readMultiple(
- Vector<MediaBuffer *> *buffers, uint32_t maxNumBuffers,
+ Vector<MediaBufferBase *> *buffers, uint32_t maxNumBuffers,
const MediaSource::ReadOptions *options) {
ALOGV("readMultiple");
if (buffers == NULL || !buffers->isEmpty()) {
@@ -171,13 +171,13 @@
size_t length = reply.readInt32();
buf = new RemoteMediaBufferWrapper(mem);
buf->set_range(offset, length);
- buf->meta_data()->updateFromParcel(reply);
+ 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);
+ buf->meta_data().updateFromParcel(reply);
}
buffers->push_back(buf);
++bufferCount;
@@ -341,7 +341,7 @@
uint32_t bufferCount = 0;
for (; bufferCount < maxNumBuffers; ++bufferCount, ++mBuffersSinceStop) {
MediaBuffer *buf = nullptr;
- ret = read(&buf, useOptions ? &opts : nullptr);
+ ret = read((MediaBufferBase **)&buf, useOptions ? &opts : nullptr);
opts.clearNonPersistent(); // Remove options that only apply to first buffer.
if (ret != NO_ERROR || buf == nullptr) {
break;
@@ -364,7 +364,7 @@
} else {
ALOGD("Large buffer %zu without IMemory!", length);
ret = mGroup->acquire_buffer(
- &transferBuf, false /* nonBlocking */, length);
+ (MediaBufferBase **)&transferBuf, false /* nonBlocking */, length);
if (ret != OK
|| transferBuf == nullptr
|| transferBuf->mMemory == nullptr) {
@@ -408,7 +408,7 @@
}
reply->writeInt32(offset);
reply->writeInt32(length);
- buf->meta_data()->writeToParcel(*reply);
+ buf->meta_data().writeToParcel(*reply);
transferBuf->addRemoteRefcount(1);
if (transferBuf != buf) {
transferBuf->release(); // release local ref
@@ -421,7 +421,7 @@
buf, buf->mMemory->size(), length);
reply->writeInt32(INLINE_BUFFER);
reply->writeByteArray(length, (uint8_t*)buf->data() + offset);
- buf->meta_data()->writeToParcel(*reply);
+ buf->meta_data().writeToParcel(*reply);
inlineTransferSize += length;
if (inlineTransferSize > kInlineMaxTransfer) {
maxNumBuffers = 0; // stop readMultiple if inline transfer is too large.
diff --git a/media/libmedia/JetPlayer.cpp b/media/libmedia/JetPlayer.cpp
index 34deb59..0d3c1ba 100644
--- a/media/libmedia/JetPlayer.cpp
+++ b/media/libmedia/JetPlayer.cpp
@@ -36,6 +36,7 @@
mPaused(false),
mMaxTracks(maxTracks),
mEasData(NULL),
+ mIoWrapper(NULL),
mTrackBufferSize(trackBufferSize)
{
ALOGV("JetPlayer constructor");
@@ -50,7 +51,6 @@
{
ALOGV("~JetPlayer");
release();
-
}
//-------------------------------------------------------------------------------------------------
@@ -138,7 +138,8 @@
JET_Shutdown(mEasData);
EAS_Shutdown(mEasData);
}
- mIoWrapper.clear();
+ delete mIoWrapper;
+ mIoWrapper = NULL;
if (mAudioTrack != 0) {
mAudioTrack->stop();
mAudioTrack->flush();
@@ -329,6 +330,7 @@
Mutex::Autolock lock(mMutex);
+ delete mIoWrapper;
mIoWrapper = new MidiIoWrapper(path);
EAS_RESULT result = JET_OpenFile(mEasData, mIoWrapper->getLocator());
@@ -347,6 +349,7 @@
Mutex::Autolock lock(mMutex);
+ delete mIoWrapper;
mIoWrapper = new MidiIoWrapper(fd, offset, length);
EAS_RESULT result = JET_OpenFile(mEasData, mIoWrapper->getLocator());
diff --git a/media/libmedia/MediaCodecInfo.cpp b/media/libmedia/MediaCodecInfo.cpp
index a570ffe..5308e1c 100644
--- a/media/libmedia/MediaCodecInfo.cpp
+++ b/media/libmedia/MediaCodecInfo.cpp
@@ -141,6 +141,10 @@
return mIsEncoder;
}
+uint32_t MediaCodecInfo::rank() const {
+ return mRank;
+}
+
void MediaCodecInfo::getSupportedMimes(Vector<AString> *mimes) const {
mimes->clear();
for (size_t ix = 0; ix < mCaps.size(); ix++) {
@@ -170,10 +174,12 @@
AString name = AString::FromParcel(parcel);
AString owner = AString::FromParcel(parcel);
bool isEncoder = static_cast<bool>(parcel.readInt32());
+ uint32_t rank = parcel.readUint32();
sp<MediaCodecInfo> info = new MediaCodecInfo;
info->mName = name;
info->mOwner = owner;
info->mIsEncoder = isEncoder;
+ info->mRank = rank;
size_t size = static_cast<size_t>(parcel.readInt32());
for (size_t i = 0; i < size; i++) {
AString mime = AString::FromParcel(parcel);
@@ -191,6 +197,7 @@
mName.writeToParcel(parcel);
mOwner.writeToParcel(parcel);
parcel->writeInt32(mIsEncoder);
+ parcel->writeUint32(mRank);
parcel->writeInt32(mCaps.size());
for (size_t i = 0; i < mCaps.size(); i++) {
mCaps.keyAt(i).writeToParcel(parcel);
@@ -210,7 +217,7 @@
return -1;
}
-MediaCodecInfo::MediaCodecInfo() {
+MediaCodecInfo::MediaCodecInfo() : mRank(0x100) {
}
void MediaCodecInfoWriter::setName(const char* name) {
@@ -225,6 +232,10 @@
mInfo->mIsEncoder = isEncoder;
}
+void MediaCodecInfoWriter::setRank(uint32_t rank) {
+ mInfo->mRank = rank;
+}
+
std::unique_ptr<MediaCodecInfo::CapabilitiesWriter>
MediaCodecInfoWriter::addMime(const char *mime) {
ssize_t ix = mInfo->getCapabilityIndex(mime);
diff --git a/media/libmedia/MidiIoWrapper.cpp b/media/libmedia/MidiIoWrapper.cpp
index 0896e75..5ca3b48 100644
--- a/media/libmedia/MidiIoWrapper.cpp
+++ b/media/libmedia/MidiIoWrapper.cpp
@@ -38,6 +38,7 @@
mFd = open(path, O_RDONLY | O_LARGEFILE);
mBase = 0;
mLength = lseek(mFd, 0, SEEK_END);
+ mDataSource = nullptr;
}
MidiIoWrapper::MidiIoWrapper(int fd, off64_t offset, int64_t size) {
@@ -45,6 +46,7 @@
mFd = fd < 0 ? -1 : dup(fd);
mBase = offset;
mLength = size;
+ mDataSource = nullptr;
}
MidiIoWrapper::MidiIoWrapper(DataSourceBase *source) {
diff --git a/media/libmedia/NdkWrapper.cpp b/media/libmedia/NdkWrapper.cpp
index 936e92f..5e47b48 100644
--- a/media/libmedia/NdkWrapper.cpp
+++ b/media/libmedia/NdkWrapper.cpp
@@ -574,17 +574,13 @@
//////////// AMediaCodecCryptoInfoWrapper
// static
-sp<AMediaCodecCryptoInfoWrapper> AMediaCodecCryptoInfoWrapper::Create(sp<MetaData> meta) {
- if (meta == NULL) {
- ALOGE("Create: Unexpected. No meta data for sample.");
- return NULL;
- }
+sp<AMediaCodecCryptoInfoWrapper> AMediaCodecCryptoInfoWrapper::Create(MetaDataBase &meta) {
uint32_t type;
const void *crypteddata;
size_t cryptedsize;
- if (!meta->findData(kKeyEncryptedSizes, &type, &crypteddata, &cryptedsize)) {
+ if (!meta.findData(kKeyEncryptedSizes, &type, &crypteddata, &cryptedsize)) {
return NULL;
}
@@ -597,7 +593,7 @@
const void *cleardata;
size_t clearsize;
- if (meta->findData(kKeyPlainSizes, &type, &cleardata, &clearsize)) {
+ if (meta.findData(kKeyPlainSizes, &type, &cleardata, &clearsize)) {
if (clearsize != cryptedsize) {
// The two must be of the same length.
ALOGE("Create: mismatch cryptedsize: %zu != clearsize: %zu", cryptedsize, clearsize);
@@ -607,7 +603,7 @@
const void *key;
size_t keysize;
- if (meta->findData(kKeyCryptoKey, &type, &key, &keysize)) {
+ if (meta.findData(kKeyCryptoKey, &type, &key, &keysize)) {
if (keysize != kAESBlockSize) {
// Keys must be 16 bytes in length.
ALOGE("Create: Keys must be %zu bytes in length: %zu", kAESBlockSize, keysize);
@@ -617,7 +613,7 @@
const void *iv;
size_t ivsize;
- if (meta->findData(kKeyCryptoIV, &type, &iv, &ivsize)) {
+ if (meta.findData(kKeyCryptoIV, &type, &iv, &ivsize)) {
if (ivsize != kAESBlockSize) {
// IVs must be 16 bytes in length.
ALOGE("Create: IV must be %zu bytes in length: %zu", kAESBlockSize, ivsize);
@@ -626,7 +622,7 @@
}
int32_t mode;
- if (!meta->findInt32(kKeyCryptoMode, &mode)) {
+ if (!meta.findInt32(kKeyCryptoMode, &mode)) {
mode = CryptoPlugin::kMode_AES_CTR;
}
diff --git a/media/libmedia/include/media/DrmHal.h b/media/libmedia/include/media/DrmHal.h
index 1c09036..c64b003 100644
--- a/media/libmedia/include/media/DrmHal.h
+++ b/media/libmedia/include/media/DrmHal.h
@@ -119,7 +119,7 @@
virtual status_t setPropertyString(String8 const &name, String8 const &value ) const;
virtual status_t setPropertyByteArray(String8 const &name,
Vector<uint8_t> const &value ) const;
- virtual status_t getMetrics(MediaAnalyticsItem *item);
+ virtual status_t getMetrics(os::PersistableBundle *metrics);
virtual status_t setCipherAlgorithm(Vector<uint8_t> const &sessionId,
String8 const &algorithm);
@@ -188,6 +188,7 @@
Vector<Vector<uint8_t>> mOpenSessions;
void closeOpenSessions();
+ void cleanup();
/**
* mInitCheck is:
@@ -203,7 +204,8 @@
void writeByteArray(Parcel &obj, const hidl_vec<uint8_t>& array);
- void reportMetrics() const;
+ void reportPluginMetrics() const;
+ void reportFrameworkMetrics() const;
status_t getPropertyStringInternal(String8 const &name, String8 &value) const;
status_t getPropertyByteArrayInternal(String8 const &name,
Vector<uint8_t> &value) const;
diff --git a/media/libmedia/include/media/DrmMetrics.h b/media/libmedia/include/media/DrmMetrics.h
index bb7509b..5c2fdf2 100644
--- a/media/libmedia/include/media/DrmMetrics.h
+++ b/media/libmedia/include/media/DrmMetrics.h
@@ -20,6 +20,7 @@
#include <map>
#include <android/hardware/drm/1.0/types.h>
+#include <binder/PersistableBundle.h>
#include <media/CounterMetric.h>
#include <media/EventMetric.h>
@@ -28,19 +29,20 @@
/**
* This class contains the definition of metrics captured within MediaDrm.
* It also contains a method for exporting all of the metrics to a
- * MediaAnalyticsItem instance.
+ * PersistableBundle.
*/
class MediaDrmMetrics {
public:
explicit MediaDrmMetrics();
+ virtual ~MediaDrmMetrics() {};
// Count of openSession calls.
CounterMetric<status_t> mOpenSessionCounter;
// Count of closeSession calls.
CounterMetric<status_t> mCloseSessionCounter;
// Count and timing of getKeyRequest calls.
- EventMetric<status_t> mGetKeyRequestTiming;
+ EventMetric<status_t> mGetKeyRequestTimeUs;
// Count and timing of provideKeyResponse calls.
- EventMetric<status_t> mProvideKeyResponseTiming;
+ EventMetric<status_t> mProvideKeyResponseTimeUs;
// Count of getProvisionRequest calls.
CounterMetric<status_t> mGetProvisionRequestCounter;
// Count of provideProvisionResponse calls.
@@ -55,10 +57,37 @@
// Count getPropertyByteArray calls to retrieve the device unique id.
CounterMetric<status_t> mGetDeviceUniqueIdCounter;
- // TODO: Add session start and end time support. These are a special case.
+ // Adds a session start time record.
+ void SetSessionStart(const Vector<uint8_t>& sessionId);
- // Export the metrics to a MediaAnalyticsItem.
- void Export(MediaAnalyticsItem* item);
+ // Adds a session end time record.
+ void SetSessionEnd(const Vector<uint8_t>& sessionId);
+
+ // The app package name is the application package name that is using the
+ // instance. The app package name is held here for convenience. It is not
+ // serialized or exported with the metrics.
+ void SetAppPackageName(const String8& appPackageName) { mAppPackageName = appPackageName; }
+ const String8& GetAppPackageName() { return mAppPackageName; }
+
+ // Export the metrics to a PersistableBundle.
+ void Export(os::PersistableBundle* metricsBundle);
+
+ // Get the serialized metrics. Metrics are formatted as a serialized
+ // DrmFrameworkMetrics proto. If there is a failure serializing the metrics,
+ // this returns an error. The parameter |serlializedMetrics| is owned by the
+ // caller and must not be null.
+ status_t GetSerializedMetrics(std::string* serializedMetrics);
+
+ protected:
+ // This is visible for testing only.
+ virtual int64_t GetCurrentTimeMs();
+
+ private:
+ // Session lifetimes. A pair of values representing the milliseconds since
+ // epoch, UTC. The first value is the start time, the second is the end time.
+ std::map<std::string, std::pair<int64_t, int64_t>> mSessionLifespans;
+
+ String8 mAppPackageName;
};
} // namespace android
diff --git a/media/libmedia/include/media/IDrm.h b/media/libmedia/include/media/IDrm.h
index c3ae684..a19b06b 100644
--- a/media/libmedia/include/media/IDrm.h
+++ b/media/libmedia/include/media/IDrm.h
@@ -15,6 +15,7 @@
*/
#include <binder/IInterface.h>
+#include <binder/PersistableBundle.h>
#include <media/stagefright/foundation/ABase.h>
#include <media/drm/DrmAPI.h>
#include <media/IDrmClient.h>
@@ -98,7 +99,7 @@
virtual status_t setPropertyByteArray(String8 const &name,
Vector<uint8_t> const &value) const = 0;
- virtual status_t getMetrics(MediaAnalyticsItem *item) = 0;
+ virtual status_t getMetrics(os::PersistableBundle *metrics) = 0;
virtual status_t setCipherAlgorithm(Vector<uint8_t> const &sessionId,
String8 const &algorithm) = 0;
diff --git a/media/libmedia/include/media/IMediaExtractor.h b/media/libmedia/include/media/IMediaExtractor.h
index 9899429..75e4ee2 100644
--- a/media/libmedia/include/media/IMediaExtractor.h
+++ b/media/libmedia/include/media/IMediaExtractor.h
@@ -60,13 +60,8 @@
// CAN_SEEK_BACKWARD | CAN_SEEK_FORWARD | CAN_SEEK | CAN_PAUSE
virtual uint32_t flags() const = 0;
- // for DRM
- virtual char* getDrmTrackInfo(size_t trackID, int *len) = 0;
-
virtual status_t setMediaCas(const HInterfaceToken &casToken) = 0;
- virtual void setUID(uid_t uid) = 0;
-
virtual const char * name() = 0;
};
diff --git a/media/libmedia/include/media/IMediaSource.h b/media/libmedia/include/media/IMediaSource.h
index dabe231..7a4b1b9 100644
--- a/media/libmedia/include/media/IMediaSource.h
+++ b/media/libmedia/include/media/IMediaSource.h
@@ -64,7 +64,7 @@
//
// TODO: consider removing read() in favor of readMultiple().
virtual status_t read(
- MediaBuffer **buffer,
+ MediaBufferBase **buffer,
const MediaSource::ReadOptions *options = NULL) = 0;
// Returns a vector of new buffers of data, where the new buffers are added
@@ -80,7 +80,7 @@
// 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,
+ Vector<MediaBufferBase *> *buffers, uint32_t maxNumBuffers = 1,
const MediaSource::ReadOptions *options = nullptr) = 0;
// Returns true if |readMultiple| is supported, otherwise false.
@@ -110,7 +110,7 @@
// TODO: Implement this for local media sources.
virtual status_t readMultiple(
- Vector<MediaBuffer *> * /* buffers */, uint32_t /* maxNumBuffers = 1 */,
+ Vector<MediaBufferBase *> * /* buffers */, uint32_t /* maxNumBuffers = 1 */,
const MediaSource::ReadOptions * /* options = nullptr */) {
return ERROR_UNSUPPORTED;
}
diff --git a/media/libmedia/include/media/JetPlayer.h b/media/libmedia/include/media/JetPlayer.h
index 63d1980..bb569bc 100644
--- a/media/libmedia/include/media/JetPlayer.h
+++ b/media/libmedia/include/media/JetPlayer.h
@@ -87,7 +87,7 @@
int mMaxTracks; // max number of MIDI tracks, usually 32
EAS_DATA_HANDLE mEasData;
- sp<MidiIoWrapper> mIoWrapper;
+ MidiIoWrapper* mIoWrapper;
EAS_PCM* mAudioBuffer;// EAS renders the MIDI data into this buffer,
sp<AudioTrack> mAudioTrack; // and we play it in this audio track
int mTrackBufferSize;
diff --git a/media/libmedia/include/media/MediaBufferHolder.h b/media/libmedia/include/media/MediaBufferHolder.h
index e8e2c4b..f9dfdf5 100644
--- a/media/libmedia/include/media/MediaBufferHolder.h
+++ b/media/libmedia/include/media/MediaBufferHolder.h
@@ -24,7 +24,7 @@
namespace android {
struct MediaBufferHolder : public RefBase {
- MediaBufferHolder(MediaBuffer* buffer)
+ MediaBufferHolder(MediaBufferBase* buffer)
: mMediaBuffer(buffer) {
if (mMediaBuffer != nullptr) {
mMediaBuffer->add_ref();
@@ -37,10 +37,10 @@
}
}
- MediaBuffer* mediaBuffer() { return mMediaBuffer; }
+ MediaBufferBase* mediaBuffer() { return mMediaBuffer; }
private:
- MediaBuffer* const mMediaBuffer;
+ MediaBufferBase* const mMediaBuffer;
};
} // android
diff --git a/media/libmedia/include/media/MediaCodecInfo.h b/media/libmedia/include/media/MediaCodecInfo.h
index ab2cd24..b3777d3 100644
--- a/media/libmedia/include/media/MediaCodecInfo.h
+++ b/media/libmedia/include/media/MediaCodecInfo.h
@@ -170,6 +170,7 @@
* Currently, this is the "instance name" of the IOmx service.
*/
const char *getOwnerName() const;
+ uint32_t rank() const;
/**
* Serialization over Binder
@@ -182,6 +183,7 @@
AString mOwner;
bool mIsEncoder;
KeyedVector<AString, sp<Capabilities> > mCaps;
+ uint32_t mRank;
ssize_t getCapabilityIndex(const char *mime) const;
@@ -252,6 +254,13 @@
* @return `true` if `mime` is removed; `false` if `mime` is not found.
*/
bool removeMime(const char* mime);
+ /**
+ * Set rank of the codec. MediaCodecList will stable-sort the list according
+ * to rank in non-descending order.
+ *
+ * @param rank The rank of the component.
+ */
+ void setRank(uint32_t rank);
private:
/**
* The associated `MediaCodecInfo`.
diff --git a/media/libmedia/include/media/MidiIoWrapper.h b/media/libmedia/include/media/MidiIoWrapper.h
index a27b410..b5e565e 100644
--- a/media/libmedia/include/media/MidiIoWrapper.h
+++ b/media/libmedia/include/media/MidiIoWrapper.h
@@ -23,11 +23,11 @@
namespace android {
-class MidiIoWrapper : public RefBase {
+class MidiIoWrapper {
public:
- MidiIoWrapper(const char *path);
- MidiIoWrapper(int fd, off64_t offset, int64_t size);
- MidiIoWrapper(DataSourceBase *source);
+ explicit MidiIoWrapper(const char *path);
+ explicit MidiIoWrapper(int fd, off64_t offset, int64_t size);
+ explicit MidiIoWrapper(DataSourceBase *source);
~MidiIoWrapper();
diff --git a/media/libmedia/include/media/NdkWrapper.h b/media/libmedia/include/media/NdkWrapper.h
index 49d728d..b71b758 100644
--- a/media/libmedia/include/media/NdkWrapper.h
+++ b/media/libmedia/include/media/NdkWrapper.h
@@ -149,7 +149,7 @@
};
struct AMediaCodecCryptoInfoWrapper : public RefBase {
- static sp<AMediaCodecCryptoInfoWrapper> Create(sp<MetaData> meta);
+ static sp<AMediaCodecCryptoInfoWrapper> Create(MetaDataBase &meta);
AMediaCodecCryptoInfoWrapper(int numsubsamples,
uint8_t key[16],
diff --git a/media/libmediaextractor/Android.bp b/media/libmediaextractor/Android.bp
index 8f4ba70..b9b47cd 100644
--- a/media/libmediaextractor/Android.bp
+++ b/media/libmediaextractor/Android.bp
@@ -25,11 +25,14 @@
srcs: [
"DataSourceBase.cpp",
"MediaBuffer.cpp",
+ "MediaBufferBase.cpp",
"MediaBufferGroup.cpp",
- "MediaSourceBase.cpp",
"MediaSource.cpp",
+ "MediaTrack.cpp",
"MediaExtractor.cpp",
"MetaData.cpp",
+ "MetaDataBase.cpp",
+ "VorbisComment.cpp",
],
clang: true,
diff --git a/media/libmediaextractor/MediaBuffer.cpp b/media/libmediaextractor/MediaBuffer.cpp
index 28fc760..39f8d6e 100644
--- a/media/libmediaextractor/MediaBuffer.cpp
+++ b/media/libmediaextractor/MediaBuffer.cpp
@@ -145,8 +145,8 @@
mRangeLength = length;
}
-sp<MetaData> MediaBuffer::meta_data() {
- return mMetaData;
+MetaDataBase& MediaBuffer::meta_data() {
+ return *mMetaData;
}
void MediaBuffer::reset() {
@@ -170,6 +170,7 @@
if (mMemory.get() != nullptr) {
getSharedControl()->setDeadObject();
}
+ delete mMetaData;
}
void MediaBuffer::setObserver(MediaBufferObserver *observer) {
@@ -177,10 +178,10 @@
mObserver = observer;
}
-MediaBuffer *MediaBuffer::clone() {
+MediaBufferBase *MediaBuffer::clone() {
MediaBuffer *buffer = new MediaBuffer(mData, mSize);
buffer->set_range(mRangeOffset, mRangeLength);
- buffer->mMetaData = new MetaData(*mMetaData.get());
+ buffer->mMetaData = new MetaDataBase(*mMetaData);
add_ref();
buffer->mOriginal = this;
diff --git a/media/libmediaextractor/MediaBufferBase.cpp b/media/libmediaextractor/MediaBufferBase.cpp
new file mode 100644
index 0000000..a553289
--- /dev/null
+++ b/media/libmediaextractor/MediaBufferBase.cpp
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2018 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 "MediaBufferBase"
+#include <utils/Log.h>
+
+#include <media/stagefright/MediaBuffer.h>
+#include <media/stagefright/MediaBufferBase.h>
+
+namespace android {
+
+//static
+MediaBufferBase *MediaBufferBase::Create(size_t size) {
+ return new (std::nothrow) MediaBuffer(size);
+}
+
+} // android
diff --git a/media/libmediaextractor/MediaBufferGroup.cpp b/media/libmediaextractor/MediaBufferGroup.cpp
index 22f01a5..2a8dd41 100644
--- a/media/libmediaextractor/MediaBufferGroup.cpp
+++ b/media/libmediaextractor/MediaBufferGroup.cpp
@@ -40,7 +40,7 @@
Mutex mLock;
Condition mCondition;
size_t mGrowthLimit; // Do not automatically grow group larger than this.
- std::list<MediaBuffer *> mBuffers;
+ std::list<MediaBufferBase *> mBuffers;
};
MediaBufferGroup::MediaBufferGroup(size_t growthLimit)
@@ -94,7 +94,7 @@
}
MediaBufferGroup::~MediaBufferGroup() {
- for (MediaBuffer *buffer : mInternal->mBuffers) {
+ for (MediaBufferBase *buffer : mInternal->mBuffers) {
if (buffer->refcount() != 0) {
const int localRefcount = buffer->localRefcount();
const int remoteRefcount = buffer->remoteRefcount();
@@ -119,7 +119,7 @@
delete mInternal;
}
-void MediaBufferGroup::add_buffer(MediaBuffer *buffer) {
+void MediaBufferGroup::add_buffer(MediaBufferBase *buffer) {
Mutex::Autolock autoLock(mInternal->mLock);
// if we're above our growth limit, release buffers if we can
@@ -144,7 +144,7 @@
if (mInternal->mBuffers.size() < mInternal->mGrowthLimit) {
return true; // We can add more buffers internally.
}
- for (MediaBuffer *buffer : mInternal->mBuffers) {
+ for (MediaBufferBase *buffer : mInternal->mBuffers) {
if (buffer->refcount() == 0) {
return true;
}
@@ -153,11 +153,11 @@
}
status_t MediaBufferGroup::acquire_buffer(
- MediaBuffer **out, bool nonBlocking, size_t requestedSize) {
+ MediaBufferBase **out, bool nonBlocking, size_t requestedSize) {
Mutex::Autolock autoLock(mInternal->mLock);
for (;;) {
size_t smallest = requestedSize;
- MediaBuffer *buffer = nullptr;
+ MediaBufferBase *buffer = nullptr;
auto free = mInternal->mBuffers.end();
for (auto it = mInternal->mBuffers.begin(); it != mInternal->mBuffers.end(); ++it) {
if ((*it)->refcount() == 0) {
@@ -217,7 +217,7 @@
return mInternal->mBuffers.size();
}
-void MediaBufferGroup::signalBufferReturned(MediaBuffer *) {
+void MediaBufferGroup::signalBufferReturned(MediaBufferBase *) {
Mutex::Autolock autoLock(mInternal->mLock);
mInternal->mCondition.signal();
}
diff --git a/media/libmediaextractor/MediaExtractor.cpp b/media/libmediaextractor/MediaExtractor.cpp
index 2241567..a6b3dc9 100644
--- a/media/libmediaextractor/MediaExtractor.cpp
+++ b/media/libmediaextractor/MediaExtractor.cpp
@@ -35,10 +35,6 @@
MediaExtractor::~MediaExtractor() {}
-sp<MetaData> MediaExtractor::getMetaData() {
- return new MetaData;
-}
-
uint32_t MediaExtractor::flags() const {
return CAN_SEEK_BACKWARD | CAN_SEEK_FORWARD | CAN_PAUSE | CAN_SEEK;
}
diff --git a/media/libmediaextractor/MediaSourceBase.cpp b/media/libmediaextractor/MediaTrack.cpp
similarity index 69%
rename from media/libmediaextractor/MediaSourceBase.cpp
rename to media/libmediaextractor/MediaTrack.cpp
index 6d45c90..4963f58 100644
--- a/media/libmediaextractor/MediaSourceBase.cpp
+++ b/media/libmediaextractor/MediaTrack.cpp
@@ -14,51 +14,51 @@
* limitations under the License.
*/
-#include <media/MediaSourceBase.h>
+#include <media/MediaTrack.h>
namespace android {
-MediaSourceBase::MediaSourceBase() {}
+MediaTrack::MediaTrack() {}
-MediaSourceBase::~MediaSourceBase() {}
+MediaTrack::~MediaTrack() {}
////////////////////////////////////////////////////////////////////////////////
-MediaSourceBase::ReadOptions::ReadOptions() {
+MediaTrack::ReadOptions::ReadOptions() {
reset();
}
-void MediaSourceBase::ReadOptions::reset() {
+void MediaTrack::ReadOptions::reset() {
mOptions = 0;
mSeekTimeUs = 0;
mNonBlocking = false;
}
-void MediaSourceBase::ReadOptions::setNonBlocking() {
+void MediaTrack::ReadOptions::setNonBlocking() {
mNonBlocking = true;
}
-void MediaSourceBase::ReadOptions::clearNonBlocking() {
+void MediaTrack::ReadOptions::clearNonBlocking() {
mNonBlocking = false;
}
-bool MediaSourceBase::ReadOptions::getNonBlocking() const {
+bool MediaTrack::ReadOptions::getNonBlocking() const {
return mNonBlocking;
}
-void MediaSourceBase::ReadOptions::setSeekTo(int64_t time_us, SeekMode mode) {
+void MediaTrack::ReadOptions::setSeekTo(int64_t time_us, SeekMode mode) {
mOptions |= kSeekTo_Option;
mSeekTimeUs = time_us;
mSeekMode = mode;
}
-void MediaSourceBase::ReadOptions::clearSeekTo() {
+void MediaTrack::ReadOptions::clearSeekTo() {
mOptions &= ~kSeekTo_Option;
mSeekTimeUs = 0;
mSeekMode = SEEK_CLOSEST_SYNC;
}
-bool MediaSourceBase::ReadOptions::getSeekTo(
+bool MediaTrack::ReadOptions::getSeekTo(
int64_t *time_us, SeekMode *mode) const {
*time_us = mSeekTimeUs;
*mode = mSeekMode;
diff --git a/media/libmediaextractor/MetaData.cpp b/media/libmediaextractor/MetaData.cpp
index 98cddbe..1d0a607 100644
--- a/media/libmediaextractor/MetaData.cpp
+++ b/media/libmediaextractor/MetaData.cpp
@@ -31,500 +31,20 @@
namespace android {
-struct MetaData::typed_data {
- typed_data();
- ~typed_data();
- typed_data(const MetaData::typed_data &);
- typed_data &operator=(const MetaData::typed_data &);
-
- void clear();
- void setData(uint32_t type, const void *data, size_t size);
- void getData(uint32_t *type, const void **data, size_t *size) const;
- // may include hexdump of binary data if verbose=true
- String8 asString(bool verbose) const;
-
-private:
- uint32_t mType;
- size_t mSize;
-
- union {
- void *ext_data;
- float reservoir;
- } u;
-
- bool usesReservoir() const {
- return mSize <= sizeof(u.reservoir);
- }
-
- void *allocateStorage(size_t size);
- void freeStorage();
-
- void *storage() {
- return usesReservoir() ? &u.reservoir : u.ext_data;
- }
-
- const void *storage() const {
- return usesReservoir() ? &u.reservoir : u.ext_data;
- }
-};
-
-struct MetaData::Rect {
- int32_t mLeft, mTop, mRight, mBottom;
-};
-
-
-struct MetaData::MetaDataInternal {
- KeyedVector<uint32_t, MetaData::typed_data> mItems;
-};
-
-
-MetaData::MetaData()
- : mInternalData(new MetaDataInternal()) {
+MetaData::MetaData() {
}
MetaData::MetaData(const MetaData &from)
- : RefBase(),
- mInternalData(new MetaDataInternal()) {
- mInternalData->mItems = from.mInternalData->mItems;
+ : MetaDataBase(from) {
+}
+MetaData::MetaData(const MetaDataBase &from)
+ : MetaDataBase(from) {
}
MetaData::~MetaData() {
- clear();
- delete mInternalData;
}
-void MetaData::clear() {
- mInternalData->mItems.clear();
-}
-
-bool MetaData::remove(uint32_t key) {
- ssize_t i = mInternalData->mItems.indexOfKey(key);
-
- if (i < 0) {
- return false;
- }
-
- mInternalData->mItems.removeItemsAt(i);
-
- return true;
-}
-
-bool MetaData::setCString(uint32_t key, const char *value) {
- return setData(key, TYPE_C_STRING, value, strlen(value) + 1);
-}
-
-bool MetaData::setInt32(uint32_t key, int32_t value) {
- return setData(key, TYPE_INT32, &value, sizeof(value));
-}
-
-bool MetaData::setInt64(uint32_t key, int64_t value) {
- return setData(key, TYPE_INT64, &value, sizeof(value));
-}
-
-bool MetaData::setFloat(uint32_t key, float value) {
- return setData(key, TYPE_FLOAT, &value, sizeof(value));
-}
-
-bool MetaData::setPointer(uint32_t key, void *value) {
- return setData(key, TYPE_POINTER, &value, sizeof(value));
-}
-
-bool MetaData::setRect(
- uint32_t key,
- int32_t left, int32_t top,
- int32_t right, int32_t bottom) {
- Rect r;
- r.mLeft = left;
- r.mTop = top;
- r.mRight = right;
- r.mBottom = bottom;
-
- return setData(key, TYPE_RECT, &r, sizeof(r));
-}
-
-/**
- * Note that the returned pointer becomes invalid when additional metadata is set.
- */
-bool MetaData::findCString(uint32_t key, const char **value) {
- uint32_t type;
- const void *data;
- size_t size;
- if (!findData(key, &type, &data, &size) || type != TYPE_C_STRING) {
- return false;
- }
-
- *value = (const char *)data;
-
- return true;
-}
-
-bool MetaData::findInt32(uint32_t key, int32_t *value) {
- uint32_t type = 0;
- const void *data;
- size_t size;
- if (!findData(key, &type, &data, &size) || type != TYPE_INT32) {
- return false;
- }
-
- CHECK_EQ(size, sizeof(*value));
-
- *value = *(int32_t *)data;
-
- return true;
-}
-
-bool MetaData::findInt64(uint32_t key, int64_t *value) {
- uint32_t type = 0;
- const void *data;
- size_t size;
- if (!findData(key, &type, &data, &size) || type != TYPE_INT64) {
- return false;
- }
-
- CHECK_EQ(size, sizeof(*value));
-
- *value = *(int64_t *)data;
-
- return true;
-}
-
-bool MetaData::findFloat(uint32_t key, float *value) {
- uint32_t type = 0;
- const void *data;
- size_t size;
- if (!findData(key, &type, &data, &size) || type != TYPE_FLOAT) {
- return false;
- }
-
- CHECK_EQ(size, sizeof(*value));
-
- *value = *(float *)data;
-
- return true;
-}
-
-bool MetaData::findPointer(uint32_t key, void **value) {
- uint32_t type = 0;
- const void *data;
- size_t size;
- if (!findData(key, &type, &data, &size) || type != TYPE_POINTER) {
- return false;
- }
-
- CHECK_EQ(size, sizeof(*value));
-
- *value = *(void **)data;
-
- return true;
-}
-
-bool MetaData::findRect(
- uint32_t key,
- int32_t *left, int32_t *top,
- int32_t *right, int32_t *bottom) {
- uint32_t type = 0;
- const void *data;
- size_t size;
- if (!findData(key, &type, &data, &size) || type != TYPE_RECT) {
- return false;
- }
-
- CHECK_EQ(size, sizeof(Rect));
-
- const Rect *r = (const Rect *)data;
- *left = r->mLeft;
- *top = r->mTop;
- *right = r->mRight;
- *bottom = r->mBottom;
-
- return true;
-}
-
-bool MetaData::setData(
- uint32_t key, uint32_t type, const void *data, size_t size) {
- bool overwrote_existing = true;
-
- ssize_t i = mInternalData->mItems.indexOfKey(key);
- if (i < 0) {
- typed_data item;
- i = mInternalData->mItems.add(key, item);
-
- overwrote_existing = false;
- }
-
- typed_data &item = mInternalData->mItems.editValueAt(i);
-
- item.setData(type, data, size);
-
- return overwrote_existing;
-}
-
-bool MetaData::findData(uint32_t key, uint32_t *type,
- const void **data, size_t *size) const {
- ssize_t i = mInternalData->mItems.indexOfKey(key);
-
- if (i < 0) {
- return false;
- }
-
- const typed_data &item = mInternalData->mItems.valueAt(i);
-
- item.getData(type, data, size);
-
- return true;
-}
-
-bool MetaData::hasData(uint32_t key) const {
- ssize_t i = mInternalData->mItems.indexOfKey(key);
-
- if (i < 0) {
- return false;
- }
-
- return true;
-}
-
-MetaData::typed_data::typed_data()
- : mType(0),
- mSize(0) {
-}
-
-MetaData::typed_data::~typed_data() {
- clear();
-}
-
-MetaData::typed_data::typed_data(const typed_data &from)
- : mType(from.mType),
- mSize(0) {
-
- void *dst = allocateStorage(from.mSize);
- if (dst) {
- memcpy(dst, from.storage(), mSize);
- }
-}
-
-MetaData::typed_data &MetaData::typed_data::operator=(
- const MetaData::typed_data &from) {
- if (this != &from) {
- clear();
- mType = from.mType;
- void *dst = allocateStorage(from.mSize);
- if (dst) {
- memcpy(dst, from.storage(), mSize);
- }
- }
-
- return *this;
-}
-
-void MetaData::typed_data::clear() {
- freeStorage();
-
- mType = 0;
-}
-
-void MetaData::typed_data::setData(
- uint32_t type, const void *data, size_t size) {
- clear();
-
- mType = type;
-
- void *dst = allocateStorage(size);
- if (dst) {
- memcpy(dst, data, size);
- }
-}
-
-void MetaData::typed_data::getData(
- uint32_t *type, const void **data, size_t *size) const {
- *type = mType;
- *size = mSize;
- *data = storage();
-}
-
-void *MetaData::typed_data::allocateStorage(size_t size) {
- mSize = size;
-
- if (usesReservoir()) {
- return &u.reservoir;
- }
-
- u.ext_data = malloc(mSize);
- if (u.ext_data == NULL) {
- ALOGE("Couldn't allocate %zu bytes for item", size);
- mSize = 0;
- }
- return u.ext_data;
-}
-
-void MetaData::typed_data::freeStorage() {
- if (!usesReservoir()) {
- if (u.ext_data) {
- free(u.ext_data);
- u.ext_data = NULL;
- }
- }
-
- mSize = 0;
-}
-
-String8 MetaData::typed_data::asString(bool verbose) const {
- String8 out;
- const void *data = storage();
- switch(mType) {
- case TYPE_NONE:
- out = String8::format("no type, size %zu)", mSize);
- break;
- case TYPE_C_STRING:
- out = String8::format("(char*) %s", (const char *)data);
- break;
- case TYPE_INT32:
- out = String8::format("(int32_t) %d", *(int32_t *)data);
- break;
- case TYPE_INT64:
- out = String8::format("(int64_t) %" PRId64, *(int64_t *)data);
- break;
- case TYPE_FLOAT:
- out = String8::format("(float) %f", *(float *)data);
- break;
- case TYPE_POINTER:
- out = String8::format("(void*) %p", *(void **)data);
- break;
- case TYPE_RECT:
- {
- const Rect *r = (const Rect *)data;
- out = String8::format("Rect(%d, %d, %d, %d)",
- r->mLeft, r->mTop, r->mRight, r->mBottom);
- break;
- }
-
- default:
- out = String8::format("(unknown type %d, size %zu)", mType, mSize);
- if (verbose && mSize <= 48) { // if it's less than three lines of hex data, dump it
- AString foo;
- hexdump(data, mSize, 0, &foo);
- out.append("\n");
- out.append(foo.c_str());
- }
- break;
- }
- return out;
-}
-
-static void MakeFourCCString(uint32_t x, char *s) {
- s[0] = x >> 24;
- s[1] = (x >> 16) & 0xff;
- s[2] = (x >> 8) & 0xff;
- s[3] = x & 0xff;
- s[4] = '\0';
-}
-
-String8 MetaData::toString() const {
- String8 s;
- for (int i = mInternalData->mItems.size(); --i >= 0;) {
- int32_t key = mInternalData->mItems.keyAt(i);
- char cc[5];
- MakeFourCCString(key, cc);
- const typed_data &item = mInternalData->mItems.valueAt(i);
- s.appendFormat("%s: %s", cc, item.asString(false).string());
- if (i != 0) {
- s.append(", ");
- }
- }
- return s;
-}
-void MetaData::dumpToLog() const {
- for (int i = mInternalData->mItems.size(); --i >= 0;) {
- int32_t key = mInternalData->mItems.keyAt(i);
- char cc[5];
- MakeFourCCString(key, cc);
- const typed_data &item = mInternalData->mItems.valueAt(i);
- ALOGI("%s: %s", cc, item.asString(true /* verbose */).string());
- }
-}
-
-status_t MetaData::writeToParcel(Parcel &parcel) {
- status_t ret;
- size_t numItems = mInternalData->mItems.size();
- ret = parcel.writeUint32(uint32_t(numItems));
- if (ret) {
- return ret;
- }
- for (size_t i = 0; i < numItems; i++) {
- int32_t key = mInternalData->mItems.keyAt(i);
- const typed_data &item = mInternalData->mItems.valueAt(i);
- uint32_t type;
- const void *data;
- size_t size;
- item.getData(&type, &data, &size);
- 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;
-}
-
-status_t MetaData::updateFromParcel(const Parcel &parcel) {
- uint32_t numItems;
- if (parcel.readUint32(&numItems) == OK) {
-
- for (size_t i = 0; i < numItems; i++) {
- int32_t key;
- uint32_t type;
- uint32_t size;
- status_t ret = parcel.readInt32(&key);
- ret |= parcel.readUint32(&type);
- ret |= parcel.readUint32(&size);
- if (ret != OK) {
- break;
- }
- // 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;
- }
- ALOGW("no metadata in parcel");
- return UNKNOWN_ERROR;
-}
-
-
/* static */
sp<MetaData> MetaData::createFromParcel(const Parcel &parcel) {
@@ -533,7 +53,5 @@
return meta;
}
-
-
} // namespace android
diff --git a/media/libmediaextractor/MetaDataBase.cpp b/media/libmediaextractor/MetaDataBase.cpp
new file mode 100644
index 0000000..bfea6f1
--- /dev/null
+++ b/media/libmediaextractor/MetaDataBase.cpp
@@ -0,0 +1,533 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "MetaDataBase"
+#include <inttypes.h>
+#include <binder/Parcel.h>
+#include <utils/KeyedVector.h>
+#include <utils/Log.h>
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/foundation/AString.h>
+#include <media/stagefright/foundation/hexdump.h>
+#include <media/stagefright/MetaDataBase.h>
+
+namespace android {
+
+struct MetaDataBase::typed_data {
+ typed_data();
+ ~typed_data();
+
+ typed_data(const MetaDataBase::typed_data &);
+ typed_data &operator=(const MetaDataBase::typed_data &);
+
+ void clear();
+ void setData(uint32_t type, const void *data, size_t size);
+ void getData(uint32_t *type, const void **data, size_t *size) const;
+ // may include hexdump of binary data if verbose=true
+ String8 asString(bool verbose) const;
+
+private:
+ uint32_t mType;
+ size_t mSize;
+
+ union {
+ void *ext_data;
+ float reservoir;
+ } u;
+
+ bool usesReservoir() const {
+ return mSize <= sizeof(u.reservoir);
+ }
+
+ void *allocateStorage(size_t size);
+ void freeStorage();
+
+ void *storage() {
+ return usesReservoir() ? &u.reservoir : u.ext_data;
+ }
+
+ const void *storage() const {
+ return usesReservoir() ? &u.reservoir : u.ext_data;
+ }
+};
+
+struct MetaDataBase::Rect {
+ int32_t mLeft, mTop, mRight, mBottom;
+};
+
+
+struct MetaDataBase::MetaDataInternal {
+ KeyedVector<uint32_t, MetaDataBase::typed_data> mItems;
+};
+
+
+MetaDataBase::MetaDataBase()
+ : mInternalData(new MetaDataInternal()) {
+}
+
+MetaDataBase::MetaDataBase(const MetaDataBase &from)
+ : mInternalData(new MetaDataInternal()) {
+ mInternalData->mItems = from.mInternalData->mItems;
+}
+
+MetaDataBase& MetaDataBase::operator = (const MetaDataBase &rhs) {
+ this->mInternalData->mItems = rhs.mInternalData->mItems;
+ return *this;
+}
+
+MetaDataBase::~MetaDataBase() {
+ clear();
+ delete mInternalData;
+}
+
+void MetaDataBase::clear() {
+ mInternalData->mItems.clear();
+}
+
+bool MetaDataBase::remove(uint32_t key) {
+ ssize_t i = mInternalData->mItems.indexOfKey(key);
+
+ if (i < 0) {
+ return false;
+ }
+
+ mInternalData->mItems.removeItemsAt(i);
+
+ return true;
+}
+
+bool MetaDataBase::setCString(uint32_t key, const char *value) {
+ return setData(key, TYPE_C_STRING, value, strlen(value) + 1);
+}
+
+bool MetaDataBase::setInt32(uint32_t key, int32_t value) {
+ return setData(key, TYPE_INT32, &value, sizeof(value));
+}
+
+bool MetaDataBase::setInt64(uint32_t key, int64_t value) {
+ return setData(key, TYPE_INT64, &value, sizeof(value));
+}
+
+bool MetaDataBase::setFloat(uint32_t key, float value) {
+ return setData(key, TYPE_FLOAT, &value, sizeof(value));
+}
+
+bool MetaDataBase::setPointer(uint32_t key, void *value) {
+ return setData(key, TYPE_POINTER, &value, sizeof(value));
+}
+
+bool MetaDataBase::setRect(
+ uint32_t key,
+ int32_t left, int32_t top,
+ int32_t right, int32_t bottom) {
+ Rect r;
+ r.mLeft = left;
+ r.mTop = top;
+ r.mRight = right;
+ r.mBottom = bottom;
+
+ return setData(key, TYPE_RECT, &r, sizeof(r));
+}
+
+/**
+ * Note that the returned pointer becomes invalid when additional metadata is set.
+ */
+bool MetaDataBase::findCString(uint32_t key, const char **value) const {
+ uint32_t type;
+ const void *data;
+ size_t size;
+ if (!findData(key, &type, &data, &size) || type != TYPE_C_STRING) {
+ return false;
+ }
+
+ *value = (const char *)data;
+
+ return true;
+}
+
+bool MetaDataBase::findInt32(uint32_t key, int32_t *value) const {
+ uint32_t type = 0;
+ const void *data;
+ size_t size;
+ if (!findData(key, &type, &data, &size) || type != TYPE_INT32) {
+ return false;
+ }
+
+ CHECK_EQ(size, sizeof(*value));
+
+ *value = *(int32_t *)data;
+
+ return true;
+}
+
+bool MetaDataBase::findInt64(uint32_t key, int64_t *value) const {
+ uint32_t type = 0;
+ const void *data;
+ size_t size;
+ if (!findData(key, &type, &data, &size) || type != TYPE_INT64) {
+ return false;
+ }
+
+ CHECK_EQ(size, sizeof(*value));
+
+ *value = *(int64_t *)data;
+
+ return true;
+}
+
+bool MetaDataBase::findFloat(uint32_t key, float *value) const {
+ uint32_t type = 0;
+ const void *data;
+ size_t size;
+ if (!findData(key, &type, &data, &size) || type != TYPE_FLOAT) {
+ return false;
+ }
+
+ CHECK_EQ(size, sizeof(*value));
+
+ *value = *(float *)data;
+
+ return true;
+}
+
+bool MetaDataBase::findPointer(uint32_t key, void **value) const {
+ uint32_t type = 0;
+ const void *data;
+ size_t size;
+ if (!findData(key, &type, &data, &size) || type != TYPE_POINTER) {
+ return false;
+ }
+
+ CHECK_EQ(size, sizeof(*value));
+
+ *value = *(void **)data;
+
+ return true;
+}
+
+bool MetaDataBase::findRect(
+ uint32_t key,
+ int32_t *left, int32_t *top,
+ int32_t *right, int32_t *bottom) const {
+ uint32_t type = 0;
+ const void *data;
+ size_t size;
+ if (!findData(key, &type, &data, &size) || type != TYPE_RECT) {
+ return false;
+ }
+
+ CHECK_EQ(size, sizeof(Rect));
+
+ const Rect *r = (const Rect *)data;
+ *left = r->mLeft;
+ *top = r->mTop;
+ *right = r->mRight;
+ *bottom = r->mBottom;
+
+ return true;
+}
+
+bool MetaDataBase::setData(
+ uint32_t key, uint32_t type, const void *data, size_t size) {
+ bool overwrote_existing = true;
+
+ ssize_t i = mInternalData->mItems.indexOfKey(key);
+ if (i < 0) {
+ typed_data item;
+ i = mInternalData->mItems.add(key, item);
+
+ overwrote_existing = false;
+ }
+
+ typed_data &item = mInternalData->mItems.editValueAt(i);
+
+ item.setData(type, data, size);
+
+ return overwrote_existing;
+}
+
+bool MetaDataBase::findData(uint32_t key, uint32_t *type,
+ const void **data, size_t *size) const {
+ ssize_t i = mInternalData->mItems.indexOfKey(key);
+
+ if (i < 0) {
+ return false;
+ }
+
+ const typed_data &item = mInternalData->mItems.valueAt(i);
+
+ item.getData(type, data, size);
+
+ return true;
+}
+
+bool MetaDataBase::hasData(uint32_t key) const {
+ ssize_t i = mInternalData->mItems.indexOfKey(key);
+
+ if (i < 0) {
+ return false;
+ }
+
+ return true;
+}
+
+MetaDataBase::typed_data::typed_data()
+ : mType(0),
+ mSize(0) {
+}
+
+MetaDataBase::typed_data::~typed_data() {
+ clear();
+}
+
+MetaDataBase::typed_data::typed_data(const typed_data &from)
+ : mType(from.mType),
+ mSize(0) {
+
+ void *dst = allocateStorage(from.mSize);
+ if (dst) {
+ memcpy(dst, from.storage(), mSize);
+ }
+}
+
+MetaDataBase::typed_data &MetaDataBase::typed_data::operator=(
+ const MetaDataBase::typed_data &from) {
+ if (this != &from) {
+ clear();
+ mType = from.mType;
+ void *dst = allocateStorage(from.mSize);
+ if (dst) {
+ memcpy(dst, from.storage(), mSize);
+ }
+ }
+
+ return *this;
+}
+
+void MetaDataBase::typed_data::clear() {
+ freeStorage();
+
+ mType = 0;
+}
+
+void MetaDataBase::typed_data::setData(
+ uint32_t type, const void *data, size_t size) {
+ clear();
+
+ mType = type;
+
+ void *dst = allocateStorage(size);
+ if (dst) {
+ memcpy(dst, data, size);
+ }
+}
+
+void MetaDataBase::typed_data::getData(
+ uint32_t *type, const void **data, size_t *size) const {
+ *type = mType;
+ *size = mSize;
+ *data = storage();
+}
+
+void *MetaDataBase::typed_data::allocateStorage(size_t size) {
+ mSize = size;
+
+ if (usesReservoir()) {
+ return &u.reservoir;
+ }
+
+ u.ext_data = malloc(mSize);
+ if (u.ext_data == NULL) {
+ ALOGE("Couldn't allocate %zu bytes for item", size);
+ mSize = 0;
+ }
+ return u.ext_data;
+}
+
+void MetaDataBase::typed_data::freeStorage() {
+ if (!usesReservoir()) {
+ if (u.ext_data) {
+ free(u.ext_data);
+ u.ext_data = NULL;
+ }
+ }
+
+ mSize = 0;
+}
+
+String8 MetaDataBase::typed_data::asString(bool verbose) const {
+ String8 out;
+ const void *data = storage();
+ switch(mType) {
+ case TYPE_NONE:
+ out = String8::format("no type, size %zu)", mSize);
+ break;
+ case TYPE_C_STRING:
+ out = String8::format("(char*) %s", (const char *)data);
+ break;
+ case TYPE_INT32:
+ out = String8::format("(int32_t) %d", *(int32_t *)data);
+ break;
+ case TYPE_INT64:
+ out = String8::format("(int64_t) %" PRId64, *(int64_t *)data);
+ break;
+ case TYPE_FLOAT:
+ out = String8::format("(float) %f", *(float *)data);
+ break;
+ case TYPE_POINTER:
+ out = String8::format("(void*) %p", *(void **)data);
+ break;
+ case TYPE_RECT:
+ {
+ const Rect *r = (const Rect *)data;
+ out = String8::format("Rect(%d, %d, %d, %d)",
+ r->mLeft, r->mTop, r->mRight, r->mBottom);
+ break;
+ }
+
+ default:
+ out = String8::format("(unknown type %d, size %zu)", mType, mSize);
+ if (verbose && mSize <= 48) { // if it's less than three lines of hex data, dump it
+ AString foo;
+ hexdump(data, mSize, 0, &foo);
+ out.append("\n");
+ out.append(foo.c_str());
+ }
+ break;
+ }
+ return out;
+}
+
+static void MakeFourCCString(uint32_t x, char *s) {
+ s[0] = x >> 24;
+ s[1] = (x >> 16) & 0xff;
+ s[2] = (x >> 8) & 0xff;
+ s[3] = x & 0xff;
+ s[4] = '\0';
+}
+
+String8 MetaDataBase::toString() const {
+ String8 s;
+ for (int i = mInternalData->mItems.size(); --i >= 0;) {
+ int32_t key = mInternalData->mItems.keyAt(i);
+ char cc[5];
+ MakeFourCCString(key, cc);
+ const typed_data &item = mInternalData->mItems.valueAt(i);
+ s.appendFormat("%s: %s", cc, item.asString(false).string());
+ if (i != 0) {
+ s.append(", ");
+ }
+ }
+ return s;
+}
+
+void MetaDataBase::dumpToLog() const {
+ for (int i = mInternalData->mItems.size(); --i >= 0;) {
+ int32_t key = mInternalData->mItems.keyAt(i);
+ char cc[5];
+ MakeFourCCString(key, cc);
+ const typed_data &item = mInternalData->mItems.valueAt(i);
+ ALOGI("%s: %s", cc, item.asString(true /* verbose */).string());
+ }
+}
+
+status_t MetaDataBase::writeToParcel(Parcel &parcel) {
+ status_t ret;
+ size_t numItems = mInternalData->mItems.size();
+ ret = parcel.writeUint32(uint32_t(numItems));
+ if (ret) {
+ return ret;
+ }
+ for (size_t i = 0; i < numItems; i++) {
+ int32_t key = mInternalData->mItems.keyAt(i);
+ const typed_data &item = mInternalData->mItems.valueAt(i);
+ uint32_t type;
+ const void *data;
+ size_t size;
+ item.getData(&type, &data, &size);
+ 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;
+}
+
+status_t MetaDataBase::updateFromParcel(const Parcel &parcel) {
+ uint32_t numItems;
+ if (parcel.readUint32(&numItems) == OK) {
+
+ for (size_t i = 0; i < numItems; i++) {
+ int32_t key;
+ uint32_t type;
+ uint32_t size;
+ status_t ret = parcel.readInt32(&key);
+ ret |= parcel.readUint32(&type);
+ ret |= parcel.readUint32(&size);
+ if (ret != OK) {
+ break;
+ }
+ // 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;
+ }
+ ALOGW("no metadata in parcel");
+ return UNKNOWN_ERROR;
+}
+
+} // namespace android
+
diff --git a/media/libmediaextractor/VorbisComment.cpp b/media/libmediaextractor/VorbisComment.cpp
new file mode 100644
index 0000000..fabaf51
--- /dev/null
+++ b/media/libmediaextractor/VorbisComment.cpp
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "VorbisComment"
+#include <utils/Log.h>
+
+#include "media/VorbisComment.h"
+
+#include <media/stagefright/foundation/base64.h>
+#include <media/stagefright/foundation/ABuffer.h>
+#include <media/stagefright/foundation/AString.h>
+#include <media/stagefright/foundation/ByteUtils.h>
+#include <media/stagefright/MetaDataBase.h>
+
+namespace android {
+
+static void extractAlbumArt(
+ MetaDataBase *fileMeta, const void *data, size_t size) {
+ ALOGV("extractAlbumArt from '%s'", (const char *)data);
+
+ sp<ABuffer> flacBuffer = decodeBase64(AString((const char *)data, size));
+ if (flacBuffer == NULL) {
+ ALOGE("malformed base64 encoded data.");
+ return;
+ }
+
+ size_t flacSize = flacBuffer->size();
+ uint8_t *flac = flacBuffer->data();
+ ALOGV("got flac of size %zu", flacSize);
+
+ uint32_t picType;
+ uint32_t typeLen;
+ uint32_t descLen;
+ uint32_t dataLen;
+ char type[128];
+
+ if (flacSize < 8) {
+ return;
+ }
+
+ picType = U32_AT(flac);
+
+ if (picType != 3) {
+ // This is not a front cover.
+ return;
+ }
+
+ typeLen = U32_AT(&flac[4]);
+ if (typeLen > sizeof(type) - 1) {
+ return;
+ }
+
+ // we've already checked above that flacSize >= 8
+ if (flacSize - 8 < typeLen) {
+ return;
+ }
+
+ memcpy(type, &flac[8], typeLen);
+ type[typeLen] = '\0';
+
+ ALOGV("picType = %d, type = '%s'", picType, type);
+
+ if (!strcmp(type, "-->")) {
+ // This is not inline cover art, but an external url instead.
+ return;
+ }
+
+ if (flacSize < 32 || flacSize - 32 < typeLen) {
+ return;
+ }
+
+ descLen = U32_AT(&flac[8 + typeLen]);
+ if (flacSize - 32 - typeLen < descLen) {
+ return;
+ }
+
+ dataLen = U32_AT(&flac[8 + typeLen + 4 + descLen + 16]);
+
+ // we've already checked above that (flacSize - 32 - typeLen - descLen) >= 0
+ if (flacSize - 32 - typeLen - descLen < dataLen) {
+ return;
+ }
+
+ ALOGV("got image data, %zu trailing bytes",
+ flacSize - 32 - typeLen - descLen - dataLen);
+
+ fileMeta->setData(
+ kKeyAlbumArt, 0, &flac[8 + typeLen + 4 + descLen + 20], dataLen);
+
+ fileMeta->setCString(kKeyAlbumArtMIME, type);
+}
+
+void parseVorbisComment(
+ MetaDataBase *fileMeta, const char *comment, size_t commentLength)
+{
+ struct {
+ const char *const mTag;
+ uint32_t mKey;
+ } kMap[] = {
+ { "TITLE", kKeyTitle },
+ { "ARTIST", kKeyArtist },
+ { "ALBUMARTIST", kKeyAlbumArtist },
+ { "ALBUM ARTIST", kKeyAlbumArtist },
+ { "COMPILATION", kKeyCompilation },
+ { "ALBUM", kKeyAlbum },
+ { "COMPOSER", kKeyComposer },
+ { "GENRE", kKeyGenre },
+ { "AUTHOR", kKeyAuthor },
+ { "TRACKNUMBER", kKeyCDTrackNumber },
+ { "DISCNUMBER", kKeyDiscNumber },
+ { "DATE", kKeyDate },
+ { "YEAR", kKeyYear },
+ { "LYRICIST", kKeyWriter },
+ { "METADATA_BLOCK_PICTURE", kKeyAlbumArt },
+ { "ANDROID_LOOP", kKeyAutoLoop },
+ };
+
+ for (size_t j = 0; j < sizeof(kMap) / sizeof(kMap[0]); ++j) {
+ size_t tagLen = strlen(kMap[j].mTag);
+ if (!strncasecmp(kMap[j].mTag, comment, tagLen)
+ && comment[tagLen] == '=') {
+ if (kMap[j].mKey == kKeyAlbumArt) {
+ extractAlbumArt(
+ fileMeta,
+ &comment[tagLen + 1],
+ commentLength - tagLen - 1);
+ } else if (kMap[j].mKey == kKeyAutoLoop) {
+ if (!strcasecmp(&comment[tagLen + 1], "true")) {
+ fileMeta->setInt32(kKeyAutoLoop, true);
+ }
+ } else {
+ fileMeta->setCString(kMap[j].mKey, &comment[tagLen + 1]);
+ }
+ }
+ }
+
+}
+
+} // namespace android
diff --git a/media/libmediaextractor/include/media/DataSourceBase.h b/media/libmediaextractor/include/media/DataSourceBase.h
index f964137..8028dd7 100644
--- a/media/libmediaextractor/include/media/DataSourceBase.h
+++ b/media/libmediaextractor/include/media/DataSourceBase.h
@@ -58,20 +58,6 @@
bool getUInt32Var(off64_t offset, uint32_t *x, size_t size);
bool getUInt64Var(off64_t offset, uint64_t *x, size_t size);
- // Reads in "count" entries of type T into vector *x.
- // Returns true if "count" entries can be read.
- // If fewer than "count" entries can be read, return false. In this case,
- // the output vector *x will still have those entries that were read. Call
- // x->size() to obtain the number of entries read.
- // The optional parameter chunkSize specifies how many entries should be
- // read from the data source at one time into a temporary buffer. Increasing
- // chunkSize can improve the performance at the cost of extra memory usage.
- // The default value for chunkSize is set to read at least 4k bytes at a
- // time, depending on sizeof(T).
- template <typename T>
- bool getVector(off64_t offset, Vector<T>* x, size_t count,
- size_t chunkSize = (4095 / sizeof(T)) + 1);
-
// May return ERROR_UNSUPPORTED.
virtual status_t getSize(off64_t *size);
@@ -110,51 +96,6 @@
DataSourceBase &operator=(const DataSourceBase &);
};
-template <typename T>
-bool DataSourceBase::getVector(off64_t offset, Vector<T>* x, size_t count,
- size_t chunkSize)
-{
- x->clear();
- if (chunkSize == 0) {
- return false;
- }
- if (count == 0) {
- return true;
- }
-
- T tmp[chunkSize];
- ssize_t numBytesRead;
- size_t numBytesPerChunk = chunkSize * sizeof(T);
- size_t i;
-
- for (i = 0; i + chunkSize < count; i += chunkSize) {
- // This loops is executed when more than chunkSize records need to be
- // read.
- numBytesRead = this->readAt(offset, (void*)&tmp, numBytesPerChunk);
- if (numBytesRead == -1) { // If readAt() returns -1, there is an error.
- return false;
- }
- if (static_cast<size_t>(numBytesRead) < numBytesPerChunk) {
- // This case is triggered when the stream ends before the whole
- // chunk is read.
- x->appendArray(tmp, (size_t)numBytesRead / sizeof(T));
- return false;
- }
- x->appendArray(tmp, chunkSize);
- offset += numBytesPerChunk;
- }
-
- // There are (count - i) more records to read.
- // Right now, (count - i) <= chunkSize.
- // We do the same thing as above, but with chunkSize replaced by count - i.
- numBytesRead = this->readAt(offset, (void*)&tmp, (count - i) * sizeof(T));
- if (numBytesRead == -1) {
- return false;
- }
- x->appendArray(tmp, (size_t)numBytesRead / sizeof(T));
- return x->size() == count;
-}
-
} // namespace android
#endif // DATA_SOURCE_BASE_H_
diff --git a/media/libmediaextractor/include/media/MediaExtractor.h b/media/libmediaextractor/include/media/MediaExtractor.h
index 2e65620..4ba98da 100644
--- a/media/libmediaextractor/include/media/MediaExtractor.h
+++ b/media/libmediaextractor/include/media/MediaExtractor.h
@@ -28,8 +28,8 @@
namespace android {
class DataSourceBase;
-class MetaData;
-struct MediaSourceBase;
+class MetaDataBase;
+struct MediaTrack;
class ExtractorAllocTracker {
@@ -49,17 +49,18 @@
public:
virtual ~MediaExtractor();
virtual size_t countTracks() = 0;
- virtual MediaSourceBase *getTrack(size_t index) = 0;
+ virtual MediaTrack *getTrack(size_t index) = 0;
enum GetTrackMetaDataFlags {
kIncludeExtensiveMetaData = 1
};
- virtual sp<MetaData> getTrackMetaData(
+ virtual status_t getTrackMetaData(
+ MetaDataBase& meta,
size_t index, uint32_t flags = 0) = 0;
// Return container specific meta-data. The default implementation
// returns an empty metadata object.
- virtual sp<MetaData> getMetaData();
+ virtual status_t getMetaData(MetaDataBase& meta) = 0;
enum Flags {
CAN_SEEK_BACKWARD = 1, // the "seek 10secs back button"
@@ -72,12 +73,6 @@
// CAN_SEEK_BACKWARD | CAN_SEEK_FORWARD | CAN_SEEK | CAN_PAUSE
virtual uint32_t flags() const;
- // for DRM
- virtual char* getDrmTrackInfo(size_t /*trackID*/, int * /*len*/) {
- return NULL;
- }
- virtual void setUID(uid_t /*uid*/) {
- }
virtual status_t setMediaCas(const uint8_t* /*casToken*/, size_t /*size*/) {
return INVALID_OPERATION;
}
diff --git a/media/libmediaextractor/include/media/MediaSource.h b/media/libmediaextractor/include/media/MediaSource.h
index 45070d6..73c4703 100644
--- a/media/libmediaextractor/include/media/MediaSource.h
+++ b/media/libmediaextractor/include/media/MediaSource.h
@@ -1,4 +1,5 @@
/*
+
* Copyright (C) 2009 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -24,15 +25,71 @@
#include <media/stagefright/MetaData.h>
#include <utils/RefBase.h>
-#include "media/MediaSourceBase.h"
+#include <media/MediaTrack.h>
namespace android {
class MediaBuffer;
-struct MediaSource : public MediaSourceBase, public virtual RefBase {
+struct MediaSource : public virtual RefBase {
MediaSource();
+ // To be called before any other methods on this object, except
+ // getFormat().
+ virtual status_t start(MetaData *params = NULL) = 0;
+
+ // Any blocking read call returns immediately with a result of NO_INIT.
+ // It is an error to call any methods other than start after this call
+ // returns. Any buffers the object may be holding onto at the time of
+ // the stop() call are released.
+ // Also, it is imperative that any buffers output by this object and
+ // held onto by callers be released before a call to stop() !!!
+ virtual status_t stop() = 0;
+
+ // Returns the format of the data output by this media source.
+ virtual sp<MetaData> getFormat() = 0;
+
+ // Options that modify read() behaviour. The default is to
+ // a) not request a seek
+ // b) not be late, i.e. lateness_us = 0
+ typedef MediaTrack::ReadOptions ReadOptions;
+
+ // Returns a new buffer of data. Call blocks until a
+ // buffer is available, an error is encountered of the end of the stream
+ // is reached.
+ // End of stream is signalled 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.
+ virtual status_t read(
+ MediaBufferBase **buffer, const ReadOptions *options = NULL) = 0;
+
+ // Causes this source to suspend pulling data from its upstream source
+ // until a subsequent read-with-seek. This is currently not supported
+ // as such by any source. E.g. MediaCodecSource does not suspend its
+ // upstream source, and instead discard upstream data while paused.
+ virtual status_t pause() {
+ return ERROR_UNSUPPORTED;
+ }
+
+ // The consumer of this media source requests the source stops sending
+ // buffers with timestamp larger than or equal to stopTimeUs. stopTimeUs
+ // must be in the same time base as the startTime passed in start(). If
+ // source does not support this request, ERROR_UNSUPPORTED will be returned.
+ // If stopTimeUs is invalid, BAD_VALUE will be returned. This could be
+ // called at any time even before source starts and it could be called
+ // multiple times. Setting stopTimeUs to be -1 will effectively cancel the stopTimeUs
+ // set previously. If stopTimeUs is larger than or equal to last buffer's timestamp,
+ // source will start to drop buffer when it gets a buffer with timestamp larger
+ // than or equal to stopTimeUs. If stopTimeUs is smaller than or equal to last
+ // buffer's timestamp, source will drop all the incoming buffers immediately.
+ // After setting stopTimeUs, source may still stop sending buffers with timestamp
+ // less than stopTimeUs if it is stopped by the consumer.
+ virtual status_t setStopTimeUs(int64_t /* stopTimeUs */) {
+ return ERROR_UNSUPPORTED;
+ }
+
+protected:
virtual ~MediaSource();
private:
diff --git a/media/libmediaextractor/include/media/MediaSourceBase.h b/media/libmediaextractor/include/media/MediaTrack.h
similarity index 63%
rename from media/libmediaextractor/include/media/MediaSourceBase.h
rename to media/libmediaextractor/include/media/MediaTrack.h
index 9db6099..adea61a 100644
--- a/media/libmediaextractor/include/media/MediaSourceBase.h
+++ b/media/libmediaextractor/include/media/MediaTrack.h
@@ -30,7 +30,7 @@
namespace android {
-class MediaBuffer;
+class MediaBufferBase;
class SourceBaseAllocTracker {
public:
@@ -42,14 +42,14 @@
}
};
-struct MediaSourceBase
+struct MediaTrack
// : public SourceBaseAllocTracker
{
- MediaSourceBase();
+ MediaTrack();
// To be called before any other methods on this object, except
// getFormat().
- virtual status_t start(MetaData *params = NULL) = 0;
+ virtual status_t start(MetaDataBase *params = NULL) = 0;
// Any blocking read call returns immediately with a result of NO_INIT.
// It is an error to call any methods other than start after this call
@@ -59,8 +59,8 @@
// held onto by callers be released before a call to stop() !!!
virtual status_t stop() = 0;
- // Returns the format of the data output by this media source.
- virtual sp<MetaData> getFormat() = 0;
+ // Returns the format of the data output by this media track.
+ virtual status_t getFormat(MetaDataBase& format) = 0;
// Options that modify read() behaviour. The default is to
// a) not request a seek
@@ -111,38 +111,13 @@
// MediaSource has changed mid-stream, the client can continue reading
// but should be prepared for buffers of the new configuration.
virtual status_t read(
- MediaBuffer **buffer, const ReadOptions *options = NULL) = 0;
+ MediaBufferBase **buffer, const ReadOptions *options = NULL) = 0;
- // Causes this source to suspend pulling data from its upstream source
- // until a subsequent read-with-seek. This is currently not supported
- // as such by any source. E.g. MediaCodecSource does not suspend its
- // upstream source, and instead discard upstream data while paused.
- virtual status_t pause() {
- return ERROR_UNSUPPORTED;
- }
-
- // The consumer of this media source requests the source stops sending
- // buffers with timestamp larger than or equal to stopTimeUs. stopTimeUs
- // must be in the same time base as the startTime passed in start(). If
- // source does not support this request, ERROR_UNSUPPORTED will be returned.
- // If stopTimeUs is invalid, BAD_VALUE will be returned. This could be
- // called at any time even before source starts and it could be called
- // multiple times. Setting stopTimeUs to be -1 will effectively cancel the stopTimeUs
- // set previously. If stopTimeUs is larger than or equal to last buffer's timestamp,
- // source will start to drop buffer when it gets a buffer with timestamp larger
- // than or equal to stopTimeUs. If stopTimeUs is smaller than or equal to last
- // buffer's timestamp, source will drop all the incoming buffers immediately.
- // After setting stopTimeUs, source may still stop sending buffers with timestamp
- // less than stopTimeUs if it is stopped by the consumer.
- virtual status_t setStopTimeUs(int64_t /* stopTimeUs */) {
- return ERROR_UNSUPPORTED;
- }
-
- virtual ~MediaSourceBase();
+ virtual ~MediaTrack();
private:
- MediaSourceBase(const MediaSourceBase &);
- MediaSourceBase &operator=(const MediaSourceBase &);
+ MediaTrack(const MediaTrack &);
+ MediaTrack &operator=(const MediaTrack &);
};
} // namespace android
diff --git a/media/libmediaextractor/include/media/VorbisComment.h b/media/libmediaextractor/include/media/VorbisComment.h
new file mode 100644
index 0000000..8ba3295
--- /dev/null
+++ b/media/libmediaextractor/include/media/VorbisComment.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2018 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 VORBIS_COMMENT_H_
+#define VORBIS_COMMENT_H_
+
+namespace android {
+
+class MetaDataBase;
+
+void parseVorbisComment(
+ MetaDataBase *fileMeta, const char *comment, size_t commentLength);
+
+} // namespace android
+
+#endif // VORBIS_COMMENT_H_
diff --git a/media/libmediaextractor/include/media/stagefright/MediaBuffer.h b/media/libmediaextractor/include/media/stagefright/MediaBuffer.h
index a8f8375..f944d51 100644
--- a/media/libmediaextractor/include/media/stagefright/MediaBuffer.h
+++ b/media/libmediaextractor/include/media/stagefright/MediaBuffer.h
@@ -26,27 +26,16 @@
#include <binder/MemoryDealer.h>
#include <utils/Errors.h>
#include <utils/RefBase.h>
+#include <media/stagefright/MediaBufferBase.h>
namespace android {
struct ABuffer;
class MediaBuffer;
class MediaBufferObserver;
-class MetaData;
+class MetaDataBase;
-class MediaBufferObserver {
-public:
- MediaBufferObserver() {}
- virtual ~MediaBufferObserver() {}
-
- virtual void signalBufferReturned(MediaBuffer *buffer) = 0;
-
-private:
- MediaBufferObserver(const MediaBufferObserver &);
- MediaBufferObserver &operator=(const MediaBufferObserver &);
-};
-
-class MediaBuffer {
+class MediaBuffer : public MediaBufferBase {
public:
// allocations larger than or equal to this will use shared memory.
static const size_t kSharedMemThreshold = 64 * 1024;
@@ -70,42 +59,42 @@
//
// If no MediaBufferGroup is set, the local reference count must be zero
// when called, whereupon the MediaBuffer is deleted.
- void release();
+ virtual void release();
// Increments the local reference count.
// Use only when MediaBufferGroup is set.
- void add_ref();
+ virtual void add_ref();
- void *data() const;
- size_t size() const;
+ virtual void *data() const;
+ virtual size_t size() const;
- size_t range_offset() const;
- size_t range_length() const;
+ virtual size_t range_offset() const;
+ virtual size_t range_length() const;
- void set_range(size_t offset, size_t length);
+ virtual void set_range(size_t offset, size_t length);
- sp<MetaData> meta_data();
+ MetaDataBase& meta_data();
// Clears meta data and resets the range to the full extent.
- void reset();
+ virtual void reset();
- void setObserver(MediaBufferObserver *group);
+ virtual void setObserver(MediaBufferObserver *group);
// Returns a clone of this MediaBuffer increasing its reference count.
// The clone references the same data but has its own range and
// MetaData.
- MediaBuffer *clone();
+ virtual MediaBufferBase *clone();
// sum of localRefcount() and remoteRefcount()
- int refcount() const {
+ virtual int refcount() const {
return localRefcount() + remoteRefcount();
}
- int localRefcount() const {
+ virtual int localRefcount() const {
return mRefCount;
}
- int remoteRefcount() const {
+ virtual int remoteRefcount() const {
if (mMemory.get() == nullptr || mMemory->pointer() == nullptr) return 0;
int32_t remoteRefcount =
reinterpret_cast<SharedControl *>(mMemory->pointer())->getRemoteRefcount();
@@ -165,7 +154,7 @@
bool mOwnsData;
- sp<MetaData> mMetaData;
+ MetaDataBase* mMetaData;
MediaBuffer *mOriginal;
diff --git a/media/libmediaextractor/include/media/stagefright/MediaBufferBase.h b/media/libmediaextractor/include/media/stagefright/MediaBufferBase.h
new file mode 100644
index 0000000..6c8d94a
--- /dev/null
+++ b/media/libmediaextractor/include/media/stagefright/MediaBufferBase.h
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2009 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 MEDIA_BUFFER_BASE_H_
+
+#define MEDIA_BUFFER_BASE_H_
+
+namespace android {
+
+class MediaBufferBase;
+class MetaDataBase;
+
+class MediaBufferObserver {
+public:
+ MediaBufferObserver() {}
+ virtual ~MediaBufferObserver() {}
+
+ virtual void signalBufferReturned(MediaBufferBase *buffer) = 0;
+
+private:
+ MediaBufferObserver(const MediaBufferObserver &);
+ MediaBufferObserver &operator=(const MediaBufferObserver &);
+};
+
+class MediaBufferBase {
+public:
+ static MediaBufferBase *Create(size_t size);
+
+ // 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() = 0;
+
+ // Increments the local reference count.
+ // Use only when MediaBufferGroup is set.
+ virtual void add_ref() = 0;
+
+ virtual void *data() const = 0;
+ virtual size_t size() const = 0;
+
+ virtual size_t range_offset() const = 0;
+ virtual size_t range_length() const = 0;
+
+ virtual void set_range(size_t offset, size_t length) = 0;
+
+ virtual MetaDataBase& meta_data() = 0;
+
+ // Clears meta data and resets the range to the full extent.
+ virtual void reset() = 0;
+
+ virtual void setObserver(MediaBufferObserver *group) = 0;
+
+ // Returns a clone of this MediaBufferBase increasing its reference
+ // count. The clone references the same data but has its own range and
+ // MetaData.
+ virtual MediaBufferBase *clone() = 0;
+
+ virtual int refcount() const = 0;
+
+ virtual int localRefcount() const = 0;
+ virtual int remoteRefcount() const = 0;
+
+ virtual ~MediaBufferBase() {};
+};
+
+} // namespace android
+
+#endif // MEDIA_BUFFER_BASE_H_
diff --git a/media/libmediaextractor/include/media/stagefright/MediaBufferGroup.h b/media/libmediaextractor/include/media/stagefright/MediaBufferGroup.h
index 63d0a18..75d5df7 100644
--- a/media/libmediaextractor/include/media/stagefright/MediaBufferGroup.h
+++ b/media/libmediaextractor/include/media/stagefright/MediaBufferGroup.h
@@ -18,11 +18,15 @@
#define MEDIA_BUFFER_GROUP_H_
-#include <media/stagefright/MediaBuffer.h>
+#include <list>
+
+#include <media/stagefright/MediaBufferBase.h>
+#include <utils/Errors.h>
+#include <utils/threads.h>
namespace android {
-class MediaBuffer;
+class MediaBufferBase;
class MediaBufferGroup : public MediaBufferObserver {
public:
@@ -33,7 +37,7 @@
~MediaBufferGroup();
- void add_buffer(MediaBuffer *buffer);
+ void add_buffer(MediaBufferBase *buffer);
bool has_buffers();
@@ -46,16 +50,14 @@
// If requestedSize is > 0, the returned MediaBuffer should have buffer
// size of at least requstedSize.
status_t acquire_buffer(
- MediaBuffer **buffer, bool nonBlocking = false, size_t requestedSize = 0);
+ MediaBufferBase **buffer, bool nonBlocking = false, size_t requestedSize = 0);
size_t buffers() const;
// If buffer is nullptr, have acquire_buffer() check for remote release.
- virtual void signalBufferReturned(MediaBuffer *buffer);
+ virtual void signalBufferReturned(MediaBufferBase *buffer);
private:
- friend class MediaBuffer;
-
struct InternalData;
InternalData *mInternal;
diff --git a/media/libmediaextractor/include/media/stagefright/MetaData.h b/media/libmediaextractor/include/media/stagefright/MetaData.h
index e4a84b7..f625358 100644
--- a/media/libmediaextractor/include/media/stagefright/MetaData.h
+++ b/media/libmediaextractor/include/media/stagefright/MetaData.h
@@ -24,277 +24,24 @@
#include <utils/RefBase.h>
#include <utils/String8.h>
+#include <media/stagefright/MetaDataBase.h>
namespace android {
-class Parcel;
-
-// The following keys map to int32_t data unless indicated otherwise.
-enum {
- kKeyMIMEType = 'mime', // cstring
- kKeyWidth = 'widt', // int32_t, image pixel
- kKeyHeight = 'heig', // int32_t, image pixel
- kKeyDisplayWidth = 'dWid', // int32_t, display/presentation
- kKeyDisplayHeight = 'dHgt', // int32_t, display/presentation
- kKeySARWidth = 'sarW', // int32_t, sampleAspectRatio width
- kKeySARHeight = 'sarH', // int32_t, sampleAspectRatio height
- kKeyThumbnailWidth = 'thbW', // int32_t, thumbnail width
- kKeyThumbnailHeight = 'thbH', // int32_t, thumbnail height
-
- // a rectangle, if absent assumed to be (0, 0, width - 1, height - 1)
- kKeyCropRect = 'crop',
-
- kKeyRotation = 'rotA', // int32_t (angle in degrees)
- kKeyIFramesInterval = 'ifiv', // int32_t
- kKeyStride = 'strd', // int32_t
- kKeySliceHeight = 'slht', // int32_t
- kKeyChannelCount = '#chn', // int32_t
- kKeyChannelMask = 'chnm', // int32_t
- kKeySampleRate = 'srte', // int32_t (audio sampling rate Hz)
- kKeyPcmEncoding = 'PCMe', // int32_t (audio encoding enum)
- kKeyFrameRate = 'frmR', // int32_t (video frame rate fps)
- kKeyBitRate = 'brte', // int32_t (bps)
- kKeyMaxBitRate = 'mxBr', // int32_t (bps)
- kKeyStreamHeader = 'stHd', // raw data
- kKeyESDS = 'esds', // raw data
- kKeyAACProfile = 'aacp', // int32_t
- kKeyAVCC = 'avcc', // raw data
- kKeyHVCC = 'hvcc', // raw data
- kKeyThumbnailHVCC = 'thvc', // raw data
- kKeyD263 = 'd263', // raw data
- kKeyVorbisInfo = 'vinf', // raw data
- kKeyVorbisBooks = 'vboo', // raw data
- kKeyOpusHeader = 'ohdr', // raw data
- kKeyOpusCodecDelay = 'ocod', // uint64_t (codec delay in ns)
- kKeyOpusSeekPreRoll = 'ospr', // uint64_t (seek preroll in ns)
- kKeyFlacMetadata = 'flMd', // raw data
- kKeyVp9CodecPrivate = 'vp9p', // raw data (vp9 csd information)
- kKeyWantsNALFragments = 'NALf',
- kKeyIsSyncFrame = 'sync', // int32_t (bool)
- kKeyIsCodecConfig = 'conf', // int32_t (bool)
- kKeyTime = 'time', // int64_t (usecs)
- kKeyDecodingTime = 'decT', // int64_t (decoding timestamp in usecs)
- kKeyNTPTime = 'ntpT', // uint64_t (ntp-timestamp)
- kKeyTargetTime = 'tarT', // int64_t (usecs)
- kKeyDriftTime = 'dftT', // int64_t (usecs)
- kKeyAnchorTime = 'ancT', // int64_t (usecs)
- kKeyDuration = 'dura', // int64_t (usecs)
- kKeyPixelFormat = 'pixf', // int32_t
- kKeyColorFormat = 'colf', // int32_t
- kKeyColorSpace = 'cols', // int32_t
- kKeyPlatformPrivate = 'priv', // pointer
- kKeyDecoderComponent = 'decC', // cstring
- kKeyBufferID = 'bfID',
- kKeyMaxInputSize = 'inpS',
- kKeyMaxWidth = 'maxW',
- kKeyMaxHeight = 'maxH',
- kKeyThumbnailTime = 'thbT', // int64_t (usecs)
- kKeyTrackID = 'trID',
- kKeyIsDRM = 'idrm', // int32_t (bool)
- kKeyEncoderDelay = 'encd', // int32_t (frames)
- kKeyEncoderPadding = 'encp', // int32_t (frames)
-
- kKeyAlbum = 'albu', // cstring
- kKeyArtist = 'arti', // cstring
- kKeyAlbumArtist = 'aart', // cstring
- kKeyComposer = 'comp', // cstring
- kKeyGenre = 'genr', // cstring
- kKeyTitle = 'titl', // cstring
- kKeyYear = 'year', // cstring
- kKeyAlbumArt = 'albA', // compressed image data
- kKeyAlbumArtMIME = 'alAM', // cstring
- kKeyAuthor = 'auth', // cstring
- kKeyCDTrackNumber = 'cdtr', // cstring
- kKeyDiscNumber = 'dnum', // cstring
- kKeyDate = 'date', // cstring
- kKeyWriter = 'writ', // cstring
- kKeyCompilation = 'cpil', // cstring
- kKeyLocation = 'loc ', // cstring
- kKeyTimeScale = 'tmsl', // int32_t
- kKeyCaptureFramerate = 'capF', // float (capture fps)
-
- // video profile and level
- kKeyVideoProfile = 'vprf', // int32_t
- kKeyVideoLevel = 'vlev', // int32_t
-
- // Set this key to enable authoring files in 64-bit offset
- kKey64BitFileOffset = 'fobt', // int32_t (bool)
- kKey2ByteNalLength = '2NAL', // int32_t (bool)
-
- // Identify the file output format for authoring
- // Please see <media/mediarecorder.h> for the supported
- // file output formats.
- kKeyFileType = 'ftyp', // int32_t
-
- // Track authoring progress status
- // kKeyTrackTimeStatus is used to track progress in elapsed time
- kKeyTrackTimeStatus = 'tktm', // int64_t
-
- kKeyRealTimeRecording = 'rtrc', // bool (int32_t)
- kKeyNumBuffers = 'nbbf', // int32_t
-
- // Ogg files can be tagged to be automatically looping...
- kKeyAutoLoop = 'autL', // bool (int32_t)
-
- kKeyValidSamples = 'valD', // int32_t
-
- kKeyIsUnreadable = 'unre', // bool (int32_t)
-
- // An indication that a video buffer has been rendered.
- kKeyRendered = 'rend', // bool (int32_t)
-
- // The language code for this media
- kKeyMediaLanguage = 'lang', // cstring
-
- // To store the timed text format data
- kKeyTextFormatData = 'text', // raw data
-
- kKeyRequiresSecureBuffers = 'secu', // bool (int32_t)
-
- kKeyIsADTS = 'adts', // bool (int32_t)
- kKeyAACAOT = 'aaot', // int32_t
-
- // If a MediaBuffer's data represents (at least partially) encrypted
- // data, the following fields aid in decryption.
- // The data can be thought of as pairs of plain and encrypted data
- // fragments, i.e. plain and encrypted data alternate.
- // The first fragment is by convention plain data (if that's not the
- // case, simply specify plain fragment size of 0).
- // kKeyEncryptedSizes and kKeyPlainSizes each map to an array of
- // size_t values. The sum total of all size_t values of both arrays
- // must equal the amount of data (i.e. MediaBuffer's range_length()).
- // If both arrays are present, they must be of the same size.
- // If only encrypted sizes are present it is assumed that all
- // plain sizes are 0, i.e. all fragments are encrypted.
- // To programmatically set these array, use the MetaData::setData API, i.e.
- // const size_t encSizes[];
- // meta->setData(
- // kKeyEncryptedSizes, 0 /* type */, encSizes, sizeof(encSizes));
- // A plain sizes array by itself makes no sense.
- kKeyEncryptedSizes = 'encr', // size_t[]
- kKeyPlainSizes = 'plai', // size_t[]
- kKeyCryptoKey = 'cryK', // uint8_t[16]
- kKeyCryptoIV = 'cryI', // uint8_t[16]
- kKeyCryptoMode = 'cryM', // int32_t
-
- kKeyCryptoDefaultIVSize = 'cryS', // int32_t
-
- kKeyPssh = 'pssh', // raw data
- kKeyCASystemID = 'caid', // int32_t
- kKeyCASessionID = 'seid', // raw data
-
- // Please see MediaFormat.KEY_IS_AUTOSELECT.
- kKeyTrackIsAutoselect = 'auto', // bool (int32_t)
- // Please see MediaFormat.KEY_IS_DEFAULT.
- kKeyTrackIsDefault = 'dflt', // bool (int32_t)
- // Similar to MediaFormat.KEY_IS_FORCED_SUBTITLE but pertains to av tracks as well.
- kKeyTrackIsForced = 'frcd', // bool (int32_t)
-
- // H264 supplemental enhancement information offsets/sizes
- kKeySEI = 'sei ', // raw data
-
- // MPEG user data offsets
- kKeyMpegUserData = 'mpud', // size_t[]
-
- // Size of NALU length in mkv/mp4
- kKeyNalLengthSize = 'nals', // int32_t
-
- // HDR related
- kKeyHdrStaticInfo = 'hdrS', // HDRStaticInfo
-
- // color aspects
- kKeyColorRange = 'cRng', // int32_t, color range, value defined by ColorAspects.Range
- kKeyColorPrimaries = 'cPrm', // int32_t,
- // color Primaries, value defined by ColorAspects.Primaries
- kKeyTransferFunction = 'tFun', // int32_t,
- // 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
-
- kKeyGridWidth = 'grdW', // int32_t, HEIF grid width
- kKeyGridHeight = 'grdH', // int32_t, HEIF grid height
- kKeyGridRows = 'grdR', // int32_t, HEIF grid rows
- kKeyGridCols = 'grdC', // int32_t, HEIF grid columns
- kKeyIccProfile = 'prof', // raw data, ICC prifile data
- kKeyIsPrimaryImage = 'prim', // bool (int32_t), image track is the primary image
- kKeyFrameCount = 'nfrm', // int32_t, total number of frame in video track
-};
-
-enum {
- kTypeESDS = 'esds',
- kTypeAVCC = 'avcc',
- kTypeHVCC = 'hvcc',
- kTypeD263 = 'd263',
-};
-
-class MetaData final : public RefBase {
+class MetaData final : public MetaDataBase, public RefBase {
public:
MetaData();
MetaData(const MetaData &from);
-
- enum Type {
- TYPE_NONE = 'none',
- TYPE_C_STRING = 'cstr',
- TYPE_INT32 = 'in32',
- TYPE_INT64 = 'in64',
- TYPE_FLOAT = 'floa',
- TYPE_POINTER = 'ptr ',
- TYPE_RECT = 'rect',
- };
-
- void clear();
- bool remove(uint32_t key);
-
- bool setCString(uint32_t key, const char *value);
- bool setInt32(uint32_t key, int32_t value);
- bool setInt64(uint32_t key, int64_t value);
- bool setFloat(uint32_t key, float value);
- bool setPointer(uint32_t key, void *value);
-
- bool setRect(
- uint32_t key,
- int32_t left, int32_t top,
- int32_t right, int32_t bottom);
-
- bool findCString(uint32_t key, const char **value);
- bool findInt32(uint32_t key, int32_t *value);
- bool findInt64(uint32_t key, int64_t *value);
- bool findFloat(uint32_t key, float *value);
- bool findPointer(uint32_t key, void **value);
-
- bool findRect(
- uint32_t key,
- int32_t *left, int32_t *top,
- int32_t *right, int32_t *bottom);
-
- bool setData(uint32_t key, uint32_t type, const void *data, size_t size);
-
- bool findData(uint32_t key, uint32_t *type,
- const void **data, size_t *size) const;
-
- bool hasData(uint32_t key) const;
-
- String8 toString() const;
- void dumpToLog() const;
+ MetaData(const MetaDataBase &from);
protected:
virtual ~MetaData();
private:
- friend class BpMediaSource;
friend class BnMediaSource;
+ friend class BpMediaSource;
friend class BpMediaExtractor;
- friend class BnMediaExtractor;
-
- status_t writeToParcel(Parcel &parcel);
- status_t updateFromParcel(const Parcel &parcel);
static sp<MetaData> createFromParcel(const Parcel &parcel);
- struct typed_data;
- struct Rect;
- struct MetaDataInternal;
- MetaDataInternal *mInternalData;
};
} // namespace android
diff --git a/media/libmediaextractor/include/media/stagefright/MetaDataBase.h b/media/libmediaextractor/include/media/stagefright/MetaDataBase.h
new file mode 100644
index 0000000..6010af4
--- /dev/null
+++ b/media/libmediaextractor/include/media/stagefright/MetaDataBase.h
@@ -0,0 +1,301 @@
+/*
+ * Copyright (C) 2009 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 META_DATA_BASE_H_
+
+#define META_DATA_BASE_H_
+
+#include <sys/types.h>
+
+#include <stdint.h>
+
+#include <utils/RefBase.h>
+#include <utils/String8.h>
+
+namespace android {
+
+// The following keys map to int32_t data unless indicated otherwise.
+enum {
+ kKeyMIMEType = 'mime', // cstring
+ kKeyWidth = 'widt', // int32_t, image pixel
+ kKeyHeight = 'heig', // int32_t, image pixel
+ kKeyDisplayWidth = 'dWid', // int32_t, display/presentation
+ kKeyDisplayHeight = 'dHgt', // int32_t, display/presentation
+ kKeySARWidth = 'sarW', // int32_t, sampleAspectRatio width
+ kKeySARHeight = 'sarH', // int32_t, sampleAspectRatio height
+ kKeyThumbnailWidth = 'thbW', // int32_t, thumbnail width
+ kKeyThumbnailHeight = 'thbH', // int32_t, thumbnail height
+
+ // a rectangle, if absent assumed to be (0, 0, width - 1, height - 1)
+ kKeyCropRect = 'crop',
+
+ kKeyRotation = 'rotA', // int32_t (angle in degrees)
+ kKeyIFramesInterval = 'ifiv', // int32_t
+ kKeyStride = 'strd', // int32_t
+ kKeySliceHeight = 'slht', // int32_t
+ kKeyChannelCount = '#chn', // int32_t
+ kKeyChannelMask = 'chnm', // int32_t
+ kKeySampleRate = 'srte', // int32_t (audio sampling rate Hz)
+ kKeyPcmEncoding = 'PCMe', // int32_t (audio encoding enum)
+ kKeyFrameRate = 'frmR', // int32_t (video frame rate fps)
+ kKeyBitRate = 'brte', // int32_t (bps)
+ kKeyMaxBitRate = 'mxBr', // int32_t (bps)
+ kKeyStreamHeader = 'stHd', // raw data
+ kKeyESDS = 'esds', // raw data
+ kKeyAACProfile = 'aacp', // int32_t
+ kKeyAVCC = 'avcc', // raw data
+ kKeyHVCC = 'hvcc', // raw data
+ kKeyThumbnailHVCC = 'thvc', // raw data
+ kKeyD263 = 'd263', // raw data
+ kKeyVorbisInfo = 'vinf', // raw data
+ kKeyVorbisBooks = 'vboo', // raw data
+ kKeyOpusHeader = 'ohdr', // raw data
+ kKeyOpusCodecDelay = 'ocod', // uint64_t (codec delay in ns)
+ kKeyOpusSeekPreRoll = 'ospr', // uint64_t (seek preroll in ns)
+ kKeyFlacMetadata = 'flMd', // raw data
+ kKeyVp9CodecPrivate = 'vp9p', // raw data (vp9 csd information)
+ kKeyWantsNALFragments = 'NALf',
+ kKeyIsSyncFrame = 'sync', // int32_t (bool)
+ kKeyIsCodecConfig = 'conf', // int32_t (bool)
+ kKeyTime = 'time', // int64_t (usecs)
+ kKeyDecodingTime = 'decT', // int64_t (decoding timestamp in usecs)
+ kKeyNTPTime = 'ntpT', // uint64_t (ntp-timestamp)
+ kKeyTargetTime = 'tarT', // int64_t (usecs)
+ kKeyDriftTime = 'dftT', // int64_t (usecs)
+ kKeyAnchorTime = 'ancT', // int64_t (usecs)
+ kKeyDuration = 'dura', // int64_t (usecs)
+ kKeyPixelFormat = 'pixf', // int32_t
+ kKeyColorFormat = 'colf', // int32_t
+ kKeyColorSpace = 'cols', // int32_t
+ kKeyPlatformPrivate = 'priv', // pointer
+ kKeyDecoderComponent = 'decC', // cstring
+ kKeyBufferID = 'bfID',
+ kKeyMaxInputSize = 'inpS',
+ kKeyMaxWidth = 'maxW',
+ kKeyMaxHeight = 'maxH',
+ kKeyThumbnailTime = 'thbT', // int64_t (usecs)
+ kKeyTrackID = 'trID',
+ kKeyIsDRM = 'idrm', // int32_t (bool)
+ kKeyEncoderDelay = 'encd', // int32_t (frames)
+ kKeyEncoderPadding = 'encp', // int32_t (frames)
+
+ kKeyAlbum = 'albu', // cstring
+ kKeyArtist = 'arti', // cstring
+ kKeyAlbumArtist = 'aart', // cstring
+ kKeyComposer = 'comp', // cstring
+ kKeyGenre = 'genr', // cstring
+ kKeyTitle = 'titl', // cstring
+ kKeyYear = 'year', // cstring
+ kKeyAlbumArt = 'albA', // compressed image data
+ kKeyAlbumArtMIME = 'alAM', // cstring
+ kKeyAuthor = 'auth', // cstring
+ kKeyCDTrackNumber = 'cdtr', // cstring
+ kKeyDiscNumber = 'dnum', // cstring
+ kKeyDate = 'date', // cstring
+ kKeyWriter = 'writ', // cstring
+ kKeyCompilation = 'cpil', // cstring
+ kKeyLocation = 'loc ', // cstring
+ kKeyTimeScale = 'tmsl', // int32_t
+ kKeyCaptureFramerate = 'capF', // float (capture fps)
+
+ // video profile and level
+ kKeyVideoProfile = 'vprf', // int32_t
+ kKeyVideoLevel = 'vlev', // int32_t
+
+ // Set this key to enable authoring files in 64-bit offset
+ kKey64BitFileOffset = 'fobt', // int32_t (bool)
+ kKey2ByteNalLength = '2NAL', // int32_t (bool)
+
+ // Identify the file output format for authoring
+ // Please see <media/mediarecorder.h> for the supported
+ // file output formats.
+ kKeyFileType = 'ftyp', // int32_t
+
+ // Track authoring progress status
+ // kKeyTrackTimeStatus is used to track progress in elapsed time
+ kKeyTrackTimeStatus = 'tktm', // int64_t
+
+ kKeyRealTimeRecording = 'rtrc', // bool (int32_t)
+ kKeyNumBuffers = 'nbbf', // int32_t
+
+ // Ogg files can be tagged to be automatically looping...
+ kKeyAutoLoop = 'autL', // bool (int32_t)
+
+ kKeyValidSamples = 'valD', // int32_t
+
+ kKeyIsUnreadable = 'unre', // bool (int32_t)
+
+ // An indication that a video buffer has been rendered.
+ kKeyRendered = 'rend', // bool (int32_t)
+
+ // The language code for this media
+ kKeyMediaLanguage = 'lang', // cstring
+
+ // To store the timed text format data
+ kKeyTextFormatData = 'text', // raw data
+
+ kKeyRequiresSecureBuffers = 'secu', // bool (int32_t)
+
+ kKeyIsADTS = 'adts', // bool (int32_t)
+ kKeyAACAOT = 'aaot', // int32_t
+
+ // If a MediaBuffer's data represents (at least partially) encrypted
+ // data, the following fields aid in decryption.
+ // The data can be thought of as pairs of plain and encrypted data
+ // fragments, i.e. plain and encrypted data alternate.
+ // The first fragment is by convention plain data (if that's not the
+ // case, simply specify plain fragment size of 0).
+ // kKeyEncryptedSizes and kKeyPlainSizes each map to an array of
+ // size_t values. The sum total of all size_t values of both arrays
+ // must equal the amount of data (i.e. MediaBuffer's range_length()).
+ // If both arrays are present, they must be of the same size.
+ // If only encrypted sizes are present it is assumed that all
+ // plain sizes are 0, i.e. all fragments are encrypted.
+ // To programmatically set these array, use the MetaDataBase::setData API, i.e.
+ // const size_t encSizes[];
+ // meta->setData(
+ // kKeyEncryptedSizes, 0 /* type */, encSizes, sizeof(encSizes));
+ // A plain sizes array by itself makes no sense.
+ kKeyEncryptedSizes = 'encr', // size_t[]
+ kKeyPlainSizes = 'plai', // size_t[]
+ kKeyCryptoKey = 'cryK', // uint8_t[16]
+ kKeyCryptoIV = 'cryI', // uint8_t[16]
+ kKeyCryptoMode = 'cryM', // int32_t
+
+ kKeyCryptoDefaultIVSize = 'cryS', // int32_t
+
+ kKeyPssh = 'pssh', // raw data
+ kKeyCASystemID = 'caid', // int32_t
+ kKeyCASessionID = 'seid', // raw data
+
+ // Please see MediaFormat.KEY_IS_AUTOSELECT.
+ kKeyTrackIsAutoselect = 'auto', // bool (int32_t)
+ // Please see MediaFormat.KEY_IS_DEFAULT.
+ kKeyTrackIsDefault = 'dflt', // bool (int32_t)
+ // Similar to MediaFormat.KEY_IS_FORCED_SUBTITLE but pertains to av tracks as well.
+ kKeyTrackIsForced = 'frcd', // bool (int32_t)
+
+ // H264 supplemental enhancement information offsets/sizes
+ kKeySEI = 'sei ', // raw data
+
+ // MPEG user data offsets
+ kKeyMpegUserData = 'mpud', // size_t[]
+
+ // Size of NALU length in mkv/mp4
+ kKeyNalLengthSize = 'nals', // int32_t
+
+ // HDR related
+ kKeyHdrStaticInfo = 'hdrS', // HDRStaticInfo
+
+ // color aspects
+ kKeyColorRange = 'cRng', // int32_t, color range, value defined by ColorAspects.Range
+ kKeyColorPrimaries = 'cPrm', // int32_t,
+ // color Primaries, value defined by ColorAspects.Primaries
+ kKeyTransferFunction = 'tFun', // int32_t,
+ // 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
+
+ kKeyGridWidth = 'grdW', // int32_t, HEIF grid width
+ kKeyGridHeight = 'grdH', // int32_t, HEIF grid height
+ kKeyGridRows = 'grdR', // int32_t, HEIF grid rows
+ kKeyGridCols = 'grdC', // int32_t, HEIF grid columns
+ kKeyIccProfile = 'prof', // raw data, ICC prifile data
+ kKeyIsPrimaryImage = 'prim', // bool (int32_t), image track is the primary image
+ kKeyFrameCount = 'nfrm', // int32_t, total number of frame in video track
+};
+
+enum {
+ kTypeESDS = 'esds',
+ kTypeAVCC = 'avcc',
+ kTypeHVCC = 'hvcc',
+ kTypeD263 = 'd263',
+};
+
+class Parcel;
+
+class MetaDataBase {
+public:
+ MetaDataBase();
+ MetaDataBase(const MetaDataBase &from);
+ MetaDataBase& operator = (const MetaDataBase &);
+
+ virtual ~MetaDataBase();
+
+ enum Type {
+ TYPE_NONE = 'none',
+ TYPE_C_STRING = 'cstr',
+ TYPE_INT32 = 'in32',
+ TYPE_INT64 = 'in64',
+ TYPE_FLOAT = 'floa',
+ TYPE_POINTER = 'ptr ',
+ TYPE_RECT = 'rect',
+ };
+
+ void clear();
+ bool remove(uint32_t key);
+
+ bool setCString(uint32_t key, const char *value);
+ bool setInt32(uint32_t key, int32_t value);
+ bool setInt64(uint32_t key, int64_t value);
+ bool setFloat(uint32_t key, float value);
+ bool setPointer(uint32_t key, void *value);
+
+ bool setRect(
+ uint32_t key,
+ int32_t left, int32_t top,
+ int32_t right, int32_t bottom);
+
+ bool findCString(uint32_t key, const char **value) const;
+ bool findInt32(uint32_t key, int32_t *value) const;
+ bool findInt64(uint32_t key, int64_t *value) const;
+ bool findFloat(uint32_t key, float *value) const;
+ bool findPointer(uint32_t key, void **value) const;
+
+ bool findRect(
+ uint32_t key,
+ int32_t *left, int32_t *top,
+ int32_t *right, int32_t *bottom) const;
+
+ bool setData(uint32_t key, uint32_t type, const void *data, size_t size);
+
+ bool findData(uint32_t key, uint32_t *type,
+ const void **data, size_t *size) const;
+
+ bool hasData(uint32_t key) const;
+
+ String8 toString() const;
+ void dumpToLog() const;
+
+private:
+ friend class BpMediaSource;
+ friend class BnMediaSource;
+ friend class BnMediaExtractor;
+ friend class MetaData;
+
+ struct typed_data;
+ struct Rect;
+ struct MetaDataInternal;
+ MetaDataInternal *mInternalData;
+ status_t writeToParcel(Parcel &parcel);
+ status_t updateFromParcel(const Parcel &parcel);
+};
+
+} // namespace android
+
+#endif // META_DATA_H_
diff --git a/media/libmediaplayer2/Android.bp b/media/libmediaplayer2/Android.bp
index 17fa01c..1fa8789 100644
--- a/media/libmediaplayer2/Android.bp
+++ b/media/libmediaplayer2/Android.bp
@@ -9,7 +9,7 @@
srcs: [
"JAudioTrack.cpp",
- "MediaPlayer2Manager.cpp",
+ "MediaPlayer2AudioOutput.cpp",
"mediaplayer2.cpp",
],
diff --git a/media/libmediaplayer2/JAudioTrack.cpp b/media/libmediaplayer2/JAudioTrack.cpp
index 6d9605a..ac0cc57 100644
--- a/media/libmediaplayer2/JAudioTrack.cpp
+++ b/media/libmediaplayer2/JAudioTrack.cpp
@@ -32,6 +32,8 @@
uint32_t sampleRate, // AudioFormat && bufferSizeInBytes
audio_format_t format, // AudioFormat && bufferSizeInBytes
audio_channel_mask_t channelMask, // AudioFormat && bufferSizeInBytes
+ callback_t cbf, // Offload
+ void* user, // Offload
size_t frameCount, // bufferSizeInBytes
audio_session_t sessionId, // AudioTrack
const audio_attributes_t* pAttributes, // AudioAttributes
@@ -90,8 +92,27 @@
jBuilderObj = env->CallObjectMethod(jBuilderObj, jSetSessionId, sessionId);
}
+ if (cbf != NULL) {
+ jmethodID jSetOffloadedPlayback = env->GetMethodID(jBuilderCls, "setOffloadedPlayback",
+ "(Z)Landroid/media/AudioTrack$Builder;");
+ jBuilderObj = env->CallObjectMethod(jBuilderObj, jSetOffloadedPlayback, true);
+ mFlags = AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD;
+ }
+
jmethodID jBuild = env->GetMethodID(jBuilderCls, "build", "()Landroid/media/AudioTrack;");
mAudioTrackObj = env->CallObjectMethod(jBuilderObj, jBuild);
+
+ if (cbf != NULL) {
+ // Set offload mode callback
+ jobject jStreamEventCallbackObj = createStreamEventCallback(cbf, user);
+ jobject jExecutorObj = createCallbackExecutor();
+ jmethodID jSetStreamEventCallback = env->GetMethodID(
+ jAudioTrackCls,
+ "setStreamEventCallback",
+ "(Ljava/util/concurrent/Executor;Landroid/media/AudioTrack$StreamEventCallback;)V");
+ env->CallVoidMethod(
+ mAudioTrackObj, jSetStreamEventCallback, jExecutorObj, jStreamEventCallbackObj);
+ }
}
JAudioTrack::~JAudioTrack() {
@@ -160,6 +181,11 @@
return true;
}
+status_t JAudioTrack::getTimestamp(ExtendedTimestamp *timestamp __unused) {
+ // TODO: Implement this after appropriate Java AudioTrack method is available.
+ return NO_ERROR;
+}
+
status_t JAudioTrack::setPlaybackRate(const AudioPlaybackRate &playbackRate) {
// TODO: existing native AudioTrack returns INVALID_OPERATION on offload/direct/fast tracks.
// Should we do the same thing?
@@ -442,6 +468,80 @@
return routedDeviceId;
}
+audio_session_t JAudioTrack::getAudioSessionId() {
+ JNIEnv *env = AndroidRuntime::getJNIEnv();
+ jmethodID jGetAudioSessionId = env->GetMethodID(mAudioTrackCls, "getAudioSessionId", "()I");
+ jint sessionId = env->CallIntMethod(mAudioTrackObj, jGetAudioSessionId);
+ return (audio_session_t) sessionId;
+}
+
+status_t JAudioTrack::setOutputDevice(audio_port_handle_t deviceId) {
+ JNIEnv *env = AndroidRuntime::getJNIEnv();
+ jclass jMP2ImplCls = env->FindClass("android/media/MediaPlayer2Impl");
+ jmethodID jSetAudioOutputDeviceById = env->GetMethodID(
+ jMP2ImplCls, "setAudioOutputDeviceById", "(Landroid/media/AudioTrack;I)Z");
+ jboolean result = env->CallStaticBooleanMethod(
+ jMP2ImplCls, jSetAudioOutputDeviceById, mAudioTrackObj, deviceId);
+ return result == true ? NO_ERROR : BAD_VALUE;
+}
+
+status_t JAudioTrack::pendingDuration(int32_t *msec) {
+ if (msec == nullptr) {
+ return BAD_VALUE;
+ }
+
+ bool isPurePcmData = audio_is_linear_pcm(format()) && (getFlags() & AUDIO_FLAG_HW_AV_SYNC) == 0;
+ if (!isPurePcmData) {
+ return INVALID_OPERATION;
+ }
+
+ // TODO: Need to know the difference btw. client and server time.
+ // If getTimestamp(ExtendedTimestamp) is ready, and un-comment below and modify appropriately.
+ // (copied from AudioTrack.cpp)
+
+// ExtendedTimestamp ets;
+// ExtendedTimestamp::LOCATION location = ExtendedTimestamp::LOCATION_SERVER;
+// if (getTimestamp_l(&ets) == OK && ets.mTimeNs[location] > 0) {
+// int64_t diff = ets.mPosition[ExtendedTimestamp::LOCATION_CLIENT]
+// - ets.mPosition[location];
+// if (diff < 0) {
+// *msec = 0;
+// } else {
+// // ms is the playback time by frames
+// int64_t ms = (int64_t)((double)diff * 1000 /
+// ((double)mSampleRate * mPlaybackRate.mSpeed));
+// // clockdiff is the timestamp age (negative)
+// int64_t clockdiff = (mState != STATE_ACTIVE) ? 0 :
+// ets.mTimeNs[location]
+// + ets.mTimebaseOffset[ExtendedTimestamp::TIMEBASE_MONOTONIC]
+// - systemTime(SYSTEM_TIME_MONOTONIC);
+//
+// //ALOGV("ms: %lld clockdiff: %lld", (long long)ms, (long long)clockdiff);
+// static const int NANOS_PER_MILLIS = 1000000;
+// *msec = (int32_t)(ms + clockdiff / NANOS_PER_MILLIS);
+// }
+// return NO_ERROR;
+// }
+
+ return NO_ERROR;
+}
+
+status_t JAudioTrack::addAudioDeviceCallback(
+ const sp<AudioSystem::AudioDeviceCallback>& callback __unused) {
+ // TODO: Implement this after appropriate Java AudioTrack method is available.
+ return NO_ERROR;
+}
+
+status_t JAudioTrack::removeAudioDeviceCallback(
+ const sp<AudioSystem::AudioDeviceCallback>& callback __unused) {
+ // TODO: Implement this after appropriate Java AudioTrack method is available.
+ return NO_ERROR;
+}
+
+/////////////////////////////////////////////////////////////
+/// Private method begins ///
+/////////////////////////////////////////////////////////////
+
jobject JAudioTrack::createVolumeShaperConfigurationObj(
const sp<media::VolumeShaper::Configuration>& config) {
@@ -546,6 +646,24 @@
return env->CallObjectMethod(jBuilderObj, jBuild);
}
+jobject JAudioTrack::createStreamEventCallback(callback_t cbf, void* user) {
+ JNIEnv *env = AndroidRuntime::getJNIEnv();
+ jclass jCallbackCls = env->FindClass("android/media/MediaPlayer2Impl$StreamEventCallback");
+ jmethodID jCallbackCtor = env->GetMethodID(jCallbackCls, "<init>", "(JJJ)V");
+ jobject jCallbackObj = env->NewObject(jCallbackCls, jCallbackCtor, this, cbf, user);
+ return jCallbackObj;
+}
+
+jobject JAudioTrack::createCallbackExecutor() {
+ JNIEnv *env = AndroidRuntime::getJNIEnv();
+ jclass jExecutorsCls = env->FindClass("java/util/concurrent/Executors");
+ jmethodID jNewSingleThreadExecutor = env->GetStaticMethodID(jExecutorsCls,
+ "newSingleThreadExecutor", "()Ljava/util/concurrent/ExecutorService;");
+ jobject jSingleThreadExecutorObj =
+ env->CallStaticObjectMethod(jExecutorsCls, jNewSingleThreadExecutor);
+ return jSingleThreadExecutorObj;
+}
+
status_t JAudioTrack::javaToNativeStatus(int javaStatus) {
switch (javaStatus) {
case AUDIO_JAVA_SUCCESS:
diff --git a/media/libmediaplayer2/MediaPlayer2AudioOutput.cpp b/media/libmediaplayer2/MediaPlayer2AudioOutput.cpp
new file mode 100644
index 0000000..a8e1d1f
--- /dev/null
+++ b/media/libmediaplayer2/MediaPlayer2AudioOutput.cpp
@@ -0,0 +1,727 @@
+/*
+**
+** Copyright 2018, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "MediaPlayer2AudioOutput"
+#include <mediaplayer2/MediaPlayer2AudioOutput.h>
+
+#include <cutils/properties.h> // for property_get
+#include <utils/Log.h>
+
+#include <media/AudioPolicyHelper.h>
+#include <media/AudioTrack.h>
+#include <media/stagefright/foundation/ADebug.h>
+
+namespace {
+
+const float kMaxRequiredSpeed = 8.0f; // for PCM tracks allow up to 8x speedup.
+
+} // anonymous namespace
+
+namespace android {
+
+// TODO: Find real cause of Audio/Video delay in PV framework and remove this workaround
+/* static */ int MediaPlayer2AudioOutput::mMinBufferCount = 4;
+/* static */ bool MediaPlayer2AudioOutput::mIsOnEmulator = false;
+
+status_t MediaPlayer2AudioOutput::dump(int fd, const Vector<String16>& args) const {
+ const size_t SIZE = 256;
+ char buffer[SIZE];
+ String8 result;
+
+ result.append(" MediaPlayer2AudioOutput\n");
+ snprintf(buffer, 255, " stream type(%d), left - right volume(%f, %f)\n",
+ mStreamType, mLeftVolume, mRightVolume);
+ result.append(buffer);
+ snprintf(buffer, 255, " msec per frame(%f), latency (%d)\n",
+ mMsecsPerFrame, (mTrack != 0) ? mTrack->latency() : -1);
+ result.append(buffer);
+ snprintf(buffer, 255, " aux effect id(%d), send level (%f)\n",
+ mAuxEffectId, mSendLevel);
+ result.append(buffer);
+
+ ::write(fd, result.string(), result.size());
+ if (mTrack != 0) {
+ mTrack->dump(fd, args);
+ }
+ return NO_ERROR;
+}
+
+MediaPlayer2AudioOutput::MediaPlayer2AudioOutput(audio_session_t sessionId, uid_t uid, int pid,
+ const audio_attributes_t* attr, const sp<AudioSystem::AudioDeviceCallback>& deviceCallback)
+ : mCallback(NULL),
+ mCallbackCookie(NULL),
+ mCallbackData(NULL),
+ mStreamType(AUDIO_STREAM_MUSIC),
+ mLeftVolume(1.0),
+ mRightVolume(1.0),
+ mPlaybackRate(AUDIO_PLAYBACK_RATE_DEFAULT),
+ mSampleRateHz(0),
+ mMsecsPerFrame(0),
+ mFrameSize(0),
+ mSessionId(sessionId),
+ mUid(uid),
+ mPid(pid),
+ mSendLevel(0.0),
+ mAuxEffectId(0),
+ mFlags(AUDIO_OUTPUT_FLAG_NONE),
+ mSelectedDeviceId(AUDIO_PORT_HANDLE_NONE),
+ mRoutedDeviceId(AUDIO_PORT_HANDLE_NONE),
+ mDeviceCallbackEnabled(false),
+ mDeviceCallback(deviceCallback) {
+ ALOGV("MediaPlayer2AudioOutput(%d)", sessionId);
+ if (attr != NULL) {
+ mAttributes = (audio_attributes_t *) calloc(1, sizeof(audio_attributes_t));
+ if (mAttributes != NULL) {
+ memcpy(mAttributes, attr, sizeof(audio_attributes_t));
+ mStreamType = audio_attributes_to_stream_type(attr);
+ }
+ } else {
+ mAttributes = NULL;
+ }
+
+ setMinBufferCount();
+}
+
+MediaPlayer2AudioOutput::~MediaPlayer2AudioOutput() {
+ close();
+ free(mAttributes);
+ delete mCallbackData;
+}
+
+//static
+void MediaPlayer2AudioOutput::setMinBufferCount() {
+ char value[PROPERTY_VALUE_MAX];
+ if (property_get("ro.kernel.qemu", value, 0)) {
+ mIsOnEmulator = true;
+ mMinBufferCount = 12; // to prevent systematic buffer underrun for emulator
+ }
+}
+
+// static
+bool MediaPlayer2AudioOutput::isOnEmulator() {
+ setMinBufferCount(); // benign race wrt other threads
+ return mIsOnEmulator;
+}
+
+// static
+int MediaPlayer2AudioOutput::getMinBufferCount() {
+ setMinBufferCount(); // benign race wrt other threads
+ return mMinBufferCount;
+}
+
+ssize_t MediaPlayer2AudioOutput::bufferSize() const {
+ Mutex::Autolock lock(mLock);
+ if (mTrack == 0) {
+ return NO_INIT;
+ }
+ return mTrack->frameCount() * mFrameSize;
+}
+
+ssize_t MediaPlayer2AudioOutput::frameCount() const {
+ Mutex::Autolock lock(mLock);
+ if (mTrack == 0) {
+ return NO_INIT;
+ }
+ return mTrack->frameCount();
+}
+
+ssize_t MediaPlayer2AudioOutput::channelCount() const {
+ Mutex::Autolock lock(mLock);
+ if (mTrack == 0) {
+ return NO_INIT;
+ }
+ return mTrack->channelCount();
+}
+
+ssize_t MediaPlayer2AudioOutput::frameSize() const {
+ Mutex::Autolock lock(mLock);
+ if (mTrack == 0) {
+ return NO_INIT;
+ }
+ return mFrameSize;
+}
+
+uint32_t MediaPlayer2AudioOutput::latency () const {
+ Mutex::Autolock lock(mLock);
+ if (mTrack == 0) {
+ return 0;
+ }
+ return mTrack->latency();
+}
+
+float MediaPlayer2AudioOutput::msecsPerFrame() const {
+ Mutex::Autolock lock(mLock);
+ return mMsecsPerFrame;
+}
+
+status_t MediaPlayer2AudioOutput::getPosition(uint32_t *position) const {
+ Mutex::Autolock lock(mLock);
+ if (mTrack == 0) {
+ return NO_INIT;
+ }
+ return mTrack->getPosition(position);
+}
+
+status_t MediaPlayer2AudioOutput::getTimestamp(AudioTimestamp &ts) const {
+ Mutex::Autolock lock(mLock);
+ if (mTrack == 0) {
+ return NO_INIT;
+ }
+ return mTrack->getTimestamp(ts);
+}
+
+// TODO: Remove unnecessary calls to getPlayedOutDurationUs()
+// as it acquires locks and may query the audio driver.
+//
+// Some calls could conceivably retrieve extrapolated data instead of
+// accessing getTimestamp() or getPosition() every time a data buffer with
+// a media time is received.
+//
+// Calculate duration of played samples if played at normal rate (i.e., 1.0).
+int64_t MediaPlayer2AudioOutput::getPlayedOutDurationUs(int64_t nowUs) const {
+ Mutex::Autolock lock(mLock);
+ if (mTrack == 0 || mSampleRateHz == 0) {
+ return 0;
+ }
+
+ uint32_t numFramesPlayed;
+ int64_t numFramesPlayedAtUs;
+ AudioTimestamp ts;
+
+ status_t res = mTrack->getTimestamp(ts);
+ if (res == OK) { // case 1: mixing audio tracks and offloaded tracks.
+ numFramesPlayed = ts.mPosition;
+ numFramesPlayedAtUs = ts.mTime.tv_sec * 1000000LL + ts.mTime.tv_nsec / 1000;
+ //ALOGD("getTimestamp: OK %d %lld", numFramesPlayed, (long long)numFramesPlayedAtUs);
+ } else if (res == WOULD_BLOCK) { // case 2: transitory state on start of a new track
+ numFramesPlayed = 0;
+ numFramesPlayedAtUs = nowUs;
+ //ALOGD("getTimestamp: WOULD_BLOCK %d %lld",
+ // numFramesPlayed, (long long)numFramesPlayedAtUs);
+ } else { // case 3: transitory at new track or audio fast tracks.
+ res = mTrack->getPosition(&numFramesPlayed);
+ CHECK_EQ(res, (status_t)OK);
+ numFramesPlayedAtUs = nowUs;
+ numFramesPlayedAtUs += 1000LL * mTrack->latency() / 2; /* XXX */
+ //ALOGD("getPosition: %u %lld", numFramesPlayed, (long long)numFramesPlayedAtUs);
+ }
+
+ // CHECK_EQ(numFramesPlayed & (1 << 31), 0); // can't be negative until 12.4 hrs, test
+ // TODO: remove the (int32_t) casting below as it may overflow at 12.4 hours.
+ int64_t durationUs = (int64_t)((int32_t)numFramesPlayed * 1000000LL / mSampleRateHz)
+ + nowUs - numFramesPlayedAtUs;
+ if (durationUs < 0) {
+ // Occurs when numFramesPlayed position is very small and the following:
+ // (1) In case 1, the time nowUs is computed before getTimestamp() is called and
+ // numFramesPlayedAtUs is greater than nowUs by time more than numFramesPlayed.
+ // (2) In case 3, using getPosition and adding mAudioSink->latency() to
+ // numFramesPlayedAtUs, by a time amount greater than numFramesPlayed.
+ //
+ // Both of these are transitory conditions.
+ ALOGV("getPlayedOutDurationUs: negative duration %lld set to zero", (long long)durationUs);
+ durationUs = 0;
+ }
+ ALOGV("getPlayedOutDurationUs(%lld) nowUs(%lld) frames(%u) framesAt(%lld)",
+ (long long)durationUs, (long long)nowUs,
+ numFramesPlayed, (long long)numFramesPlayedAtUs);
+ return durationUs;
+}
+
+status_t MediaPlayer2AudioOutput::getFramesWritten(uint32_t *frameswritten) const {
+ Mutex::Autolock lock(mLock);
+ if (mTrack == 0) {
+ return NO_INIT;
+ }
+ ExtendedTimestamp ets;
+ status_t status = mTrack->getTimestamp(&ets);
+ if (status == OK || status == WOULD_BLOCK) {
+ *frameswritten = (uint32_t)ets.mPosition[ExtendedTimestamp::LOCATION_CLIENT];
+ }
+ return status;
+}
+
+status_t MediaPlayer2AudioOutput::setParameters(const String8& keyValuePairs) {
+ Mutex::Autolock lock(mLock);
+ if (mTrack == 0) {
+ return NO_INIT;
+ }
+ return mTrack->setParameters(keyValuePairs);
+}
+
+String8 MediaPlayer2AudioOutput::getParameters(const String8& keys) {
+ Mutex::Autolock lock(mLock);
+ if (mTrack == 0) {
+ return String8::empty();
+ }
+ return mTrack->getParameters(keys);
+}
+
+void MediaPlayer2AudioOutput::setAudioAttributes(const audio_attributes_t * attributes) {
+ Mutex::Autolock lock(mLock);
+ if (attributes == NULL) {
+ free(mAttributes);
+ mAttributes = NULL;
+ } else {
+ if (mAttributes == NULL) {
+ mAttributes = (audio_attributes_t *) calloc(1, sizeof(audio_attributes_t));
+ }
+ memcpy(mAttributes, attributes, sizeof(audio_attributes_t));
+ mStreamType = audio_attributes_to_stream_type(attributes);
+ }
+}
+
+void MediaPlayer2AudioOutput::setAudioStreamType(audio_stream_type_t streamType) {
+ Mutex::Autolock lock(mLock);
+ // do not allow direct stream type modification if attributes have been set
+ if (mAttributes == NULL) {
+ mStreamType = streamType;
+ }
+}
+
+void MediaPlayer2AudioOutput::close_l() {
+ mTrack.clear();
+}
+
+status_t MediaPlayer2AudioOutput::open(
+ uint32_t sampleRate, int channelCount, audio_channel_mask_t channelMask,
+ audio_format_t format, int bufferCount,
+ AudioCallback cb, void *cookie,
+ audio_output_flags_t flags,
+ const audio_offload_info_t *offloadInfo,
+ bool doNotReconnect,
+ uint32_t suggestedFrameCount) {
+ ALOGV("open(%u, %d, 0x%x, 0x%x, %d, %d 0x%x)", sampleRate, channelCount, channelMask,
+ format, bufferCount, mSessionId, flags);
+
+ // offloading is only supported in callback mode for now.
+ // offloadInfo must be present if offload flag is set
+ if (((flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) != 0) &&
+ ((cb == NULL) || (offloadInfo == NULL))) {
+ return BAD_VALUE;
+ }
+
+ // compute frame count for the AudioTrack internal buffer
+ size_t frameCount;
+ if ((flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) != 0) {
+ frameCount = 0; // AudioTrack will get frame count from AudioFlinger
+ } else {
+ // try to estimate the buffer processing fetch size from AudioFlinger.
+ // framesPerBuffer is approximate and generally correct, except when it's not :-).
+ uint32_t afSampleRate;
+ size_t afFrameCount;
+ if (AudioSystem::getOutputFrameCount(&afFrameCount, mStreamType) != NO_ERROR) {
+ return NO_INIT;
+ }
+ if (AudioSystem::getOutputSamplingRate(&afSampleRate, mStreamType) != NO_ERROR) {
+ return NO_INIT;
+ }
+ const size_t framesPerBuffer =
+ (unsigned long long)sampleRate * afFrameCount / afSampleRate;
+
+ if (bufferCount == 0) {
+ // use suggestedFrameCount
+ bufferCount = (suggestedFrameCount + framesPerBuffer - 1) / framesPerBuffer;
+ }
+ // Check argument bufferCount against the mininum buffer count
+ if (bufferCount != 0 && bufferCount < mMinBufferCount) {
+ ALOGV("bufferCount (%d) increased to %d", bufferCount, mMinBufferCount);
+ bufferCount = mMinBufferCount;
+ }
+ // if frameCount is 0, then AudioTrack will get frame count from AudioFlinger
+ // which will be the minimum size permitted.
+ frameCount = bufferCount * framesPerBuffer;
+ }
+
+ if (channelMask == CHANNEL_MASK_USE_CHANNEL_ORDER) {
+ channelMask = audio_channel_out_mask_from_count(channelCount);
+ if (0 == channelMask) {
+ ALOGE("open() error, can\'t derive mask for %d audio channels", channelCount);
+ return NO_INIT;
+ }
+ }
+
+ Mutex::Autolock lock(mLock);
+ mCallback = cb;
+ mCallbackCookie = cookie;
+
+ sp<AudioTrack> t;
+ CallbackData *newcbd = NULL;
+
+ ALOGV("creating new AudioTrack");
+
+ if (mCallback != NULL) {
+ newcbd = new CallbackData(this);
+ t = new AudioTrack(
+ mStreamType,
+ sampleRate,
+ format,
+ channelMask,
+ frameCount,
+ flags,
+ CallbackWrapper,
+ newcbd,
+ 0, // notification frames
+ mSessionId,
+ AudioTrack::TRANSFER_CALLBACK,
+ offloadInfo,
+ mUid,
+ mPid,
+ mAttributes,
+ doNotReconnect,
+ 1.0f, // default value for maxRequiredSpeed
+ mSelectedDeviceId);
+ } else {
+ // TODO: Due to buffer memory concerns, we use a max target playback speed
+ // based on mPlaybackRate at the time of open (instead of kMaxRequiredSpeed),
+ // also clamping the target speed to 1.0 <= targetSpeed <= kMaxRequiredSpeed.
+ const float targetSpeed =
+ std::min(std::max(mPlaybackRate.mSpeed, 1.0f), kMaxRequiredSpeed);
+ ALOGW_IF(targetSpeed != mPlaybackRate.mSpeed,
+ "track target speed:%f clamped from playback speed:%f",
+ targetSpeed, mPlaybackRate.mSpeed);
+ t = new AudioTrack(
+ mStreamType,
+ sampleRate,
+ format,
+ channelMask,
+ frameCount,
+ flags,
+ NULL, // callback
+ NULL, // user data
+ 0, // notification frames
+ mSessionId,
+ AudioTrack::TRANSFER_DEFAULT,
+ NULL, // offload info
+ mUid,
+ mPid,
+ mAttributes,
+ doNotReconnect,
+ targetSpeed,
+ mSelectedDeviceId);
+ }
+
+ if ((t == 0) || (t->initCheck() != NO_ERROR)) {
+ ALOGE("Unable to create audio track");
+ delete newcbd;
+ // t goes out of scope, so reference count drops to zero
+ return NO_INIT;
+ } else {
+ // successful AudioTrack initialization implies a legacy stream type was generated
+ // from the audio attributes
+ mStreamType = t->streamType();
+ }
+
+ CHECK((t != NULL) && ((mCallback == NULL) || (newcbd != NULL)));
+
+ mCallbackData = newcbd;
+ ALOGV("setVolume");
+ t->setVolume(mLeftVolume, mRightVolume);
+
+ mSampleRateHz = sampleRate;
+ mFlags = flags;
+ mMsecsPerFrame = 1E3f / (mPlaybackRate.mSpeed * sampleRate);
+ mFrameSize = t->frameSize();
+ mTrack = t;
+
+ return updateTrack_l();
+}
+
+status_t MediaPlayer2AudioOutput::updateTrack_l() {
+ 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 ((mTrack->getFlags()
+ & (AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD | AUDIO_OUTPUT_FLAG_DIRECT)) == 0) {
+ res = mTrack->setPlaybackRate(mPlaybackRate);
+ if (res == NO_ERROR) {
+ mTrack->setAuxEffectSendLevel(mSendLevel);
+ res = mTrack->attachAuxEffect(mAuxEffectId);
+ }
+ }
+ mTrack->setOutputDevice(mSelectedDeviceId);
+ if (mDeviceCallbackEnabled) {
+ mTrack->addAudioDeviceCallback(mDeviceCallback.promote());
+ }
+ ALOGV("updateTrack_l() DONE status %d", res);
+ return res;
+}
+
+status_t MediaPlayer2AudioOutput::start() {
+ ALOGV("start");
+ Mutex::Autolock lock(mLock);
+ if (mCallbackData != NULL) {
+ mCallbackData->endTrackSwitch();
+ }
+ if (mTrack != 0) {
+ mTrack->setVolume(mLeftVolume, mRightVolume);
+ mTrack->setAuxEffectSendLevel(mSendLevel);
+ status_t status = mTrack->start();
+ return status;
+ }
+ return NO_INIT;
+}
+
+ssize_t MediaPlayer2AudioOutput::write(const void* buffer, size_t size, bool blocking) {
+ Mutex::Autolock lock(mLock);
+ LOG_ALWAYS_FATAL_IF(mCallback != NULL, "Don't call write if supplying a callback.");
+
+ //ALOGV("write(%p, %u)", buffer, size);
+ if (mTrack != 0) {
+ return mTrack->write(buffer, size, blocking);
+ }
+ return NO_INIT;
+}
+
+void MediaPlayer2AudioOutput::stop() {
+ ALOGV("stop");
+ Mutex::Autolock lock(mLock);
+ if (mTrack != 0) {
+ mTrack->stop();
+ }
+}
+
+void MediaPlayer2AudioOutput::flush() {
+ ALOGV("flush");
+ Mutex::Autolock lock(mLock);
+ if (mTrack != 0) {
+ mTrack->flush();
+ }
+}
+
+void MediaPlayer2AudioOutput::pause() {
+ ALOGV("pause");
+ Mutex::Autolock lock(mLock);
+ if (mTrack != 0) {
+ mTrack->pause();
+ }
+}
+
+void MediaPlayer2AudioOutput::close() {
+ ALOGV("close");
+ sp<AudioTrack> track;
+ {
+ Mutex::Autolock lock(mLock);
+ track = mTrack;
+ close_l(); // clears mTrack
+ }
+ // destruction of the track occurs outside of mutex.
+}
+
+void MediaPlayer2AudioOutput::setVolume(float left, float right) {
+ ALOGV("setVolume(%f, %f)", left, right);
+ Mutex::Autolock lock(mLock);
+ mLeftVolume = left;
+ mRightVolume = right;
+ if (mTrack != 0) {
+ mTrack->setVolume(left, right);
+ }
+}
+
+status_t MediaPlayer2AudioOutput::setPlaybackRate(const AudioPlaybackRate &rate) {
+ ALOGV("setPlaybackRate(%f %f %d %d)",
+ rate.mSpeed, rate.mPitch, rate.mFallbackMode, rate.mStretchMode);
+ Mutex::Autolock lock(mLock);
+ if (mTrack == 0) {
+ // remember rate so that we can set it when the track is opened
+ mPlaybackRate = rate;
+ return OK;
+ }
+ status_t res = mTrack->setPlaybackRate(rate);
+ if (res != NO_ERROR) {
+ return res;
+ }
+ // rate.mSpeed is always greater than 0 if setPlaybackRate succeeded
+ CHECK_GT(rate.mSpeed, 0.f);
+ mPlaybackRate = rate;
+ if (mSampleRateHz != 0) {
+ mMsecsPerFrame = 1E3f / (rate.mSpeed * mSampleRateHz);
+ }
+ return res;
+}
+
+status_t MediaPlayer2AudioOutput::getPlaybackRate(AudioPlaybackRate *rate) {
+ ALOGV("setPlaybackRate");
+ Mutex::Autolock lock(mLock);
+ if (mTrack == 0) {
+ return NO_INIT;
+ }
+ *rate = mTrack->getPlaybackRate();
+ return NO_ERROR;
+}
+
+status_t MediaPlayer2AudioOutput::setAuxEffectSendLevel(float level) {
+ ALOGV("setAuxEffectSendLevel(%f)", level);
+ Mutex::Autolock lock(mLock);
+ mSendLevel = level;
+ if (mTrack != 0) {
+ return mTrack->setAuxEffectSendLevel(level);
+ }
+ return NO_ERROR;
+}
+
+status_t MediaPlayer2AudioOutput::attachAuxEffect(int effectId) {
+ ALOGV("attachAuxEffect(%d)", effectId);
+ Mutex::Autolock lock(mLock);
+ mAuxEffectId = effectId;
+ if (mTrack != 0) {
+ return mTrack->attachAuxEffect(effectId);
+ }
+ return NO_ERROR;
+}
+
+status_t MediaPlayer2AudioOutput::setOutputDevice(audio_port_handle_t deviceId) {
+ ALOGV("setOutputDevice(%d)", deviceId);
+ Mutex::Autolock lock(mLock);
+ mSelectedDeviceId = deviceId;
+ if (mTrack != 0) {
+ return mTrack->setOutputDevice(deviceId);
+ }
+ return NO_ERROR;
+}
+
+status_t MediaPlayer2AudioOutput::getRoutedDeviceId(audio_port_handle_t* deviceId) {
+ ALOGV("getRoutedDeviceId");
+ Mutex::Autolock lock(mLock);
+ if (mTrack != 0) {
+ mRoutedDeviceId = mTrack->getRoutedDeviceId();
+ }
+ *deviceId = mRoutedDeviceId;
+ return NO_ERROR;
+}
+
+status_t MediaPlayer2AudioOutput::enableAudioDeviceCallback(bool enabled) {
+ ALOGV("enableAudioDeviceCallback, %d", enabled);
+ Mutex::Autolock lock(mLock);
+ mDeviceCallbackEnabled = enabled;
+ if (mTrack != 0) {
+ status_t status;
+ if (enabled) {
+ status = mTrack->addAudioDeviceCallback(mDeviceCallback.promote());
+ } else {
+ status = mTrack->removeAudioDeviceCallback(mDeviceCallback.promote());
+ }
+ return status;
+ }
+ return NO_ERROR;
+}
+
+// static
+void MediaPlayer2AudioOutput::CallbackWrapper(
+ int event, void *cookie, void *info) {
+ //ALOGV("callbackwrapper");
+ CallbackData *data = (CallbackData*)cookie;
+ // lock to ensure we aren't caught in the middle of a track switch.
+ data->lock();
+ MediaPlayer2AudioOutput *me = data->getOutput();
+ AudioTrack::Buffer *buffer = (AudioTrack::Buffer *)info;
+ if (me == NULL) {
+ // no output set, likely because the track was scheduled to be reused
+ // by another player, but the format turned out to be incompatible.
+ data->unlock();
+ if (buffer != NULL) {
+ buffer->size = 0;
+ }
+ return;
+ }
+
+ switch(event) {
+ case AudioTrack::EVENT_MORE_DATA: {
+ size_t actualSize = (*me->mCallback)(
+ me, buffer->raw, buffer->size, me->mCallbackCookie,
+ CB_EVENT_FILL_BUFFER);
+
+ // Log when no data is returned from the callback.
+ // (1) We may have no data (especially with network streaming sources).
+ // (2) We may have reached the EOS and the audio track is not stopped yet.
+ // Note that AwesomePlayer/AudioPlayer will only return zero size when it reaches the EOS.
+ // NuPlayer2Renderer will return zero when it doesn't have data (it doesn't block to fill).
+ //
+ // This is a benign busy-wait, with the next data request generated 10 ms or more later;
+ // nevertheless for power reasons, we don't want to see too many of these.
+
+ ALOGV_IF(actualSize == 0 && buffer->size > 0, "callbackwrapper: empty buffer returned");
+
+ buffer->size = actualSize;
+ } break;
+
+ case AudioTrack::EVENT_STREAM_END:
+ // currently only occurs for offloaded callbacks
+ ALOGV("callbackwrapper: deliver EVENT_STREAM_END");
+ (*me->mCallback)(me, NULL /* buffer */, 0 /* size */,
+ me->mCallbackCookie, CB_EVENT_STREAM_END);
+ break;
+
+ case AudioTrack::EVENT_NEW_IAUDIOTRACK :
+ ALOGV("callbackwrapper: deliver EVENT_TEAR_DOWN");
+ (*me->mCallback)(me, NULL /* buffer */, 0 /* size */,
+ me->mCallbackCookie, CB_EVENT_TEAR_DOWN);
+ break;
+
+ case AudioTrack::EVENT_UNDERRUN:
+ // This occurs when there is no data available, typically
+ // when there is a failure to supply data to the AudioTrack. It can also
+ // occur in non-offloaded mode when the audio device comes out of standby.
+ //
+ // If an AudioTrack underruns it outputs silence. Since this happens suddenly
+ // it may sound like an audible pop or glitch.
+ //
+ // The underrun event is sent once per track underrun; the condition is reset
+ // when more data is sent to the AudioTrack.
+ ALOGD("callbackwrapper: EVENT_UNDERRUN (discarded)");
+ break;
+
+ default:
+ ALOGE("received unknown event type: %d inside CallbackWrapper !", event);
+ }
+
+ data->unlock();
+}
+
+audio_session_t MediaPlayer2AudioOutput::getSessionId() const
+{
+ Mutex::Autolock lock(mLock);
+ return mSessionId;
+}
+
+uint32_t MediaPlayer2AudioOutput::getSampleRate() const
+{
+ Mutex::Autolock lock(mLock);
+ if (mTrack == 0) {
+ return 0;
+ }
+ return mTrack->getSampleRate();
+}
+
+int64_t MediaPlayer2AudioOutput::getBufferDurationInUs() const
+{
+ Mutex::Autolock lock(mLock);
+ if (mTrack == 0) {
+ return 0;
+ }
+ int64_t duration;
+ if (mTrack->getBufferDurationInUs(&duration) != OK) {
+ return 0;
+ }
+ return duration;
+}
+
+} // namespace android
diff --git a/media/libmediaplayer2/MediaPlayer2Manager.cpp b/media/libmediaplayer2/MediaPlayer2Manager.cpp
deleted file mode 100644
index 44df2ac..0000000
--- a/media/libmediaplayer2/MediaPlayer2Manager.cpp
+++ /dev/null
@@ -1,2086 +0,0 @@
-/*
-**
-** Copyright 2017, 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.
-*/
-
-// Proxy for media player implementations
-
-//#define LOG_NDEBUG 0
-#define LOG_TAG "MediaPlayer2Manager"
-#include <utils/Log.h>
-
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <sys/time.h>
-#include <dirent.h>
-#include <unistd.h>
-
-#include <string.h>
-
-#include <cutils/atomic.h>
-#include <cutils/properties.h> // for property_get
-
-#include <utils/misc.h>
-
-#include <binder/IPCThreadState.h>
-#include <binder/IServiceManager.h>
-#include <binder/MemoryHeapBase.h>
-#include <binder/MemoryBase.h>
-#include <utils/Errors.h> // for status_t
-#include <utils/String8.h>
-#include <utils/SystemClock.h>
-#include <utils/Timers.h>
-#include <utils/Vector.h>
-
-#include <media/AudioPolicyHelper.h>
-#include <media/DataSourceDesc.h>
-#include <media/MediaHTTPService.h>
-#include <media/Metadata.h>
-#include <media/AudioTrack.h>
-#include <media/MemoryLeakTrackUtil.h>
-#include <media/NdkWrapper.h>
-
-#include <media/stagefright/InterfaceUtils.h>
-#include <media/stagefright/MediaCodecList.h>
-#include <media/stagefright/MediaErrors.h>
-#include <media/stagefright/Utils.h>
-#include <media/stagefright/foundation/ADebug.h>
-#include <media/stagefright/foundation/ALooperRoster.h>
-#include <media/stagefright/SurfaceUtils.h>
-#include <mediautils/BatteryNotifier.h>
-
-#include <mediaplayer2/MediaPlayer2EngineClient.h>
-#include <mediaplayer2/MediaPlayer2Interface.h>
-
-#include <memunreachable/memunreachable.h>
-#include <system/audio.h>
-#include <system/window.h>
-
-#include <private/android_filesystem_config.h>
-
-#include <nuplayer2/NuPlayer2Driver.h>
-#include "MediaPlayer2Manager.h"
-
-static const int kDumpLockRetries = 50;
-static const int kDumpLockSleepUs = 20000;
-
-namespace {
-using android::media::Metadata;
-using android::status_t;
-using android::OK;
-using android::BAD_VALUE;
-using android::NOT_ENOUGH_DATA;
-using android::Parcel;
-
-// Max number of entries in the filter.
-const int kMaxFilterSize = 64; // I pulled that out of thin air.
-
-const float kMaxRequiredSpeed = 8.0f; // for PCM tracks allow up to 8x speedup.
-
-// FIXME: Move all the metadata related function in the Metadata.cpp
-
-
-// Unmarshall a filter from a Parcel.
-// Filter format in a parcel:
-//
-// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
-// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-// | number of entries (n) |
-// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-// | metadata type 1 |
-// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-// | metadata type 2 |
-// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-// ....
-// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-// | metadata type n |
-// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-//
-// @param p Parcel that should start with a filter.
-// @param[out] filter On exit contains the list of metadata type to be
-// filtered.
-// @param[out] status On exit contains the status code to be returned.
-// @return true if the parcel starts with a valid filter.
-bool unmarshallFilter(const Parcel& p,
- Metadata::Filter *filter,
- status_t *status)
-{
- int32_t val;
- if (p.readInt32(&val) != OK)
- {
- ALOGE("Failed to read filter's length");
- *status = NOT_ENOUGH_DATA;
- return false;
- }
-
- if( val > kMaxFilterSize || val < 0)
- {
- ALOGE("Invalid filter len %d", val);
- *status = BAD_VALUE;
- return false;
- }
-
- const size_t num = val;
-
- filter->clear();
- filter->setCapacity(num);
-
- size_t size = num * sizeof(Metadata::Type);
-
-
- if (p.dataAvail() < size)
- {
- ALOGE("Filter too short expected %zu but got %zu", size, p.dataAvail());
- *status = NOT_ENOUGH_DATA;
- return false;
- }
-
- const Metadata::Type *data =
- static_cast<const Metadata::Type*>(p.readInplace(size));
-
- if (NULL == data)
- {
- ALOGE("Filter had no data");
- *status = BAD_VALUE;
- return false;
- }
-
- // TODO: The stl impl of vector would be more efficient here
- // because it degenerates into a memcpy on pod types. Try to
- // replace later or use stl::set.
- for (size_t i = 0; i < num; ++i)
- {
- filter->add(*data);
- ++data;
- }
- *status = OK;
- return true;
-}
-
-// @param filter Of metadata type.
-// @param val To be searched.
-// @return true if a match was found.
-bool findMetadata(const Metadata::Filter& filter, const int32_t val)
-{
- // Deal with empty and ANY right away
- if (filter.isEmpty()) return false;
- if (filter[0] == Metadata::kAny) return true;
-
- return filter.indexOf(val) >= 0;
-}
-
-} // anonymous namespace
-
-
-namespace {
-using android::Parcel;
-using android::String16;
-
-// marshalling tag indicating flattened utf16 tags
-// keep in sync with frameworks/base/media/java/android/media/AudioAttributes.java
-const int32_t kAudioAttributesMarshallTagFlattenTags = 1;
-
-// Audio attributes format in a parcel:
-//
-// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
-// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-// | usage |
-// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-// | content_type |
-// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-// | source |
-// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-// | flags |
-// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-// | kAudioAttributesMarshallTagFlattenTags | // ignore tags if not found
-// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-// | flattened tags in UTF16 |
-// | ... |
-// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-//
-// @param p Parcel that contains audio attributes.
-// @param[out] attributes On exit points to an initialized audio_attributes_t structure
-// @param[out] status On exit contains the status code to be returned.
-void unmarshallAudioAttributes(const Parcel& parcel, audio_attributes_t *attributes)
-{
- attributes->usage = (audio_usage_t) parcel.readInt32();
- attributes->content_type = (audio_content_type_t) parcel.readInt32();
- attributes->source = (audio_source_t) parcel.readInt32();
- attributes->flags = (audio_flags_mask_t) parcel.readInt32();
- const bool hasFlattenedTag = (parcel.readInt32() == kAudioAttributesMarshallTagFlattenTags);
- if (hasFlattenedTag) {
- // the tags are UTF16, convert to UTF8
- String16 tags = parcel.readString16();
- ssize_t realTagSize = utf16_to_utf8_length(tags.string(), tags.size());
- if (realTagSize <= 0) {
- strcpy(attributes->tags, "");
- } else {
- // copy the flattened string into the attributes as the destination for the conversion:
- // copying array size -1, array for tags was calloc'd, no need to NULL-terminate it
- size_t tagSize = realTagSize > AUDIO_ATTRIBUTES_TAGS_MAX_SIZE - 1 ?
- AUDIO_ATTRIBUTES_TAGS_MAX_SIZE - 1 : realTagSize;
- utf16_to_utf8(tags.string(), tagSize, attributes->tags,
- sizeof(attributes->tags) / sizeof(attributes->tags[0]));
- }
- } else {
- ALOGE("unmarshallAudioAttributes() received unflattened tags, ignoring tag values");
- strcpy(attributes->tags, "");
- }
-}
-} // anonymous namespace
-
-
-namespace android {
-
-extern ALooperRoster gLooperRoster;
-
-MediaPlayer2Manager gMediaPlayer2Manager;
-
-// TODO: Find real cause of Audio/Video delay in PV framework and remove this workaround
-/* static */ int MediaPlayer2Manager::AudioOutput::mMinBufferCount = 4;
-/* static */ bool MediaPlayer2Manager::AudioOutput::mIsOnEmulator = false;
-
-// static
-MediaPlayer2Manager& MediaPlayer2Manager::get() {
- return gMediaPlayer2Manager;
-}
-
-MediaPlayer2Manager::MediaPlayer2Manager() {
- ALOGV("MediaPlayer2Manager created");
- // TODO: remove all unnecessary pid/uid handling.
- mPid = IPCThreadState::self()->getCallingPid();
- mUid = IPCThreadState::self()->getCallingUid();
- mNextConnId = 1;
-}
-
-MediaPlayer2Manager::~MediaPlayer2Manager() {
- ALOGV("MediaPlayer2Manager destroyed");
-}
-
-sp<MediaPlayer2Engine> MediaPlayer2Manager::create(
- const sp<MediaPlayer2EngineClient>& client,
- audio_session_t audioSessionId)
-{
- int32_t connId = android_atomic_inc(&mNextConnId);
-
- sp<Client> c = new Client(
- mPid, connId, client, audioSessionId, mUid);
-
- if (!c->init()) {
- return NULL;
- }
-
- ALOGV("Create new client(%d) from pid %d, uid %d, ", connId, mPid, mUid);
-
- wp<Client> w = c;
- {
- Mutex::Autolock lock(mLock);
- mClients.add(w);
- }
- return c;
-}
-
-status_t MediaPlayer2Manager::AudioOutput::dump(int fd, const Vector<String16>& args) const
-{
- const size_t SIZE = 256;
- char buffer[SIZE];
- String8 result;
-
- result.append(" AudioOutput\n");
- snprintf(buffer, 255, " stream type(%d), left - right volume(%f, %f)\n",
- mStreamType, mLeftVolume, mRightVolume);
- result.append(buffer);
- snprintf(buffer, 255, " msec per frame(%f), latency (%d)\n",
- mMsecsPerFrame, (mTrack != 0) ? mTrack->latency() : -1);
- result.append(buffer);
- snprintf(buffer, 255, " aux effect id(%d), send level (%f)\n",
- mAuxEffectId, mSendLevel);
- result.append(buffer);
-
- ::write(fd, result.string(), result.size());
- if (mTrack != 0) {
- mTrack->dump(fd, args);
- }
- return NO_ERROR;
-}
-
-status_t MediaPlayer2Manager::Client::dump(int fd, const Vector<String16>& args)
-{
- const size_t SIZE = 256;
- char buffer[SIZE];
- String8 result;
- result.append(" Client\n");
- snprintf(buffer, 255, " pid(%d), connId(%d), status(%d), looping(%s)\n",
- mPid, mConnId, mStatus, mLoop?"true": "false");
- result.append(buffer);
-
- sp<MediaPlayer2Interface> p;
- sp<AudioOutput> audioOutput;
- bool locked = false;
- for (int i = 0; i < kDumpLockRetries; ++i) {
- if (mLock.tryLock() == NO_ERROR) {
- locked = true;
- break;
- }
- usleep(kDumpLockSleepUs);
- }
-
- if (locked) {
- p = mPlayer;
- audioOutput = mAudioOutput;
- mLock.unlock();
- } else {
- result.append(" lock is taken, no dump from player and audio output\n");
- }
- write(fd, result.string(), result.size());
-
- if (p != NULL) {
- p->dump(fd, args);
- }
- if (audioOutput != 0) {
- audioOutput->dump(fd, args);
- }
- write(fd, "\n", 1);
- return NO_ERROR;
-}
-
-/**
- * The only arguments this understands right now are -c, -von and -voff,
- * which are parsed by ALooperRoster::dump()
- */
-status_t MediaPlayer2Manager::dump(int fd, const Vector<String16>& args)
-{
- const size_t SIZE = 256;
- char buffer[SIZE];
- String8 result;
- SortedVector< sp<Client> > clients; //to serialise the mutex unlock & client destruction.
-
- if (checkCallingPermission(String16("android.permission.DUMP")) == false) {
- snprintf(buffer, SIZE, "Permission Denial: "
- "can't dump MediaPlayer2Manager from pid=%d, uid=%d\n",
- mPid, mUid);
- result.append(buffer);
- } else {
- Mutex::Autolock lock(mLock);
- for (int i = 0, n = mClients.size(); i < n; ++i) {
- sp<Client> c = mClients[i].promote();
- if (c != 0) c->dump(fd, args);
- clients.add(c);
- }
-
- result.append(" Files opened and/or mapped:\n");
- snprintf(buffer, SIZE, "/proc/%d/maps", getpid());
- FILE *f = fopen(buffer, "r");
- if (f) {
- while (!feof(f)) {
- fgets(buffer, SIZE, f);
- if (strstr(buffer, " /storage/") ||
- strstr(buffer, " /system/sounds/") ||
- strstr(buffer, " /data/") ||
- strstr(buffer, " /system/media/")) {
- result.append(" ");
- result.append(buffer);
- }
- }
- fclose(f);
- } else {
- result.append("couldn't open ");
- result.append(buffer);
- result.append("\n");
- }
-
- snprintf(buffer, SIZE, "/proc/%d/fd", getpid());
- DIR *d = opendir(buffer);
- if (d) {
- struct dirent *ent;
- while((ent = readdir(d)) != NULL) {
- if (strcmp(ent->d_name,".") && strcmp(ent->d_name,"..")) {
- snprintf(buffer, SIZE, "/proc/%d/fd/%s", getpid(), ent->d_name);
- struct stat s;
- if (lstat(buffer, &s) == 0) {
- if ((s.st_mode & S_IFMT) == S_IFLNK) {
- char linkto[256];
- int len = readlink(buffer, linkto, sizeof(linkto));
- if(len > 0) {
- if(len > 255) {
- linkto[252] = '.';
- linkto[253] = '.';
- linkto[254] = '.';
- linkto[255] = 0;
- } else {
- linkto[len] = 0;
- }
- if (strstr(linkto, "/storage/") == linkto ||
- strstr(linkto, "/system/sounds/") == linkto ||
- strstr(linkto, "/data/") == linkto ||
- strstr(linkto, "/system/media/") == linkto) {
- result.append(" ");
- result.append(buffer);
- result.append(" -> ");
- result.append(linkto);
- result.append("\n");
- }
- }
- } else {
- result.append(" unexpected type for ");
- result.append(buffer);
- result.append("\n");
- }
- }
- }
- }
- closedir(d);
- } else {
- result.append("couldn't open ");
- result.append(buffer);
- result.append("\n");
- }
-
- gLooperRoster.dump(fd, args);
-
- bool dumpMem = false;
- bool unreachableMemory = false;
- for (size_t i = 0; i < args.size(); i++) {
- if (args[i] == String16("-m")) {
- dumpMem = true;
- } else if (args[i] == String16("--unreachable")) {
- unreachableMemory = true;
- }
- }
- if (dumpMem) {
- result.append("\nDumping memory:\n");
- std::string s = dumpMemoryAddresses(100 /* limit */);
- result.append(s.c_str(), s.size());
- }
- if (unreachableMemory) {
- result.append("\nDumping unreachable memory:\n");
- // TODO - should limit be an argument parameter?
- // TODO: enable GetUnreachableMemoryString if it's part of stable API
- //std::string s = GetUnreachableMemoryString(true /* contents */, 10000 /* limit */);
- //result.append(s.c_str(), s.size());
- }
- }
- write(fd, result.string(), result.size());
- return NO_ERROR;
-}
-
-void MediaPlayer2Manager::removeClient(const wp<Client>& client)
-{
- Mutex::Autolock lock(mLock);
- mClients.remove(client);
-}
-
-bool MediaPlayer2Manager::hasClient(wp<Client> client)
-{
- Mutex::Autolock lock(mLock);
- return mClients.indexOf(client) != NAME_NOT_FOUND;
-}
-
-MediaPlayer2Manager::Client::Client(
- pid_t pid,
- int32_t connId,
- const sp<MediaPlayer2EngineClient>& client,
- audio_session_t audioSessionId,
- uid_t uid)
-{
- ALOGV("Client(%d) constructor", connId);
- mPid = pid;
- mConnId = connId;
- mClient = client;
- mLoop = false;
- mStatus = NO_INIT;
- mAudioSessionId = audioSessionId;
- mUid = uid;
- mAudioAttributes = NULL;
-
-#if CALLBACK_ANTAGONIZER
- ALOGD("create Antagonizer");
- mAntagonizer = new Antagonizer(notify, this);
-#endif
-}
-
-bool MediaPlayer2Manager::Client::init() {
- sp<MediaPlayer2Interface> p = new NuPlayer2Driver(mPid, mUid);
- status_t init_result = p->initCheck();
- if (init_result != NO_ERROR) {
- ALOGE("Failed to create player object, initCheck failed(%d)", init_result);
- return false;
- }
-
- p->setNotifyCallback(this, notify);
- mAudioDeviceUpdatedListener = new AudioDeviceUpdatedNotifier(p);
- mAudioOutput = new AudioOutput(mAudioSessionId, mUid,
- mPid, mAudioAttributes, mAudioDeviceUpdatedListener);
- p->setAudioSink(mAudioOutput);
-
- mPlayer = p;
- return true;
-}
-
-MediaPlayer2Manager::Client::~Client()
-{
- ALOGV("Client(%d) destructor pid = %d", mConnId, mPid);
- mAudioOutput.clear();
- wp<Client> client(this);
- disconnect();
- gMediaPlayer2Manager.removeClient(client);
- if (mAudioAttributes != NULL) {
- free(mAudioAttributes);
- }
- mAudioDeviceUpdatedListener.clear();
-}
-
-void MediaPlayer2Manager::Client::disconnect()
-{
- ALOGV("disconnect(%d) from pid %d", mConnId, mPid);
- // grab local reference and clear main reference to prevent future
- // access to object
- sp<MediaPlayer2Interface> p;
- {
- Mutex::Autolock l(mLock);
- p = mPlayer;
- mClient.clear();
- mPlayer.clear();
- }
-
- // clear the notification to prevent callbacks to dead client
- // and reset the player. We assume the player will serialize
- // access to itself if necessary.
- if (p != 0) {
- p->setNotifyCallback(0, 0);
-#if CALLBACK_ANTAGONIZER
- ALOGD("kill Antagonizer");
- mAntagonizer->kill();
-#endif
- p->reset();
- }
-
- {
- Mutex::Autolock l(mLock);
- disconnectNativeWindow_l();
- }
-
- IPCThreadState::self()->flushCommands();
-}
-
-void MediaPlayer2Manager::Client::AudioDeviceUpdatedNotifier::onAudioDeviceUpdate(
- audio_io_handle_t audioIo,
- audio_port_handle_t deviceId) {
- sp<MediaPlayer2Interface> listener = mListener.promote();
- if (listener != NULL) {
- listener->sendEvent(0, MEDIA2_AUDIO_ROUTING_CHANGED, audioIo, deviceId);
- } else {
- ALOGW("listener for process %d death is gone", MEDIA2_AUDIO_ROUTING_CHANGED);
- }
-}
-
-status_t MediaPlayer2Manager::Client::setDataSource(
- const sp<DataSourceDesc> &dsd) {
- sp<MediaPlayer2Interface> p = getPlayer();
- if (p == NULL) {
- return NO_INIT;
- }
-
- if (dsd == NULL) {
- return BAD_VALUE;
- }
-
- status_t status = p->setDataSource(dsd);
- if (status != OK) {
- ALOGE("setDataSource error: %d", status);
- return status;
- }
-
- return status;
-}
-
-void MediaPlayer2Manager::Client::disconnectNativeWindow_l() {
- if (mConnectedWindow != NULL && mConnectedWindow->getANativeWindow() != NULL) {
- status_t err = native_window_api_disconnect(
- mConnectedWindow->getANativeWindow(), NATIVE_WINDOW_API_MEDIA);
-
- if (err != OK) {
- ALOGW("nativeWindowDisconnect returned an error: %s (%d)",
- strerror(-err), err);
- }
- }
- mConnectedWindow.clear();
-}
-
-status_t MediaPlayer2Manager::Client::setVideoSurfaceTexture(
- const sp<ANativeWindowWrapper>& nww)
-{
- ALOGV("[%d] setVideoSurfaceTexture(%p)",
- mConnId,
- (nww == NULL ? NULL : nww->getANativeWindow()));
- sp<MediaPlayer2Interface> p = getPlayer();
- if (p == 0) return UNKNOWN_ERROR;
-
- if (nww != NULL && nww->getANativeWindow() != NULL) {
- if (mConnectedWindow != NULL
- && mConnectedWindow->getANativeWindow() == nww->getANativeWindow()) {
- return OK;
- }
- status_t err = native_window_api_connect(
- nww->getANativeWindow(), NATIVE_WINDOW_API_MEDIA);
-
- if (err != OK) {
- ALOGE("setVideoSurfaceTexture failed: %d", err);
- // Note that we must do the reset before disconnecting from the ANW.
- // Otherwise queue/dequeue calls could be made on the disconnected
- // ANW, which may result in errors.
- reset();
-
- Mutex::Autolock lock(mLock);
- disconnectNativeWindow_l();
-
- return err;
- }
- }
-
- // Note that we must set the player's new GraphicBufferProducer before
- // disconnecting the old one. Otherwise queue/dequeue calls could be made
- // on the disconnected ANW, which may result in errors.
- status_t err = p->setVideoSurfaceTexture(nww);
-
- mLock.lock();
- disconnectNativeWindow_l();
-
- if (err == OK) {
- mConnectedWindow = nww;
- mLock.unlock();
- } else if (nww != NULL) {
- mLock.unlock();
- status_t err = native_window_api_disconnect(
- nww->getANativeWindow(), NATIVE_WINDOW_API_MEDIA);
-
- if (err != OK) {
- ALOGW("nativeWindowDisconnect returned an error: %s (%d)",
- strerror(-err), err);
- }
- }
-
- return err;
-}
-
-status_t MediaPlayer2Manager::Client::invoke(const Parcel& request,
- Parcel *reply)
-{
- sp<MediaPlayer2Interface> p = getPlayer();
- if (p == NULL) return UNKNOWN_ERROR;
- return p->invoke(request, reply);
-}
-
-// This call doesn't need to access the native player.
-status_t MediaPlayer2Manager::Client::setMetadataFilter(const Parcel& filter)
-{
- status_t status;
- media::Metadata::Filter allow, drop;
-
- if (unmarshallFilter(filter, &allow, &status) &&
- unmarshallFilter(filter, &drop, &status)) {
- Mutex::Autolock lock(mLock);
-
- mMetadataAllow = allow;
- mMetadataDrop = drop;
- }
- return status;
-}
-
-status_t MediaPlayer2Manager::Client::getMetadata(
- bool update_only, bool /*apply_filter*/, Parcel *reply)
-{
- sp<MediaPlayer2Interface> player = getPlayer();
- if (player == 0) return UNKNOWN_ERROR;
-
- status_t status;
- // Placeholder for the return code, updated by the caller.
- reply->writeInt32(-1);
-
- media::Metadata::Filter ids;
-
- // We don't block notifications while we fetch the data. We clear
- // mMetadataUpdated first so we don't lose notifications happening
- // during the rest of this call.
- {
- Mutex::Autolock lock(mLock);
- if (update_only) {
- ids = mMetadataUpdated;
- }
- mMetadataUpdated.clear();
- }
-
- media::Metadata metadata(reply);
-
- metadata.appendHeader();
- status = player->getMetadata(ids, reply);
-
- if (status != OK) {
- metadata.resetParcel();
- ALOGE("getMetadata failed %d", status);
- return status;
- }
-
- // FIXME: ement filtering on the result. Not critical since
- // filtering takes place on the update notifications already. This
- // would be when all the metadata are fetch and a filter is set.
-
- // Everything is fine, update the metadata length.
- metadata.updateLength();
- return OK;
-}
-
-status_t MediaPlayer2Manager::Client::setBufferingSettings(
- const BufferingSettings& buffering)
-{
- ALOGV("[%d] setBufferingSettings{%s}",
- mConnId, buffering.toString().string());
- sp<MediaPlayer2Interface> p = getPlayer();
- if (p == 0) return UNKNOWN_ERROR;
- return p->setBufferingSettings(buffering);
-}
-
-status_t MediaPlayer2Manager::Client::getBufferingSettings(
- BufferingSettings* buffering /* nonnull */)
-{
- sp<MediaPlayer2Interface> p = getPlayer();
- // TODO: create mPlayer on demand.
- if (p == 0) return UNKNOWN_ERROR;
- status_t ret = p->getBufferingSettings(buffering);
- if (ret == NO_ERROR) {
- ALOGV("[%d] getBufferingSettings{%s}",
- mConnId, buffering->toString().string());
- } else {
- ALOGE("[%d] getBufferingSettings returned %d", mConnId, ret);
- }
- return ret;
-}
-
-status_t MediaPlayer2Manager::Client::prepareAsync()
-{
- ALOGV("[%d] prepareAsync", mConnId);
- sp<MediaPlayer2Interface> p = getPlayer();
- if (p == 0) return UNKNOWN_ERROR;
- status_t ret = p->prepareAsync();
-#if CALLBACK_ANTAGONIZER
- ALOGD("start Antagonizer");
- if (ret == NO_ERROR) mAntagonizer->start();
-#endif
- return ret;
-}
-
-status_t MediaPlayer2Manager::Client::start()
-{
- ALOGV("[%d] start", mConnId);
- sp<MediaPlayer2Interface> p = getPlayer();
- if (p == 0) return UNKNOWN_ERROR;
- p->setLooping(mLoop);
- return p->start();
-}
-
-status_t MediaPlayer2Manager::Client::stop()
-{
- ALOGV("[%d] stop", mConnId);
- sp<MediaPlayer2Interface> p = getPlayer();
- if (p == 0) return UNKNOWN_ERROR;
- return p->stop();
-}
-
-status_t MediaPlayer2Manager::Client::pause()
-{
- ALOGV("[%d] pause", mConnId);
- sp<MediaPlayer2Interface> p = getPlayer();
- if (p == 0) return UNKNOWN_ERROR;
- return p->pause();
-}
-
-status_t MediaPlayer2Manager::Client::isPlaying(bool* state)
-{
- *state = false;
- sp<MediaPlayer2Interface> p = getPlayer();
- if (p == 0) return UNKNOWN_ERROR;
- *state = p->isPlaying();
- ALOGV("[%d] isPlaying: %d", mConnId, *state);
- return NO_ERROR;
-}
-
-status_t MediaPlayer2Manager::Client::setPlaybackSettings(const AudioPlaybackRate& rate)
-{
- ALOGV("[%d] setPlaybackSettings(%f, %f, %d, %d)",
- mConnId, rate.mSpeed, rate.mPitch, rate.mFallbackMode, rate.mStretchMode);
- sp<MediaPlayer2Interface> p = getPlayer();
- if (p == 0) return UNKNOWN_ERROR;
- return p->setPlaybackSettings(rate);
-}
-
-status_t MediaPlayer2Manager::Client::getPlaybackSettings(AudioPlaybackRate* rate /* nonnull */)
-{
- sp<MediaPlayer2Interface> p = getPlayer();
- if (p == 0) return UNKNOWN_ERROR;
- status_t ret = p->getPlaybackSettings(rate);
- if (ret == NO_ERROR) {
- ALOGV("[%d] getPlaybackSettings(%f, %f, %d, %d)",
- mConnId, rate->mSpeed, rate->mPitch, rate->mFallbackMode, rate->mStretchMode);
- } else {
- ALOGV("[%d] getPlaybackSettings returned %d", mConnId, ret);
- }
- return ret;
-}
-
-status_t MediaPlayer2Manager::Client::setSyncSettings(
- const AVSyncSettings& sync, float videoFpsHint)
-{
- ALOGV("[%d] setSyncSettings(%u, %u, %f, %f)",
- mConnId, sync.mSource, sync.mAudioAdjustMode, sync.mTolerance, videoFpsHint);
- sp<MediaPlayer2Interface> p = getPlayer();
- if (p == 0) return UNKNOWN_ERROR;
- return p->setSyncSettings(sync, videoFpsHint);
-}
-
-status_t MediaPlayer2Manager::Client::getSyncSettings(
- AVSyncSettings* sync /* nonnull */, float* videoFps /* nonnull */)
-{
- sp<MediaPlayer2Interface> p = getPlayer();
- if (p == 0) return UNKNOWN_ERROR;
- status_t ret = p->getSyncSettings(sync, videoFps);
- if (ret == NO_ERROR) {
- ALOGV("[%d] getSyncSettings(%u, %u, %f, %f)",
- mConnId, sync->mSource, sync->mAudioAdjustMode, sync->mTolerance, *videoFps);
- } else {
- ALOGV("[%d] getSyncSettings returned %d", mConnId, ret);
- }
- return ret;
-}
-
-status_t MediaPlayer2Manager::Client::getCurrentPosition(int *msec)
-{
- ALOGV("getCurrentPosition");
- sp<MediaPlayer2Interface> p = getPlayer();
- if (p == 0) return UNKNOWN_ERROR;
- status_t ret = p->getCurrentPosition(msec);
- if (ret == NO_ERROR) {
- ALOGV("[%d] getCurrentPosition = %d", mConnId, *msec);
- } else {
- ALOGE("getCurrentPosition returned %d", ret);
- }
- return ret;
-}
-
-status_t MediaPlayer2Manager::Client::getDuration(int *msec)
-{
- ALOGV("getDuration");
- sp<MediaPlayer2Interface> p = getPlayer();
- if (p == 0) return UNKNOWN_ERROR;
- status_t ret = p->getDuration(msec);
- if (ret == NO_ERROR) {
- ALOGV("[%d] getDuration = %d", mConnId, *msec);
- } else {
- ALOGE("getDuration returned %d", ret);
- }
- return ret;
-}
-
-status_t MediaPlayer2Manager::Client::setNextPlayer(const sp<MediaPlayer2Engine>& player) {
- ALOGV("setNextPlayer");
- Mutex::Autolock l(mLock);
- sp<Client> c = static_cast<Client*>(player.get());
- if (c != NULL && !gMediaPlayer2Manager.hasClient(c)) {
- return BAD_VALUE;
- }
-
- mNextClient = c;
-
- if (c != NULL) {
- if (mAudioOutput != NULL) {
- mAudioOutput->setNextOutput(c->mAudioOutput);
- } else {
- ALOGE("no current audio output");
- }
-
- if ((mPlayer != NULL) && (mNextClient->getPlayer() != NULL)) {
- mPlayer->setNextPlayer(mNextClient->getPlayer());
- }
- }
-
- return OK;
-}
-
-status_t MediaPlayer2Manager::Client::seekTo(int msec, MediaPlayer2SeekMode mode)
-{
- ALOGV("[%d] seekTo(%d, %d)", mConnId, msec, mode);
- sp<MediaPlayer2Interface> p = getPlayer();
- if (p == 0) return UNKNOWN_ERROR;
- return p->seekTo(msec, mode);
-}
-
-status_t MediaPlayer2Manager::Client::reset()
-{
- ALOGV("[%d] reset", mConnId);
- sp<MediaPlayer2Interface> p = getPlayer();
- if (p == 0) return UNKNOWN_ERROR;
- return p->reset();
-}
-
-status_t MediaPlayer2Manager::Client::notifyAt(int64_t mediaTimeUs)
-{
- ALOGV("[%d] notifyAt(%lld)", mConnId, (long long)mediaTimeUs);
- sp<MediaPlayer2Interface> p = getPlayer();
- if (p == 0) return UNKNOWN_ERROR;
- return p->notifyAt(mediaTimeUs);
-}
-
-status_t MediaPlayer2Manager::Client::setAudioStreamType(audio_stream_type_t type)
-{
- ALOGV("[%d] setAudioStreamType(%d)", mConnId, type);
- // TODO: for hardware output, call player instead
- Mutex::Autolock l(mLock);
- if (mAudioOutput != 0) mAudioOutput->setAudioStreamType(type);
- return NO_ERROR;
-}
-
-status_t MediaPlayer2Manager::Client::setAudioAttributes_l(const Parcel &parcel)
-{
- if (mAudioAttributes != NULL) { free(mAudioAttributes); }
- mAudioAttributes = (audio_attributes_t *) calloc(1, sizeof(audio_attributes_t));
- if (mAudioAttributes == NULL) {
- return NO_MEMORY;
- }
- unmarshallAudioAttributes(parcel, mAudioAttributes);
-
- ALOGV("setAudioAttributes_l() usage=%d content=%d flags=0x%x tags=%s",
- mAudioAttributes->usage, mAudioAttributes->content_type, mAudioAttributes->flags,
- mAudioAttributes->tags);
-
- if (mAudioOutput != 0) {
- mAudioOutput->setAudioAttributes(mAudioAttributes);
- }
- return NO_ERROR;
-}
-
-status_t MediaPlayer2Manager::Client::setLooping(int loop)
-{
- ALOGV("[%d] setLooping(%d)", mConnId, loop);
- mLoop = loop;
- sp<MediaPlayer2Interface> p = getPlayer();
- if (p != 0) return p->setLooping(loop);
- return NO_ERROR;
-}
-
-status_t MediaPlayer2Manager::Client::setVolume(float leftVolume, float rightVolume)
-{
- ALOGV("[%d] setVolume(%f, %f)", mConnId, leftVolume, rightVolume);
-
- // for hardware output, call player instead
- sp<MediaPlayer2Interface> p = getPlayer();
- {
- Mutex::Autolock l(mLock);
- if (mAudioOutput != 0) mAudioOutput->setVolume(leftVolume, rightVolume);
- }
-
- return NO_ERROR;
-}
-
-status_t MediaPlayer2Manager::Client::setAuxEffectSendLevel(float level)
-{
- ALOGV("[%d] setAuxEffectSendLevel(%f)", mConnId, level);
- Mutex::Autolock l(mLock);
- if (mAudioOutput != 0) return mAudioOutput->setAuxEffectSendLevel(level);
- return NO_ERROR;
-}
-
-status_t MediaPlayer2Manager::Client::attachAuxEffect(int effectId)
-{
- ALOGV("[%d] attachAuxEffect(%d)", mConnId, effectId);
- Mutex::Autolock l(mLock);
- if (mAudioOutput != 0) return mAudioOutput->attachAuxEffect(effectId);
- return NO_ERROR;
-}
-
-status_t MediaPlayer2Manager::Client::setParameter(int key, const Parcel &request) {
- ALOGV("[%d] setParameter(%d)", mConnId, key);
- switch (key) {
- case MEDIA2_KEY_PARAMETER_AUDIO_ATTRIBUTES:
- {
- Mutex::Autolock l(mLock);
- return setAudioAttributes_l(request);
- }
- default:
- sp<MediaPlayer2Interface> p = getPlayer();
- if (p == 0) { return UNKNOWN_ERROR; }
- return p->setParameter(key, request);
- }
-}
-
-status_t MediaPlayer2Manager::Client::getParameter(int key, Parcel *reply) {
- ALOGV("[%d] getParameter(%d)", mConnId, key);
- sp<MediaPlayer2Interface> p = getPlayer();
- if (p == 0) return UNKNOWN_ERROR;
- return p->getParameter(key, reply);
-}
-
-void MediaPlayer2Manager::Client::notify(
- const wp<MediaPlayer2Engine> &listener, int64_t srcId,
- int msg, int ext1, int ext2, const Parcel *obj)
-{
- sp<MediaPlayer2Engine> spListener = listener.promote();
- if (spListener == NULL) {
- return;
- }
- Client* client = static_cast<Client*>(spListener.get());
-
- sp<MediaPlayer2EngineClient> c;
- sp<Client> nextClient;
- status_t errStartNext = NO_ERROR;
- {
- Mutex::Autolock l(client->mLock);
- c = client->mClient;
- if (msg == MEDIA2_PLAYBACK_COMPLETE && client->mNextClient != NULL) {
- nextClient = client->mNextClient;
-
- if (client->mAudioOutput != NULL)
- client->mAudioOutput->switchToNextOutput();
-
- errStartNext = nextClient->start();
- }
- }
-
- if (nextClient != NULL) {
- sp<MediaPlayer2EngineClient> nc;
- {
- Mutex::Autolock l(nextClient->mLock);
- nc = nextClient->mClient;
- }
- if (nc != NULL) {
- if (errStartNext == NO_ERROR) {
- nc->notify(srcId, MEDIA2_INFO, MEDIA2_INFO_STARTED_AS_NEXT, 0, obj);
- } else {
- nc->notify(srcId, MEDIA2_ERROR, MEDIA2_ERROR_UNKNOWN , 0, obj);
- ALOGE("gapless:start playback for next track failed, err(%d)", errStartNext);
- }
- }
- }
-
- if (MEDIA2_INFO == msg &&
- MEDIA2_INFO_METADATA_UPDATE == ext1) {
- const media::Metadata::Type metadata_type = ext2;
-
- if(client->shouldDropMetadata(metadata_type)) {
- return;
- }
-
- // Update the list of metadata that have changed. getMetadata
- // also access mMetadataUpdated and clears it.
- client->addNewMetadataUpdate(metadata_type);
- }
-
- if (c != NULL) {
- ALOGV("[%d] notify (%p, %lld, %d, %d, %d)", client->mConnId, spListener.get(),
- (long long)srcId, msg, ext1, ext2);
- c->notify(srcId, msg, ext1, ext2, obj);
- }
-}
-
-
-bool MediaPlayer2Manager::Client::shouldDropMetadata(media::Metadata::Type code) const
-{
- Mutex::Autolock lock(mLock);
-
- if (findMetadata(mMetadataDrop, code)) {
- return true;
- }
-
- if (mMetadataAllow.isEmpty() || findMetadata(mMetadataAllow, code)) {
- return false;
- } else {
- return true;
- }
-}
-
-
-void MediaPlayer2Manager::Client::addNewMetadataUpdate(media::Metadata::Type metadata_type) {
- Mutex::Autolock lock(mLock);
- if (mMetadataUpdated.indexOf(metadata_type) < 0) {
- mMetadataUpdated.add(metadata_type);
- }
-}
-
-// Modular DRM
-status_t MediaPlayer2Manager::Client::prepareDrm(const uint8_t uuid[16],
- const Vector<uint8_t>& drmSessionId)
-{
- ALOGV("[%d] prepareDrm", mConnId);
- sp<MediaPlayer2Interface> p = getPlayer();
- if (p == 0) return UNKNOWN_ERROR;
-
- status_t ret = p->prepareDrm(uuid, drmSessionId);
- ALOGV("prepareDrm ret: %d", ret);
-
- return ret;
-}
-
-status_t MediaPlayer2Manager::Client::releaseDrm()
-{
- ALOGV("[%d] releaseDrm", mConnId);
- sp<MediaPlayer2Interface> p = getPlayer();
- if (p == 0) return UNKNOWN_ERROR;
-
- status_t ret = p->releaseDrm();
- ALOGV("releaseDrm ret: %d", ret);
-
- return ret;
-}
-
-status_t MediaPlayer2Manager::Client::setOutputDevice(audio_port_handle_t deviceId)
-{
- ALOGV("[%d] setOutputDevice", mConnId);
- {
- Mutex::Autolock l(mLock);
- if (mAudioOutput.get() != nullptr) {
- return mAudioOutput->setOutputDevice(deviceId);
- }
- }
- return NO_INIT;
-}
-
-status_t MediaPlayer2Manager::Client::getRoutedDeviceId(audio_port_handle_t* deviceId)
-{
- ALOGV("[%d] getRoutedDeviceId", mConnId);
- {
- Mutex::Autolock l(mLock);
- if (mAudioOutput.get() != nullptr) {
- return mAudioOutput->getRoutedDeviceId(deviceId);
- }
- }
- return NO_INIT;
-}
-
-status_t MediaPlayer2Manager::Client::enableAudioDeviceCallback(bool enabled)
-{
- ALOGV("[%d] enableAudioDeviceCallback, %d", mConnId, enabled);
- {
- Mutex::Autolock l(mLock);
- if (mAudioOutput.get() != nullptr) {
- return mAudioOutput->enableAudioDeviceCallback(enabled);
- }
- }
- return NO_INIT;
-}
-
-#if CALLBACK_ANTAGONIZER
-const int Antagonizer::interval = 10000; // 10 msecs
-
-Antagonizer::Antagonizer(
- MediaPlayer2Manager::NotifyCallback cb,
- const wp<MediaPlayer2Engine> &client) :
- mExit(false), mActive(false), mClient(client), mCb(cb)
-{
- createThread(callbackThread, this);
-}
-
-void Antagonizer::kill()
-{
- Mutex::Autolock _l(mLock);
- mActive = false;
- mExit = true;
- mCondition.wait(mLock);
-}
-
-int Antagonizer::callbackThread(void* user)
-{
- ALOGD("Antagonizer started");
- Antagonizer* p = reinterpret_cast<Antagonizer*>(user);
- while (!p->mExit) {
- if (p->mActive) {
- ALOGV("send event");
- p->mCb(p->mClient, 0, 0, 0);
- }
- usleep(interval);
- }
- Mutex::Autolock _l(p->mLock);
- p->mCondition.signal();
- ALOGD("Antagonizer stopped");
- return 0;
-}
-#endif
-
-#undef LOG_TAG
-#define LOG_TAG "AudioSink"
-MediaPlayer2Manager::AudioOutput::AudioOutput(audio_session_t sessionId, uid_t uid, int pid,
- const audio_attributes_t* attr, const sp<AudioSystem::AudioDeviceCallback>& deviceCallback)
- : mCallback(NULL),
- mCallbackCookie(NULL),
- mCallbackData(NULL),
- mStreamType(AUDIO_STREAM_MUSIC),
- mLeftVolume(1.0),
- mRightVolume(1.0),
- mPlaybackRate(AUDIO_PLAYBACK_RATE_DEFAULT),
- mSampleRateHz(0),
- mMsecsPerFrame(0),
- mFrameSize(0),
- mSessionId(sessionId),
- mUid(uid),
- mPid(pid),
- mSendLevel(0.0),
- mAuxEffectId(0),
- mFlags(AUDIO_OUTPUT_FLAG_NONE),
- mSelectedDeviceId(AUDIO_PORT_HANDLE_NONE),
- mRoutedDeviceId(AUDIO_PORT_HANDLE_NONE),
- mDeviceCallbackEnabled(false),
- mDeviceCallback(deviceCallback)
-{
- ALOGV("AudioOutput(%d)", sessionId);
- if (attr != NULL) {
- mAttributes = (audio_attributes_t *) calloc(1, sizeof(audio_attributes_t));
- if (mAttributes != NULL) {
- memcpy(mAttributes, attr, sizeof(audio_attributes_t));
- mStreamType = audio_attributes_to_stream_type(attr);
- }
- } else {
- mAttributes = NULL;
- }
-
- setMinBufferCount();
-}
-
-MediaPlayer2Manager::AudioOutput::~AudioOutput()
-{
- close();
- free(mAttributes);
- delete mCallbackData;
-}
-
-//static
-void MediaPlayer2Manager::AudioOutput::setMinBufferCount()
-{
- char value[PROPERTY_VALUE_MAX];
- if (property_get("ro.kernel.qemu", value, 0)) {
- mIsOnEmulator = true;
- mMinBufferCount = 12; // to prevent systematic buffer underrun for emulator
- }
-}
-
-// static
-bool MediaPlayer2Manager::AudioOutput::isOnEmulator()
-{
- setMinBufferCount(); // benign race wrt other threads
- return mIsOnEmulator;
-}
-
-// static
-int MediaPlayer2Manager::AudioOutput::getMinBufferCount()
-{
- setMinBufferCount(); // benign race wrt other threads
- return mMinBufferCount;
-}
-
-ssize_t MediaPlayer2Manager::AudioOutput::bufferSize() const
-{
- Mutex::Autolock lock(mLock);
- if (mTrack == 0) return NO_INIT;
- return mTrack->frameCount() * mFrameSize;
-}
-
-ssize_t MediaPlayer2Manager::AudioOutput::frameCount() const
-{
- Mutex::Autolock lock(mLock);
- if (mTrack == 0) return NO_INIT;
- return mTrack->frameCount();
-}
-
-ssize_t MediaPlayer2Manager::AudioOutput::channelCount() const
-{
- Mutex::Autolock lock(mLock);
- if (mTrack == 0) return NO_INIT;
- return mTrack->channelCount();
-}
-
-ssize_t MediaPlayer2Manager::AudioOutput::frameSize() const
-{
- Mutex::Autolock lock(mLock);
- if (mTrack == 0) return NO_INIT;
- return mFrameSize;
-}
-
-uint32_t MediaPlayer2Manager::AudioOutput::latency () const
-{
- Mutex::Autolock lock(mLock);
- if (mTrack == 0) return 0;
- return mTrack->latency();
-}
-
-float MediaPlayer2Manager::AudioOutput::msecsPerFrame() const
-{
- Mutex::Autolock lock(mLock);
- return mMsecsPerFrame;
-}
-
-status_t MediaPlayer2Manager::AudioOutput::getPosition(uint32_t *position) const
-{
- Mutex::Autolock lock(mLock);
- if (mTrack == 0) return NO_INIT;
- return mTrack->getPosition(position);
-}
-
-status_t MediaPlayer2Manager::AudioOutput::getTimestamp(AudioTimestamp &ts) const
-{
- Mutex::Autolock lock(mLock);
- if (mTrack == 0) return NO_INIT;
- return mTrack->getTimestamp(ts);
-}
-
-// TODO: Remove unnecessary calls to getPlayedOutDurationUs()
-// as it acquires locks and may query the audio driver.
-//
-// Some calls could conceivably retrieve extrapolated data instead of
-// accessing getTimestamp() or getPosition() every time a data buffer with
-// a media time is received.
-//
-// Calculate duration of played samples if played at normal rate (i.e., 1.0).
-int64_t MediaPlayer2Manager::AudioOutput::getPlayedOutDurationUs(int64_t nowUs) const
-{
- Mutex::Autolock lock(mLock);
- if (mTrack == 0 || mSampleRateHz == 0) {
- return 0;
- }
-
- uint32_t numFramesPlayed;
- int64_t numFramesPlayedAtUs;
- AudioTimestamp ts;
-
- status_t res = mTrack->getTimestamp(ts);
- if (res == OK) { // case 1: mixing audio tracks and offloaded tracks.
- numFramesPlayed = ts.mPosition;
- numFramesPlayedAtUs = ts.mTime.tv_sec * 1000000LL + ts.mTime.tv_nsec / 1000;
- //ALOGD("getTimestamp: OK %d %lld", numFramesPlayed, (long long)numFramesPlayedAtUs);
- } else if (res == WOULD_BLOCK) { // case 2: transitory state on start of a new track
- numFramesPlayed = 0;
- numFramesPlayedAtUs = nowUs;
- //ALOGD("getTimestamp: WOULD_BLOCK %d %lld",
- // numFramesPlayed, (long long)numFramesPlayedAtUs);
- } else { // case 3: transitory at new track or audio fast tracks.
- res = mTrack->getPosition(&numFramesPlayed);
- CHECK_EQ(res, (status_t)OK);
- numFramesPlayedAtUs = nowUs;
- numFramesPlayedAtUs += 1000LL * mTrack->latency() / 2; /* XXX */
- //ALOGD("getPosition: %u %lld", numFramesPlayed, (long long)numFramesPlayedAtUs);
- }
-
- // CHECK_EQ(numFramesPlayed & (1 << 31), 0); // can't be negative until 12.4 hrs, test
- // TODO: remove the (int32_t) casting below as it may overflow at 12.4 hours.
- int64_t durationUs = (int64_t)((int32_t)numFramesPlayed * 1000000LL / mSampleRateHz)
- + nowUs - numFramesPlayedAtUs;
- if (durationUs < 0) {
- // Occurs when numFramesPlayed position is very small and the following:
- // (1) In case 1, the time nowUs is computed before getTimestamp() is called and
- // numFramesPlayedAtUs is greater than nowUs by time more than numFramesPlayed.
- // (2) In case 3, using getPosition and adding mAudioSink->latency() to
- // numFramesPlayedAtUs, by a time amount greater than numFramesPlayed.
- //
- // Both of these are transitory conditions.
- ALOGV("getPlayedOutDurationUs: negative duration %lld set to zero", (long long)durationUs);
- durationUs = 0;
- }
- ALOGV("getPlayedOutDurationUs(%lld) nowUs(%lld) frames(%u) framesAt(%lld)",
- (long long)durationUs, (long long)nowUs,
- numFramesPlayed, (long long)numFramesPlayedAtUs);
- return durationUs;
-}
-
-status_t MediaPlayer2Manager::AudioOutput::getFramesWritten(uint32_t *frameswritten) const
-{
- Mutex::Autolock lock(mLock);
- if (mTrack == 0) return NO_INIT;
- ExtendedTimestamp ets;
- status_t status = mTrack->getTimestamp(&ets);
- if (status == OK || status == WOULD_BLOCK) {
- *frameswritten = (uint32_t)ets.mPosition[ExtendedTimestamp::LOCATION_CLIENT];
- }
- return status;
-}
-
-status_t MediaPlayer2Manager::AudioOutput::setParameters(const String8& keyValuePairs)
-{
- Mutex::Autolock lock(mLock);
- if (mTrack == 0) return NO_INIT;
- return mTrack->setParameters(keyValuePairs);
-}
-
-String8 MediaPlayer2Manager::AudioOutput::getParameters(const String8& keys)
-{
- Mutex::Autolock lock(mLock);
- if (mTrack == 0) return String8::empty();
- return mTrack->getParameters(keys);
-}
-
-void MediaPlayer2Manager::AudioOutput::setAudioAttributes(const audio_attributes_t * attributes) {
- Mutex::Autolock lock(mLock);
- if (attributes == NULL) {
- free(mAttributes);
- mAttributes = NULL;
- } else {
- if (mAttributes == NULL) {
- mAttributes = (audio_attributes_t *) calloc(1, sizeof(audio_attributes_t));
- }
- memcpy(mAttributes, attributes, sizeof(audio_attributes_t));
- mStreamType = audio_attributes_to_stream_type(attributes);
- }
-}
-
-void MediaPlayer2Manager::AudioOutput::setAudioStreamType(audio_stream_type_t streamType)
-{
- Mutex::Autolock lock(mLock);
- // do not allow direct stream type modification if attributes have been set
- if (mAttributes == NULL) {
- mStreamType = streamType;
- }
-}
-
-void MediaPlayer2Manager::AudioOutput::deleteRecycledTrack_l()
-{
- ALOGV("deleteRecycledTrack_l");
- if (mRecycledTrack != 0) {
-
- if (mCallbackData != NULL) {
- mCallbackData->setOutput(NULL);
- mCallbackData->endTrackSwitch();
- }
-
- if ((mRecycledTrack->getFlags() & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) == 0) {
- int32_t msec = 0;
- if (!mRecycledTrack->stopped()) { // check if active
- (void)mRecycledTrack->pendingDuration(&msec);
- }
- mRecycledTrack->stop(); // ensure full data drain
- ALOGD("deleting recycled track, waiting for data drain (%d msec)", msec);
- if (msec > 0) {
- static const int32_t WAIT_LIMIT_MS = 3000;
- if (msec > WAIT_LIMIT_MS) {
- msec = WAIT_LIMIT_MS;
- }
- usleep(msec * 1000LL);
- }
- }
- // An offloaded track isn't flushed because the STREAM_END is reported
- // slightly prematurely to allow time for the gapless track switch
- // but this means that if we decide not to recycle the track there
- // could be a small amount of residual data still playing. We leave
- // AudioFlinger to drain the track.
-
- mRecycledTrack.clear();
- close_l();
- delete mCallbackData;
- mCallbackData = NULL;
- }
-}
-
-void MediaPlayer2Manager::AudioOutput::close_l()
-{
- mTrack.clear();
-}
-
-status_t MediaPlayer2Manager::AudioOutput::open(
- uint32_t sampleRate, int channelCount, audio_channel_mask_t channelMask,
- audio_format_t format, int bufferCount,
- AudioCallback cb, void *cookie,
- audio_output_flags_t flags,
- const audio_offload_info_t *offloadInfo,
- bool doNotReconnect,
- uint32_t suggestedFrameCount)
-{
- ALOGV("open(%u, %d, 0x%x, 0x%x, %d, %d 0x%x)", sampleRate, channelCount, channelMask,
- format, bufferCount, mSessionId, flags);
-
- // offloading is only supported in callback mode for now.
- // offloadInfo must be present if offload flag is set
- if (((flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) != 0) &&
- ((cb == NULL) || (offloadInfo == NULL))) {
- return BAD_VALUE;
- }
-
- // compute frame count for the AudioTrack internal buffer
- size_t frameCount;
- if ((flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) != 0) {
- frameCount = 0; // AudioTrack will get frame count from AudioFlinger
- } else {
- // try to estimate the buffer processing fetch size from AudioFlinger.
- // framesPerBuffer is approximate and generally correct, except when it's not :-).
- uint32_t afSampleRate;
- size_t afFrameCount;
- if (AudioSystem::getOutputFrameCount(&afFrameCount, mStreamType) != NO_ERROR) {
- return NO_INIT;
- }
- if (AudioSystem::getOutputSamplingRate(&afSampleRate, mStreamType) != NO_ERROR) {
- return NO_INIT;
- }
- const size_t framesPerBuffer =
- (unsigned long long)sampleRate * afFrameCount / afSampleRate;
-
- if (bufferCount == 0) {
- // use suggestedFrameCount
- bufferCount = (suggestedFrameCount + framesPerBuffer - 1) / framesPerBuffer;
- }
- // Check argument bufferCount against the mininum buffer count
- if (bufferCount != 0 && bufferCount < mMinBufferCount) {
- ALOGV("bufferCount (%d) increased to %d", bufferCount, mMinBufferCount);
- bufferCount = mMinBufferCount;
- }
- // if frameCount is 0, then AudioTrack will get frame count from AudioFlinger
- // which will be the minimum size permitted.
- frameCount = bufferCount * framesPerBuffer;
- }
-
- if (channelMask == CHANNEL_MASK_USE_CHANNEL_ORDER) {
- channelMask = audio_channel_out_mask_from_count(channelCount);
- if (0 == channelMask) {
- ALOGE("open() error, can\'t derive mask for %d audio channels", channelCount);
- return NO_INIT;
- }
- }
-
- Mutex::Autolock lock(mLock);
- mCallback = cb;
- mCallbackCookie = cookie;
-
- // Check whether we can recycle the track
- bool reuse = false;
- bool bothOffloaded = false;
-
- if (mRecycledTrack != 0) {
- // check whether we are switching between two offloaded tracks
- bothOffloaded = (flags & mRecycledTrack->getFlags()
- & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) != 0;
-
- // check if the existing track can be reused as-is, or if a new track needs to be created.
- reuse = true;
-
- if ((mCallbackData == NULL && mCallback != NULL) ||
- (mCallbackData != NULL && mCallback == NULL)) {
- // recycled track uses callbacks but the caller wants to use writes, or vice versa
- ALOGV("can't chain callback and write");
- reuse = false;
- } else if ((mRecycledTrack->getSampleRate() != sampleRate) ||
- (mRecycledTrack->channelCount() != (uint32_t)channelCount) ) {
- ALOGV("samplerate, channelcount differ: %u/%u Hz, %u/%d ch",
- mRecycledTrack->getSampleRate(), sampleRate,
- mRecycledTrack->channelCount(), channelCount);
- reuse = false;
- } else if (flags != mFlags) {
- ALOGV("output flags differ %08x/%08x", flags, mFlags);
- reuse = false;
- } else if (mRecycledTrack->format() != format) {
- reuse = false;
- }
- } else {
- ALOGV("no track available to recycle");
- }
-
- ALOGV_IF(bothOffloaded, "both tracks offloaded");
-
- // If we can't recycle and both tracks are offloaded
- // we must close the previous output before opening a new one
- if (bothOffloaded && !reuse) {
- ALOGV("both offloaded and not recycling");
- deleteRecycledTrack_l();
- }
-
- sp<AudioTrack> t;
- CallbackData *newcbd = NULL;
-
- // We don't attempt to create a new track if we are recycling an
- // offloaded track. But, if we are recycling a non-offloaded or we
- // are switching where one is offloaded and one isn't then we create
- // the new track in advance so that we can read additional stream info
-
- if (!(reuse && bothOffloaded)) {
- ALOGV("creating new AudioTrack");
-
- if (mCallback != NULL) {
- newcbd = new CallbackData(this);
- t = new AudioTrack(
- mStreamType,
- sampleRate,
- format,
- channelMask,
- frameCount,
- flags,
- CallbackWrapper,
- newcbd,
- 0, // notification frames
- mSessionId,
- AudioTrack::TRANSFER_CALLBACK,
- offloadInfo,
- mUid,
- mPid,
- mAttributes,
- doNotReconnect,
- 1.0f, // default value for maxRequiredSpeed
- mSelectedDeviceId);
- } else {
- // TODO: Due to buffer memory concerns, we use a max target playback speed
- // based on mPlaybackRate at the time of open (instead of kMaxRequiredSpeed),
- // also clamping the target speed to 1.0 <= targetSpeed <= kMaxRequiredSpeed.
- const float targetSpeed =
- std::min(std::max(mPlaybackRate.mSpeed, 1.0f), kMaxRequiredSpeed);
- ALOGW_IF(targetSpeed != mPlaybackRate.mSpeed,
- "track target speed:%f clamped from playback speed:%f",
- targetSpeed, mPlaybackRate.mSpeed);
- t = new AudioTrack(
- mStreamType,
- sampleRate,
- format,
- channelMask,
- frameCount,
- flags,
- NULL, // callback
- NULL, // user data
- 0, // notification frames
- mSessionId,
- AudioTrack::TRANSFER_DEFAULT,
- NULL, // offload info
- mUid,
- mPid,
- mAttributes,
- doNotReconnect,
- targetSpeed,
- mSelectedDeviceId);
- }
-
- if ((t == 0) || (t->initCheck() != NO_ERROR)) {
- ALOGE("Unable to create audio track");
- delete newcbd;
- // t goes out of scope, so reference count drops to zero
- return NO_INIT;
- } else {
- // successful AudioTrack initialization implies a legacy stream type was generated
- // from the audio attributes
- mStreamType = t->streamType();
- }
- }
-
- if (reuse) {
- CHECK(mRecycledTrack != NULL);
-
- if (!bothOffloaded) {
- if (mRecycledTrack->frameCount() != t->frameCount()) {
- ALOGV("framecount differs: %zu/%zu frames",
- mRecycledTrack->frameCount(), t->frameCount());
- reuse = false;
- }
- }
-
- if (reuse) {
- ALOGV("chaining to next output and recycling track");
- close_l();
- mTrack = mRecycledTrack;
- mRecycledTrack.clear();
- if (mCallbackData != NULL) {
- mCallbackData->setOutput(this);
- }
- delete newcbd;
- return updateTrack();
- }
- }
-
- // we're not going to reuse the track, unblock and flush it
- // this was done earlier if both tracks are offloaded
- if (!bothOffloaded) {
- deleteRecycledTrack_l();
- }
-
- CHECK((t != NULL) && ((mCallback == NULL) || (newcbd != NULL)));
-
- mCallbackData = newcbd;
- ALOGV("setVolume");
- t->setVolume(mLeftVolume, mRightVolume);
-
- mSampleRateHz = sampleRate;
- mFlags = flags;
- mMsecsPerFrame = 1E3f / (mPlaybackRate.mSpeed * sampleRate);
- mFrameSize = t->frameSize();
- mTrack = t;
-
- return updateTrack();
-}
-
-status_t MediaPlayer2Manager::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 ((mTrack->getFlags()
- & (AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD | AUDIO_OUTPUT_FLAG_DIRECT)) == 0) {
- res = mTrack->setPlaybackRate(mPlaybackRate);
- if (res == NO_ERROR) {
- mTrack->setAuxEffectSendLevel(mSendLevel);
- res = mTrack->attachAuxEffect(mAuxEffectId);
- }
- }
- mTrack->setOutputDevice(mSelectedDeviceId);
- if (mDeviceCallbackEnabled) {
- mTrack->addAudioDeviceCallback(mDeviceCallback.promote());
- }
- ALOGV("updateTrack() DONE status %d", res);
- return res;
-}
-
-status_t MediaPlayer2Manager::AudioOutput::start()
-{
- ALOGV("start");
- Mutex::Autolock lock(mLock);
- if (mCallbackData != NULL) {
- mCallbackData->endTrackSwitch();
- }
- if (mTrack != 0) {
- mTrack->setVolume(mLeftVolume, mRightVolume);
- mTrack->setAuxEffectSendLevel(mSendLevel);
- status_t status = mTrack->start();
- return status;
- }
- return NO_INIT;
-}
-
-void MediaPlayer2Manager::AudioOutput::setNextOutput(const sp<AudioOutput>& nextOutput) {
- Mutex::Autolock lock(mLock);
- mNextOutput = nextOutput;
-}
-
-void MediaPlayer2Manager::AudioOutput::switchToNextOutput() {
- ALOGV("switchToNextOutput");
-
- // Try to acquire the callback lock before moving track (without incurring deadlock).
- const unsigned kMaxSwitchTries = 100;
- Mutex::Autolock lock(mLock);
- for (unsigned tries = 0;;) {
- if (mTrack == 0) {
- return;
- }
- if (mNextOutput != NULL && mNextOutput != this) {
- if (mCallbackData != NULL) {
- // two alternative approaches
-#if 1
- CallbackData *callbackData = mCallbackData;
- mLock.unlock();
- // proper acquisition sequence
- callbackData->lock();
- mLock.lock();
- // Caution: it is unlikely that someone deleted our callback or changed our target
- if (callbackData != mCallbackData || mNextOutput == NULL || mNextOutput == this) {
- // fatal if we are starved out.
- LOG_ALWAYS_FATAL_IF(++tries > kMaxSwitchTries,
- "switchToNextOutput() cannot obtain correct lock sequence");
- callbackData->unlock();
- continue;
- }
- callbackData->mSwitching = true; // begin track switch
- callbackData->setOutput(NULL);
-#else
- // tryBeginTrackSwitch() returns false if the callback has the lock.
- if (!mCallbackData->tryBeginTrackSwitch()) {
- // fatal if we are starved out.
- LOG_ALWAYS_FATAL_IF(++tries > kMaxSwitchTries,
- "switchToNextOutput() cannot obtain callback lock");
- mLock.unlock();
- usleep(5 * 1000 /* usec */); // allow callback to use AudioOutput
- mLock.lock();
- continue;
- }
-#endif
- }
-
- Mutex::Autolock nextLock(mNextOutput->mLock);
-
- // If the next output track is not NULL, then it has been
- // opened already for playback.
- // This is possible even without the next player being started,
- // for example, the next player could be prepared and seeked.
- //
- // Presuming it isn't advisable to force the track over.
- if (mNextOutput->mTrack == NULL) {
- ALOGD("Recycling track for gapless playback");
- delete mNextOutput->mCallbackData;
- mNextOutput->mCallbackData = mCallbackData;
- mNextOutput->mRecycledTrack = mTrack;
- mNextOutput->mSampleRateHz = mSampleRateHz;
- mNextOutput->mMsecsPerFrame = mMsecsPerFrame;
- mNextOutput->mFlags = mFlags;
- mNextOutput->mFrameSize = mFrameSize;
- close_l();
- mCallbackData = NULL; // destruction handled by mNextOutput
- } else {
- ALOGW("Ignoring gapless playback because next player has already started");
- // remove track in case resource needed for future players.
- if (mCallbackData != NULL) {
- mCallbackData->endTrackSwitch(); // release lock for callbacks before close.
- }
- close_l();
- }
- }
- break;
- }
-}
-
-ssize_t MediaPlayer2Manager::AudioOutput::write(const void* buffer, size_t size, bool blocking)
-{
- Mutex::Autolock lock(mLock);
- LOG_ALWAYS_FATAL_IF(mCallback != NULL, "Don't call write if supplying a callback.");
-
- //ALOGV("write(%p, %u)", buffer, size);
- if (mTrack != 0) {
- return mTrack->write(buffer, size, blocking);
- }
- return NO_INIT;
-}
-
-void MediaPlayer2Manager::AudioOutput::stop()
-{
- ALOGV("stop");
- Mutex::Autolock lock(mLock);
- if (mTrack != 0) mTrack->stop();
-}
-
-void MediaPlayer2Manager::AudioOutput::flush()
-{
- ALOGV("flush");
- Mutex::Autolock lock(mLock);
- if (mTrack != 0) mTrack->flush();
-}
-
-void MediaPlayer2Manager::AudioOutput::pause()
-{
- ALOGV("pause");
- Mutex::Autolock lock(mLock);
- if (mTrack != 0) mTrack->pause();
-}
-
-void MediaPlayer2Manager::AudioOutput::close()
-{
- ALOGV("close");
- sp<AudioTrack> track;
- {
- Mutex::Autolock lock(mLock);
- track = mTrack;
- close_l(); // clears mTrack
- }
- // destruction of the track occurs outside of mutex.
-}
-
-void MediaPlayer2Manager::AudioOutput::setVolume(float left, float right)
-{
- ALOGV("setVolume(%f, %f)", left, right);
- Mutex::Autolock lock(mLock);
- mLeftVolume = left;
- mRightVolume = right;
- if (mTrack != 0) {
- mTrack->setVolume(left, right);
- }
-}
-
-status_t MediaPlayer2Manager::AudioOutput::setPlaybackRate(const AudioPlaybackRate &rate)
-{
- ALOGV("setPlaybackRate(%f %f %d %d)",
- rate.mSpeed, rate.mPitch, rate.mFallbackMode, rate.mStretchMode);
- Mutex::Autolock lock(mLock);
- if (mTrack == 0) {
- // remember rate so that we can set it when the track is opened
- mPlaybackRate = rate;
- return OK;
- }
- status_t res = mTrack->setPlaybackRate(rate);
- if (res != NO_ERROR) {
- return res;
- }
- // rate.mSpeed is always greater than 0 if setPlaybackRate succeeded
- CHECK_GT(rate.mSpeed, 0.f);
- mPlaybackRate = rate;
- if (mSampleRateHz != 0) {
- mMsecsPerFrame = 1E3f / (rate.mSpeed * mSampleRateHz);
- }
- return res;
-}
-
-status_t MediaPlayer2Manager::AudioOutput::getPlaybackRate(AudioPlaybackRate *rate)
-{
- ALOGV("setPlaybackRate");
- Mutex::Autolock lock(mLock);
- if (mTrack == 0) {
- return NO_INIT;
- }
- *rate = mTrack->getPlaybackRate();
- return NO_ERROR;
-}
-
-status_t MediaPlayer2Manager::AudioOutput::setAuxEffectSendLevel(float level)
-{
- ALOGV("setAuxEffectSendLevel(%f)", level);
- Mutex::Autolock lock(mLock);
- mSendLevel = level;
- if (mTrack != 0) {
- return mTrack->setAuxEffectSendLevel(level);
- }
- return NO_ERROR;
-}
-
-status_t MediaPlayer2Manager::AudioOutput::attachAuxEffect(int effectId)
-{
- ALOGV("attachAuxEffect(%d)", effectId);
- Mutex::Autolock lock(mLock);
- mAuxEffectId = effectId;
- if (mTrack != 0) {
- return mTrack->attachAuxEffect(effectId);
- }
- return NO_ERROR;
-}
-
-status_t MediaPlayer2Manager::AudioOutput::setOutputDevice(audio_port_handle_t deviceId)
-{
- ALOGV("setOutputDevice(%d)", deviceId);
- Mutex::Autolock lock(mLock);
- mSelectedDeviceId = deviceId;
- if (mTrack != 0) {
- return mTrack->setOutputDevice(deviceId);
- }
- return NO_ERROR;
-}
-
-status_t MediaPlayer2Manager::AudioOutput::getRoutedDeviceId(audio_port_handle_t* deviceId)
-{
- ALOGV("getRoutedDeviceId");
- Mutex::Autolock lock(mLock);
- if (mTrack != 0) {
- mRoutedDeviceId = mTrack->getRoutedDeviceId();
- }
- *deviceId = mRoutedDeviceId;
- return NO_ERROR;
-}
-
-status_t MediaPlayer2Manager::AudioOutput::enableAudioDeviceCallback(bool enabled)
-{
- ALOGV("enableAudioDeviceCallback, %d", enabled);
- Mutex::Autolock lock(mLock);
- mDeviceCallbackEnabled = enabled;
- if (mTrack != 0) {
- status_t status;
- if (enabled) {
- status = mTrack->addAudioDeviceCallback(mDeviceCallback.promote());
- } else {
- status = mTrack->removeAudioDeviceCallback(mDeviceCallback.promote());
- }
- return status;
- }
- return NO_ERROR;
-}
-
-// static
-void MediaPlayer2Manager::AudioOutput::CallbackWrapper(
- int event, void *cookie, void *info) {
- //ALOGV("callbackwrapper");
- CallbackData *data = (CallbackData*)cookie;
- // lock to ensure we aren't caught in the middle of a track switch.
- data->lock();
- AudioOutput *me = data->getOutput();
- AudioTrack::Buffer *buffer = (AudioTrack::Buffer *)info;
- if (me == NULL) {
- // no output set, likely because the track was scheduled to be reused
- // by another player, but the format turned out to be incompatible.
- data->unlock();
- if (buffer != NULL) {
- buffer->size = 0;
- }
- return;
- }
-
- switch(event) {
- case AudioTrack::EVENT_MORE_DATA: {
- size_t actualSize = (*me->mCallback)(
- me, buffer->raw, buffer->size, me->mCallbackCookie,
- CB_EVENT_FILL_BUFFER);
-
- // Log when no data is returned from the callback.
- // (1) We may have no data (especially with network streaming sources).
- // (2) We may have reached the EOS and the audio track is not stopped yet.
- // Note that AwesomePlayer/AudioPlayer will only return zero size when it reaches the EOS.
- // NuPlayer2Renderer will return zero when it doesn't have data (it doesn't block to fill).
- //
- // This is a benign busy-wait, with the next data request generated 10 ms or more later;
- // nevertheless for power reasons, we don't want to see too many of these.
-
- ALOGV_IF(actualSize == 0 && buffer->size > 0, "callbackwrapper: empty buffer returned");
-
- buffer->size = actualSize;
- } break;
-
- case AudioTrack::EVENT_STREAM_END:
- // currently only occurs for offloaded callbacks
- ALOGV("callbackwrapper: deliver EVENT_STREAM_END");
- (*me->mCallback)(me, NULL /* buffer */, 0 /* size */,
- me->mCallbackCookie, CB_EVENT_STREAM_END);
- break;
-
- case AudioTrack::EVENT_NEW_IAUDIOTRACK :
- ALOGV("callbackwrapper: deliver EVENT_TEAR_DOWN");
- (*me->mCallback)(me, NULL /* buffer */, 0 /* size */,
- me->mCallbackCookie, CB_EVENT_TEAR_DOWN);
- break;
-
- case AudioTrack::EVENT_UNDERRUN:
- // This occurs when there is no data available, typically
- // when there is a failure to supply data to the AudioTrack. It can also
- // occur in non-offloaded mode when the audio device comes out of standby.
- //
- // If an AudioTrack underruns it outputs silence. Since this happens suddenly
- // it may sound like an audible pop or glitch.
- //
- // The underrun event is sent once per track underrun; the condition is reset
- // when more data is sent to the AudioTrack.
- ALOGD("callbackwrapper: EVENT_UNDERRUN (discarded)");
- break;
-
- default:
- ALOGE("received unknown event type: %d inside CallbackWrapper !", event);
- }
-
- data->unlock();
-}
-
-audio_session_t MediaPlayer2Manager::AudioOutput::getSessionId() const
-{
- Mutex::Autolock lock(mLock);
- return mSessionId;
-}
-
-uint32_t MediaPlayer2Manager::AudioOutput::getSampleRate() const
-{
- Mutex::Autolock lock(mLock);
- if (mTrack == 0) return 0;
- return mTrack->getSampleRate();
-}
-
-int64_t MediaPlayer2Manager::AudioOutput::getBufferDurationInUs() const
-{
- Mutex::Autolock lock(mLock);
- if (mTrack == 0) {
- return 0;
- }
- int64_t duration;
- if (mTrack->getBufferDurationInUs(&duration) != OK) {
- return 0;
- }
- return duration;
-}
-
-} // namespace android
diff --git a/media/libmediaplayer2/MediaPlayer2Manager.h b/media/libmediaplayer2/MediaPlayer2Manager.h
deleted file mode 100644
index 7e6f0de..0000000
--- a/media/libmediaplayer2/MediaPlayer2Manager.h
+++ /dev/null
@@ -1,385 +0,0 @@
-/*
-**
-** Copyright 2017, 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_MEDIAPLAYER2MANAGER_H
-#define ANDROID_MEDIAPLAYER2MANAGER_H
-
-#include <arpa/inet.h>
-
-#include <utils/threads.h>
-#include <utils/Errors.h>
-#include <utils/KeyedVector.h>
-#include <utils/String8.h>
-#include <utils/Vector.h>
-
-#include <media/Metadata.h>
-#include <media/stagefright/foundation/ABase.h>
-#include <mediaplayer2/MediaPlayer2Engine.h>
-#include <mediaplayer2/MediaPlayer2Interface.h>
-
-#include <system/audio.h>
-
-namespace android {
-
-struct ANativeWindowWrapper;
-struct AudioPlaybackRate;
-class AudioTrack;
-struct AVSyncSettings;
-class DataSource;
-struct MediaHTTPService;
-class MediaPlayer2EngineClient;
-
-#define CALLBACK_ANTAGONIZER 0
-#if CALLBACK_ANTAGONIZER
-class Antagonizer {
-public:
- Antagonizer(
- MediaPlayer2Interface::NotifyCallback cb,
- const wp<MediaPlayer2Engine> &client);
- void start() { mActive = true; }
- void stop() { mActive = false; }
- void kill();
-private:
- static const int interval;
- Antagonizer();
- static int callbackThread(void *cookie);
- Mutex mLock;
- Condition mCondition;
- bool mExit;
- bool mActive;
- wp<MediaPlayer2Engine> mClient;
- MediaPlayer2Interface::NotifyCallback mCb;
-};
-#endif
-
-class MediaPlayer2Manager {
- class Client;
-
- class AudioOutput : public MediaPlayer2Interface::AudioSink
- {
- class CallbackData;
-
- public:
- AudioOutput(
- audio_session_t sessionId,
- uid_t uid,
- int pid,
- const audio_attributes_t * attr,
- const sp<AudioSystem::AudioDeviceCallback>& deviceCallback);
- virtual ~AudioOutput();
-
- virtual bool ready() const { return mTrack != 0; }
- virtual ssize_t bufferSize() const;
- virtual ssize_t frameCount() const;
- virtual ssize_t channelCount() const;
- virtual ssize_t frameSize() const;
- virtual uint32_t latency() const;
- virtual float msecsPerFrame() const;
- virtual status_t getPosition(uint32_t *position) const;
- virtual status_t getTimestamp(AudioTimestamp &ts) const;
- virtual int64_t getPlayedOutDurationUs(int64_t nowUs) const;
- virtual status_t getFramesWritten(uint32_t *frameswritten) const;
- virtual audio_session_t getSessionId() const;
- virtual uint32_t getSampleRate() const;
- virtual int64_t getBufferDurationInUs() const;
-
- virtual status_t open(
- uint32_t sampleRate, int channelCount, audio_channel_mask_t channelMask,
- audio_format_t format, int bufferCount,
- AudioCallback cb, void *cookie,
- audio_output_flags_t flags = AUDIO_OUTPUT_FLAG_NONE,
- const audio_offload_info_t *offloadInfo = NULL,
- bool doNotReconnect = false,
- uint32_t suggestedFrameCount = 0);
-
- virtual status_t start();
- virtual ssize_t write(const void* buffer, size_t size, bool blocking = true);
- virtual void stop();
- virtual void flush();
- virtual void pause();
- virtual void close();
- void setAudioStreamType(audio_stream_type_t streamType);
- virtual audio_stream_type_t getAudioStreamType() const { return mStreamType; }
- void setAudioAttributes(const audio_attributes_t * attributes);
-
- void setVolume(float left, float right);
- virtual status_t setPlaybackRate(const AudioPlaybackRate& rate);
- virtual status_t getPlaybackRate(AudioPlaybackRate* rate /* nonnull */);
-
- status_t setAuxEffectSendLevel(float level);
- status_t attachAuxEffect(int effectId);
- virtual status_t dump(int fd, const Vector<String16>& args) const;
-
- static bool isOnEmulator();
- static int getMinBufferCount();
- void setNextOutput(const sp<AudioOutput>& nextOutput);
- void switchToNextOutput();
- virtual bool needsTrailingPadding() { return mNextOutput == NULL; }
- virtual status_t setParameters(const String8& keyValuePairs);
- virtual String8 getParameters(const String8& keys);
-
- // AudioRouting
- virtual status_t setOutputDevice(audio_port_handle_t deviceId);
- virtual status_t getRoutedDeviceId(audio_port_handle_t* deviceId);
- virtual status_t enableAudioDeviceCallback(bool enabled);
-
- private:
- static void setMinBufferCount();
- static void CallbackWrapper(
- int event, void *me, void *info);
- void deleteRecycledTrack_l();
- void close_l();
- status_t updateTrack();
-
- sp<AudioTrack> mTrack;
- sp<AudioTrack> mRecycledTrack;
- sp<AudioOutput> mNextOutput;
- AudioCallback mCallback;
- void * mCallbackCookie;
- CallbackData * mCallbackData;
- audio_stream_type_t mStreamType;
- audio_attributes_t * mAttributes;
- float mLeftVolume;
- float mRightVolume;
- AudioPlaybackRate mPlaybackRate;
- uint32_t mSampleRateHz; // sample rate of the content, as set in open()
- float mMsecsPerFrame;
- size_t mFrameSize;
- audio_session_t mSessionId;
- uid_t mUid;
- int mPid;
- float mSendLevel;
- int mAuxEffectId;
- audio_output_flags_t mFlags;
- audio_port_handle_t mSelectedDeviceId;
- audio_port_handle_t mRoutedDeviceId;
- bool mDeviceCallbackEnabled;
- wp<AudioSystem::AudioDeviceCallback> mDeviceCallback;
- mutable Mutex mLock;
-
- // static variables below not protected by mutex
- static bool mIsOnEmulator;
- static int mMinBufferCount; // 12 for emulator; otherwise 4
-
- // CallbackData is what is passed to the AudioTrack as the "user" data.
- // We need to be able to target this to a different Output on the fly,
- // so we can't use the Output itself for this.
- class CallbackData {
- friend AudioOutput;
- public:
- explicit CallbackData(AudioOutput *cookie) {
- mData = cookie;
- mSwitching = false;
- }
- AudioOutput * getOutput() const { return mData; }
- void setOutput(AudioOutput* newcookie) { mData = newcookie; }
- // lock/unlock are used by the callback before accessing the payload of this object
- void lock() const { mLock.lock(); }
- void unlock() const { mLock.unlock(); }
-
- // tryBeginTrackSwitch/endTrackSwitch are used when the CallbackData is handed over
- // to the next sink.
-
- // tryBeginTrackSwitch() returns true only if it obtains the lock.
- bool tryBeginTrackSwitch() {
- LOG_ALWAYS_FATAL_IF(mSwitching, "tryBeginTrackSwitch() already called");
- if (mLock.tryLock() != OK) {
- return false;
- }
- mSwitching = true;
- return true;
- }
- void endTrackSwitch() {
- if (mSwitching) {
- mLock.unlock();
- }
- mSwitching = false;
- }
- private:
- AudioOutput * mData;
- mutable Mutex mLock; // a recursive mutex might make this unnecessary.
- bool mSwitching;
- DISALLOW_EVIL_CONSTRUCTORS(CallbackData);
- };
-
- }; // AudioOutput
-
-
-public:
- MediaPlayer2Manager();
- virtual ~MediaPlayer2Manager();
-
- static MediaPlayer2Manager& get();
-
- // MediaPlayer2Manager interface
- virtual sp<MediaPlayer2Engine> create(const sp<MediaPlayer2EngineClient>& client,
- audio_session_t audioSessionId);
-
- virtual status_t dump(int fd, const Vector<String16>& args);
-
- void removeClient(const wp<Client>& client);
- bool hasClient(wp<Client> client);
-
-private:
- class Client : public MediaPlayer2Engine {
- // MediaPlayer2Engine interface
- virtual void disconnect();
- virtual status_t setVideoSurfaceTexture(
- const sp<ANativeWindowWrapper>& nww) override;
- virtual status_t setBufferingSettings(const BufferingSettings& buffering) override;
- virtual status_t getBufferingSettings(
- BufferingSettings* buffering /* nonnull */) override;
- virtual status_t prepareAsync();
- virtual status_t start();
- virtual status_t stop();
- virtual status_t pause();
- virtual status_t isPlaying(bool* state);
- virtual status_t setPlaybackSettings(const AudioPlaybackRate& rate);
- virtual status_t getPlaybackSettings(AudioPlaybackRate* rate /* nonnull */);
- virtual status_t setSyncSettings(const AVSyncSettings& rate, float videoFpsHint);
- virtual status_t getSyncSettings(AVSyncSettings* rate /* nonnull */,
- float* videoFps /* nonnull */);
- virtual status_t seekTo(
- int msec,
- MediaPlayer2SeekMode mode = MediaPlayer2SeekMode::SEEK_PREVIOUS_SYNC);
- virtual status_t getCurrentPosition(int* msec);
- virtual status_t getDuration(int* msec);
- virtual status_t reset();
- virtual status_t notifyAt(int64_t mediaTimeUs);
- virtual status_t setAudioStreamType(audio_stream_type_t type);
- virtual status_t setLooping(int loop);
- virtual status_t setVolume(float leftVolume, float rightVolume);
- virtual status_t invoke(const Parcel& request, Parcel *reply);
- virtual status_t setMetadataFilter(const Parcel& filter);
- virtual status_t getMetadata(bool update_only,
- bool apply_filter,
- Parcel *reply);
- virtual status_t setAuxEffectSendLevel(float level);
- virtual status_t attachAuxEffect(int effectId);
- virtual status_t setParameter(int key, const Parcel &request);
- virtual status_t getParameter(int key, Parcel *reply);
- virtual status_t setNextPlayer(const sp<MediaPlayer2Engine>& player);
-
- virtual status_t setDataSource(const sp<DataSourceDesc> &dsd);
-
- static void notify(const wp<MediaPlayer2Engine> &listener, int64_t srcId,
- int msg, int ext1, int ext2, const Parcel *obj);
-
- pid_t pid() const { return mPid; }
- virtual status_t dump(int fd, const Vector<String16>& args);
-
- audio_session_t getAudioSessionId() { return mAudioSessionId; }
- // Modular DRM
- virtual status_t prepareDrm(const uint8_t uuid[16], const Vector<uint8_t>& drmSessionId);
- virtual status_t releaseDrm();
- // AudioRouting
- virtual status_t setOutputDevice(audio_port_handle_t deviceId);
- virtual status_t getRoutedDeviceId(audio_port_handle_t* deviceId);
- virtual status_t enableAudioDeviceCallback(bool enabled);
-
- private:
- class AudioDeviceUpdatedNotifier: public AudioSystem::AudioDeviceCallback
- {
- public:
- AudioDeviceUpdatedNotifier(const sp<MediaPlayer2Interface>& listener) {
- mListener = listener;
- }
- ~AudioDeviceUpdatedNotifier() {}
-
- virtual void onAudioDeviceUpdate(audio_io_handle_t audioIo,
- audio_port_handle_t deviceId);
-
- private:
- wp<MediaPlayer2Interface> mListener;
- };
-
- friend class MediaPlayer2Manager;
- Client(pid_t pid,
- int32_t connId,
- const sp<MediaPlayer2EngineClient>& client,
- audio_session_t audioSessionId,
- uid_t uid);
- Client();
- virtual ~Client();
- bool init();
-
- void deletePlayer();
-
- sp<MediaPlayer2Interface> getPlayer() const { Mutex::Autolock lock(mLock); return mPlayer; }
-
-
-
- // @param type Of the metadata to be tested.
- // @return true if the metadata should be dropped according to
- // the filters.
- bool shouldDropMetadata(media::Metadata::Type type) const;
-
- // Add a new element to the set of metadata updated. Noop if
- // the element exists already.
- // @param type Of the metadata to be recorded.
- void addNewMetadataUpdate(media::Metadata::Type type);
-
- // Disconnect from the currently connected ANativeWindow.
- void disconnectNativeWindow_l();
-
- status_t setAudioAttributes_l(const Parcel &request);
-
- mutable Mutex mLock;
- sp<MediaPlayer2Interface> mPlayer;
- sp<MediaPlayer2EngineClient> mClient;
- sp<AudioOutput> mAudioOutput;
- pid_t mPid;
- status_t mStatus;
- bool mLoop;
- int32_t mConnId;
- audio_session_t mAudioSessionId;
- audio_attributes_t * mAudioAttributes;
- uid_t mUid;
- sp<ANativeWindowWrapper> mConnectedWindow;
- sp<Client> mNextClient;
-
- // Metadata filters.
- media::Metadata::Filter mMetadataAllow; // protected by mLock
- media::Metadata::Filter mMetadataDrop; // protected by mLock
-
- // Metadata updated. For each MEDIA_INFO_METADATA_UPDATE
- // notification we try to update mMetadataUpdated which is a
- // set: no duplicate.
- // getMetadata clears this set.
- media::Metadata::Filter mMetadataUpdated; // protected by mLock
-
- sp<AudioDeviceUpdatedNotifier> mAudioDeviceUpdatedListener;
-#if CALLBACK_ANTAGONIZER
- Antagonizer* mAntagonizer;
-#endif
- }; // Client
-
-// ----------------------------------------------------------------------------
-
- pid_t mPid;
- uid_t mUid;
-
- mutable Mutex mLock;
- SortedVector< wp<Client> > mClients;
- int32_t mNextConnId;
-};
-
-// ----------------------------------------------------------------------------
-
-}; // namespace android
-
-#endif // ANDROID_MEDIAPLAYER2MANAGER_H
diff --git a/media/libmediaplayer2/include/mediaplayer2/JAudioTrack.h b/media/libmediaplayer2/include/mediaplayer2/JAudioTrack.h
index 10fa5e8..301825b 100644
--- a/media/libmediaplayer2/include/mediaplayer2/JAudioTrack.h
+++ b/media/libmediaplayer2/include/mediaplayer2/JAudioTrack.h
@@ -19,6 +19,7 @@
#include <jni.h>
#include <media/AudioResamplerPublic.h>
+#include <media/AudioSystem.h>
#include <media/VolumeShaper.h>
#include <system/audio.h>
#include <utils/Errors.h>
@@ -31,6 +32,42 @@
class JAudioTrack {
public:
+ /* Events used by AudioTrack callback function (callback_t).
+ * Keep in sync with frameworks/base/media/java/android/media/AudioTrack.java NATIVE_EVENT_*.
+ */
+ enum event_type {
+ EVENT_MORE_DATA = 0, // Request to write more data to buffer.
+ EVENT_NEW_IAUDIOTRACK = 6, // IAudioTrack was re-created, either due to re-routing and
+ // voluntary invalidation by mediaserver, or mediaserver crash.
+ EVENT_STREAM_END = 7, // Sent after all the buffers queued in AF and HW are played
+ // back (after stop is called) for an offloaded track.
+ };
+
+ class Buffer
+ {
+ public:
+ size_t mSize; // input/output in bytes.
+ void* mData; // pointer to the audio data.
+ };
+
+ /* As a convenience, if a callback is supplied, a handler thread
+ * is automatically created with the appropriate priority. This thread
+ * invokes the callback when a new buffer becomes available or various conditions occur.
+ *
+ * Parameters:
+ *
+ * event: type of event notified (see enum AudioTrack::event_type).
+ * user: Pointer to context for use by the callback receiver.
+ * info: Pointer to optional parameter according to event type:
+ * - EVENT_MORE_DATA: pointer to JAudioTrack::Buffer struct. The callback must not
+ * write more bytes than indicated by 'size' field and update 'size' if fewer bytes
+ * are written.
+ * - EVENT_NEW_IAUDIOTRACK: unused.
+ * - EVENT_STREAM_END: unused.
+ */
+
+ typedef void (*callback_t)(int event, void* user, void *info);
+
/* Creates an JAudioTrack object for non-offload mode.
* Once created, the track needs to be started before it can be used.
* Unspecified values are set to appropriate default values.
@@ -49,6 +86,9 @@
* output sink.
* (TODO: How can we check whether a format is supported?)
* channelMask: Channel mask, such that audio_is_output_channel(channelMask) is true.
+ * cbf: Callback function. If not null, this function is called periodically
+ * to provide new data and inform of marker, position updates, etc.
+ * user: Context for use by the callback receiver.
* frameCount: Minimum size of track PCM buffer in frames. This defines the
* application's contribution to the latency of the track.
* The actual size selected by the JAudioTrack could be larger if the
@@ -68,35 +108,20 @@
uint32_t sampleRate,
audio_format_t format,
audio_channel_mask_t channelMask,
+ callback_t cbf,
+ void* user,
size_t frameCount = 0,
audio_session_t sessionId = AUDIO_SESSION_ALLOCATE,
const audio_attributes_t* pAttributes = NULL,
float maxRequiredSpeed = 1.0f);
/*
- Temporarily removed constructor arguments:
-
- // Q. Values are in audio-base.h, but where can we find explanation for them?
- audio_output_flags_t flags,
-
// Q. May be used in AudioTrack.setPreferredDevice(AudioDeviceInfo)?
audio_port_handle_t selectedDeviceId,
- // Should be deleted, since we don't use Binder anymore.
- bool doNotReconnect,
-
- // Do we need UID and PID?
- uid_t uid,
- pid_t pid,
-
- // TODO: Uses these values when Java AudioTrack supports the offload mode.
- callback_t cbf,
- void* user,
+ // TODO: No place to use these values.
int32_t notificationFrames,
const audio_offload_info_t *offloadInfo,
-
- // Fixed to false, but what is this?
- threadCanCallJava
*/
virtual ~JAudioTrack();
@@ -138,6 +163,46 @@
*/
bool getTimestamp(AudioTimestamp& timestamp);
+ // TODO: This doc is just copied from AudioTrack.h. Revise it after implemenation.
+ /* Return the extended timestamp, with additional timebase info and improved drain behavior.
+ *
+ * This is similar to the AudioTrack.java API:
+ * getTimestamp(@NonNull AudioTimestamp timestamp, @AudioTimestamp.Timebase int timebase)
+ *
+ * Some differences between this method and the getTimestamp(AudioTimestamp& timestamp) method
+ *
+ * 1. stop() by itself does not reset the frame position.
+ * A following start() resets the frame position to 0.
+ * 2. flush() by itself does not reset the frame position.
+ * The frame position advances by the number of frames flushed,
+ * when the first frame after flush reaches the audio sink.
+ * 3. BOOTTIME clock offsets are provided to help synchronize with
+ * non-audio streams, e.g. sensor data.
+ * 4. Position is returned with 64 bits of resolution.
+ *
+ * Parameters:
+ * timestamp: A pointer to the caller allocated ExtendedTimestamp.
+ *
+ * Returns NO_ERROR on success; timestamp is filled with valid data.
+ * BAD_VALUE if timestamp is NULL.
+ * WOULD_BLOCK if called immediately after start() when the number
+ * of frames consumed is less than the
+ * overall hardware latency to physical output. In WOULD_BLOCK cases,
+ * one might poll again, or use getPosition(), or use 0 position and
+ * current time for the timestamp.
+ * If WOULD_BLOCK is returned, the timestamp is still
+ * modified with the LOCATION_CLIENT portion filled.
+ * DEAD_OBJECT if AudioFlinger dies or the output device changes and
+ * the track cannot be automatically restored.
+ * The application needs to recreate the AudioTrack
+ * because the audio device changed or AudioFlinger died.
+ * This typically occurs for direct or offloaded tracks
+ * or if mDoNotReconnect is true.
+ * INVALID_OPERATION if called on a offloaded or direct track.
+ * Use getTimestamp(AudioTimestamp& timestamp) instead.
+ */
+ status_t getTimestamp(ExtendedTimestamp *timestamp);
+
/* Set source playback rate for timestretch
* 1.0 is normal speed: < 1.0 is slower, > 1.0 is faster
* 1.0 is normal pitch: < 1.0 is lower pitch, > 1.0 is higher pitch
@@ -270,7 +335,65 @@
*/
audio_port_handle_t getRoutedDeviceId();
+ /* Returns the ID of the audio session this AudioTrack belongs to. */
+ audio_session_t getAudioSessionId();
+
+ /* Selects the audio device to use for output of this AudioTrack. A value of
+ * AUDIO_PORT_HANDLE_NONE indicates default routing.
+ *
+ * Parameters:
+ * The device ID of the selected device (as returned by the AudioDevicesManager API).
+ *
+ * Returned value:
+ * - NO_ERROR: successful operation
+ * - BAD_VALUE: failed to find the valid output device with given device Id.
+ */
+ status_t setOutputDevice(audio_port_handle_t deviceId);
+
+ // TODO: Add AUDIO_OUTPUT_FLAG_DIRECT when it is possible to check.
+ // TODO: Add AUDIO_FLAG_HW_AV_SYNC when it is possible to check.
+ /* Returns the flags */
+ audio_output_flags_t getFlags() const { return mFlags; }
+
+ /* Obtain the pending duration in milliseconds for playback of pure PCM data remaining in
+ * AudioTrack.
+ *
+ * Returns NO_ERROR if successful.
+ * INVALID_OPERATION if the AudioTrack does not contain pure PCM data.
+ * BAD_VALUE if msec is nullptr.
+ */
+ status_t pendingDuration(int32_t *msec);
+
+ /* Adds an AudioDeviceCallback. The caller will be notified when the audio device to which this
+ * AudioTrack is routed is updated.
+ * Replaces any previously installed callback.
+ *
+ * Parameters:
+ *
+ * callback: The callback interface
+ *
+ * Returns NO_ERROR if successful.
+ * INVALID_OPERATION if the same callback is already installed.
+ * NO_INIT or PREMISSION_DENIED if AudioFlinger service is not reachable
+ * BAD_VALUE if the callback is NULL
+ */
+ status_t addAudioDeviceCallback(const sp<AudioSystem::AudioDeviceCallback>& callback);
+
+ /* Removes an AudioDeviceCallback.
+ *
+ * Parameters:
+ *
+ * callback: The callback interface
+ *
+ * Returns NO_ERROR if successful.
+ * INVALID_OPERATION if the callback is not installed
+ * BAD_VALUE if the callback is NULL
+ */
+ status_t removeAudioDeviceCallback(const sp<AudioSystem::AudioDeviceCallback>& callback);
+
private:
+ audio_output_flags_t mFlags;
+
jclass mAudioTrackCls;
jobject mAudioTrackObj;
@@ -282,6 +405,12 @@
jobject createVolumeShaperOperationObj(
const sp<media::VolumeShaper::Operation>& operation);
+ /* Creates a Java StreamEventCallback object */
+ jobject createStreamEventCallback(callback_t cbf, void* user);
+
+ /* Creates a Java Executor object for running a callback */
+ jobject createCallbackExecutor();
+
status_t javaToNativeStatus(int javaStatus);
};
diff --git a/media/libmediaplayer2/include/mediaplayer2/MediaPlayer2AudioOutput.h b/media/libmediaplayer2/include/mediaplayer2/MediaPlayer2AudioOutput.h
new file mode 100644
index 0000000..5d5b8e4
--- /dev/null
+++ b/media/libmediaplayer2/include/mediaplayer2/MediaPlayer2AudioOutput.h
@@ -0,0 +1,191 @@
+/*
+**
+** Copyright 2018, 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_MEDIAPLAYER2AUDIOOUTPUT_H
+#define ANDROID_MEDIAPLAYER2AUDIOOUTPUT_H
+
+#include <mediaplayer2/MediaPlayer2Interface.h>
+
+#include <utils/String16.h>
+#include <utils/Vector.h>
+
+namespace android {
+
+class AudioTrack;
+
+class MediaPlayer2AudioOutput : public MediaPlayer2Interface::AudioSink
+{
+ class CallbackData;
+
+public:
+ MediaPlayer2AudioOutput(audio_session_t sessionId,
+ uid_t uid,
+ int pid,
+ const audio_attributes_t * attr,
+ const sp<AudioSystem::AudioDeviceCallback>& deviceCallback);
+ virtual ~MediaPlayer2AudioOutput();
+
+ virtual bool ready() const {
+ return mTrack != 0;
+ }
+ virtual ssize_t bufferSize() const;
+ virtual ssize_t frameCount() const;
+ virtual ssize_t channelCount() const;
+ virtual ssize_t frameSize() const;
+ virtual uint32_t latency() const;
+ virtual float msecsPerFrame() const;
+ virtual status_t getPosition(uint32_t *position) const;
+ virtual status_t getTimestamp(AudioTimestamp &ts) const;
+ virtual int64_t getPlayedOutDurationUs(int64_t nowUs) const;
+ virtual status_t getFramesWritten(uint32_t *frameswritten) const;
+ virtual audio_session_t getSessionId() const;
+ virtual uint32_t getSampleRate() const;
+ virtual int64_t getBufferDurationInUs() const;
+
+ virtual status_t open(
+ uint32_t sampleRate, int channelCount, audio_channel_mask_t channelMask,
+ audio_format_t format, int bufferCount,
+ AudioCallback cb, void *cookie,
+ audio_output_flags_t flags = AUDIO_OUTPUT_FLAG_NONE,
+ const audio_offload_info_t *offloadInfo = NULL,
+ bool doNotReconnect = false,
+ uint32_t suggestedFrameCount = 0);
+
+ virtual status_t start();
+ virtual ssize_t write(const void* buffer, size_t size, bool blocking = true);
+ virtual void stop();
+ virtual void flush();
+ virtual void pause();
+ virtual void close();
+ void setAudioStreamType(audio_stream_type_t streamType);
+ virtual audio_stream_type_t getAudioStreamType() const {
+ return mStreamType;
+ }
+ void setAudioAttributes(const audio_attributes_t * attributes);
+
+ void setVolume(float left, float right);
+ virtual status_t setPlaybackRate(const AudioPlaybackRate& rate);
+ virtual status_t getPlaybackRate(AudioPlaybackRate* rate /* nonnull */);
+
+ status_t setAuxEffectSendLevel(float level);
+ status_t attachAuxEffect(int effectId);
+ virtual status_t dump(int fd, const Vector<String16>& args) const;
+
+ static bool isOnEmulator();
+ static int getMinBufferCount();
+ virtual bool needsTrailingPadding() {
+ return true;
+ // TODO: return correct value.
+ //return mNextOutput == NULL;
+ }
+ virtual status_t setParameters(const String8& keyValuePairs);
+ virtual String8 getParameters(const String8& keys);
+
+ // AudioRouting
+ virtual status_t setOutputDevice(audio_port_handle_t deviceId);
+ virtual status_t getRoutedDeviceId(audio_port_handle_t* deviceId);
+ virtual status_t enableAudioDeviceCallback(bool enabled);
+
+private:
+ static void setMinBufferCount();
+ static void CallbackWrapper(int event, void *me, void *info);
+ void deleteRecycledTrack_l();
+ void close_l();
+ status_t updateTrack_l();
+
+ sp<AudioTrack> mTrack;
+ AudioCallback mCallback;
+ void * mCallbackCookie;
+ CallbackData * mCallbackData;
+ audio_stream_type_t mStreamType;
+ audio_attributes_t * mAttributes;
+ float mLeftVolume;
+ float mRightVolume;
+ AudioPlaybackRate mPlaybackRate;
+ uint32_t mSampleRateHz; // sample rate of the content, as set in open()
+ float mMsecsPerFrame;
+ size_t mFrameSize;
+ audio_session_t mSessionId;
+ uid_t mUid;
+ int mPid;
+ float mSendLevel;
+ int mAuxEffectId;
+ audio_output_flags_t mFlags;
+ audio_port_handle_t mSelectedDeviceId;
+ audio_port_handle_t mRoutedDeviceId;
+ bool mDeviceCallbackEnabled;
+ wp<AudioSystem::AudioDeviceCallback> mDeviceCallback;
+ mutable Mutex mLock;
+
+ // static variables below not protected by mutex
+ static bool mIsOnEmulator;
+ static int mMinBufferCount; // 12 for emulator; otherwise 4
+
+ // CallbackData is what is passed to the AudioTrack as the "user" data.
+ // We need to be able to target this to a different Output on the fly,
+ // so we can't use the Output itself for this.
+ class CallbackData {
+ friend MediaPlayer2AudioOutput;
+ public:
+ explicit CallbackData(MediaPlayer2AudioOutput *cookie) {
+ mData = cookie;
+ mSwitching = false;
+ }
+ MediaPlayer2AudioOutput *getOutput() const {
+ return mData;
+ }
+ void setOutput(MediaPlayer2AudioOutput* newcookie) {
+ mData = newcookie;
+ }
+ // lock/unlock are used by the callback before accessing the payload of this object
+ void lock() const {
+ mLock.lock();
+ }
+ void unlock() const {
+ mLock.unlock();
+ }
+
+ // tryBeginTrackSwitch/endTrackSwitch are used when the CallbackData is handed over
+ // to the next sink.
+
+ // tryBeginTrackSwitch() returns true only if it obtains the lock.
+ bool tryBeginTrackSwitch() {
+ LOG_ALWAYS_FATAL_IF(mSwitching, "tryBeginTrackSwitch() already called");
+ if (mLock.tryLock() != OK) {
+ return false;
+ }
+ mSwitching = true;
+ return true;
+ }
+ void endTrackSwitch() {
+ if (mSwitching) {
+ mLock.unlock();
+ }
+ mSwitching = false;
+ }
+
+ private:
+ MediaPlayer2AudioOutput *mData;
+ mutable Mutex mLock; // a recursive mutex might make this unnecessary.
+ bool mSwitching;
+ DISALLOW_EVIL_CONSTRUCTORS(CallbackData);
+ };
+};
+
+}; // namespace android
+
+#endif // ANDROID_MEDIAPLAYER2AUDIOOUTPUT_H
diff --git a/media/libmediaplayer2/include/mediaplayer2/MediaPlayer2Engine.h b/media/libmediaplayer2/include/mediaplayer2/MediaPlayer2Engine.h
deleted file mode 100644
index 2d1a24b..0000000
--- a/media/libmediaplayer2/include/mediaplayer2/MediaPlayer2Engine.h
+++ /dev/null
@@ -1,124 +0,0 @@
-/*
- * Copyright 2017 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_MEDIAPLAYER2ENGINE_H
-#define ANDROID_MEDIAPLAYER2ENGINE_H
-
-#include <utils/RefBase.h>
-#include <binder/Parcel.h>
-#include <utils/KeyedVector.h>
-#include <system/audio.h>
-
-#include <media/MediaSource.h>
-
-// Fwd decl to make sure everyone agrees that the scope of struct sockaddr_in is
-// global, and not in android::
-struct sockaddr_in;
-
-namespace android {
-
-struct ANativeWindowWrapper;
-struct AVSyncSettings;
-struct AudioPlaybackRate;
-struct BufferingSettings;
-class DataSource;
-struct DataSourceDesc;
-struct IStreamSource;
-struct MediaHTTPService;
-class Parcel;
-
-typedef MediaSource::ReadOptions::SeekMode MediaPlayer2SeekMode;
-
-class MediaPlayer2Engine: public RefBase
-{
-public:
- virtual void disconnect() = 0;
-
- virtual status_t setDataSource(const sp<DataSourceDesc>& source) = 0;
- virtual status_t setVideoSurfaceTexture(const sp<ANativeWindowWrapper>& nww) = 0;
- virtual status_t getBufferingSettings(
- BufferingSettings* buffering /* nonnull */) = 0;
- virtual status_t setBufferingSettings(const BufferingSettings& buffering) = 0;
- virtual status_t prepareAsync() = 0;
- virtual status_t start() = 0;
- virtual status_t stop() = 0;
- virtual status_t pause() = 0;
- virtual status_t isPlaying(bool* state) = 0;
- virtual status_t setPlaybackSettings(const AudioPlaybackRate& rate) = 0;
- virtual status_t getPlaybackSettings(AudioPlaybackRate* rate /* nonnull */) = 0;
- virtual status_t setSyncSettings(const AVSyncSettings& sync, float videoFpsHint) = 0;
- virtual status_t getSyncSettings(AVSyncSettings* sync /* nonnull */,
- float* videoFps /* nonnull */) = 0;
- virtual status_t seekTo(
- int msec,
- MediaPlayer2SeekMode mode = MediaPlayer2SeekMode::SEEK_PREVIOUS_SYNC) = 0;
- virtual status_t getCurrentPosition(int* msec) = 0;
- virtual status_t getDuration(int* msec) = 0;
- virtual status_t notifyAt(int64_t mediaTimeUs) = 0;
- virtual status_t reset() = 0;
- virtual status_t setAudioStreamType(audio_stream_type_t type) = 0;
- virtual status_t setLooping(int loop) = 0;
- virtual status_t setVolume(float leftVolume, float rightVolume) = 0;
- virtual status_t setAuxEffectSendLevel(float level) = 0;
- virtual status_t attachAuxEffect(int effectId) = 0;
- virtual status_t setParameter(int key, const Parcel& request) = 0;
- virtual status_t getParameter(int key, Parcel* reply) = 0;
- virtual status_t setNextPlayer(const sp<MediaPlayer2Engine>& next) = 0;
-
- // Modular DRM
- virtual status_t prepareDrm(const uint8_t uuid[16],
- const Vector<uint8_t>& drmSessionId) = 0;
- virtual status_t releaseDrm() = 0;
-
- // Invoke a generic method on the player by using opaque parcels
- // for the request and reply.
- // @param request Parcel that must start with the media player
- // interface token.
- // @param[out] reply Parcel to hold the reply data. Cannot be null.
- // @return OK if the invocation was made successfully.
- virtual status_t invoke(const Parcel& request, Parcel *reply) = 0;
-
- // Set a new metadata filter.
- // @param filter A set of allow and drop rules serialized in a Parcel.
- // @return OK if the invocation was made successfully.
- virtual status_t setMetadataFilter(const Parcel& filter) = 0;
-
- // Retrieve a set of metadata.
- // @param update_only Include only the metadata that have changed
- // since the last invocation of getMetadata.
- // The set is built using the unfiltered
- // notifications the native player sent to the
- // MediaPlayer2Manager during that period of
- // time. If false, all the metadatas are considered.
- // @param apply_filter If true, once the metadata set has been built based
- // on the value update_only, the current filter is
- // applied.
- // @param[out] metadata On exit contains a set (possibly empty) of metadata.
- // Valid only if the call returned OK.
- // @return OK if the invocation was made successfully.
- virtual status_t getMetadata(bool update_only,
- bool apply_filter,
- Parcel *metadata) = 0;
-
- // AudioRouting
- virtual status_t setOutputDevice(audio_port_handle_t deviceId) = 0;
- virtual status_t getRoutedDeviceId(audio_port_handle_t *deviceId) = 0;
- virtual status_t enableAudioDeviceCallback(bool enabled) = 0;
-};
-
-}; // namespace android
-
-#endif // ANDROID_MEDIAPLAYER2ENGINE_H
diff --git a/media/libmediaplayer2/include/mediaplayer2/MediaPlayer2EngineClient.h b/media/libmediaplayer2/include/mediaplayer2/MediaPlayer2EngineClient.h
deleted file mode 100644
index 0b066aa..0000000
--- a/media/libmediaplayer2/include/mediaplayer2/MediaPlayer2EngineClient.h
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright 2017 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_MEDIAPLAYER2ENGINECLIENT_H
-#define ANDROID_MEDIAPLAYER2ENGINECLIENT_H
-
-#include <utils/RefBase.h>
-#include <binder/Parcel.h>
-
-namespace android {
-
-class MediaPlayer2EngineClient: public RefBase
-{
-public:
- virtual void notify(int64_t srcId, int msg, int ext1, int ext2, const Parcel *obj) = 0;
-};
-
-}; // namespace android
-
-#endif // ANDROID_MEDIAPLAYER2ENGINECLIENT_H
diff --git a/media/libmediaplayer2/include/mediaplayer2/MediaPlayer2Interface.h b/media/libmediaplayer2/include/mediaplayer2/MediaPlayer2Interface.h
index b1cdf96..02bf891 100644
--- a/media/libmediaplayer2/include/mediaplayer2/MediaPlayer2Interface.h
+++ b/media/libmediaplayer2/include/mediaplayer2/MediaPlayer2Interface.h
@@ -21,18 +21,17 @@
#include <sys/types.h>
#include <utils/Errors.h>
-#include <utils/KeyedVector.h>
#include <utils/String8.h>
#include <utils/RefBase.h>
+#include <media/AVSyncSettings.h>
#include <media/AudioResamplerPublic.h>
#include <media/AudioSystem.h>
#include <media/AudioTimestamp.h>
-#include <media/AVSyncSettings.h>
#include <media/BufferingSettings.h>
#include <media/Metadata.h>
#include <media/stagefright/foundation/AHandler.h>
-#include <mediaplayer2/mediaplayer2.h>
+#include <mediaplayer2/MediaPlayer2Types.h>
// Fwd decl to make sure everyone agrees that the scope of struct sockaddr_in is
// global, and not in android::
@@ -40,14 +39,10 @@
namespace android {
-class DataSource;
struct DataSourceDesc;
-struct MediaHTTPService;
class Parcel;
struct ANativeWindowWrapper;
-template<typename T> class SortedVector;
-
#define DEFAULT_AUDIOSINK_BUFFERCOUNT 4
#define DEFAULT_AUDIOSINK_BUFFERSIZE 1200
#define DEFAULT_AUDIOSINK_SAMPLERATE 44100
@@ -58,14 +53,14 @@
// duration below which we do not allow deep audio buffering
#define AUDIO_SINK_MIN_DEEP_BUFFER_DURATION_US 5000000
-// abstract base class - use MediaPlayer2Interface
-class MediaPlayer2Interface : public AHandler
+class MediaPlayer2InterfaceListener: public RefBase
{
public:
- // callback mechanism for passing messages to MediaPlayer2 object
- typedef void (*NotifyCallback)(const wp<MediaPlayer2Engine> &listener,
- int64_t srcId, int msg, int ext1, int ext2, const Parcel *obj);
+ virtual void notify(int64_t srcId, int msg, int ext1, int ext2, const Parcel *obj) = 0;
+};
+class MediaPlayer2Interface : public AHandler {
+public:
// AudioSink: abstraction layer for audio output
class AudioSink : public RefBase {
public:
@@ -79,29 +74,28 @@
// Callback returns the number of bytes actually written to the buffer.
typedef size_t (*AudioCallback)(
- AudioSink *audioSink, void *buffer, size_t size, void *cookie,
- cb_event_t event);
+ AudioSink *audioSink, void *buffer, size_t size, void *cookie, cb_event_t event);
- virtual ~AudioSink() {}
- virtual bool ready() const = 0; // audio output is open and ready
- virtual ssize_t bufferSize() const = 0;
- virtual ssize_t frameCount() const = 0;
- virtual ssize_t channelCount() const = 0;
- virtual ssize_t frameSize() const = 0;
- virtual uint32_t latency() const = 0;
- virtual float msecsPerFrame() const = 0;
- virtual status_t getPosition(uint32_t *position) const = 0;
- virtual status_t getTimestamp(AudioTimestamp &ts) const = 0;
- virtual int64_t getPlayedOutDurationUs(int64_t nowUs) const = 0;
- virtual status_t getFramesWritten(uint32_t *frameswritten) const = 0;
+ virtual ~AudioSink() {}
+ virtual bool ready() const = 0; // audio output is open and ready
+ virtual ssize_t bufferSize() const = 0;
+ virtual ssize_t frameCount() const = 0;
+ virtual ssize_t channelCount() const = 0;
+ virtual ssize_t frameSize() const = 0;
+ virtual uint32_t latency() const = 0;
+ virtual float msecsPerFrame() const = 0;
+ virtual status_t getPosition(uint32_t *position) const = 0;
+ virtual status_t getTimestamp(AudioTimestamp &ts) const = 0;
+ virtual int64_t getPlayedOutDurationUs(int64_t nowUs) const = 0;
+ virtual status_t getFramesWritten(uint32_t *frameswritten) const = 0;
virtual audio_session_t getSessionId() const = 0;
virtual audio_stream_type_t getAudioStreamType() const = 0;
- virtual uint32_t getSampleRate() const = 0;
- virtual int64_t getBufferDurationInUs() const = 0;
+ virtual uint32_t getSampleRate() const = 0;
+ virtual int64_t getBufferDurationInUs() const = 0;
// If no callback is specified, use the "write" API below to submit
// audio data.
- virtual status_t open(
+ virtual status_t open(
uint32_t sampleRate, int channelCount, audio_channel_mask_t channelMask,
audio_format_t format=AUDIO_FORMAT_PCM_16_BIT,
int bufferCount=DEFAULT_AUDIOSINK_BUFFERCOUNT,
@@ -112,7 +106,7 @@
bool doNotReconnect = false,
uint32_t suggestedFrameCount = 0) = 0;
- virtual status_t start() = 0;
+ virtual status_t start() = 0;
/* Input parameter |size| is in byte units stored in |buffer|.
* Data is copied over and actual number of bytes written (>= 0)
@@ -124,19 +118,25 @@
* buffer, unless an error occurs or the copy operation is
* prematurely stopped.
*/
- virtual ssize_t write(const void* buffer, size_t size, bool blocking = true) = 0;
+ virtual ssize_t write(const void* buffer, size_t size, bool blocking = true) = 0;
- virtual void stop() = 0;
- virtual void flush() = 0;
- virtual void pause() = 0;
- virtual void close() = 0;
+ virtual void stop() = 0;
+ virtual void flush() = 0;
+ virtual void pause() = 0;
+ virtual void close() = 0;
- virtual status_t setPlaybackRate(const AudioPlaybackRate& rate) = 0;
- virtual status_t getPlaybackRate(AudioPlaybackRate* rate /* nonnull */) = 0;
- virtual bool needsTrailingPadding() { return true; }
+ virtual status_t setPlaybackRate(const AudioPlaybackRate& rate) = 0;
+ virtual status_t getPlaybackRate(AudioPlaybackRate* rate /* nonnull */) = 0;
+ virtual bool needsTrailingPadding() {
+ return true;
+ }
- virtual status_t setParameters(const String8& /* keyValuePairs */) { return NO_ERROR; }
- virtual String8 getParameters(const String8& /* keys */) { return String8::empty(); }
+ virtual status_t setParameters(const String8& /* keyValuePairs */) {
+ return NO_ERROR;
+ }
+ virtual String8 getParameters(const String8& /* keys */) {
+ return String8::empty();
+ }
// AudioRouting
virtual status_t setOutputDevice(audio_port_handle_t deviceId);
@@ -144,46 +144,48 @@
virtual status_t enableAudioDeviceCallback(bool enabled);
};
- MediaPlayer2Interface() : mClient(0), mNotify(0) { }
- virtual ~MediaPlayer2Interface() { }
- virtual status_t initCheck() = 0;
+ MediaPlayer2Interface() : mListener(NULL) { }
+ virtual ~MediaPlayer2Interface() { }
+ virtual status_t initCheck() = 0;
- virtual void setAudioSink(const sp<AudioSink>& audioSink) { mAudioSink = audioSink; }
-
- virtual status_t setDataSource(const sp<DataSourceDesc>& /* dsd */) {
- return INVALID_OPERATION;
+ virtual void setAudioSink(const sp<AudioSink>& audioSink) {
+ mAudioSink = audioSink;
}
- // pass the buffered native window to the media player service
- virtual status_t setVideoSurfaceTexture(const sp<ANativeWindowWrapper>& nww) = 0;
+ virtual status_t setDataSource(const sp<DataSourceDesc> &dsd) = 0;
- virtual status_t getBufferingSettings(
- BufferingSettings* buffering /* nonnull */) {
+ virtual status_t prepareNextDataSource(const sp<DataSourceDesc> &dsd) = 0;
+
+ virtual status_t playNextDataSource(int64_t srcId) = 0;
+
+ // pass the buffered native window to the media player service
+ virtual status_t setVideoSurfaceTexture(const sp<ANativeWindowWrapper>& nww) = 0;
+
+ virtual status_t getBufferingSettings(BufferingSettings* buffering /* nonnull */) {
*buffering = BufferingSettings();
return OK;
}
- virtual status_t setBufferingSettings(const BufferingSettings& /* buffering */) {
+ virtual status_t setBufferingSettings(const BufferingSettings& /* buffering */) {
return OK;
}
- virtual status_t prepare() = 0;
- virtual status_t prepareAsync() = 0;
- virtual status_t start() = 0;
- virtual status_t stop() = 0;
- virtual status_t pause() = 0;
- virtual bool isPlaying() = 0;
- virtual status_t setPlaybackSettings(const AudioPlaybackRate& rate) {
+ virtual status_t prepareAsync() = 0;
+ virtual status_t start() = 0;
+ virtual status_t stop() = 0;
+ virtual status_t pause() = 0;
+ virtual bool isPlaying() = 0;
+ virtual status_t setPlaybackSettings(const AudioPlaybackRate& rate) {
// by default, players only support setting rate to the default
if (!isAudioPlaybackRateEqual(rate, AUDIO_PLAYBACK_RATE_DEFAULT)) {
return BAD_VALUE;
}
return OK;
}
- virtual status_t getPlaybackSettings(AudioPlaybackRate* rate /* nonnull */) {
+ virtual status_t getPlaybackSettings(AudioPlaybackRate* rate /* nonnull */) {
*rate = AUDIO_PLAYBACK_RATE_DEFAULT;
return OK;
}
- virtual status_t setSyncSettings(const AVSyncSettings& sync, float /* videoFps */) {
+ virtual status_t setSyncSettings(const AVSyncSettings& sync, float /* videoFps */) {
// By default, players only support setting sync source to default; all other sync
// settings are ignored. There is no requirement for getters to return set values.
if (sync.mSource != AVSYNC_SOURCE_DEFAULT) {
@@ -191,27 +193,23 @@
}
return OK;
}
- virtual status_t getSyncSettings(
- AVSyncSettings* sync /* nonnull */, float* videoFps /* nonnull */) {
+ virtual status_t getSyncSettings(
+ AVSyncSettings* sync /* nonnull */, float* videoFps /* nonnull */) {
*sync = AVSyncSettings();
*videoFps = -1.f;
return OK;
}
- virtual status_t seekTo(
- int msec, MediaPlayer2SeekMode mode = MediaPlayer2SeekMode::SEEK_PREVIOUS_SYNC) = 0;
- virtual status_t getCurrentPosition(int *msec) = 0;
- virtual status_t getDuration(int *msec) = 0;
- virtual status_t reset() = 0;
- virtual status_t notifyAt(int64_t /* mediaTimeUs */) {
+ virtual status_t seekTo(
+ int64_t msec, MediaPlayer2SeekMode mode = MediaPlayer2SeekMode::SEEK_PREVIOUS_SYNC) = 0;
+ virtual status_t getCurrentPosition(int64_t *msec) = 0;
+ virtual status_t getDuration(int64_t *msec) = 0;
+ virtual status_t reset() = 0;
+ virtual status_t notifyAt(int64_t /* mediaTimeUs */) {
return INVALID_OPERATION;
}
- virtual status_t setLooping(int loop) = 0;
- virtual status_t setParameter(int key, const Parcel &request) = 0;
- virtual status_t getParameter(int key, Parcel *reply) = 0;
-
- virtual status_t setNextPlayer(const sp<MediaPlayer2Interface>& /* next */) {
- return OK;
- }
+ virtual status_t setLooping(int loop) = 0;
+ virtual status_t setParameter(int key, const Parcel &request) = 0;
+ virtual status_t getParameter(int key, Parcel *reply) = 0;
// Invoke a generic method on the player by using opaque parcels
// for the request and reply.
@@ -220,7 +218,7 @@
// data sent by the java layer.
// @param[out] reply Parcel to hold the reply data. Cannot be null.
// @return OK if the call was successful.
- virtual status_t invoke(const Parcel& request, Parcel *reply) = 0;
+ virtual status_t invoke(const Parcel& request, Parcel *reply) = 0;
// The Client in the MetadataPlayerService calls this method on
// the native player to retrieve all or a subset of metadata.
@@ -229,28 +227,26 @@
// the known metadata should be returned.
// @param[inout] records Parcel where the player appends its metadata.
// @return OK if the call was successful.
- virtual status_t getMetadata(const media::Metadata::Filter& /* ids */,
- Parcel* /* records */) {
+ virtual status_t getMetadata(const media::Metadata::Filter& /* ids */,
+ Parcel* /* records */) {
return INVALID_OPERATION;
};
- void setNotifyCallback(
- const wp<MediaPlayer2Engine> &client, NotifyCallback notifyFunc) {
- Mutex::Autolock autoLock(mNotifyLock);
- mClient = client; mNotify = notifyFunc;
+ void setListener(const sp<MediaPlayer2InterfaceListener> &listener) {
+ Mutex::Autolock autoLock(mListenerLock);
+ mListener = listener;
}
- void sendEvent(int64_t srcId, int msg, int ext1=0, int ext2=0,
- const Parcel *obj=NULL) {
- NotifyCallback notifyCB;
- wp<MediaPlayer2Engine> client;
+ void sendEvent(int64_t srcId, int msg, int ext1=0, int ext2=0, const Parcel *obj=NULL) {
+ sp<MediaPlayer2InterfaceListener> listener;
{
- Mutex::Autolock autoLock(mNotifyLock);
- notifyCB = mNotify;
- client = mClient;
+ Mutex::Autolock autoLock(mListenerLock);
+ listener = mListener;
}
- if (notifyCB) notifyCB(client, srcId, msg, ext1, ext2, obj);
+ if (listener) {
+ listener->notify(srcId, msg, ext1, ext2, obj);
+ }
}
virtual status_t dump(int /* fd */, const Vector<String16>& /* args */) const {
@@ -260,7 +256,8 @@
virtual void onMessageReceived(const sp<AMessage> & /* msg */) override { }
// Modular DRM
- virtual status_t prepareDrm(const uint8_t /* uuid */[16], const Vector<uint8_t>& /* drmSessionId */) {
+ virtual status_t prepareDrm(const uint8_t /* uuid */[16],
+ const Vector<uint8_t>& /* drmSessionId */) {
return INVALID_OPERATION;
}
virtual status_t releaseDrm() {
@@ -268,14 +265,11 @@
}
protected:
- sp<AudioSink> mAudioSink;
+ sp<AudioSink> mAudioSink;
private:
- friend class MediaPlayer2Manager;
-
- Mutex mNotifyLock;
- wp<MediaPlayer2Engine> mClient;
- NotifyCallback mNotify;
+ Mutex mListenerLock;
+ sp<MediaPlayer2InterfaceListener> mListener;
};
}; // namespace android
diff --git a/media/libmediaplayer2/include/mediaplayer2/MediaPlayer2Types.h b/media/libmediaplayer2/include/mediaplayer2/MediaPlayer2Types.h
new file mode 100644
index 0000000..260c7ed
--- /dev/null
+++ b/media/libmediaplayer2/include/mediaplayer2/MediaPlayer2Types.h
@@ -0,0 +1,195 @@
+/*
+ * Copyright 2018 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_MEDIAPLAYER2_TYPES_H
+#define ANDROID_MEDIAPLAYER2_TYPES_H
+
+#include <media/mediaplayer_common.h>
+
+#include <media/MediaSource.h>
+
+namespace android {
+
+typedef MediaSource::ReadOptions::SeekMode MediaPlayer2SeekMode;
+
+enum media2_event_type {
+ MEDIA2_NOP = 0, // interface test message
+ MEDIA2_PREPARED = 1,
+ MEDIA2_PLAYBACK_COMPLETE = 2,
+ MEDIA2_BUFFERING_UPDATE = 3,
+ MEDIA2_SEEK_COMPLETE = 4,
+ MEDIA2_SET_VIDEO_SIZE = 5,
+ MEDIA2_STARTED = 6,
+ MEDIA2_PAUSED = 7,
+ MEDIA2_STOPPED = 8,
+ MEDIA2_SKIPPED = 9,
+ MEDIA2_NOTIFY_TIME = 98,
+ MEDIA2_TIMED_TEXT = 99,
+ MEDIA2_ERROR = 100,
+ MEDIA2_INFO = 200,
+ MEDIA2_SUBTITLE_DATA = 201,
+ MEDIA2_META_DATA = 202,
+ MEDIA2_DRM_INFO = 210,
+ MEDIA2_AUDIO_ROUTING_CHANGED = 10000,
+};
+
+// Generic error codes for the media player framework. Errors are fatal, the
+// playback must abort.
+//
+// Errors are communicated back to the client using the
+// MediaPlayer2Listener::notify method defined below.
+// In this situation, 'notify' is invoked with the following:
+// 'msg' is set to MEDIA_ERROR.
+// 'ext1' should be a value from the enum media2_error_type.
+// 'ext2' contains an implementation dependant error code to provide
+// more details. Should default to 0 when not used.
+//
+// The codes are distributed as follow:
+// 0xx: Reserved
+// 1xx: Android Player errors. Something went wrong inside the MediaPlayer2.
+// 2xx: Media errors (e.g Codec not supported). There is a problem with the
+// media itself.
+// 3xx: Runtime errors. Some extraordinary condition arose making the playback
+// impossible.
+//
+enum media2_error_type {
+ // 0xx
+ MEDIA2_ERROR_UNKNOWN = 1,
+ // 1xx
+ // MEDIA2_ERROR_SERVER_DIED = 100,
+ // 2xx
+ MEDIA2_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK = 200,
+ // 3xx
+ MEDIA2_ERROR_FAILED_TO_SET_DATA_SOURCE = 300,
+};
+
+
+// Info and warning codes for the media player framework. These are non fatal,
+// the playback is going on but there might be some user visible issues.
+//
+// Info and warning messages are communicated back to the client using the
+// MediaPlayer2Listener::notify method defined below. In this situation,
+// 'notify' is invoked with the following:
+// 'msg' is set to MEDIA_INFO.
+// 'ext1' should be a value from the enum media2_info_type.
+// 'ext2' contains an implementation dependant info code to provide
+// more details. Should default to 0 when not used.
+//
+// The codes are distributed as follow:
+// 0xx: Reserved
+// 7xx: Android Player info/warning (e.g player lagging behind.)
+// 8xx: Media info/warning (e.g media badly interleaved.)
+//
+enum media2_info_type {
+ // 0xx
+ MEDIA2_INFO_UNKNOWN = 1,
+ // The player was started because it was used as the next player for another
+ // player, which just completed playback
+ MEDIA2_INFO_STARTED_AS_NEXT = 2,
+ // The player just pushed the very first video frame for rendering
+ MEDIA2_INFO_VIDEO_RENDERING_START = 3,
+ // The player just pushed the very first audio frame for rendering
+ MEDIA2_INFO_AUDIO_RENDERING_START = 4,
+ // The player just completed the playback of this data source
+ MEDIA2_INFO_PLAYBACK_COMPLETE = 5,
+ // The player just completed the playback of the full play list
+ MEDIA2_INFO_PLAYLIST_END = 6,
+
+ //1xx
+ // The player just prepared a data source.
+ MEDIA2_INFO_PREPARED = 100,
+ // The player just completed a call play().
+ MEDIA2_INFO_COMPLETE_CALL_PLAY = 101,
+ // The player just completed a call pause().
+ MEDIA2_INFO_COMPLETE_CALL_PAUSE = 102,
+ // The player just completed a call seekTo.
+ MEDIA2_INFO_COMPLETE_CALL_SEEK = 103,
+
+ // 7xx
+ // The video is too complex for the decoder: it can't decode frames fast
+ // enough. Possibly only the audio plays fine at this stage.
+ MEDIA2_INFO_VIDEO_TRACK_LAGGING = 700,
+ // MediaPlayer2 is temporarily pausing playback internally in order to
+ // buffer more data.
+ MEDIA2_INFO_BUFFERING_START = 701,
+ // MediaPlayer2 is resuming playback after filling buffers.
+ MEDIA2_INFO_BUFFERING_END = 702,
+ // Bandwidth in recent past
+ MEDIA2_INFO_NETWORK_BANDWIDTH = 703,
+
+ // 8xx
+ // Bad interleaving means that a media has been improperly interleaved or not
+ // interleaved at all, e.g has all the video samples first then all the audio
+ // ones. Video is playing but a lot of disk seek may be happening.
+ MEDIA2_INFO_BAD_INTERLEAVING = 800,
+ // The media is not seekable (e.g live stream).
+ MEDIA2_INFO_NOT_SEEKABLE = 801,
+ // New media metadata is available.
+ MEDIA2_INFO_METADATA_UPDATE = 802,
+ // Audio can not be played.
+ MEDIA2_INFO_PLAY_AUDIO_ERROR = 804,
+ // Video can not be played.
+ MEDIA2_INFO_PLAY_VIDEO_ERROR = 805,
+
+ //9xx
+ MEDIA2_INFO_TIMED_TEXT_ERROR = 900,
+};
+
+enum media_player2_states {
+ MEDIA_PLAYER2_STATE_ERROR = 0,
+ MEDIA_PLAYER2_IDLE = 1 << 0,
+ MEDIA_PLAYER2_INITIALIZED = 1 << 1,
+ MEDIA_PLAYER2_PREPARING = 1 << 2,
+ MEDIA_PLAYER2_PREPARED = 1 << 3,
+ MEDIA_PLAYER2_STARTED = 1 << 4,
+ MEDIA_PLAYER2_PAUSED = 1 << 5,
+ MEDIA_PLAYER2_STOPPED = 1 << 6,
+ MEDIA_PLAYER2_PLAYBACK_COMPLETE = 1 << 7
+};
+
+// Keep KEY_PARAMETER_* in sync with MediaPlayer2.java.
+// The same enum space is used for both set and get, in case there are future keys that
+// can be both set and get. But as of now, all parameters are either set only or get only.
+enum media2_parameter_keys {
+ // Streaming/buffering parameters
+ MEDIA2_KEY_PARAMETER_CACHE_STAT_COLLECT_FREQ_MS = 1100, // set only
+
+ // Return a Parcel containing a single int, which is the channel count of the
+ // audio track, or zero for error (e.g. no audio track) or unknown.
+ MEDIA2_KEY_PARAMETER_AUDIO_CHANNEL_COUNT = 1200, // get only
+
+ // Playback rate expressed in permille (1000 is normal speed), saved as int32_t, with negative
+ // values used for rewinding or reverse playback.
+ MEDIA2_KEY_PARAMETER_PLAYBACK_RATE_PERMILLE = 1300, // set only
+
+ // Set a Parcel containing the value of a parcelled Java AudioAttribute instance
+ MEDIA2_KEY_PARAMETER_AUDIO_ATTRIBUTES = 1400 // set only
+};
+
+// Keep INVOKE_ID_* in sync with MediaPlayer2.java.
+enum media_player2_invoke_ids {
+ MEDIA_PLAYER2_INVOKE_ID_GET_TRACK_INFO = 1,
+ MEDIA_PLAYER2_INVOKE_ID_ADD_EXTERNAL_SOURCE = 2,
+ MEDIA_PLAYER2_INVOKE_ID_ADD_EXTERNAL_SOURCE_FD = 3,
+ MEDIA_PLAYER2_INVOKE_ID_SELECT_TRACK = 4,
+ MEDIA_PLAYER2_INVOKE_ID_UNSELECT_TRACK = 5,
+ MEDIA_PLAYER2_INVOKE_ID_SET_VIDEO_SCALING_MODE = 6,
+ MEDIA_PLAYER2_INVOKE_ID_GET_SELECTED_TRACK = 7
+};
+
+}; // namespace android
+
+#endif // ANDROID_MEDIAPLAYER2_TYPES_H
diff --git a/media/libmediaplayer2/include/mediaplayer2/mediaplayer2.h b/media/libmediaplayer2/include/mediaplayer2/mediaplayer2.h
index e9d6f84..3433cb1 100644
--- a/media/libmediaplayer2/include/mediaplayer2/mediaplayer2.h
+++ b/media/libmediaplayer2/include/mediaplayer2/mediaplayer2.h
@@ -17,179 +17,27 @@
#ifndef ANDROID_MEDIAPLAYER2_H
#define ANDROID_MEDIAPLAYER2_H
-#include <media/mediaplayer_common.h>
-
-#include <arpa/inet.h>
-
+#include <media/AVSyncSettings.h>
#include <media/AudioResamplerPublic.h>
#include <media/BufferingSettings.h>
-#include <mediaplayer2/MediaPlayer2EngineClient.h>
-#include <mediaplayer2/MediaPlayer2Engine.h>
+#include <media/Metadata.h>
+#include <media/mediaplayer_common.h>
+#include <mediaplayer2/MediaPlayer2Interface.h>
+#include <mediaplayer2/MediaPlayer2Types.h>
-#include <utils/Condition.h>
-#include <utils/KeyedVector.h>
-#include <utils/String8.h>
-#include <utils/ThreadDefs.h>
+#include <utils/Errors.h>
+#include <utils/Mutex.h>
+#include <utils/RefBase.h>
+#include <utils/String16.h>
+#include <utils/Vector.h>
+#include <system/audio-base.h>
namespace android {
-struct AVSyncSettings;
struct ANativeWindowWrapper;
-class DataSource;
struct DataSourceDesc;
-struct MediaHTTPService;
+class MediaPlayer2AudioOutput;
-enum media2_event_type {
- MEDIA2_NOP = 0, // interface test message
- MEDIA2_PREPARED = 1,
- MEDIA2_PLAYBACK_COMPLETE = 2,
- MEDIA2_BUFFERING_UPDATE = 3,
- MEDIA2_SEEK_COMPLETE = 4,
- MEDIA2_SET_VIDEO_SIZE = 5,
- MEDIA2_STARTED = 6,
- MEDIA2_PAUSED = 7,
- MEDIA2_STOPPED = 8,
- MEDIA2_SKIPPED = 9,
- MEDIA2_NOTIFY_TIME = 98,
- MEDIA2_TIMED_TEXT = 99,
- MEDIA2_ERROR = 100,
- MEDIA2_INFO = 200,
- MEDIA2_SUBTITLE_DATA = 201,
- MEDIA2_META_DATA = 202,
- MEDIA2_DRM_INFO = 210,
- MEDIA2_AUDIO_ROUTING_CHANGED = 10000,
-};
-
-// Generic error codes for the media player framework. Errors are fatal, the
-// playback must abort.
-//
-// Errors are communicated back to the client using the
-// MediaPlayer2Listener::notify method defined below.
-// In this situation, 'notify' is invoked with the following:
-// 'msg' is set to MEDIA_ERROR.
-// 'ext1' should be a value from the enum media2_error_type.
-// 'ext2' contains an implementation dependant error code to provide
-// more details. Should default to 0 when not used.
-//
-// The codes are distributed as follow:
-// 0xx: Reserved
-// 1xx: Android Player errors. Something went wrong inside the MediaPlayer2.
-// 2xx: Media errors (e.g Codec not supported). There is a problem with the
-// media itself.
-// 3xx: Runtime errors. Some extraordinary condition arose making the playback
-// impossible.
-//
-enum media2_error_type {
- // 0xx
- MEDIA2_ERROR_UNKNOWN = 1,
- // 1xx
- // MEDIA2_ERROR_SERVER_DIED = 100,
- // 2xx
- MEDIA2_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK = 200,
- // 3xx
- MEDIA2_ERROR_FAILED_TO_SET_DATA_SOURCE = 300,
-};
-
-
-// Info and warning codes for the media player framework. These are non fatal,
-// the playback is going on but there might be some user visible issues.
-//
-// Info and warning messages are communicated back to the client using the
-// MediaPlayer2Listener::notify method defined below. In this situation,
-// 'notify' is invoked with the following:
-// 'msg' is set to MEDIA_INFO.
-// 'ext1' should be a value from the enum media2_info_type.
-// 'ext2' contains an implementation dependant info code to provide
-// more details. Should default to 0 when not used.
-//
-// The codes are distributed as follow:
-// 0xx: Reserved
-// 7xx: Android Player info/warning (e.g player lagging behind.)
-// 8xx: Media info/warning (e.g media badly interleaved.)
-//
-enum media2_info_type {
- // 0xx
- MEDIA2_INFO_UNKNOWN = 1,
- // The player was started because it was used as the next player for another
- // player, which just completed playback
- MEDIA2_INFO_STARTED_AS_NEXT = 2,
- // The player just pushed the very first video frame for rendering
- MEDIA2_INFO_RENDERING_START = 3,
- // 7xx
- // The video is too complex for the decoder: it can't decode frames fast
- // enough. Possibly only the audio plays fine at this stage.
- MEDIA2_INFO_VIDEO_TRACK_LAGGING = 700,
- // MediaPlayer2 is temporarily pausing playback internally in order to
- // buffer more data.
- MEDIA2_INFO_BUFFERING_START = 701,
- // MediaPlayer2 is resuming playback after filling buffers.
- MEDIA2_INFO_BUFFERING_END = 702,
- // Bandwidth in recent past
- MEDIA2_INFO_NETWORK_BANDWIDTH = 703,
-
- // 8xx
- // Bad interleaving means that a media has been improperly interleaved or not
- // interleaved at all, e.g has all the video samples first then all the audio
- // ones. Video is playing but a lot of disk seek may be happening.
- MEDIA2_INFO_BAD_INTERLEAVING = 800,
- // The media is not seekable (e.g live stream).
- MEDIA2_INFO_NOT_SEEKABLE = 801,
- // New media metadata is available.
- MEDIA2_INFO_METADATA_UPDATE = 802,
- // Audio can not be played.
- MEDIA2_INFO_PLAY_AUDIO_ERROR = 804,
- // Video can not be played.
- MEDIA2_INFO_PLAY_VIDEO_ERROR = 805,
-
- //9xx
- MEDIA2_INFO_TIMED_TEXT_ERROR = 900,
-};
-
-
-
-enum media_player2_states {
- MEDIA_PLAYER2_STATE_ERROR = 0,
- MEDIA_PLAYER2_IDLE = 1 << 0,
- MEDIA_PLAYER2_INITIALIZED = 1 << 1,
- MEDIA_PLAYER2_PREPARING = 1 << 2,
- MEDIA_PLAYER2_PREPARED = 1 << 3,
- MEDIA_PLAYER2_STARTED = 1 << 4,
- MEDIA_PLAYER2_PAUSED = 1 << 5,
- MEDIA_PLAYER2_STOPPED = 1 << 6,
- MEDIA_PLAYER2_PLAYBACK_COMPLETE = 1 << 7
-};
-
-// Keep KEY_PARAMETER_* in sync with MediaPlayer2.java.
-// The same enum space is used for both set and get, in case there are future keys that
-// can be both set and get. But as of now, all parameters are either set only or get only.
-enum media2_parameter_keys {
- // Streaming/buffering parameters
- MEDIA2_KEY_PARAMETER_CACHE_STAT_COLLECT_FREQ_MS = 1100, // set only
-
- // Return a Parcel containing a single int, which is the channel count of the
- // audio track, or zero for error (e.g. no audio track) or unknown.
- MEDIA2_KEY_PARAMETER_AUDIO_CHANNEL_COUNT = 1200, // get only
-
- // Playback rate expressed in permille (1000 is normal speed), saved as int32_t, with negative
- // values used for rewinding or reverse playback.
- MEDIA2_KEY_PARAMETER_PLAYBACK_RATE_PERMILLE = 1300, // set only
-
- // Set a Parcel containing the value of a parcelled Java AudioAttribute instance
- MEDIA2_KEY_PARAMETER_AUDIO_ATTRIBUTES = 1400 // set only
-};
-
-// Keep INVOKE_ID_* in sync with MediaPlayer2.java.
-enum media_player2_invoke_ids {
- MEDIA_PLAYER2_INVOKE_ID_GET_TRACK_INFO = 1,
- MEDIA_PLAYER2_INVOKE_ID_ADD_EXTERNAL_SOURCE = 2,
- MEDIA_PLAYER2_INVOKE_ID_ADD_EXTERNAL_SOURCE_FD = 3,
- MEDIA_PLAYER2_INVOKE_ID_SELECT_TRACK = 4,
- MEDIA_PLAYER2_INVOKE_ID_UNSELECT_TRACK = 5,
- MEDIA_PLAYER2_INVOKE_ID_SET_VIDEO_SCALING_MODE = 6,
- MEDIA_PLAYER2_INVOKE_ID_GET_SELECTED_TRACK = 7
-};
-
-// ----------------------------------------------------------------------------
// ref-counted object for callbacks
class MediaPlayer2Listener: virtual public RefBase
{
@@ -197,20 +45,24 @@
virtual void notify(int64_t srcId, int msg, int ext1, int ext2, const Parcel *obj) = 0;
};
-class MediaPlayer2 : public MediaPlayer2EngineClient
+class MediaPlayer2 : public MediaPlayer2InterfaceListener
{
public:
- MediaPlayer2();
~MediaPlayer2();
+
+ static sp<MediaPlayer2> Create();
+ static status_t DumpAll(int fd, const Vector<String16>& args);
+
void disconnect();
status_t getSrcId(int64_t *srcId);
status_t setDataSource(const sp<DataSourceDesc> &dsd);
+ status_t prepareNextDataSource(const sp<DataSourceDesc> &dsd);
+ status_t playNextDataSource(int64_t srcId);
status_t setVideoSurfaceTexture(const sp<ANativeWindowWrapper>& nww);
status_t setListener(const sp<MediaPlayer2Listener>& listener);
status_t getBufferingSettings(BufferingSettings* buffering /* nonnull */);
status_t setBufferingSettings(const BufferingSettings& buffering);
- status_t prepare();
status_t prepareAsync();
status_t start();
status_t stop();
@@ -225,11 +77,11 @@
status_t getVideoWidth(int *w);
status_t getVideoHeight(int *h);
status_t seekTo(
- int msec,
+ int64_t msec,
MediaPlayer2SeekMode mode = MediaPlayer2SeekMode::SEEK_PREVIOUS_SYNC);
status_t notifyAt(int64_t mediaTimeUs);
- status_t getCurrentPosition(int *msec);
- status_t getDuration(int *msec);
+ status_t getCurrentPosition(int64_t *msec);
+ status_t getDuration(int64_t *msec);
status_t reset();
status_t setAudioStreamType(audio_stream_type_t type);
status_t getAudioStreamType(audio_stream_type_t *type);
@@ -247,7 +99,6 @@
status_t attachAuxEffect(int effectId);
status_t setParameter(int key, const Parcel& request);
status_t getParameter(int key, Parcel* reply);
- status_t setNextMediaPlayer(const sp<MediaPlayer2>& player);
// Modular DRM
status_t prepareDrm(const uint8_t uuid[16], const Vector<uint8_t>& drmSessionId);
@@ -257,30 +108,48 @@
audio_port_handle_t getRoutedDeviceId();
status_t enableAudioDeviceCallback(bool enabled);
-private:
- void clear_l();
- status_t seekTo_l(int msec, MediaPlayer2SeekMode mode);
- status_t prepareAsync_l();
- status_t getDuration_l(int *msec);
- status_t attachNewPlayer(const sp<MediaPlayer2Engine>& player, long srcId);
- status_t reset_l();
- status_t checkStateForKeySet_l(int key);
+ status_t dump(int fd, const Vector<String16>& args);
- sp<MediaPlayer2Engine> mPlayer;
+private:
+ MediaPlayer2();
+ bool init();
+
+ // @param type Of the metadata to be tested.
+ // @return true if the metadata should be dropped according to
+ // the filters.
+ bool shouldDropMetadata(media::Metadata::Type type) const;
+
+ // Add a new element to the set of metadata updated. Noop if
+ // the element exists already.
+ // @param type Of the metadata to be recorded.
+ void addNewMetadataUpdate(media::Metadata::Type type);
+
+ // Disconnect from the currently connected ANativeWindow.
+ void disconnectNativeWindow_l();
+
+ status_t setAudioAttributes_l(const Parcel &request);
+
+ void clear_l();
+ status_t seekTo_l(int64_t msec, MediaPlayer2SeekMode mode);
+ status_t prepareAsync_l();
+ status_t getDuration_l(int64_t *msec);
+ status_t reset_l();
+ status_t checkStateForKeySet_l(int key);
+
+ pid_t mPid;
+ uid_t mUid;
+ sp<MediaPlayer2Interface> mPlayer;
+ sp<MediaPlayer2AudioOutput> mAudioOutput;
int64_t mSrcId;
thread_id_t mLockThreadId;
- Mutex mLock;
+ mutable Mutex mLock;
Mutex mNotifyLock;
- Condition mSignal;
sp<MediaPlayer2Listener> mListener;
- void* mCookie;
media_player2_states mCurrentState;
- int mCurrentPosition;
+ int64_t mCurrentPosition;
MediaPlayer2SeekMode mCurrentSeekMode;
- int mSeekPosition;
+ int64_t mSeekPosition;
MediaPlayer2SeekMode mSeekMode;
- bool mPrepareSync;
- status_t mPrepareStatus;
audio_stream_type_t mStreamType;
Parcel* mAudioAttributesParcel;
bool mLoop;
@@ -289,7 +158,20 @@
int mVideoWidth;
int mVideoHeight;
audio_session_t mAudioSessionId;
+ audio_attributes_t * mAudioAttributes;
float mSendLevel;
+
+ sp<ANativeWindowWrapper> mConnectedWindow;
+
+ // Metadata filters.
+ media::Metadata::Filter mMetadataAllow; // protected by mLock
+ media::Metadata::Filter mMetadataDrop; // protected by mLock
+
+ // Metadata updated. For each MEDIA_INFO_METADATA_UPDATE
+ // notification we try to update mMetadataUpdated which is a
+ // set: no duplicate.
+ // getMetadata clears this set.
+ media::Metadata::Filter mMetadataUpdated; // protected by mLock
};
}; // namespace android
diff --git a/media/libmediaplayer2/mediaplayer2.cpp b/media/libmediaplayer2/mediaplayer2.cpp
index ab30273..c465caa 100644
--- a/media/libmediaplayer2/mediaplayer2.cpp
+++ b/media/libmediaplayer2/mediaplayer2.cpp
@@ -18,44 +18,391 @@
//#define LOG_NDEBUG 0
#define LOG_TAG "MediaPlayer2Native"
-#include <fcntl.h>
-#include <inttypes.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include <utils/Log.h>
-
#include <binder/IServiceManager.h>
#include <binder/IPCThreadState.h>
-#include <media/AudioResamplerPublic.h>
#include <media/AudioSystem.h>
-#include <media/AVSyncSettings.h>
-#include <media/DataSource.h>
#include <media/DataSourceDesc.h>
#include <media/MediaAnalyticsItem.h>
+#include <media/MemoryLeakTrackUtil.h>
+#include <media/Metadata.h>
#include <media/NdkWrapper.h>
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/foundation/ALooperRoster.h>
+#include <mediaplayer2/MediaPlayer2AudioOutput.h>
#include <mediaplayer2/mediaplayer2.h>
-#include <binder/MemoryBase.h>
-
-#include <utils/KeyedVector.h>
+#include <utils/Log.h>
+#include <utils/SortedVector.h>
#include <utils/String8.h>
#include <system/audio.h>
#include <system/window.h>
-#include "MediaPlayer2Manager.h"
+#include <nuplayer2/NuPlayer2Driver.h>
+
+#include <dirent.h>
+#include <sys/stat.h>
namespace android {
-MediaPlayer2::MediaPlayer2()
-{
+extern ALooperRoster gLooperRoster;
+
+namespace {
+
+const int kDumpLockRetries = 50;
+const int kDumpLockSleepUs = 20000;
+
+// Max number of entries in the filter.
+const int kMaxFilterSize = 64; // I pulled that out of thin air.
+
+// FIXME: Move all the metadata related function in the Metadata.cpp
+
+// Unmarshall a filter from a Parcel.
+// Filter format in a parcel:
+//
+// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// | number of entries (n) |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// | metadata type 1 |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// | metadata type 2 |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// ....
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// | metadata type n |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+//
+// @param p Parcel that should start with a filter.
+// @param[out] filter On exit contains the list of metadata type to be
+// filtered.
+// @param[out] status On exit contains the status code to be returned.
+// @return true if the parcel starts with a valid filter.
+bool unmarshallFilter(const Parcel& p,
+ media::Metadata::Filter *filter,
+ status_t *status) {
+ int32_t val;
+ if (p.readInt32(&val) != OK) {
+ ALOGE("Failed to read filter's length");
+ *status = NOT_ENOUGH_DATA;
+ return false;
+ }
+
+ if (val > kMaxFilterSize || val < 0) {
+ ALOGE("Invalid filter len %d", val);
+ *status = BAD_VALUE;
+ return false;
+ }
+
+ const size_t num = val;
+
+ filter->clear();
+ filter->setCapacity(num);
+
+ size_t size = num * sizeof(media::Metadata::Type);
+
+
+ if (p.dataAvail() < size) {
+ ALOGE("Filter too short expected %zu but got %zu", size, p.dataAvail());
+ *status = NOT_ENOUGH_DATA;
+ return false;
+ }
+
+ const media::Metadata::Type *data =
+ static_cast<const media::Metadata::Type*>(p.readInplace(size));
+
+ if (NULL == data) {
+ ALOGE("Filter had no data");
+ *status = BAD_VALUE;
+ return false;
+ }
+
+ // TODO: The stl impl of vector would be more efficient here
+ // because it degenerates into a memcpy on pod types. Try to
+ // replace later or use stl::set.
+ for (size_t i = 0; i < num; ++i) {
+ filter->add(*data);
+ ++data;
+ }
+ *status = OK;
+ return true;
+}
+
+// @param filter Of metadata type.
+// @param val To be searched.
+// @return true if a match was found.
+bool findMetadata(const media::Metadata::Filter& filter, const int32_t val) {
+ // Deal with empty and ANY right away
+ if (filter.isEmpty()) {
+ return false;
+ }
+ if (filter[0] == media::Metadata::kAny) {
+ return true;
+ }
+
+ return filter.indexOf(val) >= 0;
+}
+
+// marshalling tag indicating flattened utf16 tags
+// keep in sync with frameworks/base/media/java/android/media/AudioAttributes.java
+const int32_t kAudioAttributesMarshallTagFlattenTags = 1;
+
+// Audio attributes format in a parcel:
+//
+// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// | usage |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// | content_type |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// | source |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// | flags |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// | kAudioAttributesMarshallTagFlattenTags | // ignore tags if not found
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// | flattened tags in UTF16 |
+// | ... |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+//
+// @param p Parcel that contains audio attributes.
+// @param[out] attributes On exit points to an initialized audio_attributes_t structure
+// @param[out] status On exit contains the status code to be returned.
+void unmarshallAudioAttributes(const Parcel& parcel, audio_attributes_t *attributes) {
+ attributes->usage = (audio_usage_t) parcel.readInt32();
+ attributes->content_type = (audio_content_type_t) parcel.readInt32();
+ attributes->source = (audio_source_t) parcel.readInt32();
+ attributes->flags = (audio_flags_mask_t) parcel.readInt32();
+ const bool hasFlattenedTag = (parcel.readInt32() == kAudioAttributesMarshallTagFlattenTags);
+ if (hasFlattenedTag) {
+ // the tags are UTF16, convert to UTF8
+ String16 tags = parcel.readString16();
+ ssize_t realTagSize = utf16_to_utf8_length(tags.string(), tags.size());
+ if (realTagSize <= 0) {
+ strcpy(attributes->tags, "");
+ } else {
+ // copy the flattened string into the attributes as the destination for the conversion:
+ // copying array size -1, array for tags was calloc'd, no need to NULL-terminate it
+ size_t tagSize = realTagSize > AUDIO_ATTRIBUTES_TAGS_MAX_SIZE - 1 ?
+ AUDIO_ATTRIBUTES_TAGS_MAX_SIZE - 1 : realTagSize;
+ utf16_to_utf8(tags.string(), tagSize, attributes->tags,
+ sizeof(attributes->tags) / sizeof(attributes->tags[0]));
+ }
+ } else {
+ ALOGE("unmarshallAudioAttributes() received unflattened tags, ignoring tag values");
+ strcpy(attributes->tags, "");
+ }
+}
+
+class AudioDeviceUpdatedNotifier: public AudioSystem::AudioDeviceCallback {
+public:
+ AudioDeviceUpdatedNotifier(const sp<MediaPlayer2Interface>& listener)
+ : mListener(listener) { }
+
+ ~AudioDeviceUpdatedNotifier() { }
+
+ virtual void onAudioDeviceUpdate(audio_io_handle_t audioIo,
+ audio_port_handle_t deviceId) override {
+ sp<MediaPlayer2Interface> listener = mListener.promote();
+ if (listener != NULL) {
+ listener->sendEvent(0, MEDIA2_AUDIO_ROUTING_CHANGED, audioIo, deviceId);
+ } else {
+ ALOGW("listener for process %d death is gone", MEDIA2_AUDIO_ROUTING_CHANGED);
+ }
+ }
+
+private:
+ wp<MediaPlayer2Interface> mListener;
+};
+
+class proxyListener : public MediaPlayer2InterfaceListener {
+public:
+ proxyListener(const wp<MediaPlayer2> &player)
+ : mPlayer(player) { }
+
+ ~proxyListener() { };
+
+ virtual void notify(int64_t srcId, int msg, int ext1, int ext2, const Parcel *obj) override {
+ sp<MediaPlayer2> player = mPlayer.promote();
+ if (player != NULL) {
+ player->notify(srcId, msg, ext1, ext2, obj);
+ }
+ }
+
+private:
+ wp<MediaPlayer2> mPlayer;
+};
+
+Mutex sRecordLock;
+SortedVector<wp<MediaPlayer2> > *sPlayers;
+
+void ensureInit_l() {
+ if (sPlayers == NULL) {
+ sPlayers = new SortedVector<wp<MediaPlayer2> >();
+ }
+}
+
+void addPlayer(const wp<MediaPlayer2>& player) {
+ Mutex::Autolock lock(sRecordLock);
+ ensureInit_l();
+ sPlayers->add(player);
+}
+
+void removePlayer(const wp<MediaPlayer2>& player) {
+ Mutex::Autolock lock(sRecordLock);
+ ensureInit_l();
+ sPlayers->remove(player);
+}
+
+/**
+ * The only arguments this understands right now are -c, -von and -voff,
+ * which are parsed by ALooperRoster::dump()
+ */
+status_t dumpPlayers(int fd, const Vector<String16>& args) {
+ const size_t SIZE = 256;
+ char buffer[SIZE];
+ String8 result;
+ SortedVector< sp<MediaPlayer2> > players; //to serialise the mutex unlock & client destruction.
+
+ if (checkCallingPermission(String16("android.permission.DUMP")) == false) {
+ snprintf(buffer, SIZE, "Permission Denial: can't dump MediaPlayer2\n");
+ result.append(buffer);
+ } else {
+ {
+ Mutex::Autolock lock(sRecordLock);
+ ensureInit_l();
+ for (int i = 0, n = sPlayers->size(); i < n; ++i) {
+ sp<MediaPlayer2> p = (*sPlayers)[i].promote();
+ if (p != 0) {
+ p->dump(fd, args);
+ }
+ players.add(p);
+ }
+ }
+
+ result.append(" Files opened and/or mapped:\n");
+ snprintf(buffer, SIZE, "/proc/%d/maps", getpid());
+ FILE *f = fopen(buffer, "r");
+ if (f) {
+ while (!feof(f)) {
+ fgets(buffer, SIZE, f);
+ if (strstr(buffer, " /storage/") ||
+ strstr(buffer, " /system/sounds/") ||
+ strstr(buffer, " /data/") ||
+ strstr(buffer, " /system/media/")) {
+ result.append(" ");
+ result.append(buffer);
+ }
+ }
+ fclose(f);
+ } else {
+ result.append("couldn't open ");
+ result.append(buffer);
+ result.append("\n");
+ }
+
+ snprintf(buffer, SIZE, "/proc/%d/fd", getpid());
+ DIR *d = opendir(buffer);
+ if (d) {
+ struct dirent *ent;
+ while((ent = readdir(d)) != NULL) {
+ if (strcmp(ent->d_name,".") && strcmp(ent->d_name,"..")) {
+ snprintf(buffer, SIZE, "/proc/%d/fd/%s", getpid(), ent->d_name);
+ struct stat s;
+ if (lstat(buffer, &s) == 0) {
+ if ((s.st_mode & S_IFMT) == S_IFLNK) {
+ char linkto[256];
+ int len = readlink(buffer, linkto, sizeof(linkto));
+ if(len > 0) {
+ if(len > 255) {
+ linkto[252] = '.';
+ linkto[253] = '.';
+ linkto[254] = '.';
+ linkto[255] = 0;
+ } else {
+ linkto[len] = 0;
+ }
+ if (strstr(linkto, "/storage/") == linkto ||
+ strstr(linkto, "/system/sounds/") == linkto ||
+ strstr(linkto, "/data/") == linkto ||
+ strstr(linkto, "/system/media/") == linkto) {
+ result.append(" ");
+ result.append(buffer);
+ result.append(" -> ");
+ result.append(linkto);
+ result.append("\n");
+ }
+ }
+ } else {
+ result.append(" unexpected type for ");
+ result.append(buffer);
+ result.append("\n");
+ }
+ }
+ }
+ }
+ closedir(d);
+ } else {
+ result.append("couldn't open ");
+ result.append(buffer);
+ result.append("\n");
+ }
+
+ gLooperRoster.dump(fd, args);
+
+ bool dumpMem = false;
+ bool unreachableMemory = false;
+ for (size_t i = 0; i < args.size(); i++) {
+ if (args[i] == String16("-m")) {
+ dumpMem = true;
+ } else if (args[i] == String16("--unreachable")) {
+ unreachableMemory = true;
+ }
+ }
+ if (dumpMem) {
+ result.append("\nDumping memory:\n");
+ std::string s = dumpMemoryAddresses(100 /* limit */);
+ result.append(s.c_str(), s.size());
+ }
+ if (unreachableMemory) {
+ result.append("\nDumping unreachable memory:\n");
+ // TODO - should limit be an argument parameter?
+ // TODO: enable GetUnreachableMemoryString if it's part of stable API
+ //std::string s = GetUnreachableMemoryString(true /* contents */, 10000 /* limit */);
+ //result.append(s.c_str(), s.size());
+ }
+ }
+ write(fd, result.string(), result.size());
+ return NO_ERROR;
+}
+
+} // anonymous namespace
+
+//static
+sp<MediaPlayer2> MediaPlayer2::Create() {
+ sp<MediaPlayer2> player = new MediaPlayer2();
+
+ if (!player->init()) {
+ return NULL;
+ }
+
+ ALOGV("Create new player(%p)", player.get());
+
+ addPlayer(player);
+ return player;
+}
+
+// static
+status_t MediaPlayer2::DumpAll(int fd, const Vector<String16>& args) {
+ return dumpPlayers(fd, args);
+}
+
+MediaPlayer2::MediaPlayer2() {
ALOGV("constructor");
mSrcId = 0;
+ mLockThreadId = 0;
mListener = NULL;
- mCookie = NULL;
mStreamType = AUDIO_STREAM_MUSIC;
mAudioAttributesParcel = NULL;
mCurrentPosition = -1;
@@ -63,19 +410,21 @@
mSeekPosition = -1;
mSeekMode = MediaPlayer2SeekMode::SEEK_PREVIOUS_SYNC;
mCurrentState = MEDIA_PLAYER2_IDLE;
- mPrepareSync = false;
- mPrepareStatus = NO_ERROR;
mLoop = false;
mLeftVolume = mRightVolume = 1.0;
mVideoWidth = mVideoHeight = 0;
- mLockThreadId = 0;
mAudioSessionId = (audio_session_t) AudioSystem::newAudioUniqueId(AUDIO_UNIQUE_ID_USE_SESSION);
AudioSystem::acquireAudioSessionId(mAudioSessionId, -1);
mSendLevel = 0;
+
+ // TODO: get pid and uid from JAVA
+ mPid = IPCThreadState::self()->getCallingPid();
+ mUid = IPCThreadState::self()->getCallingUid();
+
+ mAudioAttributes = NULL;
}
-MediaPlayer2::~MediaPlayer2()
-{
+MediaPlayer2::~MediaPlayer2() {
ALOGV("destructor");
if (mAudioAttributesParcel != NULL) {
delete mAudioAttributesParcel;
@@ -83,13 +432,21 @@
}
AudioSystem::releaseAudioSessionId(mAudioSessionId, -1);
disconnect();
- IPCThreadState::self()->flushCommands();
+ removePlayer(this);
+ if (mAudioAttributes != NULL) {
+ free(mAudioAttributes);
+ }
}
-void MediaPlayer2::disconnect()
-{
+bool MediaPlayer2::init() {
+ // TODO: after merge with NuPlayer2Driver, MediaPlayer2 will have its own
+ // looper for notification.
+ return true;
+}
+
+void MediaPlayer2::disconnect() {
ALOGV("disconnect");
- sp<MediaPlayer2Engine> p;
+ sp<MediaPlayer2Interface> p;
{
Mutex::Autolock _l(mLock);
p = mPlayer;
@@ -97,13 +454,17 @@
}
if (p != 0) {
- p->disconnect();
+ p->setListener(NULL);
+ p->reset();
+ }
+
+ {
+ Mutex::Autolock _l(mLock);
+ disconnectNativeWindow_l();
}
}
-// always call with lock held
-void MediaPlayer2::clear_l()
-{
+void MediaPlayer2::clear_l() {
mCurrentPosition = -1;
mCurrentSeekMode = MediaPlayer2SeekMode::SEEK_PREVIOUS_SYNC;
mSeekPosition = -1;
@@ -111,8 +472,7 @@
mVideoWidth = mVideoHeight = 0;
}
-status_t MediaPlayer2::setListener(const sp<MediaPlayer2Listener>& listener)
-{
+status_t MediaPlayer2::setListener(const sp<MediaPlayer2Listener>& listener) {
ALOGV("setListener");
Mutex::Autolock _l(mLock);
mListener = listener;
@@ -129,109 +489,236 @@
return OK;
}
-status_t MediaPlayer2::attachNewPlayer(const sp<MediaPlayer2Engine>& player, long srcId)
-{
- status_t err = UNKNOWN_ERROR;
- sp<MediaPlayer2Engine> p;
- { // scope for the lock
- Mutex::Autolock _l(mLock);
-
- if ( !( (mCurrentState & MEDIA_PLAYER2_IDLE) ||
- (mCurrentState == MEDIA_PLAYER2_STATE_ERROR ) ) ) {
- ALOGE("attachNewPlayer called in state %d", mCurrentState);
- return INVALID_OPERATION;
- }
-
- clear_l();
- p = mPlayer;
- mPlayer = player;
- mSrcId = srcId;
- if (player != 0) {
- mCurrentState = MEDIA_PLAYER2_INITIALIZED;
- err = NO_ERROR;
- } else {
- ALOGE("Unable to create media player");
- }
- }
-
- if (p != 0) {
- p->disconnect();
- }
-
- return err;
-}
-
-status_t MediaPlayer2::setDataSource(const sp<DataSourceDesc> &dsd)
-{
+status_t MediaPlayer2::setDataSource(const sp<DataSourceDesc> &dsd) {
if (dsd == NULL) {
return BAD_VALUE;
}
- ALOGV("setDataSource type(%d)", dsd->mType);
- status_t err = UNKNOWN_ERROR;
- sp<MediaPlayer2Engine> player(MediaPlayer2Manager::get().create(this, mAudioSessionId));
- if (NO_ERROR != player->setDataSource(dsd)) {
- player.clear();
+ ALOGV("setDataSource type(%d), srcId(%lld)", dsd->mType, (long long)dsd->mId);
+
+ sp<MediaPlayer2Interface> oldPlayer;
+
+ Mutex::Autolock _l(mLock);
+ {
+ if (!((mCurrentState & MEDIA_PLAYER2_IDLE)
+ || mCurrentState == MEDIA_PLAYER2_STATE_ERROR)) {
+ ALOGE("setDataSource called in wrong state %d", mCurrentState);
+ return INVALID_OPERATION;
+ }
+
+ sp<MediaPlayer2Interface> player = new NuPlayer2Driver(mPid, mUid);
+ status_t err = player->initCheck();
+ if (err != NO_ERROR) {
+ ALOGE("Failed to create player object, initCheck failed(%d)", err);
+ return err;
+ }
+
+ clear_l();
+
+ player->setListener(new proxyListener(this));
+ mAudioOutput = new MediaPlayer2AudioOutput(mAudioSessionId, mUid,
+ mPid, mAudioAttributes, new AudioDeviceUpdatedNotifier(player));
+ player->setAudioSink(mAudioOutput);
+
+ err = player->setDataSource(dsd);
+ if (err != OK) {
+ ALOGE("setDataSource error: %d", err);
+ return err;
+ }
+
+ sp<MediaPlayer2Interface> oldPlayer = mPlayer;
+ mPlayer = player;
+ mSrcId = dsd->mId;
+ mCurrentState = MEDIA_PLAYER2_INITIALIZED;
}
- err = attachNewPlayer(player, dsd->mId);
- return err;
+
+ if (oldPlayer != NULL) {
+ oldPlayer->setListener(NULL);
+ oldPlayer->reset();
+ }
+
+ return OK;
}
-status_t MediaPlayer2::invoke(const Parcel& request, Parcel *reply)
-{
+status_t MediaPlayer2::prepareNextDataSource(const sp<DataSourceDesc> &dsd) {
+ if (dsd == NULL) {
+ return BAD_VALUE;
+ }
+ ALOGV("prepareNextDataSource type(%d), srcId(%lld)", dsd->mType, (long long)dsd->mId);
+
+ Mutex::Autolock _l(mLock);
+ if (mPlayer == NULL) {
+ ALOGE("prepareNextDataSource failed: state %X, mPlayer(%p)", mCurrentState, mPlayer.get());
+ return INVALID_OPERATION;
+ }
+ return mPlayer->prepareNextDataSource(dsd);
+}
+
+status_t MediaPlayer2::playNextDataSource(int64_t srcId) {
+ ALOGV("playNextDataSource srcId(%lld)", (long long)srcId);
+
+ Mutex::Autolock _l(mLock);
+ if (mPlayer == NULL) {
+ ALOGE("playNextDataSource failed: state %X, mPlayer(%p)", mCurrentState, mPlayer.get());
+ return INVALID_OPERATION;
+ }
+ mSrcId = srcId;
+ return mPlayer->playNextDataSource(srcId);
+}
+
+status_t MediaPlayer2::invoke(const Parcel& request, Parcel *reply) {
Mutex::Autolock _l(mLock);
const bool hasBeenInitialized =
(mCurrentState != MEDIA_PLAYER2_STATE_ERROR) &&
((mCurrentState & MEDIA_PLAYER2_IDLE) != MEDIA_PLAYER2_IDLE);
- if ((mPlayer != NULL) && hasBeenInitialized) {
- ALOGV("invoke %zu", request.dataSize());
- return mPlayer->invoke(request, reply);
+ if ((mPlayer == NULL) || !hasBeenInitialized) {
+ ALOGE("invoke failed: wrong state %X, mPlayer(%p)", mCurrentState, mPlayer.get());
+ return INVALID_OPERATION;
}
- ALOGE("invoke failed: wrong state %X, mPlayer(%p)", mCurrentState, mPlayer.get());
- return INVALID_OPERATION;
+ ALOGV("invoke %zu", request.dataSize());
+ return mPlayer->invoke(request, reply);
}
-status_t MediaPlayer2::setMetadataFilter(const Parcel& filter)
-{
+// This call doesn't need to access the native player.
+status_t MediaPlayer2::setMetadataFilter(const Parcel& filter) {
ALOGD("setMetadataFilter");
- Mutex::Autolock lock(mLock);
- if (mPlayer == NULL) {
- return NO_INIT;
+
+ status_t status;
+ media::Metadata::Filter allow, drop;
+
+ if (unmarshallFilter(filter, &allow, &status) &&
+ unmarshallFilter(filter, &drop, &status)) {
+ Mutex::Autolock lock(mLock);
+
+ mMetadataAllow = allow;
+ mMetadataDrop = drop;
}
- return mPlayer->setMetadataFilter(filter);
+ return status;
}
-status_t MediaPlayer2::getMetadata(bool update_only, bool apply_filter, Parcel *metadata)
-{
+status_t MediaPlayer2::getMetadata(bool update_only, bool /* apply_filter */, Parcel *reply) {
ALOGD("getMetadata");
+ sp<MediaPlayer2Interface> player;
+ media::Metadata::Filter ids;
Mutex::Autolock lock(mLock);
- if (mPlayer == NULL) {
+ {
+ if (mPlayer == NULL) {
+ return NO_INIT;
+ }
+
+ player = mPlayer;
+ // Placeholder for the return code, updated by the caller.
+ reply->writeInt32(-1);
+
+ // We don't block notifications while we fetch the data. We clear
+ // mMetadataUpdated first so we don't lose notifications happening
+ // during the rest of this call.
+ if (update_only) {
+ ids = mMetadataUpdated;
+ }
+ mMetadataUpdated.clear();
+ }
+
+ media::Metadata metadata(reply);
+
+ metadata.appendHeader();
+ status_t status = player->getMetadata(ids, reply);
+
+ if (status != OK) {
+ metadata.resetParcel();
+ ALOGE("getMetadata failed %d", status);
+ return status;
+ }
+
+ // FIXME: ement filtering on the result. Not critical since
+ // filtering takes place on the update notifications already. This
+ // would be when all the metadata are fetch and a filter is set.
+
+ // Everything is fine, update the metadata length.
+ metadata.updateLength();
+ return OK;
+}
+
+void MediaPlayer2::disconnectNativeWindow_l() {
+ if (mConnectedWindow != NULL && mConnectedWindow->getANativeWindow() != NULL) {
+ status_t err = native_window_api_disconnect(
+ mConnectedWindow->getANativeWindow(), NATIVE_WINDOW_API_MEDIA);
+
+ if (err != OK) {
+ ALOGW("nativeWindowDisconnect returned an error: %s (%d)",
+ strerror(-err), err);
+ }
+ }
+ mConnectedWindow.clear();
+}
+
+status_t MediaPlayer2::setVideoSurfaceTexture(const sp<ANativeWindowWrapper>& nww) {
+ ANativeWindow *anw = (nww == NULL ? NULL : nww->getANativeWindow());
+ ALOGV("setVideoSurfaceTexture(%p)", anw);
+ Mutex::Autolock _l(mLock);
+ if (mPlayer == 0) {
return NO_INIT;
}
- return mPlayer->getMetadata(update_only, apply_filter, metadata);
+
+ if (anw != NULL) {
+ if (mConnectedWindow != NULL
+ && mConnectedWindow->getANativeWindow() == anw) {
+ return OK;
+ }
+ status_t err = native_window_api_connect(anw, NATIVE_WINDOW_API_MEDIA);
+
+ if (err != OK) {
+ ALOGE("setVideoSurfaceTexture failed: %d", err);
+ // Note that we must do the reset before disconnecting from the ANW.
+ // Otherwise queue/dequeue calls could be made on the disconnected
+ // ANW, which may result in errors.
+ mPlayer->reset();
+ disconnectNativeWindow_l();
+ return err;
+ }
+ }
+
+ // Note that we must set the player's new GraphicBufferProducer before
+ // disconnecting the old one. Otherwise queue/dequeue calls could be made
+ // on the disconnected ANW, which may result in errors.
+ status_t err = mPlayer->setVideoSurfaceTexture(nww);
+
+ disconnectNativeWindow_l();
+
+ if (err == OK) {
+ mConnectedWindow = nww;
+ mLock.unlock();
+ } else if (anw != NULL) {
+ mLock.unlock();
+ status_t err = native_window_api_disconnect(anw, NATIVE_WINDOW_API_MEDIA);
+
+ if (err != OK) {
+ ALOGW("nativeWindowDisconnect returned an error: %s (%d)",
+ strerror(-err), err);
+ }
+ }
+
+ return err;
}
-status_t MediaPlayer2::setVideoSurfaceTexture(const sp<ANativeWindowWrapper>& nww)
-{
- ALOGV("setVideoSurfaceTexture");
- Mutex::Autolock _l(mLock);
- if (mPlayer == 0) return NO_INIT;
- return mPlayer->setVideoSurfaceTexture(nww);
-}
-
-status_t MediaPlayer2::getBufferingSettings(BufferingSettings* buffering /* nonnull */)
-{
+status_t MediaPlayer2::getBufferingSettings(BufferingSettings* buffering /* nonnull */) {
ALOGV("getBufferingSettings");
Mutex::Autolock _l(mLock);
if (mPlayer == 0) {
return NO_INIT;
}
- return mPlayer->getBufferingSettings(buffering);
+
+ status_t ret = mPlayer->getBufferingSettings(buffering);
+ if (ret == NO_ERROR) {
+ ALOGV("getBufferingSettings{%s}", buffering->toString().string());
+ } else {
+ ALOGE("getBufferingSettings returned %d", ret);
+ }
+ return ret;
}
-status_t MediaPlayer2::setBufferingSettings(const BufferingSettings& buffering)
-{
- ALOGV("setBufferingSettings");
+status_t MediaPlayer2::setBufferingSettings(const BufferingSettings& buffering) {
+ ALOGV("setBufferingSettings{%s}", buffering.toString().string());
Mutex::Autolock _l(mLock);
if (mPlayer == 0) {
@@ -240,14 +727,37 @@
return mPlayer->setBufferingSettings(buffering);
}
-// must call with lock held
-status_t MediaPlayer2::prepareAsync_l()
-{
- if ( (mPlayer != 0) && ( mCurrentState & (MEDIA_PLAYER2_INITIALIZED | MEDIA_PLAYER2_STOPPED) ) ) {
+status_t MediaPlayer2::setAudioAttributes_l(const Parcel &parcel) {
+ if (mAudioAttributes != NULL) {
+ free(mAudioAttributes);
+ }
+ mAudioAttributes = (audio_attributes_t *) calloc(1, sizeof(audio_attributes_t));
+ if (mAudioAttributes == NULL) {
+ return NO_MEMORY;
+ }
+ unmarshallAudioAttributes(parcel, mAudioAttributes);
+
+ ALOGV("setAudioAttributes_l() usage=%d content=%d flags=0x%x tags=%s",
+ mAudioAttributes->usage, mAudioAttributes->content_type, mAudioAttributes->flags,
+ mAudioAttributes->tags);
+
+ if (mAudioOutput != 0) {
+ mAudioOutput->setAudioAttributes(mAudioAttributes);
+ }
+ return NO_ERROR;
+}
+
+status_t MediaPlayer2::prepareAsync() {
+ ALOGV("prepareAsync");
+ Mutex::Autolock _l(mLock);
+ if ((mPlayer != 0) && (mCurrentState & (MEDIA_PLAYER2_INITIALIZED | MEDIA_PLAYER2_STOPPED))) {
if (mAudioAttributesParcel != NULL) {
- mPlayer->setParameter(MEDIA2_KEY_PARAMETER_AUDIO_ATTRIBUTES, *mAudioAttributesParcel);
- } else {
- mPlayer->setAudioStreamType(mStreamType);
+ status_t err = setAudioAttributes_l(*mAudioAttributesParcel);
+ if (err != OK) {
+ return err;
+ }
+ } else if (mAudioOutput != 0) {
+ mAudioOutput->setAudioStreamType(mStreamType);
}
mCurrentState = MEDIA_PLAYER2_PREPARING;
return mPlayer->prepareAsync();
@@ -256,44 +766,7 @@
return INVALID_OPERATION;
}
-// TODO: In case of error, prepareAsync provides the caller with 2 error codes,
-// one defined in the Android framework and one provided by the implementation
-// that generated the error. The sync version of prepare returns only 1 error
-// code.
-status_t MediaPlayer2::prepare()
-{
- ALOGV("prepare");
- Mutex::Autolock _l(mLock);
- mLockThreadId = getThreadId();
- if (mPrepareSync) {
- mLockThreadId = 0;
- return -EALREADY;
- }
- mPrepareSync = true;
- status_t ret = prepareAsync_l();
- if (ret != NO_ERROR) {
- mLockThreadId = 0;
- return ret;
- }
-
- if (mPrepareSync) {
- mSignal.wait(mLock); // wait for prepare done
- mPrepareSync = false;
- }
- ALOGV("prepare complete - status=%d", mPrepareStatus);
- mLockThreadId = 0;
- return mPrepareStatus;
-}
-
-status_t MediaPlayer2::prepareAsync()
-{
- ALOGV("prepareAsync");
- Mutex::Autolock _l(mLock);
- return prepareAsync_l();
-}
-
-status_t MediaPlayer2::start()
-{
+status_t MediaPlayer2::start() {
ALOGV("start");
status_t ret = NO_ERROR;
@@ -306,8 +779,14 @@
} else if ( (mPlayer != 0) && ( mCurrentState & ( MEDIA_PLAYER2_PREPARED |
MEDIA_PLAYER2_PLAYBACK_COMPLETE | MEDIA_PLAYER2_PAUSED ) ) ) {
mPlayer->setLooping(mLoop);
- mPlayer->setVolume(mLeftVolume, mRightVolume);
- mPlayer->setAuxEffectSendLevel(mSendLevel);
+
+ if (mAudioOutput != 0) {
+ mAudioOutput->setVolume(mLeftVolume, mRightVolume);
+ }
+
+ if (mAudioOutput != 0) {
+ mAudioOutput->setAuxEffectSendLevel(mSendLevel);
+ }
mCurrentState = MEDIA_PLAYER2_STARTED;
ret = mPlayer->start();
if (ret != NO_ERROR) {
@@ -327,8 +806,7 @@
return ret;
}
-status_t MediaPlayer2::stop()
-{
+status_t MediaPlayer2::stop() {
ALOGV("stop");
Mutex::Autolock _l(mLock);
if (mCurrentState & MEDIA_PLAYER2_STOPPED) return NO_ERROR;
@@ -346,8 +824,7 @@
return INVALID_OPERATION;
}
-status_t MediaPlayer2::pause()
-{
+status_t MediaPlayer2::pause() {
ALOGV("pause");
Mutex::Autolock _l(mLock);
if (mCurrentState & (MEDIA_PLAYER2_PAUSED|MEDIA_PLAYER2_PLAYBACK_COMPLETE))
@@ -365,12 +842,10 @@
return INVALID_OPERATION;
}
-bool MediaPlayer2::isPlaying()
-{
+bool MediaPlayer2::isPlaying() {
Mutex::Autolock _l(mLock);
if (mPlayer != 0) {
- bool temp = false;
- mPlayer->isPlaying(&temp);
+ bool temp = mPlayer->isPlaying();
ALOGV("isPlaying: %d", temp);
if ((mCurrentState & MEDIA_PLAYER2_STARTED) && ! temp) {
ALOGE("internal/external state mismatch corrected");
@@ -385,13 +860,12 @@
return false;
}
-status_t MediaPlayer2::setPlaybackSettings(const AudioPlaybackRate& rate)
-{
+status_t MediaPlayer2::setPlaybackSettings(const AudioPlaybackRate& rate) {
ALOGV("setPlaybackSettings: %f %f %d %d",
rate.mSpeed, rate.mPitch, rate.mFallbackMode, rate.mStretchMode);
// Negative speed and pitch does not make sense. Further validation will
// be done by the respective mediaplayers.
- if (rate.mSpeed < 0.f || rate.mPitch < 0.f) {
+ if (rate.mSpeed <= 0.f || rate.mPitch < 0.f) {
return BAD_VALUE;
}
Mutex::Autolock _l(mLock);
@@ -399,36 +873,27 @@
return INVALID_OPERATION;
}
- if (rate.mSpeed != 0.f && !(mCurrentState & MEDIA_PLAYER2_STARTED)
- && (mCurrentState & (MEDIA_PLAYER2_PREPARED | MEDIA_PLAYER2_PAUSED
- | MEDIA_PLAYER2_PLAYBACK_COMPLETE))) {
- mPlayer->setLooping(mLoop);
- mPlayer->setVolume(mLeftVolume, mRightVolume);
- mPlayer->setAuxEffectSendLevel(mSendLevel);
- }
-
status_t err = mPlayer->setPlaybackSettings(rate);
- if (err == OK) {
- if (rate.mSpeed == 0.f && mCurrentState == MEDIA_PLAYER2_STARTED) {
- mCurrentState = MEDIA_PLAYER2_PAUSED;
- } else if (rate.mSpeed != 0.f
- && (mCurrentState & (MEDIA_PLAYER2_PREPARED | MEDIA_PLAYER2_PAUSED
- | MEDIA_PLAYER2_PLAYBACK_COMPLETE))) {
- mCurrentState = MEDIA_PLAYER2_STARTED;
- }
- }
return err;
}
-status_t MediaPlayer2::getPlaybackSettings(AudioPlaybackRate* rate /* nonnull */)
-{
+status_t MediaPlayer2::getPlaybackSettings(AudioPlaybackRate* rate /* nonnull */) {
Mutex::Autolock _l(mLock);
- if (mPlayer == 0) return INVALID_OPERATION;
+ if (mPlayer == 0) {
+ return INVALID_OPERATION;
+ }
return mPlayer->getPlaybackSettings(rate);
+ status_t ret = mPlayer->getPlaybackSettings(rate);
+ if (ret == NO_ERROR) {
+ ALOGV("getPlaybackSettings(%f, %f, %d, %d)",
+ rate->mSpeed, rate->mPitch, rate->mFallbackMode, rate->mStretchMode);
+ } else {
+ ALOGV("getPlaybackSettings returned %d", ret);
+ }
+ return ret;
}
-status_t MediaPlayer2::setSyncSettings(const AVSyncSettings& sync, float videoFpsHint)
-{
+status_t MediaPlayer2::setSyncSettings(const AVSyncSettings& sync, float videoFpsHint) {
ALOGV("setSyncSettings: %u %u %f %f",
sync.mSource, sync.mAudioAdjustMode, sync.mTolerance, videoFpsHint);
Mutex::Autolock _l(mLock);
@@ -437,124 +902,128 @@
}
status_t MediaPlayer2::getSyncSettings(
- AVSyncSettings* sync /* nonnull */, float* videoFps /* nonnull */)
-{
+ AVSyncSettings* sync /* nonnull */, float* videoFps /* nonnull */) {
Mutex::Autolock _l(mLock);
if (mPlayer == 0) return INVALID_OPERATION;
- return mPlayer->getSyncSettings(sync, videoFps);
+ status_t ret = mPlayer->getSyncSettings(sync, videoFps);
+ if (ret == NO_ERROR) {
+ ALOGV("getSyncSettings(%u, %u, %f, %f)",
+ sync->mSource, sync->mAudioAdjustMode, sync->mTolerance, *videoFps);
+ } else {
+ ALOGV("getSyncSettings returned %d", ret);
+ }
+ return ret;
+
}
-status_t MediaPlayer2::getVideoWidth(int *w)
-{
+status_t MediaPlayer2::getVideoWidth(int *w) {
ALOGV("getVideoWidth");
Mutex::Autolock _l(mLock);
- if (mPlayer == 0) return INVALID_OPERATION;
+ if (mPlayer == 0) {
+ return INVALID_OPERATION;
+ }
*w = mVideoWidth;
return NO_ERROR;
}
-status_t MediaPlayer2::getVideoHeight(int *h)
-{
+status_t MediaPlayer2::getVideoHeight(int *h) {
ALOGV("getVideoHeight");
Mutex::Autolock _l(mLock);
- if (mPlayer == 0) return INVALID_OPERATION;
+ if (mPlayer == 0) {
+ return INVALID_OPERATION;
+ }
*h = mVideoHeight;
return NO_ERROR;
}
-status_t MediaPlayer2::getCurrentPosition(int *msec)
-{
+status_t MediaPlayer2::getCurrentPosition(int64_t *msec) {
ALOGV("getCurrentPosition");
Mutex::Autolock _l(mLock);
- if (mPlayer != 0) {
- if (mCurrentPosition >= 0) {
- ALOGV("Using cached seek position: %d", mCurrentPosition);
- *msec = mCurrentPosition;
- return NO_ERROR;
- }
- return mPlayer->getCurrentPosition(msec);
+ if (mPlayer == 0) {
+ return INVALID_OPERATION;
}
- return INVALID_OPERATION;
+ if (mCurrentPosition >= 0) {
+ ALOGV("Using cached seek position: %lld", (long long)mCurrentPosition);
+ *msec = mCurrentPosition;
+ return NO_ERROR;
+ }
+ status_t ret = mPlayer->getCurrentPosition(msec);
+ if (ret == NO_ERROR) {
+ ALOGV("getCurrentPosition = %lld", (long long)*msec);
+ } else {
+ ALOGE("getCurrentPosition returned %d", ret);
+ }
+ return ret;
}
-status_t MediaPlayer2::getDuration_l(int *msec)
-{
+status_t MediaPlayer2::getDuration(int64_t *msec) {
+ Mutex::Autolock _l(mLock);
ALOGV("getDuration_l");
bool isValidState = (mCurrentState & (MEDIA_PLAYER2_PREPARED | MEDIA_PLAYER2_STARTED |
MEDIA_PLAYER2_PAUSED | MEDIA_PLAYER2_STOPPED | MEDIA_PLAYER2_PLAYBACK_COMPLETE));
- if (mPlayer != 0 && isValidState) {
- int durationMs;
- status_t ret = mPlayer->getDuration(&durationMs);
-
- if (ret != OK) {
- // Do not enter error state just because no duration was available.
- durationMs = -1;
- ret = OK;
- }
-
- if (msec) {
- *msec = durationMs;
- }
- return ret;
+ if (mPlayer == 0 || !isValidState) {
+ ALOGE("Attempt to call getDuration in wrong state: mPlayer=%p, mCurrentState=%u",
+ mPlayer.get(), mCurrentState);
+ return INVALID_OPERATION;
}
- ALOGE("Attempt to call getDuration in wrong state: mPlayer=%p, mCurrentState=%u",
- mPlayer.get(), mCurrentState);
- return INVALID_OPERATION;
-}
+ int64_t durationMs;
+ status_t ret = mPlayer->getDuration(&durationMs);
-status_t MediaPlayer2::getDuration(int *msec)
-{
- Mutex::Autolock _l(mLock);
- return getDuration_l(msec);
-}
-
-status_t MediaPlayer2::seekTo_l(int msec, MediaPlayer2SeekMode mode)
-{
- ALOGV("seekTo (%d, %d)", msec, mode);
- if ((mPlayer != 0) && ( mCurrentState & ( MEDIA_PLAYER2_STARTED | MEDIA_PLAYER2_PREPARED |
- MEDIA_PLAYER2_PAUSED | MEDIA_PLAYER2_PLAYBACK_COMPLETE) ) ) {
- if ( msec < 0 ) {
- ALOGW("Attempt to seek to invalid position: %d", msec);
- msec = 0;
- }
-
- int durationMs;
- status_t err = mPlayer->getDuration(&durationMs);
-
- if (err != OK) {
- ALOGW("Stream has no duration and is therefore not seekable.");
- return err;
- }
-
- if (msec > durationMs) {
- ALOGW("Attempt to seek to past end of file: request = %d, "
- "durationMs = %d",
- msec,
- durationMs);
-
- msec = durationMs;
- }
-
- // cache duration
- mCurrentPosition = msec;
- mCurrentSeekMode = mode;
- if (mSeekPosition < 0) {
- mSeekPosition = msec;
- mSeekMode = mode;
- return mPlayer->seekTo(msec, mode);
- }
- else {
- ALOGV("Seek in progress - queue up seekTo[%d, %d]", msec, mode);
- return NO_ERROR;
- }
+ if (ret == NO_ERROR) {
+ ALOGV("getDuration = %lld", (long long)durationMs);
+ } else {
+ ALOGE("getDuration returned %d", ret);
+ // Do not enter error state just because no duration was available.
+ durationMs = -1;
}
- ALOGE("Attempt to perform seekTo in wrong state: mPlayer=%p, mCurrentState=%u", mPlayer.get(),
- mCurrentState);
- return INVALID_OPERATION;
+
+ if (msec) {
+ *msec = durationMs;
+ }
+ return OK;
}
-status_t MediaPlayer2::seekTo(int msec, MediaPlayer2SeekMode mode)
-{
+status_t MediaPlayer2::seekTo_l(int64_t msec, MediaPlayer2SeekMode mode) {
+ ALOGV("seekTo (%lld, %d)", (long long)msec, mode);
+ if ((mPlayer == 0) || !(mCurrentState & (MEDIA_PLAYER2_STARTED | MEDIA_PLAYER2_PREPARED |
+ MEDIA_PLAYER2_PAUSED | MEDIA_PLAYER2_PLAYBACK_COMPLETE))) {
+ ALOGE("Attempt to perform seekTo in wrong state: mPlayer=%p, mCurrentState=%u",
+ mPlayer.get(), mCurrentState);
+ return INVALID_OPERATION;
+ }
+ if (msec < 0) {
+ ALOGW("Attempt to seek to invalid position: %lld", (long long)msec);
+ msec = 0;
+ }
+
+ int64_t durationMs;
+ status_t err = mPlayer->getDuration(&durationMs);
+
+ if (err != OK) {
+ ALOGW("Stream has no duration and is therefore not seekable.");
+ return err;
+ }
+
+ if (msec > durationMs) {
+ ALOGW("Attempt to seek to past end of file: request = %lld, durationMs = %lld",
+ (long long)msec, (long long)durationMs);
+
+ msec = durationMs;
+ }
+
+ // cache duration
+ mCurrentPosition = msec;
+ mCurrentSeekMode = mode;
+ if (mSeekPosition < 0) {
+ mSeekPosition = msec;
+ mSeekMode = mode;
+ return mPlayer->seekTo(msec, mode);
+ }
+ ALOGV("Seek in progress - queue up seekTo[%lld, %d]", (long long)msec, mode);
+ return NO_ERROR;
+}
+
+status_t MediaPlayer2::seekTo(int64_t msec, MediaPlayer2SeekMode mode) {
mLockThreadId = getThreadId();
Mutex::Autolock _l(mLock);
status_t result = seekTo_l(msec, mode);
@@ -563,27 +1032,27 @@
return result;
}
-status_t MediaPlayer2::notifyAt(int64_t mediaTimeUs)
-{
+status_t MediaPlayer2::notifyAt(int64_t mediaTimeUs) {
Mutex::Autolock _l(mLock);
if (mPlayer != 0) {
- return mPlayer->notifyAt(mediaTimeUs);
+ return INVALID_OPERATION;
}
- return INVALID_OPERATION;
+
+ return mPlayer->notifyAt(mediaTimeUs);
}
-status_t MediaPlayer2::reset_l()
-{
+status_t MediaPlayer2::reset_l() {
mLoop = false;
- if (mCurrentState == MEDIA_PLAYER2_IDLE) return NO_ERROR;
- mPrepareSync = false;
+ if (mCurrentState == MEDIA_PLAYER2_IDLE) {
+ return NO_ERROR;
+ }
if (mPlayer != 0) {
status_t ret = mPlayer->reset();
if (ret != NO_ERROR) {
ALOGE("reset() failed with return code (%d)", ret);
mCurrentState = MEDIA_PLAYER2_STATE_ERROR;
} else {
- mPlayer->disconnect();
+ mPlayer->setListener(NULL);
mCurrentState = MEDIA_PLAYER2_IDLE;
}
// setDataSource has to be called again to create a
@@ -595,8 +1064,7 @@
return NO_ERROR;
}
-status_t MediaPlayer2::reset()
-{
+status_t MediaPlayer2::reset() {
ALOGV("reset");
mLockThreadId = getThreadId();
Mutex::Autolock _l(mLock);
@@ -606,8 +1074,7 @@
return result;
}
-status_t MediaPlayer2::setAudioStreamType(audio_stream_type_t type)
-{
+status_t MediaPlayer2::setAudioStreamType(audio_stream_type_t type) {
ALOGV("MediaPlayer2::setAudioStreamType");
Mutex::Autolock _l(mLock);
if (mStreamType == type) return NO_ERROR;
@@ -622,16 +1089,14 @@
return OK;
}
-status_t MediaPlayer2::getAudioStreamType(audio_stream_type_t *type)
-{
+status_t MediaPlayer2::getAudioStreamType(audio_stream_type_t *type) {
ALOGV("getAudioStreamType");
Mutex::Autolock _l(mLock);
*type = mStreamType;
return OK;
}
-status_t MediaPlayer2::setLooping(int loop)
-{
+status_t MediaPlayer2::setLooping(int loop) {
ALOGV("MediaPlayer2::setLooping");
Mutex::Autolock _l(mLock);
mLoop = (loop != 0);
@@ -651,20 +1116,18 @@
return false;
}
-status_t MediaPlayer2::setVolume(float leftVolume, float rightVolume)
-{
+status_t MediaPlayer2::setVolume(float leftVolume, float rightVolume) {
ALOGV("MediaPlayer2::setVolume(%f, %f)", leftVolume, rightVolume);
Mutex::Autolock _l(mLock);
mLeftVolume = leftVolume;
mRightVolume = rightVolume;
- if (mPlayer != 0) {
- return mPlayer->setVolume(leftVolume, rightVolume);
+ if (mAudioOutput != 0) {
+ mAudioOutput->setVolume(leftVolume, rightVolume);
}
return OK;
}
-status_t MediaPlayer2::setAudioSessionId(audio_session_t sessionId)
-{
+status_t MediaPlayer2::setAudioSessionId(audio_session_t sessionId) {
ALOGV("MediaPlayer2::setAudioSessionId(%d)", sessionId);
Mutex::Autolock _l(mLock);
if (!(mCurrentState & MEDIA_PLAYER2_IDLE)) {
@@ -682,40 +1145,36 @@
return NO_ERROR;
}
-audio_session_t MediaPlayer2::getAudioSessionId()
-{
+audio_session_t MediaPlayer2::getAudioSessionId() {
Mutex::Autolock _l(mLock);
return mAudioSessionId;
}
-status_t MediaPlayer2::setAuxEffectSendLevel(float level)
-{
+status_t MediaPlayer2::setAuxEffectSendLevel(float level) {
ALOGV("MediaPlayer2::setAuxEffectSendLevel(%f)", level);
Mutex::Autolock _l(mLock);
mSendLevel = level;
- if (mPlayer != 0) {
- return mPlayer->setAuxEffectSendLevel(level);
+ if (mAudioOutput != 0) {
+ return mAudioOutput->setAuxEffectSendLevel(level);
}
return OK;
}
-status_t MediaPlayer2::attachAuxEffect(int effectId)
-{
+status_t MediaPlayer2::attachAuxEffect(int effectId) {
ALOGV("MediaPlayer2::attachAuxEffect(%d)", effectId);
Mutex::Autolock _l(mLock);
- if (mPlayer == 0 ||
+ if (mAudioOutput == 0 ||
(mCurrentState & MEDIA_PLAYER2_IDLE) ||
(mCurrentState == MEDIA_PLAYER2_STATE_ERROR )) {
ALOGE("attachAuxEffect called in state %d, mPlayer(%p)", mCurrentState, mPlayer.get());
return INVALID_OPERATION;
}
- return mPlayer->attachAuxEffect(effectId);
+ return mAudioOutput->attachAuxEffect(effectId);
}
// always call with lock held
-status_t MediaPlayer2::checkStateForKeySet_l(int key)
-{
+status_t MediaPlayer2::checkStateForKeySet_l(int key) {
switch(key) {
case MEDIA2_KEY_PARAMETER_AUDIO_ATTRIBUTES:
if (mCurrentState & ( MEDIA_PLAYER2_PREPARED | MEDIA_PLAYER2_STARTED |
@@ -732,8 +1191,7 @@
return OK;
}
-status_t MediaPlayer2::setParameter(int key, const Parcel& request)
-{
+status_t MediaPlayer2::setParameter(int key, const Parcel& request) {
ALOGV("MediaPlayer2::setParameter(%d)", key);
status_t status = INVALID_OPERATION;
Mutex::Autolock _l(mLock);
@@ -743,10 +1201,15 @@
switch (key) {
case MEDIA2_KEY_PARAMETER_AUDIO_ATTRIBUTES:
// save the marshalled audio attributes
- if (mAudioAttributesParcel != NULL) { delete mAudioAttributesParcel; };
+ if (mAudioAttributesParcel != NULL) {
+ delete mAudioAttributesParcel;
+ }
mAudioAttributesParcel = new Parcel();
mAudioAttributesParcel->appendFrom(&request, 0, request.dataSize());
- status = OK;
+ status = setAudioAttributes_l(request);
+ if (status != OK) {
+ return status;
+ }
break;
default:
ALOGV_IF(mPlayer == NULL, "setParameter: no active player");
@@ -759,25 +1222,69 @@
return status;
}
-status_t MediaPlayer2::getParameter(int key, Parcel *reply)
-{
+status_t MediaPlayer2::getParameter(int key, Parcel *reply) {
ALOGV("MediaPlayer2::getParameter(%d)", key);
Mutex::Autolock _l(mLock);
- if (mPlayer != NULL) {
- status_t status = mPlayer->getParameter(key, reply);
- if (status != OK) {
- ALOGD("getParameter returns %d", status);
+ if (key == MEDIA2_KEY_PARAMETER_AUDIO_ATTRIBUTES) {
+ if (reply == NULL) {
+ return BAD_VALUE;
}
- return status;
+ if (mAudioAttributesParcel != NULL) {
+ reply->appendFrom(mAudioAttributesParcel, 0, mAudioAttributesParcel->dataSize());
+ }
+ return OK;
}
- ALOGV("getParameter: no active player");
- return INVALID_OPERATION;
+
+ if (mPlayer == NULL) {
+ ALOGV("getParameter: no active player");
+ return INVALID_OPERATION;
+ }
+
+ status_t status = mPlayer->getParameter(key, reply);
+ if (status != OK) {
+ ALOGD("getParameter returns %d", status);
+ }
+ return status;
}
-void MediaPlayer2::notify(int64_t srcId, int msg, int ext1, int ext2, const Parcel *obj)
-{
+bool MediaPlayer2::shouldDropMetadata(media::Metadata::Type code) const {
+ Mutex::Autolock lock(mLock);
+
+ if (findMetadata(mMetadataDrop, code)) {
+ return true;
+ }
+
+ if (mMetadataAllow.isEmpty() || findMetadata(mMetadataAllow, code)) {
+ return false;
+ } else {
+ return true;
+ }
+}
+
+
+void MediaPlayer2::addNewMetadataUpdate(media::Metadata::Type metadata_type) {
+ Mutex::Autolock lock(mLock);
+ if (mMetadataUpdated.indexOf(metadata_type) < 0) {
+ mMetadataUpdated.add(metadata_type);
+ }
+}
+
+void MediaPlayer2::notify(int64_t srcId, int msg, int ext1, int ext2, const Parcel *obj) {
ALOGV("message received srcId=%lld, msg=%d, ext1=%d, ext2=%d",
(long long)srcId, msg, ext1, ext2);
+
+ if (MEDIA2_INFO == msg && MEDIA2_INFO_METADATA_UPDATE == ext1) {
+ const media::Metadata::Type metadata_type = ext2;
+
+ if(shouldDropMetadata(metadata_type)) {
+ return;
+ }
+
+ // Update the list of metadata that have changed. getMetadata
+ // also access mMetadataUpdated and clears it.
+ addNewMetadataUpdate(metadata_type);
+ }
+
bool send = true;
bool locked = false;
@@ -808,12 +1315,6 @@
case MEDIA2_PREPARED:
ALOGV("MediaPlayer2::notify() prepared");
mCurrentState = MEDIA_PLAYER2_PREPARED;
- if (mPrepareSync) {
- ALOGV("signal application thread");
- mPrepareSync = false;
- mPrepareStatus = NO_ERROR;
- mSignal.signal();
- }
break;
case MEDIA2_DRM_INFO:
ALOGV("MediaPlayer2::notify() MEDIA2_DRM_INFO(%lld, %d, %d, %d, %p)",
@@ -834,14 +1335,6 @@
// ext2: Implementation dependant error code.
ALOGE("error (%d, %d)", ext1, ext2);
mCurrentState = MEDIA_PLAYER2_STATE_ERROR;
- if (mPrepareSync)
- {
- ALOGV("signal application thread");
- mPrepareSync = false;
- mPrepareStatus = ext1;
- mSignal.signal();
- send = false;
- }
break;
case MEDIA2_INFO:
// ext1: Media framework error code.
@@ -853,7 +1346,8 @@
case MEDIA2_SEEK_COMPLETE:
ALOGV("Received seek complete");
if (mSeekPosition != mCurrentPosition || (mSeekMode != mCurrentSeekMode)) {
- ALOGV("Executing queued seekTo(%d, %d)", mCurrentPosition, mCurrentSeekMode);
+ ALOGV("Executing queued seekTo(%lld, %d)",
+ (long long)mCurrentPosition, mCurrentSeekMode);
mSeekPosition = -1;
mSeekMode = MediaPlayer2SeekMode::SEEK_PREVIOUS_SYNC;
seekTo_l(mCurrentPosition, mCurrentSeekMode);
@@ -901,24 +1395,8 @@
}
}
-status_t MediaPlayer2::setNextMediaPlayer(const sp<MediaPlayer2>& next) {
- Mutex::Autolock _l(mLock);
- if (mPlayer == NULL) {
- return NO_INIT;
- }
-
- if (next != NULL && !(next->mCurrentState &
- (MEDIA_PLAYER2_PREPARED | MEDIA_PLAYER2_PAUSED | MEDIA_PLAYER2_PLAYBACK_COMPLETE))) {
- ALOGE("next player is not prepared");
- return INVALID_OPERATION;
- }
-
- return mPlayer->setNextPlayer(next == NULL ? NULL : next->mPlayer);
-}
-
// Modular DRM
-status_t MediaPlayer2::prepareDrm(const uint8_t uuid[16], const Vector<uint8_t>& drmSessionId)
-{
+status_t MediaPlayer2::prepareDrm(const uint8_t uuid[16], const Vector<uint8_t>& drmSessionId) {
// TODO change to ALOGV
ALOGD("prepareDrm: uuid: %p drmSessionId: %p(%zu)", uuid,
drmSessionId.array(), drmSessionId.size());
@@ -951,8 +1429,7 @@
return status;
}
-status_t MediaPlayer2::releaseDrm()
-{
+status_t MediaPlayer2::releaseDrm() {
Mutex::Autolock _l(mLock);
if (mPlayer == NULL) {
return NO_INIT;
@@ -980,39 +1457,74 @@
return status;
}
-status_t MediaPlayer2::setOutputDevice(audio_port_handle_t deviceId)
-{
+status_t MediaPlayer2::setOutputDevice(audio_port_handle_t deviceId) {
Mutex::Autolock _l(mLock);
- if (mPlayer == NULL) {
- ALOGV("setOutputDevice: player not init");
+ if (mAudioOutput == NULL) {
+ ALOGV("setOutputDevice: audio sink not init");
return NO_INIT;
}
- return mPlayer->setOutputDevice(deviceId);
+ return mAudioOutput->setOutputDevice(deviceId);
}
-audio_port_handle_t MediaPlayer2::getRoutedDeviceId()
-{
+audio_port_handle_t MediaPlayer2::getRoutedDeviceId() {
Mutex::Autolock _l(mLock);
- if (mPlayer == NULL) {
- ALOGV("getRoutedDeviceId: player not init");
+ if (mAudioOutput == NULL) {
+ ALOGV("getRoutedDeviceId: audio sink not init");
return AUDIO_PORT_HANDLE_NONE;
}
audio_port_handle_t deviceId;
- status_t status = mPlayer->getRoutedDeviceId(&deviceId);
+ status_t status = mAudioOutput->getRoutedDeviceId(&deviceId);
if (status != NO_ERROR) {
return AUDIO_PORT_HANDLE_NONE;
}
return deviceId;
}
-status_t MediaPlayer2::enableAudioDeviceCallback(bool enabled)
-{
+status_t MediaPlayer2::enableAudioDeviceCallback(bool enabled) {
Mutex::Autolock _l(mLock);
- if (mPlayer == NULL) {
+ if (mAudioOutput == NULL) {
ALOGV("addAudioDeviceCallback: player not init");
return NO_INIT;
}
- return mPlayer->enableAudioDeviceCallback(enabled);
+ return mAudioOutput->enableAudioDeviceCallback(enabled);
+}
+
+status_t MediaPlayer2::dump(int fd, const Vector<String16>& args) {
+ const size_t SIZE = 256;
+ char buffer[SIZE];
+ String8 result;
+ result.append(" MediaPlayer2\n");
+ snprintf(buffer, 255, " pid(%d), looping(%s)\n", mPid, mLoop?"true": "false");
+ result.append(buffer);
+
+ sp<MediaPlayer2Interface> player;
+ sp<MediaPlayer2AudioOutput> audioOutput;
+ bool locked = false;
+ for (int i = 0; i < kDumpLockRetries; ++i) {
+ if (mLock.tryLock() == NO_ERROR) {
+ locked = true;
+ break;
+ }
+ usleep(kDumpLockSleepUs);
+ }
+
+ if (locked) {
+ player = mPlayer;
+ audioOutput = mAudioOutput;
+ mLock.unlock();
+ } else {
+ result.append(" lock is taken, no dump from player and audio output\n");
+ }
+ write(fd, result.string(), result.size());
+
+ if (player != NULL) {
+ player->dump(fd, args);
+ }
+ if (audioOutput != 0) {
+ audioOutput->dump(fd, args);
+ }
+ write(fd, "\n", 1);
+ return NO_ERROR;
}
} // namespace android
diff --git a/media/libmediaplayer2/nuplayer2/GenericSource2.cpp b/media/libmediaplayer2/nuplayer2/GenericSource2.cpp
index 0dd5d6f..790581a 100644
--- a/media/libmediaplayer2/nuplayer2/GenericSource2.cpp
+++ b/media/libmediaplayer2/nuplayer2/GenericSource2.cpp
@@ -1167,7 +1167,7 @@
}
sp<ABuffer> NuPlayer2::GenericSource2::mediaBufferToABuffer(
- MediaBuffer* mb,
+ MediaBufferBase* mb,
media_track_type trackType) {
bool audio = trackType == MEDIA_TRACK_TYPE_AUDIO;
size_t outLength = mb->range_length();
@@ -1205,7 +1205,7 @@
if (audio && mAudioIsVorbis) {
int32_t numPageSamples;
- if (!mb->meta_data()->findInt32(kKeyValidSamples, &numPageSamples)) {
+ if (!mb->meta_data().findInt32(kKeyValidSamples, &numPageSamples)) {
numPageSamples = -1;
}
@@ -1216,12 +1216,12 @@
sp<AMessage> meta = ab->meta();
int64_t timeUs;
- CHECK(mb->meta_data()->findInt64(kKeyTime, &timeUs));
+ CHECK(mb->meta_data().findInt64(kKeyTime, &timeUs));
meta->setInt64("timeUs", timeUs);
if (trackType == MEDIA_TRACK_TYPE_VIDEO) {
int32_t layerId;
- if (mb->meta_data()->findInt32(kKeyTemporalLayerId, &layerId)) {
+ if (mb->meta_data().findInt32(kKeyTemporalLayerId, &layerId)) {
meta->setInt32("temporal-layer-id", layerId);
}
}
@@ -1234,7 +1234,7 @@
}
int64_t durationUs;
- if (mb->meta_data()->findInt64(kKeyDuration, &durationUs)) {
+ if (mb->meta_data().findInt64(kKeyDuration, &durationUs)) {
meta->setInt64("durationUs", durationUs);
}
@@ -1245,14 +1245,14 @@
uint32_t dataType; // unused
const void *seiData;
size_t seiLength;
- if (mb->meta_data()->findData(kKeySEI, &dataType, &seiData, &seiLength)) {
+ if (mb->meta_data().findData(kKeySEI, &dataType, &seiData, &seiLength)) {
sp<ABuffer> sei = ABuffer::CreateAsCopy(seiData, seiLength);;
meta->setBuffer("sei", sei);
}
const void *mpegUserDataPointer;
size_t mpegUserDataLength;
- if (mb->meta_data()->findData(
+ if (mb->meta_data().findData(
kKeyMpegUserData, &dataType, &mpegUserDataPointer, &mpegUserDataLength)) {
sp<ABuffer> mpegUserData = ABuffer::CreateAsCopy(mpegUserDataPointer, mpegUserDataLength);
meta->setBuffer("mpegUserData", mpegUserData);
@@ -1351,7 +1351,7 @@
int32_t generation = getDataGeneration(trackType);
for (size_t numBuffers = 0; numBuffers < maxBuffers; ) {
- Vector<MediaBuffer *> mediaBuffers;
+ Vector<MediaBufferBase *> mediaBuffers;
status_t err = NO_ERROR;
sp<IMediaSource> source = track->mSource;
@@ -1360,7 +1360,7 @@
err = source->readMultiple(
&mediaBuffers, maxBuffers - numBuffers, &options);
} else {
- MediaBuffer *mbuf = NULL;
+ MediaBufferBase *mbuf = NULL;
err = source->read(&mbuf, &options);
if (err == OK && mbuf != NULL) {
mediaBuffers.push_back(mbuf);
@@ -1383,9 +1383,9 @@
for (; id < count; ++id) {
int64_t timeUs;
- MediaBuffer *mbuf = mediaBuffers[id];
- if (!mbuf->meta_data()->findInt64(kKeyTime, &timeUs)) {
- mbuf->meta_data()->dumpToLog();
+ MediaBufferBase *mbuf = mediaBuffers[id];
+ if (!mbuf->meta_data().findInt64(kKeyTime, &timeUs)) {
+ mbuf->meta_data().dumpToLog();
track->mPackets->signalEOS(ERROR_MALFORMED);
break;
}
@@ -1677,7 +1677,7 @@
return OK;
}
-void NuPlayer2::GenericSource2::signalBufferReturned(MediaBuffer *buffer)
+void NuPlayer2::GenericSource2::signalBufferReturned(MediaBufferBase *buffer)
{
//ALOGV("signalBufferReturned %p refCount: %d", buffer, buffer->localRefcount());
diff --git a/media/libmediaplayer2/nuplayer2/GenericSource2.h b/media/libmediaplayer2/nuplayer2/GenericSource2.h
index 1a5409a..896c397 100644
--- a/media/libmediaplayer2/nuplayer2/GenericSource2.h
+++ b/media/libmediaplayer2/nuplayer2/GenericSource2.h
@@ -86,7 +86,7 @@
virtual bool isStreaming() const;
// Modular DRM
- virtual void signalBufferReturned(MediaBuffer *buffer);
+ virtual void signalBufferReturned(MediaBufferBase *buffer);
virtual status_t prepareDrm(
const uint8_t uuid[16],
@@ -202,7 +202,7 @@
int32_t curGen, const sp<AnotherPacketSource>& packets, const sp<AMessage>& msg);
sp<ABuffer> mediaBufferToABuffer(
- MediaBuffer *mbuf,
+ MediaBufferBase *mbuf,
media_track_type trackType);
void postReadBuffer(media_track_type trackType);
diff --git a/media/libmediaplayer2/nuplayer2/NuPlayer2.cpp b/media/libmediaplayer2/nuplayer2/NuPlayer2.cpp
index 462a904..5971a8b 100644
--- a/media/libmediaplayer2/nuplayer2/NuPlayer2.cpp
+++ b/media/libmediaplayer2/nuplayer2/NuPlayer2.cpp
@@ -423,6 +423,14 @@
msg->post();
}
+void NuPlayer2::playNextDataSource(int64_t srcId) {
+ disconnectSource();
+
+ sp<AMessage> msg = new AMessage(kWhatPlayNextDataSource, this);
+ msg->setInt64("srcId", srcId);
+ msg->post();
+}
+
status_t NuPlayer2::getBufferingSettings(
BufferingSettings *buffering /* nonnull */) {
sp<AMessage> msg = new AMessage(kWhatGetBufferingSettings, this);
@@ -538,6 +546,11 @@
}
void NuPlayer2::resetAsync() {
+ disconnectSource();
+ (new AMessage(kWhatReset, this))->post();
+}
+
+void NuPlayer2::disconnectSource() {
sp<Source> source;
{
Mutex::Autolock autoLock(mSourceLock);
@@ -554,7 +567,6 @@
source->disconnect();
}
- (new AMessage(kWhatReset, this))->post();
}
status_t NuPlayer2::notifyAt(int64_t mediaTimeUs) {
@@ -671,6 +683,32 @@
break;
}
+ case kWhatPlayNextDataSource:
+ {
+ ALOGV("kWhatPlayNextDataSource");
+ int64_t srcId;
+ CHECK(msg->findInt64("srcId", &srcId));
+ if (srcId != mNextSrcId) {
+ notifyListener(srcId, MEDIA2_ERROR, MEDIA2_ERROR_UNKNOWN, 0);
+ return;
+ }
+
+ mResetting = true;
+ stopPlaybackTimer("kWhatPlayNextDataSource");
+ stopRebufferingTimer(true);
+
+ mDeferredActions.push_back(
+ new FlushDecoderAction(
+ FLUSH_CMD_SHUTDOWN /* audio */,
+ FLUSH_CMD_SHUTDOWN /* video */));
+
+ mDeferredActions.push_back(
+ new SimpleAction(&NuPlayer2::performPlayNextDataSource));
+
+ processDeferredActions();
+ break;
+ }
+
case kWhatGetBufferingSettings:
{
sp<AReplyToken> replyID;
@@ -1382,7 +1420,7 @@
handleFlushComplete(audio, false /* isDecoder */);
finishFlushIfPossible();
} else if (what == Renderer::kWhatVideoRenderingStart) {
- notifyListener(mSrcId, MEDIA2_INFO, MEDIA2_INFO_RENDERING_START, 0);
+ notifyListener(mSrcId, MEDIA2_INFO, MEDIA2_INFO_VIDEO_RENDERING_START, 0);
} else if (what == Renderer::kWhatMediaRenderingStart) {
ALOGV("media rendering started");
notifyListener(mSrcId, MEDIA2_STARTED, 0, 0);
@@ -2403,6 +2441,67 @@
mIsDrmProtected = false;
}
+void NuPlayer2::performPlayNextDataSource() {
+ ALOGV("performPlayNextDataSource");
+
+ CHECK(mAudioDecoder == NULL);
+ CHECK(mVideoDecoder == NULL);
+
+ stopPlaybackTimer("performPlayNextDataSource");
+ stopRebufferingTimer(true);
+
+ cancelPollDuration();
+
+ ++mScanSourcesGeneration;
+ mScanSourcesPending = false;
+
+ ++mRendererGeneration;
+
+ if (mSource != NULL) {
+ mSource->stop();
+ }
+
+ long previousSrcId;
+ {
+ Mutex::Autolock autoLock(mSourceLock);
+ mSource = mNextSource;
+ mNextSource = NULL;
+ previousSrcId = mSrcId;
+ mSrcId = mNextSrcId;
+ ++mNextSrcId; // to distinguish the two sources.
+ }
+
+ if (mDriver != NULL) {
+ sp<NuPlayer2Driver> driver = mDriver.promote();
+ if (driver != NULL) {
+ notifyListener(previousSrcId, MEDIA2_INFO, MEDIA2_INFO_PLAYBACK_COMPLETE, 0);
+ notifyListener(mSrcId, MEDIA2_INFO, MEDIA2_INFO_STARTED_AS_NEXT, 0);
+ }
+ }
+
+ mStarted = false;
+ mPrepared = true; // TODO: what if it's not prepared
+ mResetting = false;
+ mSourceStarted = false;
+
+ // Modular DRM
+ if (mCrypto != NULL) {
+ // decoders will be flushed before this so their mCrypto would go away on their own
+ // TODO change to ALOGV
+ ALOGD("performReset mCrypto: %p", mCrypto.get());
+ mCrypto.clear();
+ }
+ mIsDrmProtected = false;
+
+ if (mRenderer != NULL) {
+ mRenderer->resume();
+ }
+
+ onStart();
+ mPausedByClient = false;
+ notifyListener(mSrcId, MEDIA2_STARTED, 0, 0);
+}
+
void NuPlayer2::performScanSources() {
ALOGV("performScanSources");
@@ -2666,10 +2765,10 @@
break;
}
- int posMs;
+ int64_t posMs;
int64_t timeUs, posUs;
driver->getCurrentPosition(&posMs);
- posUs = (int64_t) posMs * 1000ll;
+ posUs = posMs * 1000ll;
CHECK(buffer->meta()->findInt64("timeUs", &timeUs));
if (posUs < timeUs) {
diff --git a/media/libmediaplayer2/nuplayer2/NuPlayer2.h b/media/libmediaplayer2/nuplayer2/NuPlayer2.h
index e7b774c..96f85f9 100644
--- a/media/libmediaplayer2/nuplayer2/NuPlayer2.h
+++ b/media/libmediaplayer2/nuplayer2/NuPlayer2.h
@@ -44,6 +44,7 @@
void setDataSourceAsync(const sp<DataSourceDesc> &dsd);
void prepareNextDataSourceAsync(const sp<DataSourceDesc> &dsd);
+ void playNextDataSource(int64_t srcId);
status_t getBufferingSettings(BufferingSettings* buffering /* nonnull */);
status_t setBufferingSettings(const BufferingSettings& buffering);
@@ -121,6 +122,7 @@
kWhatSetDataSource = '=DaS',
kWhatPrepare = 'prep',
kWhatPrepareNextDataSource = 'pNDS',
+ kWhatPlayNextDataSource = 'plNS',
kWhatSetVideoSurface = '=VSu',
kWhatSetAudioSink = '=AuS',
kWhatMoreDataQueued = 'more',
@@ -269,6 +271,8 @@
mFlushComplete[1][1] = false;
}
+ void disconnectSource();
+
status_t createNuPlayer2Source(const sp<DataSourceDesc> &dsd,
sp<Source> *source,
DATA_SOURCE_TYPE *dataSourceType);
@@ -314,6 +318,7 @@
void performSeek(int64_t seekTimeUs, MediaPlayer2SeekMode mode);
void performDecoderFlush(FlushCommand audio, FlushCommand video);
void performReset();
+ void performPlayNextDataSource();
void performScanSources();
void performSetSurface(const sp<ANativeWindowWrapper> &nw);
void performResumeDecoders(bool needNotify);
diff --git a/media/libmediaplayer2/nuplayer2/NuPlayer2Decoder.cpp b/media/libmediaplayer2/nuplayer2/NuPlayer2Decoder.cpp
index a436592..645138a 100644
--- a/media/libmediaplayer2/nuplayer2/NuPlayer2Decoder.cpp
+++ b/media/libmediaplayer2/nuplayer2/NuPlayer2Decoder.cpp
@@ -1066,7 +1066,7 @@
}
// Modular DRM
- MediaBuffer *mediaBuf = NULL;
+ MediaBufferBase *mediaBuf = NULL;
sp<AMediaCodecCryptoInfoWrapper> cryptInfo;
// copy into codec buffer
@@ -1091,7 +1091,7 @@
codecBuffer->setRange(0, mediaBuf->size());
memcpy(codecBuffer->data(), mediaBuf->data(), mediaBuf->size());
- sp<MetaData> meta_data = mediaBuf->meta_data();
+ MetaDataBase &meta_data = mediaBuf->meta_data();
cryptInfo = AMediaCodecCryptoInfoWrapper::Create(meta_data);
} else { // No mediaBuf
ALOGE("onInputBufferFetched: buffer->data()/mediaBuf are NULL for %p",
diff --git a/media/libmediaplayer2/nuplayer2/NuPlayer2Driver.cpp b/media/libmediaplayer2/nuplayer2/NuPlayer2Driver.cpp
index 60a07a3..03d17a5 100644
--- a/media/libmediaplayer2/nuplayer2/NuPlayer2Driver.cpp
+++ b/media/libmediaplayer2/nuplayer2/NuPlayer2Driver.cpp
@@ -107,7 +107,6 @@
NuPlayer2Driver::NuPlayer2Driver(pid_t pid, uid_t uid)
: mState(STATE_IDLE),
- mIsAsyncPrepare(false),
mAsyncResult(UNKNOWN_ERROR),
mSrcId(0),
mSetSurfaceInProgress(false),
@@ -174,7 +173,7 @@
}
status_t NuPlayer2Driver::setDataSource(const sp<DataSourceDesc> &dsd) {
- ALOGV("setDataSource(%p) callback source", this);
+ ALOGV("setDataSource(%p)", this);
Mutex::Autolock autoLock(mLock);
if (mState != STATE_IDLE) {
@@ -193,6 +192,25 @@
return mAsyncResult;
}
+status_t NuPlayer2Driver::prepareNextDataSource(const sp<DataSourceDesc> &dsd) {
+ ALOGV("prepareNextDataSource(%p)", this);
+ Mutex::Autolock autoLock(mLock);
+
+ mPlayer->prepareNextDataSourceAsync(dsd);
+
+ return OK;
+}
+
+status_t NuPlayer2Driver::playNextDataSource(int64_t srcId) {
+ ALOGV("playNextDataSource(%p)", this);
+ Mutex::Autolock autoLock(mLock);
+
+ mSrcId = srcId;
+ mPlayer->playNextDataSource(srcId);
+
+ return OK;
+}
+
status_t NuPlayer2Driver::setVideoSurfaceTexture(const sp<ANativeWindowWrapper> &nww) {
ALOGV("setVideoSurfaceTexture(%p)", this);
Mutex::Autolock autoLock(mLock);
@@ -245,42 +263,6 @@
return mPlayer->setBufferingSettings(buffering);
}
-status_t NuPlayer2Driver::prepare() {
- ALOGV("prepare(%p)", this);
- Mutex::Autolock autoLock(mLock);
- return prepare_l();
-}
-
-status_t NuPlayer2Driver::prepare_l() {
- switch (mState) {
- case STATE_UNPREPARED:
- mState = STATE_PREPARING;
-
- // Make sure we're not posting any notifications, success or
- // failure information is only communicated through our result
- // code.
- mIsAsyncPrepare = false;
- mPlayer->prepareAsync();
- while (mState == STATE_PREPARING) {
- mCondition.wait(mLock);
- }
- return (mState == STATE_PREPARED) ? OK : UNKNOWN_ERROR;
- case STATE_STOPPED:
- // this is really just paused. handle as seek to start
- mAtEOS = false;
- mState = STATE_STOPPED_AND_PREPARING;
- mIsAsyncPrepare = false;
- mPlayer->seekToAsync(0, MediaPlayer2SeekMode::SEEK_PREVIOUS_SYNC /* mode */,
- true /* needNotify */);
- while (mState == STATE_STOPPED_AND_PREPARING) {
- mCondition.wait(mLock);
- }
- return (mState == STATE_STOPPED_AND_PREPARED) ? OK : UNKNOWN_ERROR;
- default:
- return INVALID_OPERATION;
- };
-}
-
status_t NuPlayer2Driver::prepareAsync() {
ALOGV("prepareAsync(%p)", this);
Mutex::Autolock autoLock(mLock);
@@ -288,14 +270,12 @@
switch (mState) {
case STATE_UNPREPARED:
mState = STATE_PREPARING;
- mIsAsyncPrepare = true;
mPlayer->prepareAsync();
return OK;
case STATE_STOPPED:
// this is really just paused. handle as seek to start
mAtEOS = false;
mState = STATE_STOPPED_AND_PREPARING;
- mIsAsyncPrepare = true;
mPlayer->seekToAsync(0, MediaPlayer2SeekMode::SEEK_PREVIOUS_SYNC /* mode */,
true /* needNotify */);
return OK;
@@ -312,19 +292,6 @@
status_t NuPlayer2Driver::start_l() {
switch (mState) {
- case STATE_UNPREPARED:
- {
- status_t err = prepare_l();
-
- if (err != OK) {
- return err;
- }
-
- CHECK_EQ(mState, STATE_PREPARED);
-
- // fall through
- }
-
case STATE_PAUSED:
case STATE_STOPPED_AND_PREPARED:
case STATE_PREPARED:
@@ -387,7 +354,7 @@
// down for audio offload mode. If that happens, the NuPlayerRenderer will no longer know the
// current position. So similar to seekTo, update |mPositionUs| to the pause position by calling
// getCurrentPosition here.
- int unused;
+ int64_t unused;
getCurrentPosition(&unused);
Mutex::Autolock autoLock(mLock);
@@ -417,7 +384,7 @@
status_t err = mPlayer->setPlaybackSettings(rate);
if (err == OK) {
// try to update position
- int unused;
+ int64_t unused;
getCurrentPosition(&unused);
Mutex::Autolock autoLock(mLock);
if (rate.mSpeed == 0.f && mState == STATE_RUNNING) {
@@ -444,8 +411,8 @@
return mPlayer->getSyncSettings(sync, videoFps);
}
-status_t NuPlayer2Driver::seekTo(int msec, MediaPlayer2SeekMode mode) {
- ALOGD("seekTo(%p) (%d ms, %d) at state %d", this, msec, mode, mState);
+status_t NuPlayer2Driver::seekTo(int64_t msec, MediaPlayer2SeekMode mode) {
+ ALOGD("seekTo(%p) (%lld ms, %d) at state %d", this, (long long)msec, mode, mState);
Mutex::Autolock autoLock(mLock);
int64_t seekTimeUs = msec * 1000ll;
@@ -470,13 +437,13 @@
return OK;
}
-status_t NuPlayer2Driver::getCurrentPosition(int *msec) {
+status_t NuPlayer2Driver::getCurrentPosition(int64_t *msec) {
int64_t tempUs = 0;
{
Mutex::Autolock autoLock(mLock);
if (mSeekInProgress || (mState == STATE_PAUSED && !mAtEOS)) {
tempUs = (mPositionUs <= 0) ? 0 : mPositionUs;
- *msec = (int)divRound(tempUs, (int64_t)(1000));
+ *msec = divRound(tempUs, (int64_t)(1000));
return OK;
}
}
@@ -492,11 +459,11 @@
} else {
mPositionUs = tempUs;
}
- *msec = (int)divRound(tempUs, (int64_t)(1000));
+ *msec = divRound(tempUs, (int64_t)(1000));
return OK;
}
-status_t NuPlayer2Driver::getDuration(int *msec) {
+status_t NuPlayer2Driver::getDuration(int64_t *msec) {
Mutex::Autolock autoLock(mLock);
if (mDurationUs < 0) {
@@ -562,7 +529,7 @@
// always provide duration and playing time, even if they have 0/unknown values.
// getDuration() uses mLock for mutex -- careful where we use it.
- int duration_ms = -1;
+ int64_t duration_ms = -1;
getDuration(&duration_ms);
mAnalyticsItem->setInt64(kPlayerDuration, duration_ms);
@@ -626,8 +593,6 @@
case STATE_PREPARING:
{
- CHECK(mIsAsyncPrepare);
-
notifyListener_l(mSrcId, MEDIA2_PREPARED);
break;
}
@@ -696,7 +661,7 @@
case MEDIA_PLAYER2_INVOKE_ID_SELECT_TRACK:
{
int trackIndex = request.readInt32();
- int msec = 0;
+ int64_t msec = 0;
// getCurrentPosition should always return OK
getCurrentPosition(&msec);
return mPlayer->selectTrack(trackIndex, true /* select */, msec * 1000ll);
@@ -824,10 +789,6 @@
wasSeeking = false;
mState = STATE_STOPPED_AND_PREPARED;
mCondition.broadcast();
- if (!mIsAsyncPrepare) {
- // if we are preparing synchronously, no need to notify listener
- return;
- }
} else if (mState == STATE_STOPPED) {
// no need to notify listener
return;
@@ -947,58 +908,60 @@
ALOGD("notifyListener_l(%p), (%lld, %d, %d, %d, %d), loop setting(%d, %d)",
this, (long long)srcId, msg, ext1, ext2,
(in == NULL ? -1 : (int)in->dataSize()), mAutoLoop, mLooping);
- switch (msg) {
- case MEDIA2_PLAYBACK_COMPLETE:
- {
- if (mState != STATE_RESET_IN_PROGRESS) {
- if (mAutoLoop) {
- audio_stream_type_t streamType = AUDIO_STREAM_MUSIC;
- if (mAudioSink != NULL) {
- streamType = mAudioSink->getAudioStreamType();
+ if (srcId == mSrcId) {
+ switch (msg) {
+ case MEDIA2_PLAYBACK_COMPLETE:
+ {
+ if (mState != STATE_RESET_IN_PROGRESS) {
+ if (mAutoLoop) {
+ audio_stream_type_t streamType = AUDIO_STREAM_MUSIC;
+ if (mAudioSink != NULL) {
+ streamType = mAudioSink->getAudioStreamType();
+ }
+ if (streamType == AUDIO_STREAM_NOTIFICATION) {
+ ALOGW("disabling auto-loop for notification");
+ mAutoLoop = false;
+ }
}
- if (streamType == AUDIO_STREAM_NOTIFICATION) {
- ALOGW("disabling auto-loop for notification");
- mAutoLoop = false;
+ if (mLooping || mAutoLoop) {
+ mPlayer->seekToAsync(0);
+ if (mAudioSink != NULL) {
+ // The renderer has stopped the sink at the end in order to play out
+ // the last little bit of audio. In looping mode, we need to restart it.
+ mAudioSink->start();
+ }
+ // don't send completion event when looping
+ return;
}
- }
- if (mLooping || mAutoLoop) {
- mPlayer->seekToAsync(0);
- if (mAudioSink != NULL) {
- // The renderer has stopped the sink at the end in order to play out
- // the last little bit of audio. If we're looping, we need to restart it.
- mAudioSink->start();
+ if (property_get_bool("persist.debug.sf.stats", false)) {
+ Vector<String16> args;
+ dump(-1, args);
}
- // don't send completion event when looping
- return;
+ mPlayer->pause();
+ mState = STATE_PAUSED;
}
- if (property_get_bool("persist.debug.sf.stats", false)) {
- Vector<String16> args;
- dump(-1, args);
- }
- mPlayer->pause();
- mState = STATE_PAUSED;
+ // fall through
}
- // fall through
- }
- case MEDIA2_ERROR:
- {
- // when we have an error, add it to the analytics for this playback.
- // ext1 is our primary 'error type' value. Only add ext2 when non-zero.
- // [test against msg is due to fall through from previous switch value]
- if (msg == MEDIA2_ERROR) {
- mAnalyticsItem->setInt32(kPlayerError, ext1);
- if (ext2 != 0) {
- mAnalyticsItem->setInt32(kPlayerErrorCode, ext2);
+ case MEDIA2_ERROR:
+ {
+ // when we have an error, add it to the analytics for this playback.
+ // ext1 is our primary 'error type' value. Only add ext2 when non-zero.
+ // [test against msg is due to fall through from previous switch value]
+ if (msg == MEDIA2_ERROR) {
+ mAnalyticsItem->setInt32(kPlayerError, ext1);
+ if (ext2 != 0) {
+ mAnalyticsItem->setInt32(kPlayerErrorCode, ext2);
+ }
+ mAnalyticsItem->setCString(kPlayerErrorState, stateString(mState).c_str());
}
- mAnalyticsItem->setCString(kPlayerErrorState, stateString(mState).c_str());
+ mAtEOS = true;
+ break;
}
- mAtEOS = true;
- break;
- }
- default:
- break;
+ default:
+ break;
+ }
}
sp<AMessage> notify = new AMessage(kWhatNotifyListener, this);
@@ -1025,6 +988,15 @@
Mutex::Autolock autoLock(mLock);
+ if (srcId != mSrcId) {
+ if (err == OK) {
+ notifyListener_l(srcId, MEDIA2_PREPARED);
+ } else {
+ notifyListener_l(srcId, MEDIA2_ERROR, MEDIA2_ERROR_UNKNOWN, err);
+ }
+ return;
+ }
+
if (mState != STATE_PREPARING) {
// We were preparing asynchronously when the client called
// reset(), we sent a premature "prepared" notification and
@@ -1041,14 +1013,10 @@
// update state before notifying client, so that if client calls back into NuPlayer2Driver
// in response, NuPlayer2Driver has the right state
mState = STATE_PREPARED;
- if (mIsAsyncPrepare) {
- notifyListener_l(srcId, MEDIA2_PREPARED);
- }
+ notifyListener_l(srcId, MEDIA2_PREPARED);
} else {
mState = STATE_UNPREPARED;
- if (mIsAsyncPrepare) {
- notifyListener_l(srcId, MEDIA2_ERROR, MEDIA2_ERROR_UNKNOWN, err);
- }
+ notifyListener_l(srcId, MEDIA2_ERROR, MEDIA2_ERROR_UNKNOWN, err);
}
sp<MetaData> meta = mPlayer->getFileMeta();
diff --git a/media/libmediaplayer2/nuplayer2/NuPlayer2Driver.h b/media/libmediaplayer2/nuplayer2/NuPlayer2Driver.h
index 4c57cfd..4da2566 100644
--- a/media/libmediaplayer2/nuplayer2/NuPlayer2Driver.h
+++ b/media/libmediaplayer2/nuplayer2/NuPlayer2Driver.h
@@ -28,17 +28,18 @@
struct NuPlayer2Driver : public MediaPlayer2Interface {
explicit NuPlayer2Driver(pid_t pid, uid_t uid);
- virtual status_t initCheck();
+ virtual status_t initCheck() override;
virtual status_t setDataSource(const sp<DataSourceDesc> &dsd) override;
+ virtual status_t prepareNextDataSource(const sp<DataSourceDesc> &dsd) override;
+ virtual status_t playNextDataSource(int64_t srcId) override;
- virtual status_t setVideoSurfaceTexture(const sp<ANativeWindowWrapper> &nww);
+ virtual status_t setVideoSurfaceTexture(const sp<ANativeWindowWrapper> &nww) override;
virtual status_t getBufferingSettings(
BufferingSettings* buffering /* nonnull */) override;
virtual status_t setBufferingSettings(const BufferingSettings& buffering) override;
- virtual status_t prepare();
virtual status_t prepareAsync();
virtual status_t start();
virtual status_t stop();
@@ -49,9 +50,9 @@
virtual status_t setSyncSettings(const AVSyncSettings &sync, float videoFpsHint);
virtual status_t getSyncSettings(AVSyncSettings *sync, float *videoFps);
virtual status_t seekTo(
- int msec, MediaPlayer2SeekMode mode = MediaPlayer2SeekMode::SEEK_PREVIOUS_SYNC);
- virtual status_t getCurrentPosition(int *msec);
- virtual status_t getDuration(int *msec);
+ int64_t msec, MediaPlayer2SeekMode mode = MediaPlayer2SeekMode::SEEK_PREVIOUS_SYNC);
+ virtual status_t getCurrentPosition(int64_t *msec);
+ virtual status_t getDuration(int64_t *msec);
virtual status_t reset();
virtual status_t notifyAt(int64_t mediaTimeUs) override;
virtual status_t setLooping(int loop);
@@ -114,7 +115,6 @@
State mState;
- bool mIsAsyncPrepare;
status_t mAsyncResult;
// The following are protected through "mLock"
@@ -147,7 +147,6 @@
void updateMetrics(const char *where);
void logMetrics(const char *where);
- status_t prepare_l();
status_t start_l();
void notifyListener_l(int64_t srcId, int msg, int ext1 = 0, int ext2 = 0,
const Parcel *in = NULL);
diff --git a/media/libmediaplayerservice/nuplayer/GenericSource.cpp b/media/libmediaplayerservice/nuplayer/GenericSource.cpp
index 0cf5b9d..e7c3deb 100644
--- a/media/libmediaplayerservice/nuplayer/GenericSource.cpp
+++ b/media/libmediaplayerservice/nuplayer/GenericSource.cpp
@@ -1162,7 +1162,7 @@
}
sp<ABuffer> NuPlayer::GenericSource::mediaBufferToABuffer(
- MediaBuffer* mb,
+ MediaBufferBase* mb,
media_track_type trackType) {
bool audio = trackType == MEDIA_TRACK_TYPE_AUDIO;
size_t outLength = mb->range_length();
@@ -1199,7 +1199,7 @@
if (audio && mAudioIsVorbis) {
int32_t numPageSamples;
- if (!mb->meta_data()->findInt32(kKeyValidSamples, &numPageSamples)) {
+ if (!mb->meta_data().findInt32(kKeyValidSamples, &numPageSamples)) {
numPageSamples = -1;
}
@@ -1210,12 +1210,12 @@
sp<AMessage> meta = ab->meta();
int64_t timeUs;
- CHECK(mb->meta_data()->findInt64(kKeyTime, &timeUs));
+ CHECK(mb->meta_data().findInt64(kKeyTime, &timeUs));
meta->setInt64("timeUs", timeUs);
if (trackType == MEDIA_TRACK_TYPE_VIDEO) {
int32_t layerId;
- if (mb->meta_data()->findInt32(kKeyTemporalLayerId, &layerId)) {
+ if (mb->meta_data().findInt32(kKeyTemporalLayerId, &layerId)) {
meta->setInt32("temporal-layer-id", layerId);
}
}
@@ -1228,7 +1228,7 @@
}
int64_t durationUs;
- if (mb->meta_data()->findInt64(kKeyDuration, &durationUs)) {
+ if (mb->meta_data().findInt64(kKeyDuration, &durationUs)) {
meta->setInt64("durationUs", durationUs);
}
@@ -1239,14 +1239,14 @@
uint32_t dataType; // unused
const void *seiData;
size_t seiLength;
- if (mb->meta_data()->findData(kKeySEI, &dataType, &seiData, &seiLength)) {
+ if (mb->meta_data().findData(kKeySEI, &dataType, &seiData, &seiLength)) {
sp<ABuffer> sei = ABuffer::CreateAsCopy(seiData, seiLength);;
meta->setBuffer("sei", sei);
}
const void *mpegUserDataPointer;
size_t mpegUserDataLength;
- if (mb->meta_data()->findData(
+ if (mb->meta_data().findData(
kKeyMpegUserData, &dataType, &mpegUserDataPointer, &mpegUserDataLength)) {
sp<ABuffer> mpegUserData = ABuffer::CreateAsCopy(mpegUserDataPointer, mpegUserDataLength);
meta->setBuffer("mpegUserData", mpegUserData);
@@ -1345,7 +1345,7 @@
int32_t generation = getDataGeneration(trackType);
for (size_t numBuffers = 0; numBuffers < maxBuffers; ) {
- Vector<MediaBuffer *> mediaBuffers;
+ Vector<MediaBufferBase *> mediaBuffers;
status_t err = NO_ERROR;
sp<IMediaSource> source = track->mSource;
@@ -1354,7 +1354,7 @@
err = source->readMultiple(
&mediaBuffers, maxBuffers - numBuffers, &options);
} else {
- MediaBuffer *mbuf = NULL;
+ MediaBufferBase *mbuf = NULL;
err = source->read(&mbuf, &options);
if (err == OK && mbuf != NULL) {
mediaBuffers.push_back(mbuf);
@@ -1377,9 +1377,9 @@
for (; id < count; ++id) {
int64_t timeUs;
- MediaBuffer *mbuf = mediaBuffers[id];
- if (!mbuf->meta_data()->findInt64(kKeyTime, &timeUs)) {
- mbuf->meta_data()->dumpToLog();
+ MediaBufferBase *mbuf = mediaBuffers[id];
+ if (!mbuf->meta_data().findInt64(kKeyTime, &timeUs)) {
+ mbuf->meta_data().dumpToLog();
track->mPackets->signalEOS(ERROR_MALFORMED);
break;
}
@@ -1673,7 +1673,7 @@
return OK;
}
-void NuPlayer::GenericSource::signalBufferReturned(MediaBuffer *buffer)
+void NuPlayer::GenericSource::signalBufferReturned(MediaBufferBase *buffer)
{
//ALOGV("signalBufferReturned %p refCount: %d", buffer, buffer->localRefcount());
diff --git a/media/libmediaplayerservice/nuplayer/GenericSource.h b/media/libmediaplayerservice/nuplayer/GenericSource.h
index 2406665..065cac1 100644
--- a/media/libmediaplayerservice/nuplayer/GenericSource.h
+++ b/media/libmediaplayerservice/nuplayer/GenericSource.h
@@ -86,7 +86,7 @@
virtual bool isStreaming() const;
// Modular DRM
- virtual void signalBufferReturned(MediaBuffer *buffer);
+ virtual void signalBufferReturned(MediaBufferBase *buffer);
virtual status_t prepareDrm(
const uint8_t uuid[16], const Vector<uint8_t> &drmSessionId, sp<ICrypto> *outCrypto);
@@ -201,7 +201,7 @@
int32_t curGen, const sp<AnotherPacketSource>& packets, const sp<AMessage>& msg);
sp<ABuffer> mediaBufferToABuffer(
- MediaBuffer *mbuf,
+ MediaBufferBase *mbuf,
media_track_type trackType);
void postReadBuffer(media_track_type trackType);
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
index 1aca96c..2a08f62 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
@@ -1046,7 +1046,7 @@
}
// Modular DRM
- MediaBuffer *mediaBuf = NULL;
+ MediaBufferBase *mediaBuf = NULL;
NuPlayerDrm::CryptoInfo *cryptInfo = NULL;
// copy into codec buffer
@@ -1071,7 +1071,7 @@
codecBuffer->setRange(0, mediaBuf->size());
memcpy(codecBuffer->data(), mediaBuf->data(), mediaBuf->size());
- sp<MetaData> meta_data = mediaBuf->meta_data();
+ MetaDataBase &meta_data = mediaBuf->meta_data();
cryptInfo = NuPlayerDrm::getSampleCryptoInfo(meta_data);
} else { // No mediaBuf
ALOGE("onInputBufferFetched: buffer->data()/mediaBuf are NULL for %p",
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDrm.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDrm.cpp
index b7c9db7..165e483 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDrm.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDrm.cpp
@@ -265,18 +265,13 @@
return ret;
}
-NuPlayerDrm::CryptoInfo *NuPlayerDrm::getSampleCryptoInfo(sp<MetaData> meta)
+NuPlayerDrm::CryptoInfo *NuPlayerDrm::getSampleCryptoInfo(MetaDataBase &meta)
{
uint32_t type;
const void *crypteddata;
size_t cryptedsize;
- if (meta == NULL) {
- ALOGE("getSampleCryptoInfo: Unexpected. No meta data for sample.");
- return NULL;
- }
-
- if (!meta->findData(kKeyEncryptedSizes, &type, &crypteddata, &cryptedsize)) {
+ if (!meta.findData(kKeyEncryptedSizes, &type, &crypteddata, &cryptedsize)) {
return NULL;
}
size_t numSubSamples = cryptedsize / sizeof(size_t);
@@ -288,7 +283,7 @@
const void *cleardata;
size_t clearsize;
- if (meta->findData(kKeyPlainSizes, &type, &cleardata, &clearsize)) {
+ if (meta.findData(kKeyPlainSizes, &type, &cleardata, &clearsize)) {
if (clearsize != cryptedsize) {
// The two must be of the same length.
ALOGE("getSampleCryptoInfo mismatch cryptedsize: %zu != clearsize: %zu",
@@ -299,7 +294,7 @@
const void *key;
size_t keysize;
- if (meta->findData(kKeyCryptoKey, &type, &key, &keysize)) {
+ if (meta.findData(kKeyCryptoKey, &type, &key, &keysize)) {
if (keysize != kBlockSize) {
ALOGE("getSampleCryptoInfo Keys must be %d bytes in length: %zu",
kBlockSize, keysize);
@@ -310,7 +305,7 @@
const void *iv;
size_t ivsize;
- if (meta->findData(kKeyCryptoIV, &type, &iv, &ivsize)) {
+ if (meta.findData(kKeyCryptoIV, &type, &iv, &ivsize)) {
if (ivsize != kBlockSize) {
ALOGE("getSampleCryptoInfo IV must be %d bytes in length: %zu",
kBlockSize, ivsize);
@@ -320,7 +315,7 @@
}
int32_t mode;
- if (!meta->findInt32(kKeyCryptoMode, &mode)) {
+ if (!meta.findInt32(kKeyCryptoMode, &mode)) {
mode = CryptoPlugin::kMode_AES_CTR;
}
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDrm.h b/media/libmediaplayerservice/nuplayer/NuPlayerDrm.h
index 6b8a2d9..50f69ff 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDrm.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDrm.h
@@ -109,7 +109,7 @@
size_t *clearbytes,
size_t *encryptedbytes);
- static CryptoInfo *getSampleCryptoInfo(sp<MetaData> meta);
+ static CryptoInfo *getSampleCryptoInfo(MetaDataBase &meta);
}; // NuPlayerDrm
diff --git a/media/libstagefright/AACWriter.cpp b/media/libstagefright/AACWriter.cpp
index 281af47..2ea5fcd 100644
--- a/media/libstagefright/AACWriter.cpp
+++ b/media/libstagefright/AACWriter.cpp
@@ -294,7 +294,7 @@
prctl(PR_SET_NAME, (unsigned long)"AACWriterThread", 0, 0, 0);
while (!mDone && err == OK) {
- MediaBuffer *buffer;
+ MediaBufferBase *buffer;
err = mSource->read(&buffer);
if (err != OK) {
@@ -316,7 +316,7 @@
}
int32_t isCodecSpecific = 0;
- if (buffer->meta_data()->findInt32(kKeyIsCodecConfig, &isCodecSpecific) && isCodecSpecific) {
+ if (buffer->meta_data().findInt32(kKeyIsCodecConfig, &isCodecSpecific) && isCodecSpecific) {
ALOGV("Drop codec specific info buffer");
buffer->release();
buffer = NULL;
@@ -324,7 +324,7 @@
}
int64_t timestampUs;
- CHECK(buffer->meta_data()->findInt64(kKeyTime, ×tampUs));
+ CHECK(buffer->meta_data().findInt64(kKeyTime, ×tampUs));
if (timestampUs > mEstimatedDurationUs) {
mEstimatedDurationUs = timestampUs;
}
diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp
index 541093a..b296622 100644
--- a/media/libstagefright/ACodec.cpp
+++ b/media/libstagefright/ACodec.cpp
@@ -6349,90 +6349,54 @@
Vector<AString> matchingCodecs;
Vector<AString> owners;
- AString mime;
-
AString componentName;
- int32_t encoder = false;
- if (msg->findString("componentName", &componentName)) {
- sp<IMediaCodecList> list = MediaCodecList::getInstance();
- if (list == nullptr) {
- ALOGE("Unable to obtain MediaCodecList while "
- "attempting to create codec \"%s\"",
- componentName.c_str());
- mCodec->signalError(OMX_ErrorUndefined, NO_INIT);
- return false;
- }
- ssize_t index = list->findCodecByName(componentName.c_str());
- if (index < 0) {
- ALOGE("Unable to find codec \"%s\"",
- componentName.c_str());
- mCodec->signalError(OMX_ErrorInvalidComponent, NAME_NOT_FOUND);
- return false;
- }
- sp<MediaCodecInfo> info = list->getCodecInfo(index);
- if (info == nullptr) {
- ALOGE("Unexpected error (index out-of-bound) while "
- "retrieving information for codec \"%s\"",
- componentName.c_str());
- mCodec->signalError(OMX_ErrorUndefined, UNKNOWN_ERROR);
- return false;
- }
- matchingCodecs.add(info->getCodecName());
- owners.add(info->getOwnerName() == nullptr ?
- "default" : info->getOwnerName());
- } else {
- CHECK(msg->findString("mime", &mime));
+ CHECK(msg->findString("componentName", &componentName));
- if (!msg->findInt32("encoder", &encoder)) {
- encoder = false;
- }
-
- MediaCodecList::findMatchingCodecs(
- mime.c_str(),
- encoder, // createEncoder
- 0, // flags
- &matchingCodecs,
- &owners);
+ sp<IMediaCodecList> list = MediaCodecList::getInstance();
+ if (list == nullptr) {
+ ALOGE("Unable to obtain MediaCodecList while "
+ "attempting to create codec \"%s\"",
+ componentName.c_str());
+ mCodec->signalError(OMX_ErrorUndefined, NO_INIT);
+ return false;
}
+ ssize_t index = list->findCodecByName(componentName.c_str());
+ if (index < 0) {
+ ALOGE("Unable to find codec \"%s\"",
+ componentName.c_str());
+ mCodec->signalError(OMX_ErrorInvalidComponent, NAME_NOT_FOUND);
+ return false;
+ }
+ sp<MediaCodecInfo> info = list->getCodecInfo(index);
+ if (info == nullptr) {
+ ALOGE("Unexpected error (index out-of-bound) while "
+ "retrieving information for codec \"%s\"",
+ componentName.c_str());
+ mCodec->signalError(OMX_ErrorUndefined, UNKNOWN_ERROR);
+ return false;
+ }
+ AString owner = (info->getOwnerName() == nullptr) ? "default" : info->getOwnerName();
sp<CodecObserver> observer = new CodecObserver;
sp<IOMX> omx;
sp<IOMXNode> omxNode;
status_t err = NAME_NOT_FOUND;
- for (size_t matchIndex = 0; matchIndex < matchingCodecs.size();
- ++matchIndex) {
- componentName = matchingCodecs[matchIndex];
-
- OMXClient client;
- if (client.connect(owners[matchIndex].c_str()) != OK) {
- mCodec->signalError(OMX_ErrorUndefined, NO_INIT);
- return false;
- }
- omx = client.interface();
-
- pid_t tid = gettid();
- int prevPriority = androidGetThreadPriority(tid);
- androidSetThreadPriority(tid, ANDROID_PRIORITY_FOREGROUND);
- err = omx->allocateNode(componentName.c_str(), observer, &omxNode);
- androidSetThreadPriority(tid, prevPriority);
-
- if (err == OK) {
- break;
- } else {
- ALOGW("Allocating component '%s' failed, try next one.", componentName.c_str());
- }
-
- omxNode = NULL;
+ OMXClient client;
+ if (client.connect(owner.c_str()) != OK) {
+ mCodec->signalError(OMX_ErrorUndefined, NO_INIT);
+ return false;
}
+ omx = client.interface();
- if (omxNode == NULL) {
- if (!mime.empty()) {
- ALOGE("Unable to instantiate a %scoder for type '%s' with err %#x.",
- encoder ? "en" : "de", mime.c_str(), err);
- } else {
- ALOGE("Unable to instantiate codec '%s' with err %#x.", componentName.c_str(), err);
- }
+ pid_t tid = gettid();
+ int prevPriority = androidGetThreadPriority(tid);
+ androidSetThreadPriority(tid, ANDROID_PRIORITY_FOREGROUND);
+ err = omx->allocateNode(componentName.c_str(), observer, &omxNode);
+ androidSetThreadPriority(tid, prevPriority);
+
+ if (err != OK) {
+ ALOGE("Unable to instantiate codec '%s' with err %#x.", componentName.c_str(), err);
mCodec->signalError((OMX_ERRORTYPE)err, makeNoSideEffectStatus(err));
return false;
@@ -7289,12 +7253,16 @@
}
}
- float rate;
- if (params->findFloat("operating-rate", &rate) && rate > 0) {
- status_t err = setOperatingRate(rate, mIsVideo);
+ int32_t rateInt = -1;
+ float rateFloat = -1;
+ if (!params->findFloat("operating-rate", &rateFloat)) {
+ params->findInt32("operating-rate", &rateInt);
+ rateFloat = (float) rateInt; // 16MHz (FLINTMAX) is OK for upper bound.
+ }
+ if (rateFloat > 0) {
+ status_t err = setOperatingRate(rateFloat, mIsVideo);
if (err != OK) {
- ALOGE("Failed to set parameter 'operating-rate' (err %d)", err);
- return err;
+ ALOGI("Failed to set parameter 'operating-rate' (err %d)", err);
}
}
@@ -7319,10 +7287,8 @@
}
}
- status_t err = configureTemporalLayers(params, false /* inConfigure */, mOutputFormat);
- if (err != OK) {
- err = OK; // ignore failure
- }
+ // Ignore errors as failure is expected for codecs that aren't video encoders.
+ (void)configureTemporalLayers(params, false /* inConfigure */, mOutputFormat);
return setVendorParameters(params);
}
diff --git a/media/libstagefright/ACodecBufferChannel.cpp b/media/libstagefright/ACodecBufferChannel.cpp
index 3c7ae3e..710ae68 100644
--- a/media/libstagefright/ACodecBufferChannel.cpp
+++ b/media/libstagefright/ACodecBufferChannel.cpp
@@ -22,6 +22,7 @@
#include <android/hardware/cas/native/1.0/IDescrambler.h>
#include <binder/MemoryDealer.h>
+#include <hidlmemory/FrameworkUtils.h>
#include <media/openmax/OMX_Core.h>
#include <media/stagefright/foundation/AMessage.h>
#include <media/stagefright/foundation/AUtils.h>
@@ -34,6 +35,7 @@
#include "include/SharedMemoryBuffer.h"
namespace android {
+using hardware::fromHeap;
using hardware::hidl_handle;
using hardware::hidl_string;
using hardware::hidl_vec;
@@ -162,7 +164,7 @@
size_t size;
it->mSharedEncryptedBuffer->getMemory(&offset, &size);
hardware::cas::native::V1_0::SharedBuffer srcBuffer = {
- .heapBase = mHidlMemory,
+ .heapBase = *mHidlMemory,
.offset = (uint64_t) offset,
.size = size
};
@@ -308,11 +310,8 @@
}
} else if (mDescrambler != nullptr) {
sp<IMemoryHeap> heap = dealer->getMemoryHeap();
- native_handle_t* nativeHandle = native_handle_create(1, 0);
- if (nativeHandle != nullptr) {
- int fd = heap->getHeapID();
- nativeHandle->data[0] = fd;
- mHidlMemory = hidl_memory("ashmem", hidl_handle(nativeHandle), heap->getSize());
+ mHidlMemory = fromHeap(heap);
+ if (mHidlMemory != NULL) {
ALOGV("created hidl_memory for descrambler");
} else {
ALOGE("failed to create hidl_memory for descrambler");
diff --git a/media/libstagefright/AMRWriter.cpp b/media/libstagefright/AMRWriter.cpp
index 910abc6..41106a1 100644
--- a/media/libstagefright/AMRWriter.cpp
+++ b/media/libstagefright/AMRWriter.cpp
@@ -193,7 +193,7 @@
prctl(PR_SET_NAME, (unsigned long)"AMRWriter", 0, 0, 0);
while (!mDone) {
- MediaBuffer *buffer;
+ MediaBufferBase *buffer;
err = mSource->read(&buffer);
if (err != OK) {
@@ -215,7 +215,7 @@
}
int64_t timestampUs;
- CHECK(buffer->meta_data()->findInt64(kKeyTime, ×tampUs));
+ CHECK(buffer->meta_data().findInt64(kKeyTime, ×tampUs));
if (timestampUs > mEstimatedDurationUs) {
mEstimatedDurationUs = timestampUs;
}
diff --git a/media/libstagefright/Android.bp b/media/libstagefright/Android.bp
index 905817f..65348e5 100644
--- a/media/libstagefright/Android.bp
+++ b/media/libstagefright/Android.bp
@@ -63,6 +63,7 @@
"C2OMXNode.cpp",
"CCodec.cpp",
"CCodecBufferChannel.cpp",
+ "Codec2InfoBuilder.cpp",
"CodecBase.cpp",
"CallbackDataSource.cpp",
"CallbackMediaSource.cpp",
@@ -100,7 +101,6 @@
"SkipCutBuffer.cpp",
"StagefrightMediaScanner.cpp",
"StagefrightMetadataRetriever.cpp",
- "SurfaceMediaSource.cpp",
"SurfaceUtils.cpp",
"Utils.cpp",
"ThrottledSource.cpp",
@@ -139,6 +139,7 @@
"libstagefright_xmlparser",
"libdl",
"libRScpp",
+ "libhidlallocatorutils",
"libhidlbase",
"libhidlmemory",
// TODO: Remove libv4l2_c2_componentstore.
diff --git a/media/libstagefright/AudioPlayer.cpp b/media/libstagefright/AudioPlayer.cpp
index 16ea5b5b..a6f0a0b 100644
--- a/media/libstagefright/AudioPlayer.cpp
+++ b/media/libstagefright/AudioPlayer.cpp
@@ -543,7 +543,7 @@
}
if(mInputBuffer->range_length() != 0) {
- CHECK(mInputBuffer->meta_data()->findInt64(
+ CHECK(mInputBuffer->meta_data().findInt64(
kKeyTime, &mPositionTimeMediaUs));
}
diff --git a/media/libstagefright/AudioSource.cpp b/media/libstagefright/AudioSource.cpp
index 70ce38c..2ae3218 100644
--- a/media/libstagefright/AudioSource.cpp
+++ b/media/libstagefright/AudioSource.cpp
@@ -240,7 +240,7 @@
}
status_t AudioSource::read(
- MediaBuffer **out, const ReadOptions * /* options */) {
+ MediaBufferBase **out, const ReadOptions * /* options */) {
Mutex::Autolock autoLock(mLock);
*out = NULL;
@@ -265,7 +265,7 @@
// Mute/suppress the recording sound
int64_t timeUs;
- CHECK(buffer->meta_data()->findInt64(kKeyTime, &timeUs));
+ CHECK(buffer->meta_data().findInt64(kKeyTime, &timeUs));
int64_t elapsedTimeUs = timeUs - mStartTimeUs;
if (elapsedTimeUs < kAutoRampStartUs) {
memset((uint8_t *) buffer->data(), 0, buffer->range_length());
@@ -289,7 +289,7 @@
if (mSampleRate != mOutSampleRate) {
timeUs *= (int64_t)mSampleRate / (int64_t)mOutSampleRate;
- buffer->meta_data()->setInt64(kKeyTime, timeUs);
+ buffer->meta_data().setInt64(kKeyTime, timeUs);
}
*out = buffer;
@@ -311,7 +311,7 @@
return OK;
}
-void AudioSource::signalBufferReturned(MediaBuffer *buffer) {
+void AudioSource::signalBufferReturned(MediaBufferBase *buffer) {
ALOGV("signalBufferReturned: %p", buffer->data());
Mutex::Autolock autoLock(mLock);
--mNumClientOwnedBuffers;
@@ -433,11 +433,11 @@
(mSampleRate >> 1)) / mSampleRate;
if (mNumFramesReceived == 0) {
- buffer->meta_data()->setInt64(kKeyAnchorTime, mStartTimeUs);
+ buffer->meta_data().setInt64(kKeyAnchorTime, mStartTimeUs);
}
- buffer->meta_data()->setInt64(kKeyTime, mPrevSampleTimeUs);
- buffer->meta_data()->setInt64(kKeyDriftTime, timeUs - mInitialReadTimeUs);
+ buffer->meta_data().setInt64(kKeyTime, mPrevSampleTimeUs);
+ buffer->meta_data().setInt64(kKeyDriftTime, timeUs - mInitialReadTimeUs);
mPrevSampleTimeUs = timestampUs;
mNumFramesReceived += bufferSize / frameSize;
mBuffersReceived.push_back(buffer);
diff --git a/media/libstagefright/BufferImpl.cpp b/media/libstagefright/BufferImpl.cpp
index bd2443d..d2eee33 100644
--- a/media/libstagefright/BufferImpl.cpp
+++ b/media/libstagefright/BufferImpl.cpp
@@ -64,10 +64,87 @@
return ICrypto::kDestinationTypeNativeHandle;
}
+// Codec2Buffer
+
+bool Codec2Buffer::canCopyLinear(const std::shared_ptr<C2Buffer> &buffer) const {
+ if (const_cast<Codec2Buffer *>(this)->base() == nullptr) {
+ return false;
+ }
+ if (buffer->data().type() != C2BufferData::LINEAR) {
+ return false;
+ }
+ if (buffer->data().linearBlocks().size() == 0u) {
+ // Nothing to copy, so we can copy by doing nothing.
+ return true;
+ } else if (buffer->data().linearBlocks().size() > 1u) {
+ // We don't know how to copy more than one blocks.
+ return false;
+ }
+ if (buffer->data().linearBlocks()[0].size() > capacity()) {
+ // It won't fit.
+ return false;
+ }
+ return true;
+}
+
+bool Codec2Buffer::copyLinear(const std::shared_ptr<C2Buffer> &buffer) {
+ // We assume that all canCopyLinear() checks passed.
+ if (buffer->data().linearBlocks().size() == 0u) {
+ setRange(0, 0);
+ return true;
+ }
+ C2ReadView view = buffer->data().linearBlocks()[0].map().get();
+ if (view.error() != C2_OK) {
+ ALOGD("Error while mapping: %d", view.error());
+ return false;
+ }
+ if (view.capacity() > capacity()) {
+ ALOGD("C2ConstLinearBlock lied --- it actually doesn't fit: view(%u) > this(%zu)",
+ view.capacity(), capacity());
+ return false;
+ }
+ memcpy(base(), view.data(), view.capacity());
+ setRange(0, view.capacity());
+ return true;
+}
+
+// LocalLinearBuffer
+
+bool LocalLinearBuffer::canCopy(const std::shared_ptr<C2Buffer> &buffer) const {
+ return canCopyLinear(buffer);
+}
+
+bool LocalLinearBuffer::copy(const std::shared_ptr<C2Buffer> &buffer) {
+ return copyLinear(buffer);
+}
+
+// DummyContainerBuffer
+
+DummyContainerBuffer::DummyContainerBuffer(
+ const sp<AMessage> &format, const std::shared_ptr<C2Buffer> &buffer)
+ : Codec2Buffer(format, new ABuffer(nullptr, 1)),
+ mBufferRef(buffer) {
+ setRange(0, buffer ? 1 : 0);
+}
+
+std::shared_ptr<C2Buffer> DummyContainerBuffer::asC2Buffer() {
+ return std::move(mBufferRef);
+}
+
+bool DummyContainerBuffer::canCopy(const std::shared_ptr<C2Buffer> &) const {
+ return !mBufferRef;
+}
+
+bool DummyContainerBuffer::copy(const std::shared_ptr<C2Buffer> &buffer) {
+ mBufferRef = buffer;
+ setRange(0, mBufferRef ? 1 : 0);
+ return true;
+}
+
// LinearBlockBuffer
// static
-sp<LinearBlockBuffer> LinearBlockBuffer::allocate(
+sp<LinearBlockBuffer> LinearBlockBuffer::Allocate(
const sp<AMessage> &format, const std::shared_ptr<C2LinearBlock> &block) {
C2WriteView writeView(block->map().get());
if (writeView.error() != C2_OK) {
@@ -76,15 +153,23 @@
return new LinearBlockBuffer(format, std::move(writeView), block);
}
-C2ConstLinearBlock LinearBlockBuffer::share() {
- return mBlock->share(offset(), size(), C2Fence());
+std::shared_ptr<C2Buffer> LinearBlockBuffer::asC2Buffer() {
+ return C2Buffer::CreateLinearBuffer(mBlock->share(offset(), size(), C2Fence()));
+}
+
+bool LinearBlockBuffer::canCopy(const std::shared_ptr<C2Buffer> &buffer) const {
+ return canCopyLinear(buffer);
+}
+
+bool LinearBlockBuffer::copy(const std::shared_ptr<C2Buffer> &buffer) {
+ return copyLinear(buffer);
}
LinearBlockBuffer::LinearBlockBuffer(
const sp<AMessage> &format,
C2WriteView&& writeView,
const std::shared_ptr<C2LinearBlock> &block)
- : MediaCodecBuffer(format, new ABuffer(writeView.data(), writeView.size())),
+ : Codec2Buffer(format, new ABuffer(writeView.data(), writeView.size())),
mWriteView(writeView),
mBlock(block) {
}
@@ -92,23 +177,34 @@
// ConstLinearBlockBuffer
// static
-sp<ConstLinearBlockBuffer> ConstLinearBlockBuffer::allocate(
- const sp<AMessage> &format, const C2ConstLinearBlock &block) {
- C2ReadView readView(block.map().get());
+sp<ConstLinearBlockBuffer> ConstLinearBlockBuffer::Allocate(
+ const sp<AMessage> &format, const std::shared_ptr<C2Buffer> &buffer) {
+ if (!buffer
+ || buffer->data().type() != C2BufferData::LINEAR
+ || buffer->data().linearBlocks().size() != 1u) {
+ return nullptr;
+ }
+ C2ReadView readView(buffer->data().linearBlocks()[0].map().get());
if (readView.error() != C2_OK) {
return nullptr;
}
- return new ConstLinearBlockBuffer(format, std::move(readView));
+ return new ConstLinearBlockBuffer(format, std::move(readView), buffer);
}
ConstLinearBlockBuffer::ConstLinearBlockBuffer(
const sp<AMessage> &format,
- C2ReadView&& readView)
- : MediaCodecBuffer(format, new ABuffer(
+ C2ReadView&& readView,
+ const std::shared_ptr<C2Buffer> &buffer)
+ : Codec2Buffer(format, new ABuffer(
// NOTE: ABuffer only takes non-const pointer but this data is
// supposed to be read-only.
const_cast<uint8_t *>(readView.data()), readView.capacity())),
- mReadView(readView) {
+ mReadView(readView),
+ mBufferRef(buffer) {
+}
+
+std::shared_ptr<C2Buffer> ConstLinearBlockBuffer::asC2Buffer() {
+ return std::move(mBufferRef);
}
} // namespace android
diff --git a/media/libstagefright/C2OMXNode.cpp b/media/libstagefright/C2OMXNode.cpp
index e6f81af..04faa28 100644
--- a/media/libstagefright/C2OMXNode.cpp
+++ b/media/libstagefright/C2OMXNode.cpp
@@ -234,7 +234,7 @@
std::shared_ptr<C2Buffer> c2Buffer(
// TODO: fence
new Buffer2D(block->share(
- C2Rect(block->width(), block->height()), ::android::C2Fence())),
+ C2Rect(block->width(), block->height()), ::C2Fence())),
[handle, buffer, source = getSource()](C2Buffer *ptr) {
delete ptr;
native_handle_delete(handle);
diff --git a/media/libstagefright/CCodec.cpp b/media/libstagefright/CCodec.cpp
index bb70458..f62ee41 100644
--- a/media/libstagefright/CCodec.cpp
+++ b/media/libstagefright/CCodec.cpp
@@ -313,6 +313,19 @@
}
void CCodec::configure(const sp<AMessage> &msg) {
+ std::shared_ptr<C2ComponentInterface> intf;
+ {
+ Mutexed<State>::Locked state(mState);
+ if (state->get() != ALLOCATED) {
+ state->set(RELEASED);
+ state.unlock();
+ mCallback->onError(UNKNOWN_ERROR, ACTION_CODE_FATAL);
+ state.lock();
+ return;
+ }
+ intf = state->comp->intf();
+ }
+
sp<AMessage> inputFormat(new AMessage);
sp<AMessage> outputFormat(new AMessage);
if (status_t err = [=] {
@@ -332,11 +345,34 @@
setSurface(surface);
}
+ std::vector<std::unique_ptr<C2Param>> params;
+ std::initializer_list<C2Param::Index> indices {
+ C2PortMimeConfig::input::PARAM_TYPE,
+ C2PortMimeConfig::output::PARAM_TYPE,
+ };
+ c2_status_t c2err = intf->query_vb(
+ {},
+ indices,
+ C2_DONT_BLOCK,
+ ¶ms);
+ if (c2err != C2_OK) {
+ ALOGE("Failed to query component interface: %d", c2err);
+ return UNKNOWN_ERROR;
+ }
+ if (params.size() != indices.size()) {
+ ALOGE("Component returns wrong number of params");
+ return UNKNOWN_ERROR;
+ }
+ if (!params[0] || !params[1]) {
+ ALOGE("Component returns null params");
+ return UNKNOWN_ERROR;
+ }
+ inputFormat->setString("mime", ((C2PortMimeConfig *)params[0].get())->m.value);
+ outputFormat->setString("mime", ((C2PortMimeConfig *)params[1].get())->m.value);
+
// XXX: hack
bool audio = mime.startsWithIgnoreCase("audio/");
if (encoder) {
- outputFormat->setString("mime", mime);
- inputFormat->setString("mime", AStringPrintf("%s/raw", audio ? "audio" : "video"));
if (audio) {
inputFormat->setInt32("channel-count", 1);
inputFormat->setInt32("sample-rate", 44100);
@@ -347,8 +383,6 @@
outputFormat->setInt32("height", 1920);
}
} else {
- inputFormat->setString("mime", mime);
- outputFormat->setString("mime", AStringPrintf("%s/raw", audio ? "audio" : "video"));
if (audio) {
outputFormat->setInt32("channel-count", 2);
outputFormat->setInt32("sample-rate", 44100);
@@ -712,7 +746,7 @@
switch (msg->what()) {
case kWhatAllocate: {
// C2ComponentStore::createComponent() should return within 100ms.
- setDeadline(now + 150ms);
+ setDeadline(now + 150ms, "allocate");
AString componentName;
CHECK(msg->findString("componentName", &componentName));
allocate(componentName);
@@ -720,7 +754,7 @@
}
case kWhatConfigure: {
// C2Component::commit_sm() should return within 5ms.
- setDeadline(now + 50ms);
+ setDeadline(now + 50ms, "configure");
sp<AMessage> format;
CHECK(msg->findMessage("format", &format));
configure(format);
@@ -728,31 +762,31 @@
}
case kWhatStart: {
// C2Component::start() should return within 500ms.
- setDeadline(now + 550ms);
+ setDeadline(now + 550ms, "start");
start();
break;
}
case kWhatStop: {
// C2Component::stop() should return within 500ms.
- setDeadline(now + 550ms);
+ setDeadline(now + 550ms, "stop");
stop();
break;
}
case kWhatFlush: {
// C2Component::flush_sm() should return within 5ms.
- setDeadline(now + 50ms);
+ setDeadline(now + 50ms, "flush");
flush();
break;
}
case kWhatCreateInputSurface: {
// Surface operations may be briefly blocking.
- setDeadline(now + 100ms);
+ setDeadline(now + 100ms, "createInputSurface");
createInputSurface();
break;
}
case kWhatSetInputSurface: {
// Surface operations may be briefly blocking.
- setDeadline(now + 100ms);
+ setDeadline(now + 100ms, "setInputSurface");
sp<RefBase> obj;
CHECK(msg->findObject("surface", &obj));
sp<PersistentSurface> surface(static_cast<PersistentSurface *>(obj.get()));
@@ -780,25 +814,28 @@
break;
}
}
- setDeadline(TimePoint::max());
+ setDeadline(TimePoint::max(), "none");
}
-void CCodec::setDeadline(const TimePoint &newDeadline) {
- Mutexed<TimePoint>::Locked deadline(mDeadline);
- *deadline = newDeadline;
+void CCodec::setDeadline(const TimePoint &newDeadline, const char *name) {
+ Mutexed<NamedTimePoint>::Locked deadline(mDeadline);
+ deadline->set(newDeadline, name);
}
void CCodec::initiateReleaseIfStuck() {
+ std::string name;
{
- Mutexed<TimePoint>::Locked deadline(mDeadline);
- if (*deadline >= std::chrono::steady_clock::now()) {
+ Mutexed<NamedTimePoint>::Locked deadline(mDeadline);
+ if (deadline->get() >= std::chrono::steady_clock::now()) {
// We're not stuck.
return;
}
+ name = deadline->getName();
}
+ ALOGW("previous call to %s exceeded timeout", name.c_str());
mCallback->onError(UNKNOWN_ERROR, ACTION_CODE_FATAL);
- initiateRelease();
+ initiateRelease(false);
}
} // namespace android
diff --git a/media/libstagefright/CCodecBufferChannel.cpp b/media/libstagefright/CCodecBufferChannel.cpp
index 6fba890..449c6aa 100644
--- a/media/libstagefright/CCodecBufferChannel.cpp
+++ b/media/libstagefright/CCodecBufferChannel.cpp
@@ -138,7 +138,7 @@
virtual bool registerBuffer(
const std::shared_ptr<C2Buffer> &buffer,
size_t *index,
- sp<MediaCodecBuffer> *codecBuffer) = 0;
+ sp<MediaCodecBuffer> *clientBuffer) = 0;
/**
* Register codec specific data as a buffer to be consistent with
@@ -147,9 +147,7 @@
virtual bool registerCsd(
const C2StreamCsdInfo::output * /* csd */,
size_t * /* index */,
- sp<MediaCodecBuffer> * /* codecBuffer */) {
- return false;
- }
+ sp<MediaCodecBuffer> * /* clientBuffer */) = 0;
/**
* Release the buffer obtained from registerBuffer() and get the
@@ -181,23 +179,6 @@
const static size_t kMinBufferArraySize = 16;
const static size_t kLinearBufferSize = 524288;
-template <class T>
-ssize_t findBufferSlot(
- std::vector<T> *buffers,
- size_t maxSize,
- std::function<bool(const T&)> pred) {
- auto it = std::find_if(buffers->begin(), buffers->end(), pred);
- if (it == buffers->end()) {
- if (buffers->size() < maxSize) {
- buffers->emplace_back();
- return buffers->size() - 1;
- } else {
- return -1;
- }
- }
- return std::distance(buffers->begin(), it);
-}
-
sp<LinearBlockBuffer> allocateLinearBuffer(
const std::shared_ptr<C2BlockPool> &pool,
const sp<AMessage> &format,
@@ -205,41 +186,209 @@
const C2MemoryUsage &usage) {
std::shared_ptr<C2LinearBlock> block;
- status_t err = pool->fetchLinearBlock(
- size,
- usage,
- &block);
+ status_t err = pool->fetchLinearBlock(size, usage, &block);
if (err != OK) {
return nullptr;
}
- return LinearBlockBuffer::allocate(format, block);
+ return LinearBlockBuffer::Allocate(format, block);
}
-class Buffer1D : public C2Buffer {
+class BuffersArrayImpl;
+
+/**
+ * Flexible buffer slots implementation.
+ */
+class FlexBuffersImpl {
public:
- explicit Buffer1D(C2ConstLinearBlock block) : C2Buffer({ block }) {}
+ FlexBuffersImpl() = default;
+
+ /**
+ * Assign an empty slot for a buffer and return the index. If there's no
+ * empty slot, just add one at the end and return it.
+ *
+ * \param buffer[in] a new buffer to assign a slot.
+ * \return index of the assigned slot.
+ */
+ size_t assignSlot(const sp<Codec2Buffer> &buffer) {
+ for (size_t i = 0; i < mBuffers.size(); ++i) {
+ if (mBuffers[i].clientBuffer.promote() == nullptr
+ && mBuffers[i].compBuffer.expired()) {
+ mBuffers[i].clientBuffer = buffer;
+ return i;
+ }
+ }
+ mBuffers.push_back({ buffer, std::weak_ptr<C2Buffer>() });
+ return mBuffers.size() - 1;
+ }
+
+ /**
+ * Release the slot from the client, and get the C2Buffer object back from
+ * the previously assigned buffer. Note that the slot is not completely free
+ * until the returned C2Buffer object is freed.
+ *
+ * \param buffer[in] the buffer previously assigned a slot.
+ * \return C2Buffer object from |buffer|.
+ */
+ std::shared_ptr<C2Buffer> releaseSlot(const sp<MediaCodecBuffer> &buffer) {
+ sp<Codec2Buffer> c2Buffer;
+ size_t index = mBuffers.size();
+ for (size_t i = 0; i < mBuffers.size(); ++i) {
+ if (mBuffers[i].clientBuffer.promote() == buffer) {
+ c2Buffer = mBuffers[i].clientBuffer.promote();
+ index = i;
+ break;
+ }
+ }
+ if (c2Buffer == nullptr) {
+ ALOGD("No matching buffer found");
+ return nullptr;
+ }
+ std::shared_ptr<C2Buffer> result = c2Buffer->asC2Buffer();
+ mBuffers[index].compBuffer = result;
+ return result;
+ }
+
+private:
+ friend class BuffersArrayImpl;
+
+ struct Entry {
+ wp<Codec2Buffer> clientBuffer;
+ std::weak_ptr<C2Buffer> compBuffer;
+ };
+ std::vector<Entry> mBuffers;
};
-class Buffer2D : public C2Buffer {
+/**
+ * Static buffer slots implementation based on a fixed-size array.
+ */
+class BuffersArrayImpl {
public:
- explicit Buffer2D(C2ConstGraphicBlock block) : C2Buffer({ block }) {}
+ /**
+ * Initialize buffer array from the original |impl|. The buffers known by
+ * the client is preserved, and the empty slots are populated so that the
+ * array size is at least |minSize|.
+ *
+ * \param impl[in] FlexBuffersImpl object used so far.
+ * \param minSize[in] minimum size of the buffer array.
+ * \param allocate[in] function to allocate a client buffer for an empty slot.
+ */
+ void initialize(
+ const FlexBuffersImpl &impl,
+ size_t minSize,
+ std::function<sp<Codec2Buffer>()> allocate) {
+ for (size_t i = 0; i < impl.mBuffers.size(); ++i) {
+ sp<Codec2Buffer> clientBuffer = impl.mBuffers[i].clientBuffer.promote();
+ bool ownedByClient = (clientBuffer != nullptr);
+ if (!ownedByClient) {
+ clientBuffer = allocate();
+ }
+ mBuffers.push_back({ clientBuffer, impl.mBuffers[i].compBuffer, ownedByClient });
+ }
+ for (size_t i = impl.mBuffers.size(); i < minSize; ++i) {
+ mBuffers.push_back({ allocate(), std::weak_ptr<C2Buffer>(), false });
+ }
+ }
+
+ /**
+ * Grab a buffer from the underlying array which matches the criteria.
+ *
+ * \param index[out] index of the slot.
+ * \param buffer[out] the matching buffer.
+ * \param match[in] a function to test whether the buffer matches the
+ * criteria or not.
+ * \return OK if successful,
+ * NO_MEMORY if there's no available slot meets the criteria.
+ */
+ status_t grabBuffer(
+ size_t *index,
+ sp<Codec2Buffer> *buffer,
+ std::function<bool(const sp<Codec2Buffer> &)> match =
+ [](const sp<Codec2Buffer> &) { return true; }) {
+ for (size_t i = 0; i < mBuffers.size(); ++i) {
+ if (!mBuffers[i].ownedByClient && mBuffers[i].compBuffer.expired()
+ && match(mBuffers[i].clientBuffer)) {
+ mBuffers[i].ownedByClient = true;
+ *buffer = mBuffers[i].clientBuffer;
+ (*buffer)->meta()->clear();
+ (*buffer)->setRange(0, (*buffer)->capacity());
+ *index = i;
+ return OK;
+ }
+ }
+ return NO_MEMORY;
+ }
+
+ /**
+ * Return the buffer from the client, and get the C2Buffer object back from
+ * the buffer. Note that the slot is not completely free until the returned
+ * C2Buffer object is freed.
+ *
+ * \param buffer[in] the buffer previously grabbed.
+ * \return C2Buffer object from |buffer|.
+ */
+ std::shared_ptr<C2Buffer> returnBuffer(const sp<MediaCodecBuffer> &buffer) {
+ sp<Codec2Buffer> c2Buffer;
+ size_t index = mBuffers.size();
+ for (size_t i = 0; i < mBuffers.size(); ++i) {
+ if (mBuffers[i].clientBuffer == buffer) {
+ if (!mBuffers[i].ownedByClient) {
+ ALOGD("Client returned a buffer it does not own according to our record: %zu", i);
+ }
+ c2Buffer = mBuffers[i].clientBuffer;
+ mBuffers[i].ownedByClient = false;
+ index = i;
+ break;
+ }
+ }
+ if (c2Buffer == nullptr) {
+ ALOGD("No matching buffer found");
+ return nullptr;
+ }
+ std::shared_ptr<C2Buffer> result = c2Buffer->asC2Buffer();
+ mBuffers[index].compBuffer = result;
+ return result;
+ }
+
+ /**
+ * Populate |array| with the underlying buffer array.
+ *
+ * \param array[out] an array to be filled with the underlying buffer array.
+ */
+ void getArray(Vector<sp<MediaCodecBuffer>> *array) const {
+ array->clear();
+ for (const Entry &entry : mBuffers) {
+ array->push(entry.clientBuffer);
+ }
+ }
+
+ /**
+ * The client abandoned all known buffers, so reclaim the ownership.
+ */
+ void flush() {
+ for (Entry &entry : mBuffers) {
+ entry.ownedByClient = false;
+ }
+ }
+
+private:
+ struct Entry {
+ const sp<Codec2Buffer> clientBuffer;
+ std::weak_ptr<C2Buffer> compBuffer;
+ bool ownedByClient;
+ };
+ std::vector<Entry> mBuffers;
};
class InputBuffersArray : public CCodecBufferChannel::InputBuffers {
public:
InputBuffersArray() = default;
- void add(
- size_t index,
- const sp<LinearBlockBuffer> &clientBuffer,
- bool available) {
- if (mBufferArray.size() <= index) {
- // TODO: make this more efficient
- mBufferArray.resize(index + 1);
- }
- mBufferArray[index].clientBuffer = clientBuffer;
- mBufferArray[index].available = available;
+ void initialize(
+ const FlexBuffersImpl &impl,
+ size_t minSize,
+ std::function<sp<Codec2Buffer>()> allocate) {
+ mImpl.initialize(impl, minSize, allocate);
}
bool isArrayMode() const final { return true; }
@@ -249,52 +398,30 @@
}
void getArray(Vector<sp<MediaCodecBuffer>> *array) const final {
- array->clear();
- for (const Entry &entry : mBufferArray) {
- array->push(entry.clientBuffer);
- }
+ mImpl.getArray(array);
}
bool requestNewBuffer(size_t *index, sp<MediaCodecBuffer> *buffer) override {
- for (size_t i = 0; i < mBufferArray.size(); ++i) {
- if (mBufferArray[i].available && mBufferArray[i].compBuffer.expired()) {
- mBufferArray[i].available = false;
- *index = i;
- *buffer = mBufferArray[i].clientBuffer;
- (*buffer)->meta()->clear();
- (*buffer)->setRange(0, (*buffer)->capacity());
- return true;
- }
+ sp<Codec2Buffer> c2Buffer;
+ status_t err = mImpl.grabBuffer(index, &c2Buffer);
+ if (err == OK) {
+ c2Buffer->setFormat(mFormat);
+ *buffer = c2Buffer;
+ return true;
}
return false;
}
std::shared_ptr<C2Buffer> releaseBuffer(const sp<MediaCodecBuffer> &buffer) override {
- for (size_t i = 0; i < mBufferArray.size(); ++i) {
- if (!mBufferArray[i].available && mBufferArray[i].clientBuffer == buffer) {
- std::shared_ptr<C2Buffer> buffer = std::make_shared<Buffer1D>(mBufferArray[i].clientBuffer->share());
- mBufferArray[i].available = true;
- mBufferArray[i].compBuffer = buffer;
- return buffer;
- }
- }
- return nullptr;
+ return mImpl.returnBuffer(buffer);
}
void flush() override {
- for (size_t i = 0; i < mBufferArray.size(); ++i) {
- mBufferArray[i].available = true;
- }
+ mImpl.flush();
}
private:
- struct Entry {
- sp<LinearBlockBuffer> clientBuffer;
- std::weak_ptr<C2Buffer> compBuffer;
- bool available;
- };
-
- std::vector<Entry> mBufferArray;
+ BuffersArrayImpl mImpl;
};
class LinearInputBuffers : public CCodecBufferChannel::InputBuffers {
@@ -302,68 +429,42 @@
using CCodecBufferChannel::InputBuffers::InputBuffers;
bool requestNewBuffer(size_t *index, sp<MediaCodecBuffer> *buffer) override {
- *buffer = nullptr;
- ssize_t ret = findBufferSlot<wp<LinearBlockBuffer>>(
- &mBuffers, kMinBufferArraySize,
- [] (const auto &elem) { return elem.promote() == nullptr; });
- if (ret < 0) {
- return false;
- }
- // TODO: proper max input size and usage
+ // TODO: proper max input size
// TODO: read usage from intf
C2MemoryUsage usage = { C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE };
sp<LinearBlockBuffer> newBuffer = allocateLinearBuffer(mPool, mFormat, kLinearBufferSize, usage);
if (newBuffer == nullptr) {
return false;
}
- mBuffers[ret] = newBuffer;
- *index = ret;
+ *index = mImpl.assignSlot(newBuffer);
*buffer = newBuffer;
return true;
}
std::shared_ptr<C2Buffer> releaseBuffer(const sp<MediaCodecBuffer> &buffer) override {
- auto it = std::find(mBuffers.begin(), mBuffers.end(), buffer);
- if (it == mBuffers.end()) {
- return nullptr;
- }
- sp<LinearBlockBuffer> codecBuffer = it->promote();
- // We got sp<> reference from the caller so this should never happen..
- CHECK(codecBuffer != nullptr);
- return std::make_shared<Buffer1D>(codecBuffer->share());
+ return mImpl.releaseSlot(buffer);
}
void flush() override {
+ // This is no-op by default unless we're in array mode where we need to keep
+ // track of the flushed work.
}
std::unique_ptr<CCodecBufferChannel::InputBuffers> toArrayMode() final {
std::unique_ptr<InputBuffersArray> array(new InputBuffersArray);
array->setFormat(mFormat);
- // TODO
- const size_t size = std::max(kMinBufferArraySize, mBuffers.size());
- mBuffers.resize(size);
- for (size_t i = 0; i < size; ++i) {
- sp<LinearBlockBuffer> clientBuffer = mBuffers[i].promote();
- bool available = false;
- if (clientBuffer == nullptr) {
- // TODO: proper max input size
- // TODO: read usage from intf
- C2MemoryUsage usage = { C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE };
- clientBuffer = allocateLinearBuffer(mPool, mFormat, kLinearBufferSize, usage);
- available = true;
- }
- array->add(
- i,
- clientBuffer,
- available);
- }
+ array->initialize(
+ mImpl,
+ kMinBufferArraySize,
+ [pool = mPool, format = mFormat] () -> sp<Codec2Buffer> {
+ C2MemoryUsage usage = { C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE };
+ return allocateLinearBuffer(pool, format, kLinearBufferSize, usage);
+ });
return std::move(array);
}
private:
- // Buffers we passed to the client. The index of a buffer matches what
- // was passed in BufferCallback::onInputBufferAvailable().
- std::vector<wp<LinearBlockBuffer>> mBuffers;
+ FlexBuffersImpl mImpl;
};
class GraphicInputBuffers : public CCodecBufferChannel::InputBuffers {
@@ -396,18 +497,11 @@
public:
using CCodecBufferChannel::OutputBuffers::OutputBuffers;
- void add(
- size_t index,
- const sp<MediaCodecBuffer> &clientBuffer,
- const std::shared_ptr<C2Buffer> &compBuffer,
- bool available) {
- if (mBufferArray.size() <= index) {
- // TODO: make this more efficient
- mBufferArray.resize(index + 1);
- }
- mBufferArray[index].clientBuffer = clientBuffer;
- mBufferArray[index].compBuffer = compBuffer;
- mBufferArray[index].available = available;
+ void initialize(
+ const FlexBuffersImpl &impl,
+ size_t minSize,
+ std::function<sp<Codec2Buffer>()> allocate) {
+ mImpl.initialize(impl, minSize, allocate);
}
bool isArrayMode() const final { return true; }
@@ -419,125 +513,69 @@
bool registerBuffer(
const std::shared_ptr<C2Buffer> &buffer,
size_t *index,
- sp<MediaCodecBuffer> *codecBuffer) final {
- for (size_t i = 0; i < mBufferArray.size(); ++i) {
- if (mBufferArray[i].available && copy(buffer, mBufferArray[i].clientBuffer)) {
- *index = i;
- *codecBuffer = mBufferArray[i].clientBuffer;
- (*codecBuffer)->setFormat(mFormat);
- (*codecBuffer)->meta()->clear();
- mBufferArray[i].compBuffer = buffer;
- mBufferArray[i].available = false;
- return true;
- }
+ sp<MediaCodecBuffer> *clientBuffer) final {
+ sp<Codec2Buffer> c2Buffer;
+ status_t err = mImpl.grabBuffer(
+ index,
+ &c2Buffer,
+ [buffer](const sp<Codec2Buffer> &clientBuffer) {
+ return clientBuffer->canCopy(buffer);
+ });
+ if (err != OK) {
+ return false;
}
- return false;
+ if (!c2Buffer->copy(buffer)) {
+ return false;
+ }
+ c2Buffer->setFormat(mFormat);
+ *clientBuffer = c2Buffer;
+ return true;
}
bool registerCsd(
const C2StreamCsdInfo::output *csd,
size_t *index,
- sp<MediaCodecBuffer> *codecBuffer) final {
- for (size_t i = 0; i < mBufferArray.size(); ++i) {
- if (mBufferArray[i].available
- && mBufferArray[i].clientBuffer->capacity() >= csd->flexCount()) {
- // TODO: proper format update
- sp<ABuffer> csdBuffer = ABuffer::CreateAsCopy(csd->m.value, csd->flexCount());
- mFormat = mFormat->dup();
- mFormat->setBuffer("csd-0", csdBuffer);
-
- memcpy(mBufferArray[i].clientBuffer->base(), csd->m.value, csd->flexCount());
- mBufferArray[i].clientBuffer->setRange(0, csd->flexCount());
- *index = i;
- *codecBuffer = mBufferArray[i].clientBuffer;
- (*codecBuffer)->setFormat(mFormat);
- (*codecBuffer)->meta()->clear();
- mBufferArray[i].available = false;
- return true;
- }
+ sp<MediaCodecBuffer> *clientBuffer) final {
+ sp<Codec2Buffer> c2Buffer;
+ status_t err = mImpl.grabBuffer(
+ index,
+ &c2Buffer,
+ [csd](const sp<Codec2Buffer> &clientBuffer) {
+ return clientBuffer->base() != nullptr
+ && clientBuffer->capacity() >= csd->flexCount();
+ });
+ if (err != OK) {
+ return false;
}
- return false;
+ // TODO: proper format update
+ sp<ABuffer> csdBuffer = ABuffer::CreateAsCopy(csd->m.value, csd->flexCount());
+ mFormat = mFormat->dup();
+ mFormat->setBuffer("csd-0", csdBuffer);
+
+ memcpy(c2Buffer->base(), csd->m.value, csd->flexCount());
+ c2Buffer->setRange(0, csd->flexCount());
+ c2Buffer->setFormat(mFormat);
+ *clientBuffer = c2Buffer;
+ return true;
}
std::shared_ptr<C2Buffer> releaseBuffer(const sp<MediaCodecBuffer> &buffer) final {
- for (size_t i = 0; i < mBufferArray.size(); ++i) {
- if (!mBufferArray[i].available && mBufferArray[i].clientBuffer == buffer) {
- mBufferArray[i].available = true;
- return std::move(mBufferArray[i].compBuffer);
- }
- }
- return nullptr;
+ return mImpl.returnBuffer(buffer);
}
- void flush(
- const std::list<std::unique_ptr<C2Work>> &flushedWork) override {
+ void flush(const std::list<std::unique_ptr<C2Work>> &flushedWork) override {
(void) flushedWork;
- for (size_t i = 0; i < mBufferArray.size(); ++i) {
- mBufferArray[i].available = true;
- mBufferArray[i].compBuffer.reset();
- }
+ mImpl.flush();
}
- virtual bool copy(
- const std::shared_ptr<C2Buffer> &buffer,
- const sp<MediaCodecBuffer> &clientBuffer) = 0;
-
void getArray(Vector<sp<MediaCodecBuffer>> *array) const final {
- array->clear();
- for (const Entry &entry : mBufferArray) {
- array->push(entry.clientBuffer);
- }
+ mImpl.getArray(array);
}
private:
- struct Entry {
- sp<MediaCodecBuffer> clientBuffer;
- std::shared_ptr<C2Buffer> compBuffer;
- bool available;
- };
-
- std::vector<Entry> mBufferArray;
+ BuffersArrayImpl mImpl;
};
-class LinearOutputBuffersArray : public OutputBuffersArray {
-public:
- using OutputBuffersArray::OutputBuffersArray;
-
- bool copy(
- const std::shared_ptr<C2Buffer> &buffer,
- const sp<MediaCodecBuffer> &clientBuffer) final {
- if (!buffer) {
- clientBuffer->setRange(0u, 0u);
- return true;
- }
- C2ReadView view = buffer->data().linearBlocks().front().map().get();
- if (clientBuffer->capacity() < view.capacity()) {
- ALOGV("view.capacity() = %u", view.capacity());
- return false;
- }
- clientBuffer->setRange(0u, view.capacity());
- memcpy(clientBuffer->data(), view.data(), view.capacity());
- return true;
- }
-};
-
-class GraphicOutputBuffersArray : public OutputBuffersArray {
-public:
- using OutputBuffersArray::OutputBuffersArray;
-
- bool copy(
- const std::shared_ptr<C2Buffer> &buffer,
- const sp<MediaCodecBuffer> &clientBuffer) final {
- if (!buffer) {
- clientBuffer->setRange(0u, 0u);
- return true;
- }
- clientBuffer->setRange(0u, 1u);
- return true;
- }
-};
-
-// Flexible in a sense that it does not have fixed array size.
class FlexOutputBuffers : public CCodecBufferChannel::OutputBuffers {
public:
using CCodecBufferChannel::OutputBuffers::OutputBuffers;
@@ -545,56 +583,31 @@
bool registerBuffer(
const std::shared_ptr<C2Buffer> &buffer,
size_t *index,
- sp<MediaCodecBuffer> *codecBuffer) override {
- *codecBuffer = nullptr;
- ssize_t ret = findBufferSlot<BufferInfo>(
- &mBuffers,
- std::numeric_limits<size_t>::max(),
- [] (const auto &elem) { return elem.clientBuffer.promote() == nullptr; });
- if (ret < 0) {
- return false;
- }
- sp<MediaCodecBuffer> newBuffer = convert(buffer);
- mBuffers[ret] = { newBuffer, buffer };
- *index = ret;
- *codecBuffer = newBuffer;
+ sp<MediaCodecBuffer> *clientBuffer) override {
+ sp<Codec2Buffer> newBuffer = wrap(buffer);
+ *index = mImpl.assignSlot(newBuffer);
+ *clientBuffer = newBuffer;
return true;
}
bool registerCsd(
const C2StreamCsdInfo::output *csd,
size_t *index,
- sp<MediaCodecBuffer> *codecBuffer) final {
- *codecBuffer = nullptr;
- ssize_t ret = findBufferSlot<BufferInfo>(
- &mBuffers,
- std::numeric_limits<size_t>::max(),
- [] (const auto &elem) { return elem.clientBuffer.promote() == nullptr; });
- if (ret < 0) {
- return false;
- }
+ sp<MediaCodecBuffer> *clientBuffer) final {
// TODO: proper format update
sp<ABuffer> csdBuffer = ABuffer::CreateAsCopy(csd->m.value, csd->flexCount());
mFormat = mFormat->dup();
mFormat->setBuffer("csd-0", csdBuffer);
- sp<MediaCodecBuffer> newBuffer = new MediaCodecBuffer(mFormat, csdBuffer);
- mBuffers[ret] = { newBuffer, nullptr };
- *index = ret;
- *codecBuffer = newBuffer;
+
+ sp<Codec2Buffer> newBuffer = new LocalLinearBuffer(mFormat, csdBuffer);
+ *index = mImpl.assignSlot(newBuffer);
+ *clientBuffer = newBuffer;
return true;
}
std::shared_ptr<C2Buffer> releaseBuffer(
const sp<MediaCodecBuffer> &buffer) override {
- auto it = std::find_if(
- mBuffers.begin(), mBuffers.end(),
- [buffer] (const auto &elem) {
- return elem.clientBuffer.promote() == buffer;
- });
- if (it == mBuffers.end()) {
- return nullptr;
- }
- return std::move(it->bufferRef);
+ return mImpl.releaseSlot(buffer);
}
void flush(
@@ -604,27 +617,31 @@
// track of the flushed work.
}
- virtual sp<MediaCodecBuffer> convert(const std::shared_ptr<C2Buffer> &buffer) = 0;
+ std::unique_ptr<CCodecBufferChannel::OutputBuffers> toArrayMode() override {
+ std::unique_ptr<OutputBuffersArray> array(new OutputBuffersArray);
+ array->setFormat(mFormat);
+ array->initialize(
+ mImpl,
+ kMinBufferArraySize,
+ [this]() { return allocateArrayBuffer(); });
+ return std::move(array);
+ }
-protected:
- struct BufferInfo {
- // wp<> of MediaCodecBuffer for MediaCodec.
- wp<MediaCodecBuffer> clientBuffer;
- // Buffer reference to hold until clientBuffer is valid.
- std::shared_ptr<C2Buffer> bufferRef;
- };
- // Buffers we passed to the client. The index of a buffer matches what
- // was passed in BufferCallback::onInputBufferAvailable().
- std::vector<BufferInfo> mBuffers;
+ virtual sp<Codec2Buffer> wrap(const std::shared_ptr<C2Buffer> &buffer) = 0;
+
+ virtual sp<Codec2Buffer> allocateArrayBuffer() = 0;
+
+private:
+ FlexBuffersImpl mImpl;
};
class LinearOutputBuffers : public FlexOutputBuffers {
public:
using FlexOutputBuffers::FlexOutputBuffers;
- virtual sp<MediaCodecBuffer> convert(const std::shared_ptr<C2Buffer> &buffer) override {
+ sp<Codec2Buffer> wrap(const std::shared_ptr<C2Buffer> &buffer) override {
if (buffer == nullptr) {
- return new MediaCodecBuffer(mFormat, new ABuffer(nullptr, 0));
+ return new DummyContainerBuffer(mFormat, buffer);
}
if (buffer->data().type() != C2BufferData::LINEAR) {
// We expect linear output buffers from the component.
@@ -634,28 +651,12 @@
// We expect one and only one linear block from the component.
return nullptr;
}
- return ConstLinearBlockBuffer::allocate(mFormat, buffer->data().linearBlocks().front());
+ return ConstLinearBlockBuffer::Allocate(mFormat, buffer);
}
- std::unique_ptr<CCodecBufferChannel::OutputBuffers> toArrayMode() override {
- std::unique_ptr<OutputBuffersArray> array(new LinearOutputBuffersArray);
- array->setFormat(mFormat);
-
- const size_t size = std::max(kMinBufferArraySize, mBuffers.size());
- mBuffers.resize(size);
- for (size_t i = 0; i < size; ++i) {
- sp<MediaCodecBuffer> clientBuffer = mBuffers[i].clientBuffer.promote();
- std::shared_ptr<C2Buffer> compBuffer = mBuffers[i].bufferRef;
- bool available = false;
- if (clientBuffer == nullptr) {
- // TODO: proper max input size
- clientBuffer = new MediaCodecBuffer(mFormat, new ABuffer(kLinearBufferSize));
- available = true;
- compBuffer.reset();
- }
- array->add(i, clientBuffer, compBuffer, available);
- }
- return std::move(array);
+ sp<Codec2Buffer> allocateArrayBuffer() override {
+ // TODO: proper max output size
+ return new LocalLinearBuffer(mFormat, new ABuffer(kLinearBufferSize));
}
};
@@ -663,29 +664,12 @@
public:
using FlexOutputBuffers::FlexOutputBuffers;
- sp<MediaCodecBuffer> convert(const std::shared_ptr<C2Buffer> &buffer) override {
- return new MediaCodecBuffer(
- mFormat, buffer ? new ABuffer(nullptr, 1) : new ABuffer(nullptr, 0));
+ sp<Codec2Buffer> wrap(const std::shared_ptr<C2Buffer> &buffer) override {
+ return new DummyContainerBuffer(mFormat, buffer);
}
- std::unique_ptr<CCodecBufferChannel::OutputBuffers> toArrayMode() override {
- std::unique_ptr<OutputBuffersArray> array(new GraphicOutputBuffersArray);
- array->setFormat(mFormat);
-
- const size_t size = std::max(kMinBufferArraySize, mBuffers.size());
- mBuffers.resize(size);
- for (size_t i = 0; i < size; ++i) {
- sp<MediaCodecBuffer> clientBuffer = mBuffers[i].clientBuffer.promote();
- std::shared_ptr<C2Buffer> compBuffer = mBuffers[i].bufferRef;
- bool available = false;
- if (clientBuffer == nullptr) {
- clientBuffer = new MediaCodecBuffer(mFormat, new ABuffer(nullptr, 1));
- available = true;
- compBuffer.reset();
- }
- array->add(i, clientBuffer, compBuffer, available);
- }
- return std::move(array);
+ sp<Codec2Buffer> allocateArrayBuffer() override {
+ return new DummyContainerBuffer(mFormat);
}
};
diff --git a/media/libstagefright/CallbackMediaSource.cpp b/media/libstagefright/CallbackMediaSource.cpp
index 6811882..ea7392e 100644
--- a/media/libstagefright/CallbackMediaSource.cpp
+++ b/media/libstagefright/CallbackMediaSource.cpp
@@ -36,7 +36,7 @@
return mSource->getFormat();
}
-status_t CallbackMediaSource::read(MediaBuffer **buffer, const ReadOptions *options) {
+status_t CallbackMediaSource::read(MediaBufferBase **buffer, const ReadOptions *options) {
return mSource->read(buffer, reinterpret_cast<const ReadOptions*>(options));
}
diff --git a/media/libstagefright/CameraSource.cpp b/media/libstagefright/CameraSource.cpp
index 6ed0d0e..db37021 100644
--- a/media/libstagefright/CameraSource.cpp
+++ b/media/libstagefright/CameraSource.cpp
@@ -1040,7 +1040,7 @@
releaseRecordingFrame(frame);
}
-void CameraSource::signalBufferReturned(MediaBuffer *buffer) {
+void CameraSource::signalBufferReturned(MediaBufferBase *buffer) {
ALOGV("signalBufferReturned: %p", buffer->data());
Mutex::Autolock autoLock(mLock);
for (List<sp<IMemory> >::iterator it = mFramesBeingEncoded.begin();
@@ -1059,7 +1059,7 @@
}
status_t CameraSource::read(
- MediaBuffer **buffer, const ReadOptions *options) {
+ MediaBufferBase **buffer, const ReadOptions *options) {
ALOGV("read");
*buffer = NULL;
@@ -1100,7 +1100,7 @@
*buffer = new MediaBuffer(frame->pointer(), frame->size());
(*buffer)->setObserver(this);
(*buffer)->add_ref();
- (*buffer)->meta_data()->setInt64(kKeyTime, frameTime);
+ (*buffer)->meta_data().setInt64(kKeyTime, frameTime);
}
return OK;
}
diff --git a/media/libstagefright/CameraSourceTimeLapse.cpp b/media/libstagefright/CameraSourceTimeLapse.cpp
index 970526a..3ad82d9 100644
--- a/media/libstagefright/CameraSourceTimeLapse.cpp
+++ b/media/libstagefright/CameraSourceTimeLapse.cpp
@@ -168,7 +168,7 @@
return isSuccessful;
}
-void CameraSourceTimeLapse::signalBufferReturned(MediaBuffer* buffer) {
+void CameraSourceTimeLapse::signalBufferReturned(MediaBufferBase* buffer) {
ALOGV("signalBufferReturned");
Mutex::Autolock autoLock(mQuickStopLock);
if (mQuickStop && (buffer == mLastReadBufferCopy)) {
@@ -180,9 +180,9 @@
}
void createMediaBufferCopy(
- const MediaBuffer& sourceBuffer,
+ const MediaBufferBase& sourceBuffer,
int64_t frameTime,
- MediaBuffer **newBuffer) {
+ MediaBufferBase **newBuffer) {
ALOGV("createMediaBufferCopy");
size_t sourceSize = sourceBuffer.size();
@@ -191,20 +191,20 @@
(*newBuffer) = new MediaBuffer(sourceSize);
memcpy((*newBuffer)->data(), sourcePointer, sourceSize);
- (*newBuffer)->meta_data()->setInt64(kKeyTime, frameTime);
+ (*newBuffer)->meta_data().setInt64(kKeyTime, frameTime);
}
-void CameraSourceTimeLapse::fillLastReadBufferCopy(MediaBuffer& sourceBuffer) {
+void CameraSourceTimeLapse::fillLastReadBufferCopy(MediaBufferBase& sourceBuffer) {
ALOGV("fillLastReadBufferCopy");
int64_t frameTime;
- CHECK(sourceBuffer.meta_data()->findInt64(kKeyTime, &frameTime));
+ CHECK(sourceBuffer.meta_data().findInt64(kKeyTime, &frameTime));
createMediaBufferCopy(sourceBuffer, frameTime, &mLastReadBufferCopy);
mLastReadBufferCopy->add_ref();
mLastReadBufferCopy->setObserver(this);
}
status_t CameraSourceTimeLapse::read(
- MediaBuffer **buffer, const ReadOptions *options) {
+ MediaBufferBase **buffer, const ReadOptions *options) {
ALOGV("read");
if (mLastReadBufferCopy == NULL) {
mLastReadStatus = CameraSource::read(buffer, options);
diff --git a/media/libstagefright/Codec2InfoBuilder.cpp b/media/libstagefright/Codec2InfoBuilder.cpp
new file mode 100644
index 0000000..ee7258d
--- /dev/null
+++ b/media/libstagefright/Codec2InfoBuilder.cpp
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "Codec2InfoBuilder"
+#include <log/log.h>
+
+#include <C2Component.h>
+#include <C2PlatformSupport.h>
+#include <C2V4l2Support.h>
+
+#include <cutils/properties.h>
+#include <media/stagefright/foundation/MediaDefs.h>
+#include <media/stagefright/Codec2InfoBuilder.h>
+
+namespace android {
+
+using ConstTraitsPtr = std::shared_ptr<const C2Component::Traits>;
+
+status_t Codec2InfoBuilder::buildMediaCodecList(MediaCodecListWriter* writer) {
+ // Obtain C2ComponentStore
+ std::shared_ptr<C2ComponentStore> store = GetCodec2PlatformComponentStore();
+ if (store == nullptr) {
+ ALOGE("Cannot find a component store.");
+ return NO_INIT;
+ }
+
+ std::vector<ConstTraitsPtr> traits = store->listComponents();
+
+ if (property_get_bool("debug.stagefright.ccodec_v4l2", false)) {
+ std::shared_ptr<C2ComponentStore> v4l2Store = GetCodec2VDAComponentStore();
+ if (v4l2Store == nullptr) {
+ ALOGD("Cannot find a V4L2 component store.");
+ // non-fatal.
+ } else {
+ std::vector<ConstTraitsPtr> v4l2Traits = v4l2Store->listComponents();
+ traits.insert(traits.end(), v4l2Traits.begin(), v4l2Traits.end());
+ }
+ }
+
+ for (const ConstTraitsPtr &trait : traits) {
+ std::unique_ptr<MediaCodecInfoWriter> codecInfo = writer->addMediaCodecInfo();
+ codecInfo->setName(trait->name.c_str());
+ codecInfo->setOwner("dummy");
+ // TODO: get this from trait->kind
+ codecInfo->setEncoder(trait->name.find("encoder") != std::string::npos);
+ codecInfo->setRank(trait->rank);
+ (void)codecInfo->addMime(trait->mediaType.c_str());
+ }
+ return OK;
+}
+
+} // namespace android
diff --git a/media/libstagefright/FrameDecoder.cpp b/media/libstagefright/FrameDecoder.cpp
index b529940..9748a8b 100644
--- a/media/libstagefright/FrameDecoder.cpp
+++ b/media/libstagefright/FrameDecoder.cpp
@@ -230,7 +230,7 @@
}
codecBuffer = inputBuffers[inputIndex];
- MediaBuffer *mediaBuffer = NULL;
+ MediaBufferBase *mediaBuffer = NULL;
err = mSource->read(&mediaBuffer, &options);
options.clearSeekTo();
@@ -251,7 +251,7 @@
} else {
codecBuffer->setRange(0, mediaBuffer->range_length());
- CHECK(mediaBuffer->meta_data()->findInt64(kKeyTime, &ptsUs));
+ CHECK(mediaBuffer->meta_data().findInt64(kKeyTime, &ptsUs));
memcpy(codecBuffer->data(),
(const uint8_t*)mediaBuffer->data() + mediaBuffer->range_offset(),
mediaBuffer->range_length());
@@ -387,12 +387,12 @@
status_t VideoFrameDecoder::onInputReceived(
const sp<MediaCodecBuffer> &codecBuffer,
- const sp<MetaData> &sampleMeta, bool firstSample, uint32_t *flags) {
+ MetaDataBase &sampleMeta, bool firstSample, uint32_t *flags) {
bool isSeekingClosest = (mSeekMode == MediaSource::ReadOptions::SEEK_CLOSEST)
|| (mSeekMode == MediaSource::ReadOptions::SEEK_FRAME_INDEX);
if (firstSample && isSeekingClosest) {
- sampleMeta->findInt64(kKeyTargetTime, &mTargetTimeUs);
+ sampleMeta.findInt64(kKeyTargetTime, &mTargetTimeUs);
ALOGV("Seeking closest: targetTimeUs=%lld", (long long)mTargetTimeUs);
}
diff --git a/media/libstagefright/InterfaceUtils.cpp b/media/libstagefright/InterfaceUtils.cpp
index 70fbbd7..56c5908 100644
--- a/media/libstagefright/InterfaceUtils.cpp
+++ b/media/libstagefright/InterfaceUtils.cpp
@@ -56,7 +56,7 @@
sp<IMediaSource> CreateIMediaSourceFromMediaSourceBase(
const sp<RemoteMediaExtractor> &extractor,
- MediaSourceBase *source, const sp<RefBase> &plugin) {
+ MediaTrack *source, const sp<RefBase> &plugin) {
if (source == nullptr) {
return nullptr;
}
diff --git a/media/libstagefright/JPEGSource.cpp b/media/libstagefright/JPEGSource.cpp
index ee3aedb..10eb2d2 100644
--- a/media/libstagefright/JPEGSource.cpp
+++ b/media/libstagefright/JPEGSource.cpp
@@ -21,6 +21,7 @@
#include <media/DataSource.h>
#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/JPEGSource.h>
+#include <media/stagefright/MediaBuffer.h>
#include <media/stagefright/MediaBufferGroup.h>
#include <media/stagefright/MediaDefs.h>
#include <media/stagefright/MediaErrors.h>
@@ -108,7 +109,7 @@
}
status_t JPEGSource::read(
- MediaBuffer **out, const ReadOptions *options) {
+ MediaBufferBase **out, const ReadOptions *options) {
*out = NULL;
int64_t seekTimeUs;
@@ -117,7 +118,7 @@
return UNKNOWN_ERROR;
}
- MediaBuffer *buffer;
+ MediaBufferBase *buffer;
mGroup->acquire_buffer(&buffer);
ssize_t n = mSource->readAt(mOffset, buffer->data(), mSize - mOffset);
diff --git a/media/libstagefright/MPEG2TSWriter.cpp b/media/libstagefright/MPEG2TSWriter.cpp
index 4c85b0d..770535c 100644
--- a/media/libstagefright/MPEG2TSWriter.cpp
+++ b/media/libstagefright/MPEG2TSWriter.cpp
@@ -85,8 +85,8 @@
void extractCodecSpecificData();
- void appendAACFrames(MediaBuffer *buffer);
- void appendAVCFrame(MediaBuffer *buffer);
+ void appendAACFrames(MediaBufferBase *buffer);
+ void appendAVCFrame(MediaBufferBase *buffer);
DISALLOW_EVIL_CONSTRUCTORS(SourceInfo);
};
@@ -249,7 +249,7 @@
notify->post();
}
-void MPEG2TSWriter::SourceInfo::appendAVCFrame(MediaBuffer *buffer) {
+void MPEG2TSWriter::SourceInfo::appendAVCFrame(MediaBufferBase *buffer) {
sp<AMessage> notify = mNotify->dup();
notify->setInt32("what", kNotifyBuffer);
@@ -264,11 +264,11 @@
buffer->range_length());
int64_t timeUs;
- CHECK(buffer->meta_data()->findInt64(kKeyTime, &timeUs));
+ CHECK(buffer->meta_data().findInt64(kKeyTime, &timeUs));
mBuffer->meta()->setInt64("timeUs", timeUs);
int32_t isSync;
- if (buffer->meta_data()->findInt32(kKeyIsSyncFrame, &isSync)
+ if (buffer->meta_data().findInt32(kKeyIsSyncFrame, &isSync)
&& isSync != 0) {
mBuffer->meta()->setInt32("isSync", true);
}
@@ -279,7 +279,7 @@
notify->post();
}
-void MPEG2TSWriter::SourceInfo::appendAACFrames(MediaBuffer *buffer) {
+void MPEG2TSWriter::SourceInfo::appendAACFrames(MediaBufferBase *buffer) {
sp<AMessage> notify = mNotify->dup();
notify->setInt32("what", kNotifyBuffer);
@@ -288,7 +288,7 @@
}
int64_t timeUs;
- CHECK(buffer->meta_data()->findInt64(kKeyTime, &timeUs));
+ CHECK(buffer->meta_data().findInt64(kKeyTime, &timeUs));
mBuffer->meta()->setInt64("timeUs", timeUs);
mBuffer->meta()->setInt32("isSync", true);
@@ -368,7 +368,7 @@
case kWhatRead:
{
- MediaBuffer *buffer;
+ MediaBufferBase *buffer;
status_t err = mSource->read(&buffer);
if (err != OK && err != INFO_FORMAT_CHANGED) {
diff --git a/media/libstagefright/MPEG4Writer.cpp b/media/libstagefright/MPEG4Writer.cpp
index 8db00f0..cfbbcb2 100644
--- a/media/libstagefright/MPEG4Writer.cpp
+++ b/media/libstagefright/MPEG4Writer.cpp
@@ -2809,7 +2809,7 @@
sp<MetaData> meta_data;
status_t err = OK;
- MediaBuffer *buffer;
+ MediaBufferBase *buffer;
const char *trackName = getTrackType();
while (!mDone && (err = mSource->read(&buffer)) == OK) {
if (buffer->range_length() == 0) {
@@ -2831,7 +2831,7 @@
++count;
int32_t isCodecConfig;
- if (buffer->meta_data()->findInt32(kKeyIsCodecConfig, &isCodecConfig)
+ if (buffer->meta_data().findInt32(kKeyIsCodecConfig, &isCodecConfig)
&& isCodecConfig) {
// if config format (at track addition) already had CSD, keep that
// UNLESS we have not received any frames yet.
@@ -2890,7 +2890,7 @@
memcpy(copy->data(), (uint8_t *)buffer->data() + buffer->range_offset(),
buffer->range_length());
copy->set_range(0, buffer->range_length());
- meta_data = new MetaData(*buffer->meta_data().get());
+ meta_data = new MetaData(buffer->meta_data());
buffer->release();
buffer = NULL;
diff --git a/media/libstagefright/MediaAdapter.cpp b/media/libstagefright/MediaAdapter.cpp
index 74eb1ff..f1b6e8c 100644
--- a/media/libstagefright/MediaAdapter.cpp
+++ b/media/libstagefright/MediaAdapter.cpp
@@ -72,7 +72,7 @@
return mOutputFormat;
}
-void MediaAdapter::signalBufferReturned(MediaBuffer *buffer) {
+void MediaAdapter::signalBufferReturned(MediaBufferBase *buffer) {
Mutex::Autolock autoLock(mAdapterLock);
CHECK(buffer != NULL);
buffer->setObserver(0);
@@ -82,7 +82,7 @@
}
status_t MediaAdapter::read(
- MediaBuffer **buffer, const ReadOptions * /* options */) {
+ MediaBufferBase **buffer, const ReadOptions * /* options */) {
Mutex::Autolock autoLock(mAdapterLock);
if (!mStarted) {
ALOGV("Read before even started!");
diff --git a/media/libstagefright/MediaCodec.cpp b/media/libstagefright/MediaCodec.cpp
index 2b33708..7d5c63a 100644
--- a/media/libstagefright/MediaCodec.cpp
+++ b/media/libstagefright/MediaCodec.cpp
@@ -421,13 +421,33 @@
sp<MediaCodec> MediaCodec::CreateByType(
const sp<ALooper> &looper, const AString &mime, bool encoder, status_t *err, pid_t pid,
uid_t uid) {
- sp<MediaCodec> codec = new MediaCodec(looper, pid, uid);
+ Vector<AString> matchingCodecs;
+ Vector<AString> owners;
- const status_t ret = codec->init(mime, true /* nameIsType */, encoder);
+ MediaCodecList::findMatchingCodecs(
+ mime.c_str(),
+ encoder,
+ 0,
+ &matchingCodecs,
+ &owners);
+
if (err != NULL) {
- *err = ret;
+ *err = NAME_NOT_FOUND;
}
- return ret == OK ? codec : NULL; // NULL deallocates codec.
+ for (size_t i = 0; i < matchingCodecs.size(); ++i) {
+ sp<MediaCodec> codec = new MediaCodec(looper, pid, uid);
+ AString componentName = matchingCodecs[i];
+ status_t ret = codec->init(componentName);
+ if (err != NULL) {
+ *err = ret;
+ }
+ if (ret == OK) {
+ return codec;
+ }
+ ALOGD("Allocating component '%s' failed (%d), try next one.",
+ componentName.c_str(), ret);
+ }
+ return NULL;
}
// static
@@ -435,7 +455,7 @@
const sp<ALooper> &looper, const AString &name, status_t *err, pid_t pid, uid_t uid) {
sp<MediaCodec> codec = new MediaCodec(looper, pid, uid);
- const status_t ret = codec->init(name, false /* nameIsType */, false /* encoder */);
+ const status_t ret = codec->init(name);
if (err != NULL) {
*err = ret;
}
@@ -553,11 +573,11 @@
}
//static
-sp<CodecBase> MediaCodec::GetCodecBase(const AString &name, bool nameIsType) {
+sp<CodecBase> MediaCodec::GetCodecBase(const AString &name) {
static bool ccodecEnabled = property_get_bool("debug.stagefright.ccodec", false);
- if (ccodecEnabled && !nameIsType && name.startsWithIgnoreCase("c2.")) {
+ if (ccodecEnabled && name.startsWithIgnoreCase("c2.")) {
return new CCodec;
- } else if (nameIsType || name.startsWithIgnoreCase("omx.")) {
+ } else if (name.startsWithIgnoreCase("omx.")) {
// at this time only ACodec specifies a mime type.
return new ACodec;
} else if (name.startsWithIgnoreCase("android.filter.")) {
@@ -567,48 +587,42 @@
}
}
-status_t MediaCodec::init(const AString &name, bool nameIsType, bool encoder) {
+status_t MediaCodec::init(const AString &name) {
mResourceManagerService->init();
// save init parameters for reset
mInitName = name;
- mInitNameIsType = nameIsType;
- mInitIsEncoder = encoder;
// Current video decoders do not return from OMX_FillThisBuffer
// quickly, violating the OpenMAX specs, until that is remedied
// we need to invest in an extra looper to free the main event
// queue.
- mCodec = GetCodecBase(name, nameIsType);
+ mCodec = GetCodecBase(name);
if (mCodec == NULL) {
return NAME_NOT_FOUND;
}
bool secureCodec = false;
- if (nameIsType && !strncasecmp(name.c_str(), "video/", 6)) {
- mIsVideo = true;
- } else {
- AString tmp = name;
- if (tmp.endsWith(".secure")) {
- secureCodec = true;
- tmp.erase(tmp.size() - 7, 7);
- }
- const sp<IMediaCodecList> mcl = MediaCodecList::getInstance();
- if (mcl == NULL) {
- mCodec = NULL; // remove the codec.
- return NO_INIT; // if called from Java should raise IOException
- }
- ssize_t codecIdx = mcl->findCodecByName(tmp.c_str());
- if (codecIdx >= 0) {
- const sp<MediaCodecInfo> info = mcl->getCodecInfo(codecIdx);
- Vector<AString> mimes;
- info->getSupportedMimes(&mimes);
- for (size_t i = 0; i < mimes.size(); i++) {
- if (mimes[i].startsWith("video/")) {
- mIsVideo = true;
- break;
- }
+ AString tmp = name;
+ if (tmp.endsWith(".secure")) {
+ secureCodec = true;
+ tmp.erase(tmp.size() - 7, 7);
+ }
+ const sp<IMediaCodecList> mcl = MediaCodecList::getInstance();
+ if (mcl == NULL) {
+ mCodec = NULL; // remove the codec.
+ return NO_INIT; // if called from Java should raise IOException
+ }
+ ssize_t codecIdx = mcl->findCodecByName(tmp.c_str());
+ if (codecIdx >= 0) {
+ const sp<MediaCodecInfo> info = mcl->getCodecInfo(codecIdx);
+ Vector<AString> mimes;
+ info->getSupportedMimes(&mimes);
+ for (size_t i = 0; i < mimes.size(); i++) {
+ if (mimes[i].startsWith("video/")) {
+ mIsVideo = true;
+ break;
}
}
}
@@ -638,22 +652,10 @@
sp<AMessage> msg = new AMessage(kWhatInit, this);
msg->setString("name", name);
- msg->setInt32("nameIsType", nameIsType);
-
- if (nameIsType) {
- msg->setInt32("encoder", encoder);
- }
if (mAnalyticsItem != NULL) {
- if (nameIsType) {
- // name is the mime type
- mAnalyticsItem->setCString(kCodecMime, name.c_str());
- } else {
- mAnalyticsItem->setCString(kCodecCodec, name.c_str());
- }
+ mAnalyticsItem->setCString(kCodecCodec, name.c_str());
mAnalyticsItem->setCString(kCodecMode, mIsVideo ? kCodecModeVideo : kCodecModeAudio);
- if (nameIsType)
- mAnalyticsItem->setInt32(kCodecEncoder, encoder);
}
status_t err;
@@ -719,6 +721,7 @@
if (format->findInt32("level", &level)) {
mAnalyticsItem->setInt32(kCodecLevel, level);
}
+ mAnalyticsItem->setInt32(kCodecEncoder, (flags & CONFIGURE_FLAG_ENCODE) ? 1 : 0);
}
if (mIsVideo) {
@@ -743,8 +746,7 @@
}
// Prevent possible integer overflow in downstream code.
- if (mInitIsEncoder
- && (uint64_t)mVideoWidth * mVideoHeight > (uint64_t)INT32_MAX / 4) {
+ if ((uint64_t)mVideoWidth * mVideoHeight > (uint64_t)INT32_MAX / 4) {
ALOGE("buffer size is too big, width=%d, height=%d", mVideoWidth, mVideoHeight);
return BAD_VALUE;
}
@@ -1023,7 +1025,7 @@
mHaveInputSurface = false;
if (err == OK) {
- err = init(mInitName, mInitNameIsType, mInitIsEncoder);
+ err = init(mInitName);
}
return err;
}
@@ -1973,21 +1975,8 @@
AString name;
CHECK(msg->findString("name", &name));
- int32_t nameIsType;
- int32_t encoder = false;
- CHECK(msg->findInt32("nameIsType", &nameIsType));
- if (nameIsType) {
- CHECK(msg->findInt32("encoder", &encoder));
- }
-
sp<AMessage> format = new AMessage;
-
- if (nameIsType) {
- format->setString("mime", name.c_str());
- format->setInt32("encoder", encoder);
- } else {
- format->setString("componentName", name.c_str());
- }
+ format->setString("componentName", name.c_str());
mCodec->initiateAllocateComponent(format);
break;
diff --git a/media/libstagefright/MediaCodecList.cpp b/media/libstagefright/MediaCodecList.cpp
index 54265a4..f595646 100644
--- a/media/libstagefright/MediaCodecList.cpp
+++ b/media/libstagefright/MediaCodecList.cpp
@@ -29,6 +29,7 @@
#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/foundation/AMessage.h>
#include <media/stagefright/foundation/MediaDefs.h>
+#include <media/stagefright/Codec2InfoBuilder.h>
#include <media/stagefright/MediaCodecList.h>
#include <media/stagefright/MediaErrors.h>
#include <media/stagefright/OmxInfoBuilder.h>
@@ -77,6 +78,15 @@
}
OmxInfoBuilder sOmxInfoBuilder;
+Codec2InfoBuilder sCodec2InfoBuilder;
+
+std::initializer_list<MediaCodecListBuilderBase *> GetBuilders() {
+ if (property_get_bool("debug.stagefright.ccodec", false)) {
+ return {&sOmxInfoBuilder, &sCodec2InfoBuilder};
+ } else {
+ return {&sOmxInfoBuilder};
+ }
+}
} // unnamed namespace
@@ -88,7 +98,7 @@
ALOGV("Enter profilerThreadWrapper.");
remove(kProfilingResults); // remove previous result so that it won't be loaded to
// the new MediaCodecList
- sp<MediaCodecList> codecList(new MediaCodecList(&sOmxInfoBuilder));
+ sp<MediaCodecList> codecList(new MediaCodecList(GetBuilders()));
if (codecList->initCheck() != OK) {
ALOGW("Failed to create a new MediaCodecList, skipping codec profiling.");
return nullptr;
@@ -98,7 +108,7 @@
ALOGV("Codec profiling started.");
profileCodecs(infos, kProfilingResults);
ALOGV("Codec profiling completed.");
- codecList = new MediaCodecList(&sOmxInfoBuilder);
+ codecList = new MediaCodecList(GetBuilders());
if (codecList->initCheck() != OK) {
ALOGW("Failed to parse profiling results.");
return nullptr;
@@ -116,7 +126,7 @@
Mutex::Autolock autoLock(sInitMutex);
if (sCodecList == nullptr) {
- MediaCodecList *codecList = new MediaCodecList(&sOmxInfoBuilder);
+ MediaCodecList *codecList = new MediaCodecList(GetBuilders());
if (codecList->initCheck() == OK) {
sCodecList = codecList;
@@ -169,11 +179,28 @@
return sRemoteList;
}
-MediaCodecList::MediaCodecList(MediaCodecListBuilderBase* builder) {
+MediaCodecList::MediaCodecList(std::initializer_list<MediaCodecListBuilderBase*> builders) {
mGlobalSettings = new AMessage();
mCodecInfos.clear();
MediaCodecListWriter writer(this);
- mInitCheck = builder->buildMediaCodecList(&writer);
+ for (MediaCodecListBuilderBase *builder : builders) {
+ mInitCheck = builder->buildMediaCodecList(&writer);
+ if (mInitCheck != OK) {
+ break;
+ }
+ }
+ std::stable_sort(
+ mCodecInfos.begin(),
+ mCodecInfos.end(),
+ [](const sp<MediaCodecInfo> &info1, const sp<MediaCodecInfo> &info2) {
+ if (info2 == nullptr) {
+ return false;
+ } else if (info1 == nullptr) {
+ return true;
+ } else {
+ return info1->rank() < info2->rank();
+ }
+ });
}
MediaCodecList::~MediaCodecList() {
diff --git a/media/libstagefright/MediaCodecSource.cpp b/media/libstagefright/MediaCodecSource.cpp
index 04d83af..20881a4 100644
--- a/media/libstagefright/MediaCodecSource.cpp
+++ b/media/libstagefright/MediaCodecSource.cpp
@@ -59,7 +59,7 @@
void pause();
void resume();
status_t setStopTimeUs(int64_t stopTimeUs);
- bool readBuffer(MediaBuffer **buffer);
+ bool readBuffer(MediaBufferBase **buffer);
protected:
virtual void onMessageReceived(const sp<AMessage> &msg);
@@ -86,14 +86,14 @@
int64_t mReadPendingSince;
bool mPaused;
bool mPulling;
- Vector<MediaBuffer *> mReadBuffers;
+ Vector<MediaBufferBase *> mReadBuffers;
void flush();
// if queue is empty, return false and set *|buffer| to NULL . Otherwise, pop
// buffer from front of the queue, place it into *|buffer| and return true.
- bool readBuffer(MediaBuffer **buffer);
+ bool readBuffer(MediaBufferBase **buffer);
// add a buffer to the back of the queue
- void pushBuffer(MediaBuffer *mbuf);
+ void pushBuffer(MediaBufferBase *mbuf);
};
Mutexed<Queue> mQueue;
@@ -123,11 +123,11 @@
mLooper->stop();
}
-void MediaCodecSource::Puller::Queue::pushBuffer(MediaBuffer *mbuf) {
+void MediaCodecSource::Puller::Queue::pushBuffer(MediaBufferBase *mbuf) {
mReadBuffers.push_back(mbuf);
}
-bool MediaCodecSource::Puller::Queue::readBuffer(MediaBuffer **mbuf) {
+bool MediaCodecSource::Puller::Queue::readBuffer(MediaBufferBase **mbuf) {
if (mReadBuffers.empty()) {
*mbuf = NULL;
return false;
@@ -138,14 +138,14 @@
}
void MediaCodecSource::Puller::Queue::flush() {
- MediaBuffer *mbuf;
+ MediaBufferBase *mbuf;
while (readBuffer(&mbuf)) {
// there are no null buffers in the queue
mbuf->release();
}
}
-bool MediaCodecSource::Puller::readBuffer(MediaBuffer **mbuf) {
+bool MediaCodecSource::Puller::readBuffer(MediaBufferBase **mbuf) {
Mutexed<Queue>::Locked queue(mQueue);
return queue->readBuffer(mbuf);
}
@@ -298,7 +298,7 @@
}
queue.unlock();
- MediaBuffer *mbuf = NULL;
+ MediaBufferBase *mbuf = NULL;
status_t err = mSource->read(&mbuf);
queue.lock();
@@ -413,7 +413,7 @@
}
status_t MediaCodecSource::read(
- MediaBuffer** buffer, const ReadOptions* /* options */) {
+ MediaBufferBase** buffer, const ReadOptions* /* options */) {
Mutexed<Output>::Locked output(mOutput);
*buffer = NULL;
@@ -428,7 +428,7 @@
return output->mErrorCode;
}
-void MediaCodecSource::signalBufferReturned(MediaBuffer *buffer) {
+void MediaCodecSource::signalBufferReturned(MediaBufferBase *buffer) {
buffer->setObserver(0);
buffer->release();
}
@@ -636,7 +636,7 @@
if (!reachedEOS) {
ALOGV("encoder (%s) reached EOS", mIsVideo ? "video" : "audio");
// release all unread media buffers
- for (List<MediaBuffer*>::iterator it = output->mBufferQueue.begin();
+ for (List<MediaBufferBase*>::iterator it = output->mBufferQueue.begin();
it != output->mBufferQueue.end(); it++) {
(*it)->release();
}
@@ -682,7 +682,7 @@
}
status_t MediaCodecSource::feedEncoderInputBuffers() {
- MediaBuffer* mbuf = NULL;
+ MediaBufferBase* mbuf = NULL;
while (!mAvailEncoderInputIndices.empty() && mPuller->readBuffer(&mbuf)) {
size_t bufferIndex = *mAvailEncoderInputIndices.begin();
mAvailEncoderInputIndices.erase(mAvailEncoderInputIndices.begin());
@@ -692,7 +692,7 @@
size_t size = 0;
if (mbuf != NULL) {
- CHECK(mbuf->meta_data()->findInt64(kKeyTime, &timeUs));
+ CHECK(mbuf->meta_data().findInt64(kKeyTime, &timeUs));
if (mFirstSampleSystemTimeUs < 0ll) {
mFirstSampleSystemTimeUs = systemTime() / 1000;
if (mPausePending) {
@@ -715,7 +715,7 @@
mFirstSampleTimeUs = timeUs;
}
int64_t driftTimeUs = 0;
- if (mbuf->meta_data()->findInt64(kKeyDriftTime, &driftTimeUs)
+ if (mbuf->meta_data().findInt64(kKeyDriftTime, &driftTimeUs)
&& driftTimeUs) {
driftTimeUs = timeUs - mFirstSampleTimeUs - driftTimeUs;
}
@@ -906,7 +906,7 @@
break;
}
- MediaBuffer *mbuf = new MediaBuffer(outbuf->size());
+ MediaBufferBase *mbuf = new MediaBuffer(outbuf->size());
mbuf->setObserver(this);
mbuf->add_ref();
@@ -937,7 +937,7 @@
decodingTimeUs = *(mDecodingTimeQueue.begin());
mDecodingTimeQueue.erase(mDecodingTimeQueue.begin());
}
- mbuf->meta_data()->setInt64(kKeyDecodingTime, decodingTimeUs);
+ mbuf->meta_data().setInt64(kKeyDecodingTime, decodingTimeUs);
ALOGV("[video] time %" PRId64 " us (%.2f secs), dts/pts diff %" PRId64,
timeUs, timeUs / 1E6, decodingTimeUs - timeUs);
@@ -947,18 +947,18 @@
CHECK(!mDriftTimeQueue.empty());
driftTimeUs = *(mDriftTimeQueue.begin());
mDriftTimeQueue.erase(mDriftTimeQueue.begin());
- mbuf->meta_data()->setInt64(kKeyDriftTime, driftTimeUs);
+ mbuf->meta_data().setInt64(kKeyDriftTime, driftTimeUs);
#endif // DEBUG_DRIFT_TIME
ALOGV("[audio] time %" PRId64 " us (%.2f secs), drift %" PRId64,
timeUs, timeUs / 1E6, driftTimeUs);
}
- mbuf->meta_data()->setInt64(kKeyTime, timeUs);
+ mbuf->meta_data().setInt64(kKeyTime, timeUs);
} else {
- mbuf->meta_data()->setInt64(kKeyTime, 0ll);
- mbuf->meta_data()->setInt32(kKeyIsCodecConfig, true);
+ mbuf->meta_data().setInt64(kKeyTime, 0ll);
+ mbuf->meta_data().setInt32(kKeyIsCodecConfig, true);
}
if (flags & MediaCodec::BUFFER_FLAG_SYNCFRAME) {
- mbuf->meta_data()->setInt32(kKeyIsSyncFrame, true);
+ mbuf->meta_data().setInt32(kKeyIsSyncFrame, true);
}
memcpy(mbuf->data(), outbuf->data(), outbuf->size());
diff --git a/media/libstagefright/MediaMuxer.cpp b/media/libstagefright/MediaMuxer.cpp
index 62daac8..23e543d 100644
--- a/media/libstagefright/MediaMuxer.cpp
+++ b/media/libstagefright/MediaMuxer.cpp
@@ -181,13 +181,13 @@
mediaBuffer->add_ref(); // Released in MediaAdapter::signalBufferReturned().
mediaBuffer->set_range(buffer->offset(), buffer->size());
- sp<MetaData> sampleMetaData = mediaBuffer->meta_data();
- sampleMetaData->setInt64(kKeyTime, timeUs);
+ MetaDataBase &sampleMetaData = mediaBuffer->meta_data();
+ sampleMetaData.setInt64(kKeyTime, timeUs);
// Just set the kKeyDecodingTime as the presentation time for now.
- sampleMetaData->setInt64(kKeyDecodingTime, timeUs);
+ sampleMetaData.setInt64(kKeyDecodingTime, timeUs);
if (flags & MediaCodec::BUFFER_FLAG_SYNCFRAME) {
- sampleMetaData->setInt32(kKeyIsSyncFrame, true);
+ sampleMetaData.setInt32(kKeyIsSyncFrame, true);
}
sp<MediaAdapter> currentTrack = mTrackList[trackIndex];
diff --git a/media/libstagefright/MetaDataUtils.cpp b/media/libstagefright/MetaDataUtils.cpp
index fd51a2c..af8f539 100644
--- a/media/libstagefright/MetaDataUtils.cpp
+++ b/media/libstagefright/MetaDataUtils.cpp
@@ -24,46 +24,81 @@
namespace android {
-sp<MetaData> MakeAVCCodecSpecificData(const sp<ABuffer> &accessUnit) {
+bool MakeAVCCodecSpecificData(MetaDataBase &meta, const sp<ABuffer> &accessUnit) {
int32_t width;
int32_t height;
int32_t sarWidth;
int32_t sarHeight;
sp<ABuffer> csd = MakeAVCCodecSpecificData(accessUnit, &width, &height, &sarWidth, &sarHeight);
if (csd == nullptr) {
- return nullptr;
+ return false;
}
- sp<MetaData> meta = new MetaData;
- meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_AVC);
+ meta.setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_AVC);
- meta->setData(kKeyAVCC, kTypeAVCC, csd->data(), csd->size());
- meta->setInt32(kKeyWidth, width);
- meta->setInt32(kKeyHeight, height);
+ meta.setData(kKeyAVCC, kTypeAVCC, csd->data(), csd->size());
+ meta.setInt32(kKeyWidth, width);
+ meta.setInt32(kKeyHeight, height);
if (sarWidth > 0 && sarHeight > 0) {
- meta->setInt32(kKeySARWidth, sarWidth);
- meta->setInt32(kKeySARHeight, sarHeight);
+ meta.setInt32(kKeySARWidth, sarWidth);
+ meta.setInt32(kKeySARHeight, sarHeight);
}
- return meta;
+ return true;
}
-sp<MetaData> MakeAACCodecSpecificData(
+bool MakeAACCodecSpecificData(
+ MetaDataBase &meta,
unsigned profile, unsigned sampling_freq_index,
unsigned channel_configuration) {
+ if(sampling_freq_index > 11u) {
+ return false;
+ }
int32_t sampleRate;
int32_t channelCount;
- sp<ABuffer> csd = MakeAACCodecSpecificData(profile, sampling_freq_index,
- channel_configuration, &sampleRate, &channelCount);
- if (csd == nullptr) {
- return nullptr;
- }
- sp<MetaData> meta = new MetaData;
- meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_AAC);
+ static const int32_t kSamplingFreq[] = {
+ 96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050,
+ 16000, 12000, 11025, 8000
+ };
+ sampleRate = kSamplingFreq[sampling_freq_index];
+ channelCount = channel_configuration;
- meta->setInt32(kKeySampleRate, sampleRate);
- meta->setInt32(kKeyChannelCount, channelCount);
+ static const uint8_t kStaticESDS[] = {
+ 0x03, 22,
+ 0x00, 0x00, // ES_ID
+ 0x00, // streamDependenceFlag, URL_Flag, OCRstreamFlag
- meta->setData(kKeyESDS, 0, csd->data(), csd->size());
- return meta;
+ 0x04, 17,
+ 0x40, // Audio ISO/IEC 14496-3
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+
+ 0x05, 2,
+ // AudioSpecificInfo follows
+
+ // oooo offf fccc c000
+ // o - audioObjectType
+ // f - samplingFreqIndex
+ // c - channelConfig
+ };
+
+ size_t csdSize = sizeof(kStaticESDS) + 2;
+ uint8_t *csd = new uint8_t[csdSize];
+ memcpy(csd, kStaticESDS, sizeof(kStaticESDS));
+
+ csd[sizeof(kStaticESDS)] =
+ ((profile + 1) << 3) | (sampling_freq_index >> 1);
+
+ csd[sizeof(kStaticESDS) + 1] =
+ ((sampling_freq_index << 7) & 0x80) | (channel_configuration << 3);
+
+ meta.setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_AAC);
+
+ meta.setInt32(kKeySampleRate, sampleRate);
+ meta.setInt32(kKeyChannelCount, channelCount);
+
+ meta.setData(kKeyESDS, 0, csd, csdSize);
+ delete [] csd;
+ return true;
}
} // namespace android
diff --git a/media/libstagefright/NuMediaExtractor.cpp b/media/libstagefright/NuMediaExtractor.cpp
index d96f7e0..540cf8c 100644
--- a/media/libstagefright/NuMediaExtractor.cpp
+++ b/media/libstagefright/NuMediaExtractor.cpp
@@ -45,7 +45,7 @@
mSampleTimeUs(-1ll) {
}
-NuMediaExtractor::Sample::Sample(MediaBuffer *buffer, int64_t timeUs)
+NuMediaExtractor::Sample::Sample(MediaBufferBase *buffer, int64_t timeUs)
: mBuffer(buffer),
mSampleTimeUs(timeUs) {
}
@@ -488,12 +488,12 @@
}
status_t err = OK;
- Vector<MediaBuffer *> mediaBuffers;
+ Vector<MediaBufferBase *> mediaBuffers;
if (info->mSource->supportReadMultiple()) {
options.setNonBlocking();
err = info->mSource->readMultiple(&mediaBuffers, info->mMaxFetchCount, &options);
} else {
- MediaBuffer *mbuf = NULL;
+ MediaBufferBase *mbuf = NULL;
err = info->mSource->read(&mbuf, &options);
if (err == OK && mbuf != NULL) {
mediaBuffers.push_back(mbuf);
@@ -505,7 +505,7 @@
ALOGW("read on track %zu failed with error %d", info->mTrackIndex, err);
size_t count = mediaBuffers.size();
for (size_t id = 0; id < count; ++id) {
- MediaBuffer *mbuf = mediaBuffers[id];
+ MediaBufferBase *mbuf = mediaBuffers[id];
if (mbuf != NULL) {
mbuf->release();
}
@@ -517,7 +517,7 @@
bool releaseRemaining = false;
for (size_t id = 0; id < count; ++id) {
int64_t timeUs;
- MediaBuffer *mbuf = mediaBuffers[id];
+ MediaBufferBase *mbuf = mediaBuffers[id];
if (mbuf == NULL) {
continue;
}
@@ -525,10 +525,10 @@
mbuf->release();
continue;
}
- if (mbuf->meta_data()->findInt64(kKeyTime, &timeUs)) {
+ if (mbuf->meta_data().findInt64(kKeyTime, &timeUs)) {
info->mSamples.emplace_back(mbuf, timeUs);
} else {
- mbuf->meta_data()->dumpToLog();
+ mbuf->meta_data().dumpToLog();
info->mFinalResult = ERROR_MALFORMED;
mbuf->release();
releaseRemaining = true;
@@ -565,9 +565,10 @@
return OK;
}
-status_t NuMediaExtractor::appendVorbisNumPageSamples(MediaBuffer *mbuf, const sp<ABuffer> &buffer) {
+status_t NuMediaExtractor::appendVorbisNumPageSamples(
+ MediaBufferBase *mbuf, const sp<ABuffer> &buffer) {
int32_t numPageSamples;
- if (!mbuf->meta_data()->findInt32(
+ if (!mbuf->meta_data().findInt32(
kKeyValidSamples, &numPageSamples)) {
numPageSamples = -1;
}
@@ -579,7 +580,7 @@
uint32_t type;
const void *data;
size_t size, size2;
- if (mbuf->meta_data()->findData(kKeyEncryptedSizes, &type, &data, &size)) {
+ if (mbuf->meta_data().findData(kKeyEncryptedSizes, &type, &data, &size)) {
// Signal numPageSamples (a plain int32_t) is appended at the end,
// i.e. sizeof(numPageSamples) plain bytes + 0 encrypted bytes
if (SIZE_MAX - size < sizeof(int32_t)) {
@@ -597,9 +598,9 @@
int32_t zero = 0;
memcpy(adata, data, size);
memcpy(adata + size, &zero, sizeof(zero));
- mbuf->meta_data()->setData(kKeyEncryptedSizes, type, adata, newSize);
+ mbuf->meta_data().setData(kKeyEncryptedSizes, type, adata, newSize);
- if (mbuf->meta_data()->findData(kKeyPlainSizes, &type, &data, &size2)) {
+ if (mbuf->meta_data().findData(kKeyPlainSizes, &type, &data, &size2)) {
if (size2 != size) {
return ERROR_MALFORMED;
}
@@ -612,7 +613,7 @@
// append sizeof(numPageSamples) to plain sizes.
int32_t int32Size = sizeof(numPageSamples);
memcpy(adata + size, &int32Size, sizeof(int32Size));
- mbuf->meta_data()->setData(kKeyPlainSizes, type, adata, newSize);
+ mbuf->meta_data().setData(kKeyPlainSizes, type, adata, newSize);
}
return OK;
@@ -724,7 +725,7 @@
}
TrackInfo *info = &mSelectedTracks.editItemAt(minIndex);
- *sampleMeta = info->mSamples.begin()->mBuffer->meta_data();
+ *sampleMeta = new MetaData(info->mSamples.begin()->mBuffer->meta_data());
return OK;
}
diff --git a/media/libstagefright/OmxInfoBuilder.cpp b/media/libstagefright/OmxInfoBuilder.cpp
index a6ebadd..fe141ab 100644
--- a/media/libstagefright/OmxInfoBuilder.cpp
+++ b/media/libstagefright/OmxInfoBuilder.cpp
@@ -154,22 +154,22 @@
// codec name -> index into swCodecs/hwCodecs
std::map<hidl_string, std::unique_ptr<MediaCodecInfoWriter>>
swCodecName2Info, hwCodecName2Info;
- // owner name -> MediaCodecInfo
- // This map will be used to obtain the correct IOmx service(s) needed for
- // creating IOmxNode instances and querying capabilities.
- std::map<std::string, std::vector<sp<MediaCodecInfo> > >
- owner2CodecInfo;
- for (const auto& role : roles) {
- const auto& typeName = role.type;
+ char rank[PROPERTY_VALUE_MAX];
+ uint32_t defaultRank = 0x100;
+ if (property_get("debug.stagefright.omx_default_rank", rank, nullptr)) {
+ defaultRank = std::strtoul(rank, nullptr, 10);
+ }
+ for (const IOmxStore::RoleInfo& role : roles) {
+ const hidl_string& typeName = role.type;
bool isEncoder = role.isEncoder;
bool preferPlatformNodes = role.preferPlatformNodes;
// If preferPlatformNodes is true, hardware nodes must be added after
// platform (software) nodes. hwCodecs is used to hold hardware nodes
// that need to be added after software nodes for the same role.
std::vector<const IOmxStore::NodeInfo*> hwCodecs;
- for (const auto& node : role.nodes) {
- const auto& nodeName = node.name;
+ for (const IOmxStore::NodeInfo& node : role.nodes) {
+ const hidl_string& nodeName = node.name;
bool isSoftware = hasPrefix(nodeName, "OMX.google");
MediaCodecInfoWriter* info;
if (isSoftware) {
@@ -182,6 +182,7 @@
info->setName(nodeName.c_str());
info->setOwner(node.owner.c_str());
info->setEncoder(isEncoder);
+ info->setRank(defaultRank);
} else {
// The node has been seen before. Simply retrieve the
// existing MediaCodecInfoWriter.
@@ -198,6 +199,7 @@
info->setName(nodeName.c_str());
info->setOwner(node.owner.c_str());
info->setEncoder(isEncoder);
+ info->setRank(defaultRank);
} else {
// If preferPlatformNodes is true, this node must be
// added after all software nodes.
@@ -224,9 +226,9 @@
// added in the loop above, but rather saved in hwCodecs. They are
// going to be added here.
if (preferPlatformNodes) {
- for (const auto& node : hwCodecs) {
+ for (const IOmxStore::NodeInfo *node : hwCodecs) {
MediaCodecInfoWriter* info;
- const auto& nodeName = node->name;
+ const hidl_string& nodeName = node->name;
auto c2i = hwCodecName2Info.find(nodeName);
if (c2i == hwCodecName2Info.end()) {
// Create a new MediaCodecInfo for a new node.
@@ -236,6 +238,7 @@
info->setName(nodeName.c_str());
info->setOwner(node->owner.c_str());
info->setEncoder(isEncoder);
+ info->setRank(defaultRank);
} else {
// The node has been seen before. Simply retrieve the
// existing MediaCodecInfoWriter.
diff --git a/media/libstagefright/RemoteMediaExtractor.cpp b/media/libstagefright/RemoteMediaExtractor.cpp
index 5bb0953..9d2c42b 100644
--- a/media/libstagefright/RemoteMediaExtractor.cpp
+++ b/media/libstagefright/RemoteMediaExtractor.cpp
@@ -57,13 +57,13 @@
// tracks (size_t)
mAnalyticsItem->setInt32(kExtractorTracks, ntracks);
// metadata
- sp<MetaData> pMetaData = extractor->getMetaData();
- if (pMetaData != nullptr) {
- String8 xx = pMetaData->toString();
+ MetaDataBase pMetaData;
+ if (extractor->getMetaData(pMetaData) == OK) {
+ String8 xx = pMetaData.toString();
// 'titl' -- but this verges into PII
// 'mime'
const char *mime = nullptr;
- if (pMetaData->findCString(kKeyMIMEType, &mime)) {
+ if (pMetaData.findCString(kKeyMIMEType, &mime)) {
mAnalyticsItem->setCString(kExtractorMime, mime);
}
// what else is interesting and not already available?
@@ -95,17 +95,25 @@
}
sp<IMediaSource> RemoteMediaExtractor::getTrack(size_t index) {
- MediaSourceBase *source = mExtractor->getTrack(index);
+ MediaTrack *source = mExtractor->getTrack(index);
return (source == nullptr)
? nullptr : CreateIMediaSourceFromMediaSourceBase(this, source, mExtractorPlugin);
}
sp<MetaData> RemoteMediaExtractor::getTrackMetaData(size_t index, uint32_t flags) {
- return mExtractor->getTrackMetaData(index, flags);
+ sp<MetaData> meta = new MetaData();
+ if (mExtractor->getTrackMetaData(*meta.get(), index, flags) == OK) {
+ return meta;
+ }
+ return nullptr;
}
sp<MetaData> RemoteMediaExtractor::getMetaData() {
- return mExtractor->getMetaData();
+ sp<MetaData> meta = new MetaData();
+ if (mExtractor->getMetaData(*meta.get()) == OK) {
+ return meta;
+ }
+ return nullptr;
}
status_t RemoteMediaExtractor::getMetrics(Parcel *reply) {
@@ -121,14 +129,6 @@
return mExtractor->flags();
}
-char* RemoteMediaExtractor::getDrmTrackInfo(size_t trackID, int * len) {
- return mExtractor->getDrmTrackInfo(trackID, len);
-}
-
-void RemoteMediaExtractor::setUID(uid_t uid) {
- return mExtractor->setUID(uid);
-}
-
status_t RemoteMediaExtractor::setMediaCas(const HInterfaceToken &casToken) {
return mExtractor->setMediaCas((uint8_t*)casToken.data(), casToken.size());
}
diff --git a/media/libstagefright/RemoteMediaSource.cpp b/media/libstagefright/RemoteMediaSource.cpp
index 6b48ce8..d07afec 100644
--- a/media/libstagefright/RemoteMediaSource.cpp
+++ b/media/libstagefright/RemoteMediaSource.cpp
@@ -22,7 +22,7 @@
RemoteMediaSource::RemoteMediaSource(
const sp<RemoteMediaExtractor> &extractor,
- MediaSourceBase *source,
+ MediaTrack *source,
const sp<RefBase> &plugin)
: mExtractor(extractor),
mSource(source),
@@ -42,19 +42,24 @@
}
sp<MetaData> RemoteMediaSource::getFormat() {
- return mSource->getFormat();
+ sp<MetaData> meta = new MetaData();
+ if (mSource->getFormat(*meta.get()) == OK) {
+ return meta;
+ }
+ return nullptr;
}
-status_t RemoteMediaSource::read(MediaBuffer **buffer, const MediaSource::ReadOptions *options) {
+status_t RemoteMediaSource::read(
+ MediaBufferBase **buffer, const MediaSource::ReadOptions *options) {
return mSource->read(buffer, reinterpret_cast<const MediaSource::ReadOptions*>(options));
}
status_t RemoteMediaSource::pause() {
- return mSource->pause();
+ return ERROR_UNSUPPORTED;
}
-status_t RemoteMediaSource::setStopTimeUs(int64_t stopTimeUs) {
- return mSource->setStopTimeUs(stopTimeUs);
+status_t RemoteMediaSource::setStopTimeUs(int64_t /* stopTimeUs */) {
+ return ERROR_UNSUPPORTED;
}
////////////////////////////////////////////////////////////////////////////////
@@ -62,7 +67,7 @@
// static
sp<IMediaSource> RemoteMediaSource::wrap(
const sp<RemoteMediaExtractor> &extractor,
- MediaSourceBase *source, const sp<RefBase> &plugin) {
+ MediaTrack *source, const sp<RefBase> &plugin) {
if (source == nullptr) {
return nullptr;
}
diff --git a/media/libstagefright/SimpleDecodingSource.cpp b/media/libstagefright/SimpleDecodingSource.cpp
index 9b2fb4f..404c537 100644
--- a/media/libstagefright/SimpleDecodingSource.cpp
+++ b/media/libstagefright/SimpleDecodingSource.cpp
@@ -200,7 +200,7 @@
}
status_t SimpleDecodingSource::read(
- MediaBuffer **buffer, const ReadOptions *options) {
+ MediaBufferBase **buffer, const ReadOptions *options) {
*buffer = NULL;
Mutexed<ProtectedState>::Locked me(mProtectedState);
@@ -221,7 +221,7 @@
}
status_t SimpleDecodingSource::doRead(
- Mutexed<ProtectedState>::Locked &me, MediaBuffer **buffer, const ReadOptions *options) {
+ Mutexed<ProtectedState>::Locked &me, MediaBufferBase **buffer, const ReadOptions *options) {
// |me| is always locked on entry, but is allowed to be unlocked on exit
CHECK_EQ(me->mState, STARTED);
@@ -267,7 +267,7 @@
return UNKNOWN_ERROR;
}
- MediaBuffer *in_buf;
+ MediaBufferBase *in_buf;
while (true) {
in_buf = NULL;
me.unlock();
@@ -309,7 +309,7 @@
if (in_buf != NULL) {
int64_t timestampUs = 0;
- CHECK(in_buf->meta_data()->findInt64(kKeyTime, ×tampUs));
+ CHECK(in_buf->meta_data().findInt64(kKeyTime, ×tampUs));
if (in_buf->range_length() + (mIsVorbis ? 4 : 0) > in_buffer->capacity()) {
ALOGW("'%s' received %zu input bytes for buffer of size %zu",
mComponentName.c_str(),
@@ -321,7 +321,7 @@
if (mIsVorbis) {
int32_t numPageSamples;
- if (!in_buf->meta_data()->findInt32(kKeyValidSamples, &numPageSamples)) {
+ if (!in_buf->meta_data().findInt32(kKeyValidSamples, &numPageSamples)) {
numPageSamples = -1;
}
memcpy(in_buffer->base() + cpLen, &numPageSamples, sizeof(numPageSamples));
@@ -393,7 +393,7 @@
*buffer = new MediaBuffer(out_size);
CHECK_LE(out_buffer->size(), (*buffer)->size());
memcpy((*buffer)->data(), out_buffer->data(), out_buffer->size());
- (*buffer)->meta_data()->setInt64(kKeyTime, out_pts);
+ (*buffer)->meta_data().setInt64(kKeyTime, out_pts);
mCodec->releaseOutputBuffer(out_ix);
}
return OK;
diff --git a/media/libstagefright/SurfaceMediaSource.cpp b/media/libstagefright/SurfaceMediaSource.cpp
deleted file mode 100644
index d14e86b..0000000
--- a/media/libstagefright/SurfaceMediaSource.cpp
+++ /dev/null
@@ -1,477 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-//#define LOG_NDEBUG 0
-#define LOG_TAG "SurfaceMediaSource"
-
-#include <inttypes.h>
-
-#include <media/stagefright/foundation/ADebug.h>
-#include <media/stagefright/SurfaceMediaSource.h>
-#include <media/stagefright/MediaDefs.h>
-#include <media/stagefright/MetaData.h>
-#include <OMX_IVCommon.h>
-#include <media/hardware/HardwareAPI.h>
-#include <media/hardware/MetadataBufferType.h>
-
-#include <ui/GraphicBuffer.h>
-#include <gui/BufferItem.h>
-#include <gui/ISurfaceComposer.h>
-#include <OMX_Component.h>
-
-#include <utils/Log.h>
-#include <utils/String8.h>
-
-#include <private/gui/ComposerService.h>
-
-namespace android {
-
-SurfaceMediaSource::SurfaceMediaSource(uint32_t bufferWidth, uint32_t bufferHeight) :
- mWidth(bufferWidth),
- mHeight(bufferHeight),
- mCurrentSlot(BufferQueue::INVALID_BUFFER_SLOT),
- mNumPendingBuffers(0),
- mCurrentTimestamp(0),
- mFrameRate(30),
- mStarted(false),
- mNumFramesReceived(0),
- mNumFramesEncoded(0),
- mFirstFrameTimestamp(0),
- mMaxAcquiredBufferCount(4), // XXX double-check the default
- mUseAbsoluteTimestamps(false) {
- ALOGV("SurfaceMediaSource");
-
- if (bufferWidth == 0 || bufferHeight == 0) {
- ALOGE("Invalid dimensions %dx%d", bufferWidth, bufferHeight);
- }
-
- BufferQueue::createBufferQueue(&mProducer, &mConsumer);
- mConsumer->setDefaultBufferSize(bufferWidth, bufferHeight);
- mConsumer->setConsumerUsageBits(GRALLOC_USAGE_HW_VIDEO_ENCODER |
- GRALLOC_USAGE_HW_TEXTURE);
-
- sp<ISurfaceComposer> composer(ComposerService::getComposerService());
-
- // Note that we can't create an sp<...>(this) in a ctor that will not keep a
- // reference once the ctor ends, as that would cause the refcount of 'this'
- // dropping to 0 at the end of the ctor. Since all we need is a wp<...>
- // that's what we create.
- wp<ConsumerListener> listener = static_cast<ConsumerListener*>(this);
- sp<BufferQueue::ProxyConsumerListener> proxy = new BufferQueue::ProxyConsumerListener(listener);
-
- status_t err = mConsumer->consumerConnect(proxy, false);
- if (err != NO_ERROR) {
- ALOGE("SurfaceMediaSource: error connecting to BufferQueue: %s (%d)",
- strerror(-err), err);
- }
-}
-
-SurfaceMediaSource::~SurfaceMediaSource() {
- ALOGV("~SurfaceMediaSource");
- CHECK(!mStarted);
-}
-
-nsecs_t SurfaceMediaSource::getTimestamp() {
- ALOGV("getTimestamp");
- Mutex::Autolock lock(mMutex);
- return mCurrentTimestamp;
-}
-
-void SurfaceMediaSource::setFrameAvailableListener(
- const sp<FrameAvailableListener>& listener) {
- ALOGV("setFrameAvailableListener");
- Mutex::Autolock lock(mMutex);
- mFrameAvailableListener = listener;
-}
-
-void SurfaceMediaSource::dumpState(String8& result) const
-{
- char buffer[1024];
- dumpState(result, "", buffer, 1024);
-}
-
-void SurfaceMediaSource::dumpState(
- String8& result,
- const char* /* prefix */,
- char* buffer,
- size_t /* SIZE */) const
-{
- Mutex::Autolock lock(mMutex);
-
- result.append(buffer);
- mConsumer->dumpState(result, "");
-}
-
-status_t SurfaceMediaSource::setFrameRate(int32_t fps)
-{
- ALOGV("setFrameRate");
- Mutex::Autolock lock(mMutex);
- const int MAX_FRAME_RATE = 60;
- if (fps < 0 || fps > MAX_FRAME_RATE) {
- return BAD_VALUE;
- }
- mFrameRate = fps;
- return OK;
-}
-
-MetadataBufferType SurfaceMediaSource::metaDataStoredInVideoBuffers() const {
- ALOGV("isMetaDataStoredInVideoBuffers");
- return kMetadataBufferTypeANWBuffer;
-}
-
-int32_t SurfaceMediaSource::getFrameRate( ) const {
- ALOGV("getFrameRate");
- Mutex::Autolock lock(mMutex);
- return mFrameRate;
-}
-
-status_t SurfaceMediaSource::start(MetaData *params)
-{
- ALOGV("start");
-
- Mutex::Autolock lock(mMutex);
-
- CHECK(!mStarted);
-
- mStartTimeNs = 0;
- int64_t startTimeUs;
- int32_t bufferCount = 0;
- if (params) {
- if (params->findInt64(kKeyTime, &startTimeUs)) {
- mStartTimeNs = startTimeUs * 1000;
- }
-
- if (!params->findInt32(kKeyNumBuffers, &bufferCount)) {
- ALOGE("Failed to find the advertised buffer count");
- return UNKNOWN_ERROR;
- }
-
- if (bufferCount <= 1) {
- ALOGE("bufferCount %d is too small", bufferCount);
- return BAD_VALUE;
- }
-
- mMaxAcquiredBufferCount = bufferCount;
- }
-
- CHECK_GT(mMaxAcquiredBufferCount, 1u);
-
- status_t err =
- mConsumer->setMaxAcquiredBufferCount(mMaxAcquiredBufferCount);
-
- if (err != OK) {
- return err;
- }
-
- mNumPendingBuffers = 0;
- mStarted = true;
-
- return OK;
-}
-
-status_t SurfaceMediaSource::setMaxAcquiredBufferCount(size_t count) {
- ALOGV("setMaxAcquiredBufferCount(%zu)", count);
- Mutex::Autolock lock(mMutex);
-
- CHECK_GT(count, 1u);
- mMaxAcquiredBufferCount = count;
-
- return OK;
-}
-
-status_t SurfaceMediaSource::setUseAbsoluteTimestamps() {
- ALOGV("setUseAbsoluteTimestamps");
- Mutex::Autolock lock(mMutex);
- mUseAbsoluteTimestamps = true;
-
- return OK;
-}
-
-status_t SurfaceMediaSource::stop()
-{
- ALOGV("stop");
- Mutex::Autolock lock(mMutex);
-
- if (!mStarted) {
- return OK;
- }
-
- mStarted = false;
- mFrameAvailableCondition.signal();
-
- while (mNumPendingBuffers > 0) {
- ALOGI("Still waiting for %zu buffers to be returned.",
- mNumPendingBuffers);
-
-#if DEBUG_PENDING_BUFFERS
- for (size_t i = 0; i < mPendingBuffers.size(); ++i) {
- ALOGI("%d: %p", i, mPendingBuffers.itemAt(i));
- }
-#endif
-
- mMediaBuffersAvailableCondition.wait(mMutex);
- }
-
- mMediaBuffersAvailableCondition.signal();
-
- return mConsumer->consumerDisconnect();
-}
-
-sp<MetaData> SurfaceMediaSource::getFormat()
-{
- ALOGV("getFormat");
-
- Mutex::Autolock lock(mMutex);
- sp<MetaData> meta = new MetaData;
-
- meta->setInt32(kKeyWidth, mWidth);
- meta->setInt32(kKeyHeight, mHeight);
- // The encoder format is set as an opaque colorformat
- // The encoder will later find out the actual colorformat
- // from the GL Frames itself.
- meta->setInt32(kKeyColorFormat, OMX_COLOR_FormatAndroidOpaque);
- meta->setInt32(kKeyStride, mWidth);
- meta->setInt32(kKeySliceHeight, mHeight);
- meta->setInt32(kKeyFrameRate, mFrameRate);
- meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_RAW);
- return meta;
-}
-
-// Pass the data to the MediaBuffer. Pass in only the metadata
-// Note: Call only when you have the lock
-void SurfaceMediaSource::passMetadataBuffer_l(MediaBuffer **buffer,
- ANativeWindowBuffer *bufferHandle) const {
- *buffer = new MediaBuffer(sizeof(VideoNativeMetadata));
- VideoNativeMetadata *data = (VideoNativeMetadata *)(*buffer)->data();
- if (data == NULL) {
- ALOGE("Cannot allocate memory for metadata buffer!");
- return;
- }
- data->eType = metaDataStoredInVideoBuffers();
- data->pBuffer = bufferHandle;
- data->nFenceFd = -1;
- ALOGV("handle = %p, offset = %zu, length = %zu",
- bufferHandle, (*buffer)->range_length(), (*buffer)->range_offset());
-}
-
-status_t SurfaceMediaSource::read(
- MediaBuffer **buffer, const ReadOptions * /* options */) {
- ALOGV("read");
- Mutex::Autolock lock(mMutex);
-
- *buffer = NULL;
-
- while (mStarted && mNumPendingBuffers == mMaxAcquiredBufferCount) {
- mMediaBuffersAvailableCondition.wait(mMutex);
- }
-
- // Update the current buffer info
- // TODO: mCurrentSlot can be made a bufferstate since there
- // can be more than one "current" slots.
-
- BufferItem item;
- // If the recording has started and the queue is empty, then just
- // wait here till the frames come in from the client side
- while (mStarted) {
-
- status_t err = mConsumer->acquireBuffer(&item, 0);
- if (err == BufferQueue::NO_BUFFER_AVAILABLE) {
- // wait for a buffer to be queued
- mFrameAvailableCondition.wait(mMutex);
- } else if (err == OK) {
- err = item.mFence->waitForever("SurfaceMediaSource::read");
- if (err) {
- ALOGW("read: failed to wait for buffer fence: %d", err);
- }
-
- // First time seeing the buffer? Added it to the SMS slot
- if (item.mGraphicBuffer != NULL) {
- mSlots[item.mSlot].mGraphicBuffer = item.mGraphicBuffer;
- }
- mSlots[item.mSlot].mFrameNumber = item.mFrameNumber;
-
- // check for the timing of this buffer
- if (mNumFramesReceived == 0 && !mUseAbsoluteTimestamps) {
- mFirstFrameTimestamp = item.mTimestamp;
- // Initial delay
- if (mStartTimeNs > 0) {
- if (item.mTimestamp < mStartTimeNs) {
- // This frame predates start of record, discard
- mConsumer->releaseBuffer(
- item.mSlot, item.mFrameNumber, EGL_NO_DISPLAY,
- EGL_NO_SYNC_KHR, Fence::NO_FENCE);
- continue;
- }
- mStartTimeNs = item.mTimestamp - mStartTimeNs;
- }
- }
- item.mTimestamp = mStartTimeNs + (item.mTimestamp - mFirstFrameTimestamp);
-
- mNumFramesReceived++;
-
- break;
- } else {
- ALOGE("read: acquire failed with error code %d", err);
- return ERROR_END_OF_STREAM;
- }
-
- }
-
- // If the loop was exited as a result of stopping the recording,
- // it is OK
- if (!mStarted) {
- ALOGV("Read: SurfaceMediaSource is stopped. Returning ERROR_END_OF_STREAM.");
- return ERROR_END_OF_STREAM;
- }
-
- mCurrentSlot = item.mSlot;
-
- // First time seeing the buffer? Added it to the SMS slot
- if (item.mGraphicBuffer != NULL) {
- mSlots[item.mSlot].mGraphicBuffer = item.mGraphicBuffer;
- }
- mSlots[item.mSlot].mFrameNumber = item.mFrameNumber;
-
- mCurrentBuffers.push_back(mSlots[mCurrentSlot].mGraphicBuffer);
- int64_t prevTimeStamp = mCurrentTimestamp;
- mCurrentTimestamp = item.mTimestamp;
-
- mNumFramesEncoded++;
- // Pass the data to the MediaBuffer. Pass in only the metadata
-
- passMetadataBuffer_l(buffer, mSlots[mCurrentSlot].mGraphicBuffer->getNativeBuffer());
-
- (*buffer)->setObserver(this);
- (*buffer)->add_ref();
- (*buffer)->meta_data()->setInt64(kKeyTime, mCurrentTimestamp / 1000);
- ALOGV("Frames encoded = %d, timestamp = %" PRId64 ", time diff = %" PRId64,
- mNumFramesEncoded, mCurrentTimestamp / 1000,
- mCurrentTimestamp / 1000 - prevTimeStamp / 1000);
-
- ++mNumPendingBuffers;
-
-#if DEBUG_PENDING_BUFFERS
- mPendingBuffers.push_back(*buffer);
-#endif
-
- ALOGV("returning mbuf %p", *buffer);
-
- return OK;
-}
-
-static buffer_handle_t getMediaBufferHandle(MediaBuffer *buffer) {
- // need to convert to char* for pointer arithmetic and then
- // copy the byte stream into our handle
- buffer_handle_t bufferHandle;
- memcpy(&bufferHandle, (char*)(buffer->data()) + 4, sizeof(buffer_handle_t));
- return bufferHandle;
-}
-
-void SurfaceMediaSource::signalBufferReturned(MediaBuffer *buffer) {
- ALOGV("signalBufferReturned");
-
- bool foundBuffer = false;
-
- Mutex::Autolock lock(mMutex);
-
- buffer_handle_t bufferHandle = getMediaBufferHandle(buffer);
-
- for (size_t i = 0; i < mCurrentBuffers.size(); i++) {
- if (mCurrentBuffers[i]->handle == bufferHandle) {
- mCurrentBuffers.removeAt(i);
- foundBuffer = true;
- break;
- }
- }
-
- if (!foundBuffer) {
- ALOGW("returned buffer was not found in the current buffer list");
- }
-
- for (int id = 0; id < BufferQueue::NUM_BUFFER_SLOTS; id++) {
- if (mSlots[id].mGraphicBuffer == NULL) {
- continue;
- }
-
- if (bufferHandle == mSlots[id].mGraphicBuffer->handle) {
- ALOGV("Slot %d returned, matches handle = %p", id,
- mSlots[id].mGraphicBuffer->handle);
-
- mConsumer->releaseBuffer(id, mSlots[id].mFrameNumber,
- EGL_NO_DISPLAY, EGL_NO_SYNC_KHR,
- Fence::NO_FENCE);
-
- buffer->setObserver(0);
- buffer->release();
-
- foundBuffer = true;
- break;
- }
- }
-
- if (!foundBuffer) {
- CHECK(!"signalBufferReturned: bogus buffer");
- }
-
-#if DEBUG_PENDING_BUFFERS
- for (size_t i = 0; i < mPendingBuffers.size(); ++i) {
- if (mPendingBuffers.itemAt(i) == buffer) {
- mPendingBuffers.removeAt(i);
- break;
- }
- }
-#endif
-
- --mNumPendingBuffers;
- mMediaBuffersAvailableCondition.broadcast();
-}
-
-// Part of the BufferQueue::ConsumerListener
-void SurfaceMediaSource::onFrameAvailable(const BufferItem& /* item */) {
- ALOGV("onFrameAvailable");
-
- sp<FrameAvailableListener> listener;
- { // scope for the lock
- Mutex::Autolock lock(mMutex);
- mFrameAvailableCondition.broadcast();
- listener = mFrameAvailableListener;
- }
-
- if (listener != NULL) {
- ALOGV("actually calling onFrameAvailable");
- listener->onFrameAvailable();
- }
-}
-
-// SurfaceMediaSource hijacks this event to assume
-// the prodcuer is disconnecting from the BufferQueue
-// and that it should stop the recording
-void SurfaceMediaSource::onBuffersReleased() {
- ALOGV("onBuffersReleased");
-
- Mutex::Autolock lock(mMutex);
-
- mFrameAvailableCondition.signal();
-
- for (int i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) {
- mSlots[i].mGraphicBuffer = 0;
- }
-}
-
-void SurfaceMediaSource::onSidebandStreamChanged() {
- ALOG_ASSERT(false, "SurfaceMediaSource can't consume sideband streams");
-}
-
-} // end of namespace android
diff --git a/media/libstagefright/codec2/1.0/InputSurfaceConnection.cpp b/media/libstagefright/codec2/1.0/InputSurfaceConnection.cpp
index 32d6404..693eeea 100644
--- a/media/libstagefright/codec2/1.0/InputSurfaceConnection.cpp
+++ b/media/libstagefright/codec2/1.0/InputSurfaceConnection.cpp
@@ -22,7 +22,6 @@
#include <C2BlockInternal.h>
#include <C2PlatformSupport.h>
-#include <gui/Surface.h>
#include <media/stagefright/codec2/1.0/InputSurfaceConnection.h>
#include <system/window.h>
@@ -124,7 +123,7 @@
std::shared_ptr<C2Buffer> c2Buffer(
// TODO: fence
new Buffer2D(block->share(
- C2Rect(block->width(), block->height()), ::android::C2Fence())),
+ C2Rect(block->width(), block->height()), ::C2Fence())),
[handle, bufferId, src = mSource](C2Buffer *ptr) {
delete ptr;
native_handle_delete(handle);
diff --git a/media/libstagefright/codec2/Android.bp b/media/libstagefright/codec2/Android.bp
index 1182cd8..1c32953 100644
--- a/media/libstagefright/codec2/Android.bp
+++ b/media/libstagefright/codec2/Android.bp
@@ -1,6 +1,9 @@
cc_library_shared {
name: "libstagefright_codec2",
vendor_available: true,
+ vndk: {
+ enabled: true,
+ },
tags: [
"optional",
@@ -22,6 +25,16 @@
"include",
],
+ header_libs: [
+ "libhardware_headers",
+ "libutils_headers",
+ ],
+
+ export_header_lib_headers: [
+ "libhardware_headers",
+ "libutils_headers",
+ ],
+
sanitize: {
misc_undefined: [
"unsigned-integer-overflow",
diff --git a/media/libstagefright/codec2/C2.cpp b/media/libstagefright/codec2/C2.cpp
index a51b073..359d4e5 100644
--- a/media/libstagefright/codec2/C2.cpp
+++ b/media/libstagefright/codec2/C2.cpp
@@ -22,8 +22,6 @@
#include <C2ParamDef.h>
#include <C2Work.h>
-namespace android {
-
/**
* There is nothing here yet. This library is built to see what symbols and methods get
* defined as part of the API include files.
@@ -32,5 +30,4 @@
* Codec2 clients.
*/
-} // namespace android
diff --git a/media/libstagefright/codec2/SimpleC2Component.cpp b/media/libstagefright/codec2/SimpleC2Component.cpp
index 01e9e43..333cfeb 100644
--- a/media/libstagefright/codec2/SimpleC2Component.cpp
+++ b/media/libstagefright/codec2/SimpleC2Component.cpp
@@ -226,7 +226,9 @@
releasing = std::move(state->mThread);
}
mExitRequested = true;
- releasing.join();
+ if (releasing.joinable()) {
+ releasing.join();
+ }
onRelease();
return C2_OK;
}
@@ -366,26 +368,6 @@
}
}
-namespace {
-
-class GraphicBuffer : public C2Buffer {
-public:
- GraphicBuffer(
- const std::shared_ptr<C2GraphicBlock> &block,
- const C2Rect &crop)
- : C2Buffer({ block->share(crop, ::android::C2Fence()) }) {}
-};
-
-
-class LinearBuffer : public C2Buffer {
-public:
- LinearBuffer(
- const std::shared_ptr<C2LinearBlock> &block, size_t offset, size_t size)
- : C2Buffer({ block->share(offset, size, ::android::C2Fence()) }) {}
-};
-
-} // namespace
-
std::shared_ptr<C2Buffer> SimpleC2Component::createLinearBuffer(
const std::shared_ptr<C2LinearBlock> &block) {
return createLinearBuffer(block, block->offset(), block->size());
@@ -393,7 +375,7 @@
std::shared_ptr<C2Buffer> SimpleC2Component::createLinearBuffer(
const std::shared_ptr<C2LinearBlock> &block, size_t offset, size_t size) {
- return std::make_shared<LinearBuffer>(block, offset, size);
+ return C2Buffer::CreateLinearBuffer(block->share(offset, size, ::C2Fence()));
}
std::shared_ptr<C2Buffer> SimpleC2Component::createGraphicBuffer(
@@ -402,9 +384,8 @@
}
std::shared_ptr<C2Buffer> SimpleC2Component::createGraphicBuffer(
- const std::shared_ptr<C2GraphicBlock> &block,
- const C2Rect &crop) {
- return std::make_shared<GraphicBuffer>(block, crop);
+ const std::shared_ptr<C2GraphicBlock> &block, const C2Rect &crop) {
+ return C2Buffer::CreateGraphicBuffer(block->share(crop, ::C2Fence()));
}
} // namespace android
diff --git a/media/libstagefright/codec2/SimpleC2Interface.cpp b/media/libstagefright/codec2/SimpleC2Interface.cpp
index f082243..d159426 100644
--- a/media/libstagefright/codec2/SimpleC2Interface.cpp
+++ b/media/libstagefright/codec2/SimpleC2Interface.cpp
@@ -45,15 +45,38 @@
if (heapParams) {
heapParams->clear();
for (const auto &index : heapParamIndices) {
- if (index.coreIndex() != C2StreamFormatConfig::CORE_INDEX
- || !index.forStream()
- || index.stream() != 0u) {
- heapParams->push_back(nullptr);
- }
- if (index.forInput()) {
- heapParams->push_back(C2Param::Copy(mInputFormat));
- } else {
- heapParams->push_back(C2Param::Copy(mOutputFormat));
+ switch (index.type()) {
+ case C2StreamFormatConfig::input::PARAM_TYPE:
+ if (index.stream() == 0u) {
+ heapParams->push_back(C2Param::Copy(mInputFormat));
+ } else {
+ heapParams->push_back(nullptr);
+ }
+ break;
+ case C2StreamFormatConfig::output::PARAM_TYPE:
+ if (index.stream() == 0u) {
+ heapParams->push_back(C2Param::Copy(mOutputFormat));
+ } else {
+ heapParams->push_back(nullptr);
+ }
+ break;
+ case C2PortMimeConfig::input::PARAM_TYPE:
+ if (mInputMediaType) {
+ heapParams->push_back(C2Param::Copy(*mInputMediaType));
+ } else {
+ heapParams->push_back(nullptr);
+ }
+ break;
+ case C2PortMimeConfig::output::PARAM_TYPE:
+ if (mOutputMediaType) {
+ heapParams->push_back(C2Param::Copy(*mOutputMediaType));
+ } else {
+ heapParams->push_back(nullptr);
+ }
+ break;
+ default:
+ heapParams->push_back(nullptr);
+ break;
}
}
}
diff --git a/media/libstagefright/codec2/include/C2.h b/media/libstagefright/codec2/include/C2.h
index 00e3924..e90fe42 100644
--- a/media/libstagefright/codec2/include/C2.h
+++ b/media/libstagefright/codec2/include/C2.h
@@ -17,40 +17,18 @@
#ifndef C2_H_
#define C2_H_
-#include <string>
-#include <vector>
-#include <list>
-
-#ifdef __ANDROID__
-
-#include <utils/Errors.h> // for status_t
-#include <utils/Timers.h> // for nsecs_t
-
-namespace android {
-
-#else
-
#include <errno.h>
-typedef int64_t nsecs_t;
-enum {
- GRALLOC_USAGE_SW_READ_OFTEN,
- GRALLOC_USAGE_RENDERSCRIPT,
- GRALLOC_USAGE_HW_TEXTURE,
- GRALLOC_USAGE_HW_COMPOSER,
- GRALLOC_USAGE_HW_VIDEO_ENCODER,
- GRALLOC_USAGE_PROTECTED,
- GRALLOC_USAGE_SW_WRITE_OFTEN,
- GRALLOC_USAGE_HW_RENDER,
-};
+#include <string>
-#endif
+/** nanoseconds with arbitrary origin. */
+typedef int64_t c2_nsecs_t;
/** \mainpage Codec2
*
- * Codec2 is a frame-based data processing API used by android.
+ * Codec2 is a generic frame-based data processing API.
*
- * The framework accesses components via the \ref API.
+ * The media subsystem accesses components via the \ref API.
*/
/** \ingroup API
@@ -109,53 +87,35 @@
* c2_status_t: status codes used.
*/
enum c2_status_t : int32_t {
-
/*
- * Use android status constants if available. Otherwise, define the android status constants as
- * additional enum values using POSIX errno constants.
+ * Use POSIX errno constants.
*/
-#ifndef __ANDROID__
- ALREADY_EXISTS = -EEXIST,
- BAD_VALUE = -EINVAL,
- BAD_INDEX = -EOVERFLOW,
- FAILED_TRANSACTION = -ENOTSUP,
- INVALID_OPERATION = -ENOSYS,
- NAME_NOT_FOUND = -ENOENT,
- NO_MEMORY = -ENOMEM,
- NO_INIT = -ENODEV,
- OK = 0,
- PERMISSION_DENIED = -EPERM,
- TIMED_OUT = -ETIMEDOUT,
- UNKNOWN_ERROR = -EFAULT,
- UNKNOWN_TRANSACTION = -EBADMSG,
- WOULD_BLOCK = -EWOULDBLOCK,
-#endif
-
- C2_OK = OK, ///< operation completed successfully
+ C2_OK = 0, ///< operation completed successfully
// bad input
- C2_BAD_VALUE = BAD_VALUE, ///< argument has invalid value (user error)
- C2_BAD_INDEX = BAD_INDEX, ///< argument uses invalid index (user error)
- C2_CANNOT_DO = FAILED_TRANSACTION, ///< argument/index is valid but not possible
+ C2_BAD_VALUE = EINVAL, ///< argument has invalid value (user error)
+ C2_BAD_INDEX = ENXIO, ///< argument uses invalid index (user error)
+ C2_CANNOT_DO = ENOTSUP, ///< argument/index is valid but not possible
// bad sequencing of events
- C2_DUPLICATE = ALREADY_EXISTS, ///< object already exists
- C2_NOT_FOUND = NAME_NOT_FOUND, ///< object not found
- C2_BAD_STATE = INVALID_OPERATION, ///< operation is not permitted in the current state
- C2_BLOCKING = WOULD_BLOCK, ///< operation would block but blocking is not permitted
+ C2_DUPLICATE = EEXIST, ///< object already exists
+ C2_NOT_FOUND = ENOENT, ///< object not found
+ C2_BAD_STATE = EPERM, ///< operation is not permitted in the current state
+ C2_BLOCKING = EWOULDBLOCK, ///< operation would block but blocking is not permitted
+ C2_CANCELED = EINTR, ///< operation interrupted/canceled
// bad environment
- C2_NO_MEMORY = NO_MEMORY, ///< not enough memory to complete operation
- C2_REFUSED = PERMISSION_DENIED, ///< missing permission to complete operation
+ C2_NO_MEMORY = ENOMEM, ///< not enough memory to complete operation
+ C2_REFUSED = EACCES, ///< missing permission to complete operation
- C2_TIMED_OUT = TIMED_OUT, ///< operation did not complete within timeout
+ C2_TIMED_OUT = ETIMEDOUT, ///< operation did not complete within timeout
// bad versioning
- C2_OMITTED = UNKNOWN_TRANSACTION, ///< operation is not implemented/supported (optional only)
+ C2_OMITTED = ENOSYS, ///< operation is not implemented/supported (optional only)
// unknown fatal
- C2_CORRUPTED = UNKNOWN_ERROR, ///< some unexpected error prevented the operation
- C2_NO_INIT = NO_INIT, ///< status has not been initialized
+ C2_CORRUPTED = EFAULT, ///< some unexpected error prevented the operation
+ C2_NO_INIT = ENODEV, ///< status has not been initialized
};
/**
@@ -181,15 +141,20 @@
/// \defgroup utils Utilities
/// @{
-#define C2_DO_NOT_COPY(type, args...) \
- type args& operator=(const type args&) = delete; \
- type(const type args&) = delete; \
+#define C2_DO_NOT_COPY(type) \
+ type& operator=(const type &) = delete; \
+ type(const type &) = delete; \
-#define C2_PURE __attribute__((pure))
-#define C2_CONST __attribute__((const))
-#define C2_HIDE __attribute__((visibility("hidden")))
-#define C2_INTERNAL __attribute__((internal_linkage))
+#define C2_DEFAULT_MOVE(type) \
+ type& operator=(type &&) = default; \
+ type(type &&) = default; \
+
#define C2_ALLOW_OVERFLOW __attribute__((no_sanitize("integer")))
+#define C2_CONST __attribute__((const))
+#define C2_HIDE __attribute__((visibility("hidden")))
+#define C2_INTERNAL __attribute__((internal_linkage))
+#define C2_PACK __attribute__((packed))
+#define C2_PURE __attribute__((pure))
#define DEFINE_OTHER_COMPARISON_OPERATORS(type) \
inline bool operator!=(const type &other) const { return !(*this == other); } \
@@ -313,7 +278,7 @@
/**
* Convert to a smaller counter type. This is always safe.
*/
- template<typename U, typename E=typename std::enable_if<sizeof(U) < sizeof(T)>::type>
+ template<typename U, typename E=typename std::enable_if<(sizeof(U) < sizeof(T))>::type>
inline operator c2_cntr_t<U>() {
return c2_cntr_t<U>(mValue);
}
@@ -334,7 +299,7 @@
return c2_cntr_t<T>(mValue op compat::get(value)); \
} \
\
- template<typename U, typename E=typename std::enable_if<sizeof(U) < sizeof(T)>::type> \
+ template<typename U, typename E=typename std::enable_if<(sizeof(U) < sizeof(T))>::type> \
attrib inline constexpr c2_cntr_t<U> operator op(const c2_cntr_t<U> &value) const { \
return c2_cntr_t<U>(U(mValue) op value.peeku()); \
}
@@ -548,32 +513,28 @@
/// @}
-#ifdef __ANDROID__
-} // namespace android
-#endif
-
#include <functional>
template<typename T>
-struct std::less<::android::c2_cntr_t<T>> {
- constexpr bool operator()(const ::android::c2_cntr_t<T> &lh, const ::android::c2_cntr_t<T> &rh) const {
+struct std::less<::c2_cntr_t<T>> {
+ constexpr bool operator()(const ::c2_cntr_t<T> &lh, const ::c2_cntr_t<T> &rh) const {
return lh.peeku() < rh.peeku();
}
};
template<typename T>
-struct std::less_equal<::android::c2_cntr_t<T>> {
- constexpr bool operator()(const ::android::c2_cntr_t<T> &lh, const ::android::c2_cntr_t<T> &rh) const {
+struct std::less_equal<::c2_cntr_t<T>> {
+ constexpr bool operator()(const ::c2_cntr_t<T> &lh, const ::c2_cntr_t<T> &rh) const {
return lh.peeku() <= rh.peeku();
}
};
template<typename T>
-struct std::greater<::android::c2_cntr_t<T>> {
- constexpr bool operator()(const ::android::c2_cntr_t<T> &lh, const ::android::c2_cntr_t<T> &rh) const {
+struct std::greater<::c2_cntr_t<T>> {
+ constexpr bool operator()(const ::c2_cntr_t<T> &lh, const ::c2_cntr_t<T> &rh) const {
return lh.peeku() > rh.peeku();
}
};
template<typename T>
-struct std::greater_equal<::android::c2_cntr_t<T>> {
- constexpr bool operator()(const ::android::c2_cntr_t<T> &lh, const ::android::c2_cntr_t<T> &rh) const {
+struct std::greater_equal<::c2_cntr_t<T>> {
+ constexpr bool operator()(const ::c2_cntr_t<T> &lh, const ::c2_cntr_t<T> &rh) const {
return lh.peeku() >= rh.peeku();
}
};
diff --git a/media/libstagefright/codec2/include/C2Buffer.h b/media/libstagefright/codec2/include/C2Buffer.h
index 034075f..4bdd20f 100644
--- a/media/libstagefright/codec2/include/C2Buffer.h
+++ b/media/libstagefright/codec2/include/C2Buffer.h
@@ -18,27 +18,20 @@
#define C2BUFFER_H_
#include <C2.h>
+#include <C2BufferBase.h>
#include <C2Param.h> // for C2Info
-#include <list>
#include <memory>
+#include <vector>
#ifdef __ANDROID__
-
-// #include <system/window.h>
-#include <cutils/native_handle.h>
-#include <hardware/gralloc.h> // TODO: remove
-
-typedef native_handle_t C2Handle;
-
+#include <android-C2Buffer.h>
#else
typedef void* C2Handle;
#endif
-namespace android {
-
/// \defgroup buffer Buffers
/// @{
@@ -89,7 +82,7 @@
* \retval C2_REFUSED no permission to wait for the fence (unexpected - system)
* \retval C2_CORRUPTED some unknown error prevented waiting for the fence (unexpected)
*/
- c2_status_t wait(nsecs_t timeoutNs);
+ c2_status_t wait(c2_nsecs_t timeoutNs);
/**
* Used to check if this fence is valid (if there is a chance for it to be signaled.)
@@ -550,41 +543,9 @@
ALLOCATIONS
**************************************************************************************************/
-/// \defgroup allocator Allocation and memory placement
+/// \ingroup allocator Allocation and memory placement
/// @{
-/**
- * Buffer/memory usage bits. These are used by the allocators to select optimal memory type/pool and
- * buffer layout.
- *
- * \note This struct has public fields without getters/setters. All methods are inline.
- */
-struct C2MemoryUsage {
-// public:
- // TODO: match these to gralloc1.h
- enum Consumer : uint64_t {
- // \todo do we need to distinguish often from rarely?
- CPU_READ = GRALLOC_USAGE_SW_READ_OFTEN,
- RENDERSCRIPT_READ = GRALLOC_USAGE_RENDERSCRIPT,
- HW_TEXTURE_READ = GRALLOC_USAGE_HW_TEXTURE,
- HW_COMPOSER_READ = GRALLOC_USAGE_HW_COMPOSER,
- HW_CODEC_READ = GRALLOC_USAGE_HW_VIDEO_ENCODER,
- READ_PROTECTED = GRALLOC_USAGE_PROTECTED,
- };
-
- enum Producer : uint64_t {
- CPU_WRITE = GRALLOC_USAGE_SW_WRITE_OFTEN,
- RENDERSCRIPT_WRITE = GRALLOC_USAGE_RENDERSCRIPT,
- HW_TEXTURE_WRITE = GRALLOC_USAGE_HW_RENDER,
- HW_COMPOSER_WRITE = GRALLOC_USAGE_HW_COMPOSER | GRALLOC_USAGE_HW_RENDER,
- HW_CODEC_WRITE = GRALLOC_USAGE_HW_VIDEO_ENCODER,
- WRITE_PROTECTED = GRALLOC_USAGE_PROTECTED,
- };
-
- uint64_t consumer; // e.g. input
- uint64_t producer; // e.g. output
-};
-
class C2LinearAllocation;
class C2GraphicAllocation;
@@ -785,19 +746,19 @@
/**
* Maps a portion of an allocation starting from |offset| with |size| into local process memory.
* Stores the starting address into |addr|, or NULL if the operation was unsuccessful.
- * |fenceFd| is a file descriptor referring to an acquire sync fence object. If it is already
- * safe to access the buffer contents, then -1.
+ * |fence| will contain an acquire sync fence object. If it is already
+ * safe to access the buffer contents, then it will contain an empty (already fired) fence.
*
- * \param offset starting position of the portion to be mapped (this does not have to
+ * \param offset starting position of the portion to be mapped (this does not have to
* be page aligned)
- * \param size size of the portion to be mapped (this does not have to be page
+ * \param size size of the portion to be mapped (this does not have to be page
* aligned)
- * \param usage the desired usage. \todo this must be kSoftwareRead and/or
+ * \param usage the desired usage. \todo this must be kSoftwareRead and/or
* kSoftwareWrite.
- * \param fenceFd a pointer to a file descriptor if an async mapping is requested. If
- * not-null, and acquire fence FD will be stored here on success, or -1
- * on failure. If null, the mapping will be synchronous.
- * \param addr a pointer to where the starting address of the mapped portion will be
+ * \param fence a pointer to a fence object if an async mapping is requested. If
+ * not-null, and acquire fence will be stored here on success, or empty
+ * fence on failure. If null, the mapping will be synchronous.
+ * \param addr a pointer to where the starting address of the mapped portion will be
* stored. On failure, nullptr will be stored here.
*
* \todo Only one portion can be mapped at the same time - this is true for gralloc, but there
@@ -814,19 +775,19 @@
* \retval C2_CORRUPTED some unknown error prevented the operation from completing (unexpected)
*/
virtual c2_status_t map(
- size_t offset, size_t size, C2MemoryUsage usage, int *fenceFd /* nullable */,
+ size_t offset, size_t size, C2MemoryUsage usage, C2Fence *fence /* nullable */,
void **addr /* nonnull */) = 0;
/**
* Unmaps a portion of an allocation at |addr| with |size|. These must be parameters previously
- * passed to |map|; otherwise, this operation is a no-op.
+ * passed to and returned by |map|; otherwise, this operation is a no-op.
*
- * \param addr starting address of the mapped region
- * \param size size of the mapped region
- * \param fenceFd a pointer to a file descriptor if an async unmapping is requested. If
- * not-null, a release fence FD will be stored here on success, or -1
+ * \param addr starting address of the mapped region
+ * \param size size of the mapped region
+ * \param fence a pointer to a fence object if an async unmapping is requested. If
+ * not-null, a release fence will be stored here on success, or empty fence
* on failure. This fence signals when the original allocation contains
- * any changes that happened to the mapped region. If null, the unmapping
+ * all changes that happened to the mapped region. If null, the unmapping
* will be synchronous.
*
* \retval C2_OK the operation was successful
@@ -837,7 +798,7 @@
* \retval C2_CORRUPTED some unknown error prevented the operation from completing (unexpected)
* \retval C2_REFUSED no permission to unmap the portion (unexpected - system)
*/
- virtual c2_status_t unmap(void *addr, size_t size, int *fenceFd /* nullable */) = 0;
+ virtual c2_status_t unmap(void *addr, size_t size, C2Fence *fence /* nullable */) = 0;
/**
* Returns the allocator ID for this allocation. This is useful to put the handle into context.
@@ -1378,6 +1339,9 @@
uint32_t width;
uint32_t height;
+ constexpr inline C2Rect()
+ : C2Rect(0, 0, 0, 0) { }
+
constexpr inline C2Rect(uint32_t width_, uint32_t height_)
: C2Rect(width_, height_, 0, 0) { }
@@ -1546,6 +1510,15 @@
BIG_END, // BIG_ENDIAN is a reserved macro
} endianness; ///< endianness of the samples
+ /**
+ * The following two fields define the relation between multiple planes. If multiple planes are
+ * interleaved, they share a root plane (whichever plane's start address is the lowest), and
+ * |offset| is the offset of this plane inside the root plane (in bytes). |rootIx| is the index
+ * of the root plane. If a plane is independent, rootIx is its index and offset is 0.
+ */
+ uint32_t rootIx; ///< index of the root plane
+ uint32_t offset; ///< offset of this plane inside of the root plane
+
inline constexpr ssize_t minOffset(uint32_t width, uint32_t height) const {
ssize_t offs = 0;
if (width > 0 && colInc < 0) {
@@ -1580,7 +1553,8 @@
};
type_t type; // image type
- uint32_t numPlanes; // number of planes
+ uint32_t numPlanes; // number of component planes
+ uint32_t rootPlanes; // number of layout planes (root planes)
enum plane_index_t : uint32_t {
PLANE_Y = 0,
@@ -1732,22 +1706,19 @@
* Maps a rectangular section (as defined by |rect|) of a 2D allocation into local process
* memory for flexible access. On success, it fills out |layout| with the plane specifications
* and fills the |addr| array with pointers to the first byte of the top-left pixel of each
- * plane used. Otherwise, it leaves |layout| and |addr| untouched. |fenceFd| is a file
- * descriptor referring to an acquire sync fence object. If it is already safe to access the
- * buffer contents, then -1.
+ * plane used. Otherwise, it leaves |layout| and |addr| untouched. |fence| will contain
+ * an acquire sync fence object. If it is already safe to access the
+ * buffer contents, then it will be an empty (already fired) fence.
*
- * Safe regions for the pointer addresses returned can be gotten via C2LayoutInfo.minOffse()/
+ * Safe regions for the pointer addresses returned can be gotten via C2LayoutInfo.minOffset()/
* maxOffset().
*
- * \note Only one portion of the graphic allocation can be mapped at the same time. (This is
- * from gralloc1 limitation.)
- *
* \param rect section to be mapped (this does not have to be aligned)
* \param usage the desired usage. \todo this must be kSoftwareRead and/or
* kSoftwareWrite.
- * \param fenceFd a pointer to a file descriptor if an async mapping is requested. If
- * not-null, and acquire fence FD will be stored here on success, or -1
- * on failure. If null, the mapping will be synchronous.
+ * \param fence a pointer to a fence object if an async mapping is requested. If
+ * not-null, and acquire fence will be stored here on success, or empty
+ * fence on failure. If null, the mapping will be synchronous.
* \param layout a pointer to where the mapped planes' descriptors will be
* stored. On failure, nullptr will be stored here.
* \param addr pointer to an array with at least C2PlanarLayout::MAX_NUM_PLANES
@@ -1755,7 +1726,8 @@
*
* \retval C2_OK the operation was successful
* \retval C2_REFUSED no permission to map the section
- * \retval C2_DUPLICATE there is already a mapped region (caller error)
+ * \retval C2_DUPLICATE there is already a mapped region and this allocation cannot support
+ * multi-mapping (caller error)
* \retval C2_TIMED_OUT the operation timed out
* \retval C2_NO_MEMORY not enough memory to complete the operation
* \retval C2_BAD_VALUE the parameters (rect) are invalid or outside the allocation, or the
@@ -1764,25 +1736,30 @@
*/
virtual c2_status_t map(
- C2Rect rect, C2MemoryUsage usage, int *fenceFd,
+ C2Rect rect, C2MemoryUsage usage, C2Fence *fence,
C2PlanarLayout *layout /* nonnull */, uint8_t **addr /* nonnull */) = 0;
/**
- * Unmaps the last mapped rectangular section.
+ * Unmaps a section of an allocation at |addr| with |rect|. These must be parameters previously
+ * passed to and returned by |map|; otherwise, this operation is a no-op.
*
- * \param fenceFd a pointer to a file descriptor if an async unmapping is requested. If
- * not-null, a release fence FD will be stored here on success, or -1
+ * \param addr pointer to an array with at least C2PlanarLayout::MAX_NUM_PLANES
+ * elements containing the starting addresses of the mapped layers
+ * \param rect boundaries of the mapped section
+ * \param fence a pointer to a fence object if an async unmapping is requested. If
+ * not-null, a release fence will be stored here on success, or empty fence
* on failure. This fence signals when the original allocation contains
- * any changes that happened to the mapped section. If null, the unmapping
+ * all changes that happened to the mapped section. If null, the unmapping
* will be synchronous.
*
* \retval C2_OK the operation was successful
* \retval C2_TIMED_OUT the operation timed out
- * \retval C2_NOT_FOUND there is no mapped region (caller error)
+ * \retval C2_NOT_FOUND there is no such mapped region (caller error)
* \retval C2_CORRUPTED some unknown error prevented the operation from completing (unexpected)
* \retval C2_REFUSED no permission to unmap the section (unexpected - system)
*/
- virtual c2_status_t unmap(C2Fence *fenceFd /* nullable */) = 0;
+ virtual c2_status_t unmap(
+ uint8_t **addr /* nonnull */, C2Rect rect, C2Fence *fence /* nullable */) = 0;
/**
* Returns the allocator ID for this allocation. This is useful to put the handle into context.
@@ -2301,6 +2278,4 @@
/// @}
-} // namespace android
-
#endif // C2BUFFER_H_
diff --git a/media/libstagefright/codec2/include/C2BufferBase.h b/media/libstagefright/codec2/include/C2BufferBase.h
new file mode 100644
index 0000000..68411f2
--- /dev/null
+++ b/media/libstagefright/codec2/include/C2BufferBase.h
@@ -0,0 +1,94 @@
+/*
+ * 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 C2BUFFER_BASE_H_
+#define C2BUFFER_BASE_H_
+
+/// \defgroup allocator Allocation and memory placement
+/// @{
+
+/**
+ * Buffer/memory usage bits. These shall be used by the allocators to select optimal memory type/
+ * pool and buffer layout. Usage bits are conceptually separated into read and write usage, while
+ * the buffer use life-cycle is separated into producers (writers) and consumers (readers).
+ * These two concepts are related but not equivalent: consumers may only read buffers and only
+ * producers may write to buffers; note, however, that buffer producers may also want or need to
+ * read the buffers.
+ *
+ * Read and write buffer usage bits shall be or-ed to arrive at the full buffer usage. Admittedly,
+ * this does not account for the amount of reading and writing (e.g. a buffer may have one or more
+ * readers); however, the proper information necessary to properly weigh the various usages would be
+ * the amount of data read/written for each usage type. This would result in an integer array of
+ * size 64 (or the number of distinct usages) for memory usage, and likely such detailed information
+ * would not always be available.
+ *
+ * That platform-agnostic Codec 2.0 API only defines the bare minimum usages. Platforms shall define
+ * usage bits that are appropriate for the platform.
+ */
+struct C2MemoryUsage {
+// public:
+ /**
+ * Buffer read usage.
+ */
+ enum Read : uint64_t {
+ /** Buffer is read by the CPU. */
+ CPU_READ = 1 << 0,
+ /**
+ * Buffer shall only be read by trusted hardware. The definition of trusted hardware is
+ * platform specific, but this flag is reserved to prevent mapping this block into CPU
+ * readable memory resulting in bus fault. This flag can be used when buffer access must be
+ * protected.
+ */
+ READ_PROTECTED = 1 << 1,
+ };
+
+ /**
+ * Buffer write usage.
+ */
+ enum Write : uint64_t {
+ /** Buffer is writted to by the CPU. */
+ CPU_WRITE = 1 << 2,
+ /**
+ * Buffer shall only be written to by trusted hardware. The definition of trusted hardware
+ * is platform specific, but this flag is reserved to prevent mapping this block into CPU
+ * writable memory resulting in bus fault. This flag can be used when buffer integrity must
+ * be protected.
+ */
+ WRITE_PROTECTED = 1 << 3,
+ };
+
+ enum : uint64_t {
+ /**
+ * Buffer usage bits reserved for the platform. We don't separately reserve read and
+ * write usages as platforms may have asymmetric distribution between them.
+ */
+ PLATFORM_MASK = ~(CPU_READ | CPU_WRITE | READ_PROTECTED | WRITE_PROTECTED),
+ };
+
+ /** Create a usage from separate consumer and producer usage mask. \deprecated */
+ inline C2MemoryUsage(uint64_t consumer, uint64_t producer)
+ : expected(consumer | producer) { }
+
+ inline explicit C2MemoryUsage(uint64_t expected_)
+ : expected(expected_) { }
+
+ uint64_t expected; // expected buffer usage
+};
+
+/// @}
+
+#endif // C2BUFFER_BASE_H_
+
diff --git a/media/libstagefright/codec2/include/C2Component.h b/media/libstagefright/codec2/include/C2Component.h
index 721966b..dbcd82d 100644
--- a/media/libstagefright/codec2/include/C2Component.h
+++ b/media/libstagefright/codec2/include/C2Component.h
@@ -29,36 +29,38 @@
#include <C2Param.h>
#include <C2Work.h>
-namespace android {
-
/// \defgroup components Components
/// @{
-class C2Component;
-
struct C2FieldSupportedValuesQuery {
enum type_t : uint32_t {
POSSIBLE, ///< query all possible values regardless of other settings
CURRENT, ///< query currently possible values given dependent settings
};
- const C2ParamField field;
- const type_t type;
+private:
+ C2ParamField _mField;
+ type_t _mType;
+public:
c2_status_t status;
C2FieldSupportedValues values;
C2FieldSupportedValuesQuery(const C2ParamField &field_, type_t type_)
- : field(field_), type(type_), status(C2_NO_INIT) { }
+ : _mField(field_), _mType(type_), status(C2_NO_INIT) { }
- static C2FieldSupportedValuesQuery&&
+ static C2FieldSupportedValuesQuery
Current(const C2ParamField &field_) {
- return std::move(C2FieldSupportedValuesQuery(field_, CURRENT));
+ return C2FieldSupportedValuesQuery(field_, CURRENT);
}
- static C2FieldSupportedValuesQuery&&
+ static C2FieldSupportedValuesQuery
Possible(const C2ParamField &field_) {
- return std::move(C2FieldSupportedValuesQuery(field_, POSSIBLE));
+ return C2FieldSupportedValuesQuery(field_, POSSIBLE);
}
+
+ inline C2ParamField field() const { return _mField; };
+
+ inline type_t type() const { return _mType; }
};
/**
@@ -396,7 +398,7 @@
domain_t domain; ///< component domain (e.g. audio or video)
kind_t kind; ///< component kind (e.g. encoder, decoder or filter)
rank_t rank; ///< rank used to determine component ordering (the lower the sooner)
- C2StringLiteral mediaType; ///< media type supported by the component
+ C2String mediaType; ///< media type supported by the component
/**
* name alias(es) for backward compatibility.
@@ -947,6 +949,4 @@
/// @}
-} // namespace android
-
#endif // C2COMPONENT_H_
diff --git a/media/libstagefright/codec2/include/C2Config.h b/media/libstagefright/codec2/include/C2Config.h
index 2a2b9de..3f149bb 100644
--- a/media/libstagefright/codec2/include/C2Config.h
+++ b/media/libstagefright/codec2/include/C2Config.h
@@ -19,8 +19,6 @@
#include <C2ParamDef.h>
-namespace android {
-
/// \defgroup config Component configuration
/// @{
@@ -262,6 +260,4 @@
/// @}
-} // namespace android
-
#endif
diff --git a/media/libstagefright/codec2/include/C2Param.h b/media/libstagefright/codec2/include/C2Param.h
index 4d9f707..e0a743c 100644
--- a/media/libstagefright/codec2/include/C2Param.h
+++ b/media/libstagefright/codec2/include/C2Param.h
@@ -23,13 +23,10 @@
#include <stdint.h>
#include <algorithm>
-#include <list>
#include <string>
#include <type_traits>
-
-#define C2_PACK __attribute__((packed))
-
-namespace android {
+#include <utility>
+#include <vector>
/// \addtogroup Parameters
/// @{
@@ -89,7 +86,6 @@
*/
/// \ingroup internal
-struct _C2ParamManipulator;
/**
* Parameter base class.
@@ -303,7 +299,7 @@
DEFINE_FIELD_BASED_COMPARISON_OPERATORS(Index, mIndex)
private:
- friend struct C2Param; // for setStream, makeStreamId, isValid
+ friend struct C2Param; // for setStream, MakeStreamId, isValid
friend struct _C2ParamInspector; // for testing
/**
@@ -324,7 +320,7 @@
/// returns the streamId bitfield for a given |stream|. If stream is invalid,
/// returns an invalid bitfield.
- inline static uint32_t makeStreamId(unsigned stream) {
+ inline static uint32_t MakeStreamId(unsigned stream) {
// saturate stream ID (max value is invalid)
if (stream > MAX_STREAM_ID) {
stream = MAX_STREAM_ID;
@@ -338,7 +334,7 @@
*/
inline bool setStream(unsigned stream) {
if (forStream()) {
- mIndex = (mIndex & ~STREAM_ID_MASK) | makeStreamId(stream);
+ mIndex = (mIndex & ~STREAM_ID_MASK) | MakeStreamId(stream);
return this->stream() < MAX_STREAM_ID;
}
return false;
@@ -459,7 +455,7 @@
// allow undefined or different direction (e.g. as constructed from C2PortParam() vs.
// C2PortParam::input), but still require equivalent type (stream, port or global); otherwise,
// return null.
- inline static const C2Param* ifSuitable(
+ inline static const C2Param* IfSuitable(
const C2Param* o, size_t size, Type type, size_t flexSize = 0, bool checkDir = true) {
if (o == nullptr || o->_mSize < size || (flexSize && ((o->_mSize - size) % flexSize))) {
return nullptr;
@@ -484,7 +480,7 @@
/// base constructor with stream set
inline C2Param(uint32_t paramSize, Index paramIndex, unsigned stream)
: _mSize(paramSize),
- _mIndex(paramIndex | Index::makeStreamId(stream)) {
+ _mIndex(paramIndex | Index::MakeStreamId(stream)) {
if (paramSize > sizeof(C2Param)) {
memset(this + 1, 0, paramSize - sizeof(C2Param));
}
@@ -668,7 +664,7 @@
template<typename S, typename T>
inline C2ParamField(S* param, T* offset)
: _mIndex(param->index()),
- _mFieldId(offset) {}
+ _mFieldId((T*)((uintptr_t)offset - (uintptr_t)param)) {}
/**
* Create a field identifier using a configuration parameter (variable),
@@ -702,7 +698,7 @@
*/
template<typename S>
inline C2ParamField(S* param)
- : _mIndex(param->index()), _mFieldId(0u, param->size()) {}
+ : _mIndex(param->index()), _mFieldId(0u, param->size()) { }
/**
* Equality operator.
@@ -733,44 +729,33 @@
};
/**
- * Structure uniquely specifying a field, an array element of a field, or a
- * parameter in a configuration
- */
-struct C2ParamOrField : public C2ParamField {
-//public:
- template<typename S>
- inline C2ParamOrField(S* param)
- : C2ParamField(param->index(), 0u, param->size()) {}
-};
-
-/**
* A shared (union) representation of numeric values
*/
class C2Value {
public:
/// A union of supported primitive types.
union Primitive {
- int32_t i32; ///< int32_t value
- uint32_t u32; ///< uint32_t value
- c2_cntr32_t c32; ///< c2_cntr32_t value
- int64_t i64; ///< int64_t value
+ // first member is always zero initialized so it must be the largest
uint64_t u64; ///< uint64_t value
+ int64_t i64; ///< int64_t value
c2_cntr64_t c64; ///< c2_cntr64_t value
+ uint32_t u32; ///< uint32_t value
+ int32_t i32; ///< int32_t value
+ c2_cntr32_t c32; ///< c2_cntr32_t value
float fp; ///< float value
// constructors - implicit
- Primitive(int32_t value) : i32(value) { }
- Primitive(uint32_t value) : u32(value) { }
- Primitive(c2_cntr32_t value) : c32(value) { }
- Primitive(int64_t value) : i64(value) { }
Primitive(uint64_t value) : u64(value) { }
+ Primitive(int64_t value) : i64(value) { }
Primitive(c2_cntr64_t value) : c64(value) { }
+ Primitive(uint32_t value) : u32(value) { }
+ Primitive(int32_t value) : i32(value) { }
+ Primitive(c2_cntr32_t value) : c32(value) { }
Primitive(float value) : fp(value) { }
Primitive() : u64(0) { }
- private:
- friend class C2Value;
+ /** gets value out of the union */
template<typename T> const T &ref() const;
};
@@ -857,9 +842,9 @@
STRUCT_FLAG = 0x20000, ///< structs. Marked with this flag in addition to their coreIndex.
};
- typedef std::pair<C2String, C2Value::Primitive> named_value_type;
- typedef std::vector<const named_value_type> named_values_type;
- //typedef std::pair<std::vector<C2String>, std::vector<C2Value::Primitive>> named_values_type;
+ typedef std::pair<C2String, C2Value::Primitive> NamedValueType;
+ typedef std::vector<const NamedValueType> NamedValuesType;
+ //typedef std::pair<std::vector<C2String>, std::vector<C2Value::Primitive>> NamedValuesType;
/**
* Template specialization that returns the named values for a type.
@@ -869,15 +854,15 @@
* \return a vector of name-value pairs.
*/
template<typename B>
- static named_values_type namedValuesFor(const B &);
+ static NamedValuesType namedValuesFor(const B &);
- inline C2FieldDescriptor(uint32_t type, uint32_t length, C2StringLiteral name, size_t offset, size_t size)
- : _mType((type_t)type), _mLength(length), _mName(name), _mFieldId(offset, size) { }
+ inline C2FieldDescriptor(uint32_t type, uint32_t extent, C2StringLiteral name, size_t offset, size_t size)
+ : _mType((type_t)type), _mExtent(extent), _mName(name), _mFieldId(offset, size) { }
template<typename T, class B=typename std::remove_extent<T>::type>
inline C2FieldDescriptor(const T* offset, const char *name)
- : _mType(this->getType((B*)nullptr)),
- _mLength(std::is_array<T>::value ? std::extent<T>::value : 1),
+ : _mType(this->GetType((B*)nullptr)),
+ _mExtent(std::is_array<T>::value ? std::extent<T>::value : 1),
_mName(name),
_mNamedValues(namedValuesFor(*(B*)0)),
_mFieldId(offset) {}
@@ -885,8 +870,8 @@
/*
template<typename T, typename B=typename std::remove_extent<T>::type>
inline C2FieldDescriptor<T, B, false>(T* offset, const char *name)
- : _mType(this->getType((B*)nullptr)),
- _mLength(std::is_array<T>::value ? std::extent<T>::value : 1),
+ : _mType(this->GetType((B*)nullptr)),
+ _mExtent(std::is_array<T>::value ? std::extent<T>::value : 1),
_mName(name),
_mFieldId(offset) {}
*/
@@ -894,8 +879,8 @@
/// \deprecated
template<typename T, typename S, class B=typename std::remove_extent<T>::type>
constexpr inline C2FieldDescriptor(S*, T S::* field, const char *name)
- : _mType(this->getType((B*)nullptr)),
- _mLength(std::is_array<T>::value ? std::extent<T>::value : 1),
+ : _mType(this->GetType((B*)nullptr)),
+ _mExtent(std::is_array<T>::value ? std::extent<T>::value : 1),
_mName(name),
_mFieldId(&(((S*)0)->*field)) {}
@@ -903,11 +888,11 @@
inline type_t type() const { return _mType; }
/// returns the length of the field in case it is an array. Returns 0 for
/// T[] arrays, returns 1 for T[1] arrays as well as if the field is not an array.
- inline size_t length() const { return _mLength; }
+ inline size_t extent() const { return _mExtent; }
/// returns the name of the field
inline C2StringLiteral name() const { return _mName; }
- const named_values_type &namedValues() const { return _mNamedValues; }
+ const NamedValuesType &namedValues() const { return _mNamedValues; }
#if defined(FRIEND_TEST)
friend void PrintTo(const C2FieldDescriptor &, ::std::ostream*);
@@ -916,49 +901,51 @@
#endif
private:
- const type_t _mType;
- const uint32_t _mLength; // the last member can be arbitrary length if it is T[] array,
+ type_t _mType;
+ uint32_t _mExtent; // the last member can be arbitrary length if it is T[] array,
// extending to the end of the parameter (this is marked with
// 0). T[0]-s are not fields.
- const C2StringLiteral _mName;
- const named_values_type _mNamedValues;
+ C2StringLiteral _mName;
+ NamedValuesType _mNamedValues;
- const _C2FieldId _mFieldId; // field identifier (offset and size)
+ _C2FieldId _mFieldId; // field identifier (offset and size)
// NOTE: We do not capture default value(s) here as that may depend on the component.
// NOTE: We also do not capture bestEffort, as 1) this should be true for most fields,
// 2) this is at parameter granularity.
// type resolution
- inline static type_t getType(int32_t*) { return INT32; }
- inline static type_t getType(uint32_t*) { return UINT32; }
- inline static type_t getType(c2_cntr32_t*) { return CNTR32; }
- inline static type_t getType(int64_t*) { return INT64; }
- inline static type_t getType(uint64_t*) { return UINT64; }
- inline static type_t getType(c2_cntr64_t*) { return CNTR64; }
- inline static type_t getType(float*) { return FLOAT; }
- inline static type_t getType(char*) { return STRING; }
- inline static type_t getType(uint8_t*) { return BLOB; }
+ inline static type_t GetType(int32_t*) { return INT32; }
+ inline static type_t GetType(uint32_t*) { return UINT32; }
+ inline static type_t GetType(c2_cntr32_t*) { return CNTR32; }
+ inline static type_t GetType(int64_t*) { return INT64; }
+ inline static type_t GetType(uint64_t*) { return UINT64; }
+ inline static type_t GetType(c2_cntr64_t*) { return CNTR64; }
+ inline static type_t GetType(float*) { return FLOAT; }
+ inline static type_t GetType(char*) { return STRING; }
+ inline static type_t GetType(uint8_t*) { return BLOB; }
template<typename T,
class=typename std::enable_if<std::is_enum<T>::value>::type>
- inline static type_t getType(T*) {
+ inline static type_t GetType(T*) {
typename std::underlying_type<T>::type underlying(0);
- return getType(&underlying);
+ return GetType(&underlying);
}
// verify C2Struct by having a FIELD_LIST and a CORE_INDEX.
template<typename T,
class=decltype(T::CORE_INDEX + 1), class=decltype(T::FIELD_LIST)>
- inline static type_t getType(T*) {
+ inline static type_t GetType(T*) {
static_assert(!std::is_base_of<C2Param, T>::value, "cannot use C2Params as fields");
return (type_t)(T::CORE_INDEX | STRUCT_FLAG);
}
+
+ friend struct _C2ParamInspector;
};
#define DEFINE_NO_NAMED_VALUES_FOR(type) \
-template<> inline C2FieldDescriptor::named_values_type C2FieldDescriptor::namedValuesFor(const type &) { \
- return named_values_type(); \
+template<> inline C2FieldDescriptor::NamedValuesType C2FieldDescriptor::namedValuesFor(const type &) { \
+ return NamedValuesType(); \
}
// We cannot subtype constructor for enumerated types so insted define no named values for
@@ -1031,6 +1018,14 @@
*/
inline bool isPersistent() const { return _mAttrib & IS_PERSISTENT; }
+ inline bool isStrict() const { return _mAttrib & IS_STRICT; }
+
+ inline bool isReadOnly() const { return _mAttrib & IS_READ_ONLY; }
+
+ inline bool isVisible() const { return !(_mAttrib & IS_HIDDEN); }
+
+ inline bool isPublic() const { return !(_mAttrib & IS_INTERNAL); }
+
/// Returns the name of this param.
/// This defaults to the underlying C2Struct's name, but could be altered for a component.
inline C2String name() const { return _mName; }
@@ -1039,31 +1034,52 @@
inline C2Param::Index index() const { return _mIndex; }
/// Returns the indices of parameters that this parameter has a dependency on
- inline const std::vector<C2Param::Index> &dependencies() const { return mDependencies; }
+ inline const std::vector<C2Param::Index> &dependencies() const { return _mDependencies; }
- // TODO: add more constructors that allow setting dependencies and attributes
-
+ /// \deprecated
template<typename T>
inline C2ParamDescriptor(bool isRequired, C2StringLiteral name, const T*)
: _mIndex(T::PARAM_TYPE),
_mAttrib(IS_PERSISTENT | (isRequired ? IS_REQUIRED : 0)),
_mName(name) { }
+ /// \deprecated
inline C2ParamDescriptor(
bool isRequired, C2StringLiteral name, C2Param::Index index)
: _mIndex(index),
_mAttrib(IS_PERSISTENT | (isRequired ? IS_REQUIRED : 0)),
_mName(name) { }
-private:
enum attrib_t : uint32_t {
- IS_REQUIRED = 1u << 0,
- IS_PERSISTENT = 1u << 1,
+ // flags that default on
+ IS_REQUIRED = 1u << 0, ///< parameter is required to be specified
+ IS_PERSISTENT = 1u << 1, ///< parameter retains its value
+ // flags that default off
+ IS_STRICT = 1u << 2, ///< parameter is strict
+ IS_READ_ONLY = 1u << 3, ///< parameter is publicly read-only
+ IS_HIDDEN = 1u << 4, ///< parameter shall not be visible to clients
+ IS_INTERNAL = 1u << 5, ///< parameter shall not be used by framework (other than testing)
};
+
+ inline C2ParamDescriptor(
+ C2Param::Index index, attrib_t attrib, C2StringLiteral name)
+ : _mIndex(index),
+ _mAttrib(attrib),
+ _mName(name) { }
+
+ inline C2ParamDescriptor(
+ C2Param::Index index, attrib_t attrib, C2String &&name,
+ std::vector<C2Param::Index> &&dependencies)
+ : _mIndex(index),
+ _mAttrib(attrib),
+ _mName(name),
+ _mDependencies(std::move(dependencies)) { }
+
+private:
const C2Param::Index _mIndex;
const uint32_t _mAttrib;
const C2String _mName;
- std::vector<C2Param::Index> mDependencies;
+ std::vector<C2Param::Index> _mDependencies;
friend struct _C2ParamInspector;
};
@@ -1153,7 +1169,7 @@
* private:
* // may have private constructors taking number of widths as the first argument
* // This is used by the C2Param factory methods, e.g.
- * // C2VideoFlexWidthsGlobalParam::alloc_unique(size_t, int32_t);
+ * // C2VideoFlexWidthsGlobalParam::AllocUnique(size_t, int32_t);
* C2VideoFlexWidthsStruct(size_t flexCount, int32_t value) {
* for (size_t i = 0; i < flexCount; ++i) {
* widths[i] = value;
@@ -1163,7 +1179,7 @@
* // If the last argument is T[N] or std::initializer_list<T>, the flexCount will
* // be automatically calculated and passed by the C2Param factory methods, e.g.
* // int widths[] = { 1, 2, 3 };
- * // C2VideoFlexWidthsGlobalParam::alloc_unique(widths);
+ * // C2VideoFlexWidthsGlobalParam::AllocUnique(widths);
* template<unsigned N>
* C2VideoFlexWidthsStruct(size_t flexCount, const int32_t(&init)[N]) {
* for (size_t i = 0; i < flexCount; ++i) {
@@ -1276,7 +1292,7 @@
* descriptions, but we want to conserve memory if client only wants the description
* of a few indices.
*/
- virtual std::unique_ptr<C2StructDescriptor> describe(C2Param::CoreIndex coreIndex) = 0;
+ virtual std::unique_ptr<C2StructDescriptor> describe(C2Param::CoreIndex coreIndex) const = 0;
protected:
virtual ~C2ParamReflector() = default;
@@ -1285,9 +1301,9 @@
/**
* Generic supported values for a field.
*
- * This can be either a range or a set of values. The range can be linear or geometric with a
- * clear minimum and maximum value, and can have an optional step size or geometric ratio. Values
- * can optionally represent flags.
+ * This can be either a range or a set of values. The range can be a simple range, an arithmetic,
+ * geometric or multiply-accumulate series with a clear minimum and maximum value. Values can
+ * be discrete values, or can optionally represent flags to be or-ed.
*
* \note Do not use flags to represent bitfields. Use individual values or separate fields instead.
*/
@@ -1300,17 +1316,47 @@
FLAGS ///< a list of flags that can be OR-ed
};
- type_t type;
+ type_t type; /** Type of values for this field. */
typedef C2Value::Primitive Primitive;
+ /**
+ * Range specifier for supported value. Used if type is RANGE.
+ *
+ * If step is 0 and num and denom are both 1, the supported values are any value, for which
+ * min <= value <= max.
+ *
+ * Otherwise, the range represents a geometric/arithmetic/multiply-accumulate series, where
+ * successive supported values can be derived from previous values (starting at min), using the
+ * following formula:
+ * v[0] = min
+ * v[i] = v[i-1] * num / denom + step for i >= 1, while min < v[i] <= max.
+ */
struct {
+ /** Lower end of the range (inclusive). */
Primitive min;
+ /** Upper end of the range (inclusive if permitted by series). */
Primitive max;
+ /** Step between supported values. */
Primitive step;
+ /** Numerator of a geometric series. */
Primitive num;
+ /** Denominator of a geometric series. */
Primitive denom;
} range;
+
+ /**
+ * List of values. Used if type is VALUES or FLAGS.
+ *
+ * If type is VALUES, this is the list of supported values in decreasing preference.
+ *
+ * If type is FLAGS, this vector contains { min-mask, flag1, flag2... }. Basically, the first
+ * value is the required set of flags to be set, and the rest of the values are flags that can
+ * be set independently. FLAGS is only supported for integral types. Supported flags should
+ * not overlap, as it can make validation non-deterministic. The standard validation method
+ * is that starting from the original value, if each flag is removed when fully present (the
+ * min-mask must be fully present), we shall arrive at 0.
+ */
std::vector<Primitive> values;
C2FieldSupportedValues()
@@ -1328,14 +1374,21 @@
range{min, max, (T)0, num, den} { }
template<typename T>
+ C2FieldSupportedValues(T min, T max, T step, T num, T den)
+ : type(RANGE),
+ range{min, max, step, num, den} { }
+
+ /// \deprecated
+ template<typename T>
C2FieldSupportedValues(bool flags, std::initializer_list<T> list)
: type(flags ? FLAGS : VALUES),
range{(T)0, (T)0, (T)0, (T)0, (T)0} {
- for(T value : list) {
+ for (T value : list) {
values.emplace_back(value);
}
}
+ /// \deprecated
template<typename T>
C2FieldSupportedValues(bool flags, const std::vector<T>& list)
: type(flags ? FLAGS : VALUES),
@@ -1347,20 +1400,23 @@
/// \internal
/// \todo: create separate values vs. flags initializer as for flags we want
- /// to list both allowed and disallowed flags
+ /// to list both allowed and required flags
template<typename T, typename E=decltype(C2FieldDescriptor::namedValuesFor(*(T*)0))>
C2FieldSupportedValues(bool flags, const T*)
: type(flags ? FLAGS : VALUES),
range{(T)0, (T)0, (T)0, (T)0, (T)0} {
- C2FieldDescriptor::named_values_type named = C2FieldDescriptor::namedValuesFor(*(T*)0);
- for (const C2FieldDescriptor::named_value_type &item : named) {
+ C2FieldDescriptor::NamedValuesType named = C2FieldDescriptor::namedValuesFor(*(T*)0);
+ if (flags) {
+ values.emplace_back(0); // min-mask defaults to 0
+ }
+ for (const C2FieldDescriptor::NamedValueType &item : named){
values.emplace_back(item.second);
}
}
};
/**
- * Spported values for a specific field.
+ * Supported values for a specific field.
*
* This is a pair of the field specifier together with an optional supported values object.
* This structure is used when reporting parameter configuration failures and conflicts.
@@ -1372,10 +1428,43 @@
/// blobs) describe the supported values for each element (character for string, and bytes for
/// blobs). It is optional for read-only strings and blobs.
std::unique_ptr<C2FieldSupportedValues> values;
+
+ // This struct is meant to be move constructed.
+ C2_DEFAULT_MOVE(C2ParamFieldValues);
+
+ // Copy constructor/assignment is also provided as this object may get copied.
+ C2ParamFieldValues(const C2ParamFieldValues &other)
+ : paramOrField(other.paramOrField),
+ values(other.values ? std::make_unique<C2FieldSupportedValues>(*other.values) : nullptr) { }
+
+ C2ParamFieldValues& operator=(const C2ParamFieldValues &other) {
+ paramOrField = other.paramOrField;
+ values = other.values ? std::make_unique<C2FieldSupportedValues>(*other.values) : nullptr;
+ return *this;
+ }
+
+
+ /**
+ * Construct with no values.
+ */
+ C2ParamFieldValues(const C2ParamField ¶mOrField_)
+ : paramOrField(paramOrField_) { }
+
+ /**
+ * Construct with values.
+ */
+ C2ParamFieldValues(const C2ParamField ¶mOrField_, const C2FieldSupportedValues &values_)
+ : paramOrField(paramOrField_),
+ values(std::make_unique<C2FieldSupportedValues>(values_)) { }
+
+ /**
+ * Construct from fields.
+ */
+ C2ParamFieldValues(const C2ParamField ¶mOrField_, std::unique_ptr<C2FieldSupportedValues> &&values_)
+ : paramOrField(paramOrField_),
+ values(std::move(values_)) { }
};
/// @}
-} // namespace android
-
#endif // C2PARAM_H_
diff --git a/media/libstagefright/codec2/include/C2ParamDef.h b/media/libstagefright/codec2/include/C2ParamDef.h
index 3691e01..86c6833 100644
--- a/media/libstagefright/codec2/include/C2ParamDef.h
+++ b/media/libstagefright/codec2/include/C2ParamDef.h
@@ -24,8 +24,6 @@
#include <C2Param.h>
-namespace android {
-
/// \addtogroup Parameters
/// @{
@@ -38,14 +36,14 @@
struct C2_HIDE _C2Comparable_impl
{
template<typename S, typename=decltype(S() == S())>
- static std::true_type __testEQ(int);
+ static std::true_type TestEqual(int);
template<typename>
- static std::false_type __testEQ(...);
+ static std::false_type TestEqual(...);
template<typename S, typename=decltype(S() != S())>
- static std::true_type __testNE(int);
+ static std::true_type TestNotEqual(int);
template<typename>
- static std::false_type __testNE(...);
+ static std::false_type TestNotEqual(...);
};
/**
@@ -55,30 +53,30 @@
*/
template<typename S>
struct C2_HIDE _C2Comparable
- : public std::integral_constant<bool, decltype(_C2Comparable_impl::__testEQ<S>(0))::value
- || decltype(_C2Comparable_impl::__testNE<S>(0))::value> {
+ : public std::integral_constant<bool, decltype(_C2Comparable_impl::TestEqual<S>(0))::value
+ || decltype(_C2Comparable_impl::TestNotEqual<S>(0))::value> {
};
/// Helper class that checks if a type has a CORE_INDEX constant.
struct C2_HIDE _C2CoreIndexHelper_impl
{
template<typename S, int=S::CORE_INDEX>
- static std::true_type __testCoreIndex(int);
+ static std::true_type TestCoreIndex(int);
template<typename>
- static std::false_type __testCoreIndex(...);
+ static std::false_type TestCoreIndex(...);
};
/// Helper template that verifies a type's CORE_INDEX and creates it if the type does not have one.
template<typename S, int CoreIndex,
- bool HasBase=decltype(_C2CoreIndexHelper_impl::__testCoreIndex<S>(0))::value>
-struct C2_HIDE C2CoreIndexOverride {
+ bool HasBase=decltype(_C2CoreIndexHelper_impl::TestCoreIndex<S>(0))::value>
+struct C2_HIDE _C2CoreIndexOverride {
// TODO: what if we allow structs without CORE_INDEX?
static_assert(CoreIndex == S::CORE_INDEX, "CORE_INDEX differs from structure");
};
/// Specialization for types without a CORE_INDEX.
template<typename S, int CoreIndex>
-struct C2_HIDE C2CoreIndexOverride<S, CoreIndex, false> {
+struct C2_HIDE _C2CoreIndexOverride<S, CoreIndex, false> {
public:
enum : uint32_t {
CORE_INDEX = CoreIndex, ///< CORE_INDEX override.
@@ -87,7 +85,7 @@
/// Helper template that adds a CORE_INDEX to a type if it does not have one.
template<typename S, int CoreIndex>
-struct C2_HIDE C2AddCoreIndex : public S, public C2CoreIndexOverride<S, CoreIndex> {};
+struct C2_HIDE _C2AddCoreIndex : public S, public _C2CoreIndexOverride<S, CoreIndex> {};
/**
* \brief Helper class to check struct requirements for parameters.
@@ -97,7 +95,7 @@
* - expose PARAM_TYPE, and non-flex FLEX_SIZE.
*/
template<typename S, int CoreIndex, unsigned TypeFlags>
-struct C2_HIDE C2StructCheck {
+struct C2_HIDE _C2StructCheck {
static_assert(
std::is_default_constructible<S>::value, "C2 structure must have default constructor");
static_assert(!std::is_polymorphic<S>::value, "C2 structure must not have virtual methods");
@@ -118,15 +116,15 @@
struct C2_HIDE _C2Flexible_impl {
/// specialization for types that have a FLEX_SIZE member
template<typename S, unsigned=S::FLEX_SIZE>
- static std::true_type __testFlexSize(int);
+ static std::true_type TestFlexSize(int);
template<typename>
- static std::false_type __testFlexSize(...);
+ static std::false_type TestFlexSize(...);
};
/// Helper template that returns if a type has an integer FLEX_SIZE member.
template<typename S>
struct C2_HIDE _C2Flexible
- : public std::integral_constant<bool, decltype(_C2Flexible_impl::__testFlexSize<S>(0))::value> {
+ : public std::integral_constant<bool, decltype(_C2Flexible_impl::TestFlexSize<S>(0))::value> {
};
/// Macro to test if a type is flexible (has a FLEX_SIZE member).
@@ -167,9 +165,9 @@
* flexible struct, so may not be needed here)
*/
template<typename S, int ParamIndex, unsigned TypeFlags>
-struct C2_HIDE C2FlexStructCheck :
-// add flexible flag as C2StructCheck defines PARAM_TYPE
- public C2StructCheck<S, ParamIndex | C2Param::CoreIndex::IS_FLEX_FLAG, TypeFlags> {
+struct C2_HIDE _C2FlexStructCheck :
+// add flexible flag as _C2StructCheck defines PARAM_TYPE
+ public _C2StructCheck<S, ParamIndex | C2Param::CoreIndex::IS_FLEX_FLAG, TypeFlags> {
public:
enum : uint32_t {
/// \hideinitializer
@@ -179,12 +177,12 @@
const static std::initializer_list<const C2FieldDescriptor> FIELD_LIST; // TODO assign here
// default constructor needed because of the disabled copy constructor
- inline C2FlexStructCheck() = default;
+ inline _C2FlexStructCheck() = default;
protected:
// cannot copy flexible params
- C2FlexStructCheck(const C2FlexStructCheck<S, ParamIndex, TypeFlags> &) = delete;
- C2FlexStructCheck& operator= (const C2FlexStructCheck<S, ParamIndex, TypeFlags> &) = delete;
+ _C2FlexStructCheck(const _C2FlexStructCheck<S, ParamIndex, TypeFlags> &) = delete;
+ _C2FlexStructCheck& operator= (const _C2FlexStructCheck<S, ParamIndex, TypeFlags> &) = delete;
// constants used for helper methods
enum : uint32_t {
@@ -197,7 +195,7 @@
};
/// returns the allocated size of this param with flexCount, or 0 if it would overflow.
- inline static size_t calcSize(size_t flexCount, size_t size = BASE_SIZE) {
+ inline static size_t CalcSize(size_t flexCount, size_t size = BASE_SIZE) {
if (flexCount <= (MAX_SIZE - size) / S::FLEX_SIZE) {
return size + S::FLEX_SIZE * flexCount;
}
@@ -207,7 +205,7 @@
/// dynamic new operator usable for params of type S
inline void* operator new(size_t size, size_t flexCount) noexcept {
// TODO: assert(size == BASE_SIZE);
- size = calcSize(flexCount, size);
+ size = CalcSize(flexCount, size);
if (size > 0) {
return ::operator new(size);
}
@@ -219,12 +217,12 @@
/// Expose FIELD_LIST from subClass;
template<typename S, int ParamIndex, unsigned TypeFlags>
const std::initializer_list<const C2FieldDescriptor>
-C2FlexStructCheck<S, ParamIndex, TypeFlags>::FIELD_LIST = S::FIELD_LIST;
+_C2FlexStructCheck<S, ParamIndex, TypeFlags>::FIELD_LIST = S::FIELD_LIST;
/// Define From() cast operators for params.
#define DEFINE_CAST_OPERATORS(_Type) \
inline static _Type* From(C2Param *other) { \
- return (_Type*)C2Param::ifSuitable( \
+ return (_Type*)C2Param::IfSuitable( \
other, sizeof(_Type), _Type::PARAM_TYPE, _Type::FLEX_SIZE, \
(_Type::PARAM_TYPE & T::Index::DIR_UNDEFINED) != T::Index::DIR_UNDEFINED); \
} \
@@ -234,40 +232,40 @@
inline static _Type* From(std::nullptr_t) { return nullptr; } \
/**
- * Define flexible allocators (alloc_shared or alloc_unique) for flexible params.
- * - P::alloc_xyz(flexCount, args...): allocate for given flex-count.
- * - P::alloc_xyz(args..., T[]): allocate for size of (and with) init array.
- * - P::alloc_xyz(T[]): allocate for size of (and with) init array with no other args.
- * - P::alloc_xyz(args..., std::initializer_list<T>): allocate for size of (and with) initializer
+ * Define flexible allocators (AllocShared or AllocUnique) for flexible params.
+ * - P::AllocXyz(flexCount, args...): allocate for given flex-count.
+ * - P::AllocXyz(args..., T[]): allocate for size of (and with) init array.
+ * - P::AllocXyz(T[]): allocate for size of (and with) init array with no other args.
+ * - P::AllocXyz(args..., std::initializer_list<T>): allocate for size of (and with) initializer
* list.
*/
-#define DEFINE_FLEXIBLE_ALLOC(_Type, S, ptr) \
+#define DEFINE_FLEXIBLE_ALLOC(_Type, S, ptr, Ptr) \
template<typename ...Args> \
- inline static std::ptr##_ptr<_Type> alloc_##ptr(size_t flexCount, const Args(&... args)) { \
+ inline static std::ptr##_ptr<_Type> Alloc##Ptr(size_t flexCount, const Args(&... args)) { \
return std::ptr##_ptr<_Type>(new(flexCount) _Type(flexCount, args...)); \
} \
/* NOTE: unfortunately this is not supported by clang yet */ \
template<typename ...Args, typename U=typename S::FlexType, unsigned N> \
- inline static std::ptr##_ptr<_Type> alloc_##ptr(const Args(&... args), const U(&init)[N]) { \
+ inline static std::ptr##_ptr<_Type> Alloc##Ptr(const Args(&... args), const U(&init)[N]) { \
return std::ptr##_ptr<_Type>(new(N) _Type(N, args..., init)); \
} \
/* so for now, specialize for no args */ \
template<typename U=typename S::FlexType, unsigned N> \
- inline static std::ptr##_ptr<_Type> alloc_##ptr(const U(&init)[N]) { \
+ inline static std::ptr##_ptr<_Type> Alloc##Ptr(const U(&init)[N]) { \
return std::ptr##_ptr<_Type>(new(N) _Type(N, init)); \
} \
template<typename ...Args, typename U=typename S::FlexType> \
- inline static std::ptr##_ptr<_Type> alloc_##ptr( \
+ inline static std::ptr##_ptr<_Type> Alloc##Ptr( \
const Args(&... args), const std::initializer_list<U> &init) { \
return std::ptr##_ptr<_Type>(new(init.size()) _Type(init.size(), args..., init)); \
} \
/**
- * Define flexible methods alloc_shared, alloc_unique and flexCount.
+ * Define flexible methods AllocShared, AllocUnique and flexCount.
*/
#define DEFINE_FLEXIBLE_METHODS(_Type, S) \
- DEFINE_FLEXIBLE_ALLOC(_Type, S, shared) \
- DEFINE_FLEXIBLE_ALLOC(_Type, S, unique) \
+ DEFINE_FLEXIBLE_ALLOC(_Type, S, shared, Shared) \
+ DEFINE_FLEXIBLE_ALLOC(_Type, S, unique, Unique) \
inline size_t flexCount() const { \
static_assert(sizeof(_Type) == _Type::BASE_SIZE, "incorrect BASE_SIZE"); \
size_t sz = this->size(); \
@@ -314,8 +312,8 @@
* structures.
*/
template<typename T, typename S, int ParamIndex=S::CORE_INDEX, class Flex=void>
-struct C2_HIDE C2GlobalParam : public T, public S, public C2CoreIndexOverride<S, ParamIndex>,
- public C2StructCheck<S, ParamIndex, T::PARAM_KIND | T::Type::DIR_GLOBAL> {
+struct C2_HIDE C2GlobalParam : public T, public S, public _C2CoreIndexOverride<S, ParamIndex>,
+ public _C2StructCheck<S, ParamIndex, T::PARAM_KIND | T::Type::DIR_GLOBAL> {
private:
typedef C2GlobalParam<T, S, ParamIndex> _Type;
@@ -344,14 +342,14 @@
*/
template<typename T, typename S, int ParamIndex>
struct C2_HIDE C2GlobalParam<T, S, ParamIndex, IF_FLEXIBLE(S)>
- : public T, public C2FlexStructCheck<S, ParamIndex, T::PARAM_KIND | T::Type::DIR_GLOBAL> {
+ : public T, public _C2FlexStructCheck<S, ParamIndex, T::PARAM_KIND | T::Type::DIR_GLOBAL> {
private:
typedef C2GlobalParam<T, S, ParamIndex> _Type;
/// Wrapper around base structure's constructor.
template<typename ...Args>
inline C2GlobalParam(size_t flexCount, const Args(&... args))
- : T(_Type::calcSize(flexCount), _Type::PARAM_TYPE), m(flexCount, args...) { }
+ : T(_Type::CalcSize(flexCount), _Type::PARAM_TYPE), m(flexCount, args...) { }
public:
S m; ///< wrapped flexible structure
@@ -380,8 +378,8 @@
* unspecified port expose a setPort method, and add an initial port parameter to the constructor.
*/
template<typename T, typename S, int ParamIndex=S::CORE_INDEX, class Flex=void>
-struct C2_HIDE C2PortParam : public T, public S, public C2CoreIndexOverride<S, ParamIndex>,
- private C2StructCheck<S, ParamIndex, T::PARAM_KIND | T::Index::DIR_UNDEFINED> {
+struct C2_HIDE C2PortParam : public T, public S, public _C2CoreIndexOverride<S, ParamIndex>,
+ private _C2StructCheck<S, ParamIndex, T::PARAM_KIND | T::Index::DIR_UNDEFINED> {
private:
typedef C2PortParam<T, S, ParamIndex> _Type;
@@ -398,8 +396,8 @@
DEFINE_CAST_OPERATORS(_Type)
/// Specialization for an input port parameter.
- struct input : public T, public S, public C2CoreIndexOverride<S, ParamIndex>,
- public C2StructCheck<S, ParamIndex, T::PARAM_KIND | T::Index::DIR_INPUT> {
+ struct input : public T, public S, public _C2CoreIndexOverride<S, ParamIndex>,
+ public _C2StructCheck<S, ParamIndex, T::PARAM_KIND | T::Index::DIR_INPUT> {
/// Wrapper around base structure's constructor.
template<typename ...Args>
inline input(const Args(&... args)) : T(sizeof(_Type), input::PARAM_TYPE), S(args...) { }
@@ -409,8 +407,8 @@
};
/// Specialization for an output port parameter.
- struct output : public T, public S, public C2CoreIndexOverride<S, ParamIndex>,
- public C2StructCheck<S, ParamIndex, T::PARAM_KIND | T::Index::DIR_OUTPUT> {
+ struct output : public T, public S, public _C2CoreIndexOverride<S, ParamIndex>,
+ public _C2StructCheck<S, ParamIndex, T::PARAM_KIND | T::Index::DIR_OUTPUT> {
/// Wrapper around base structure's constructor.
template<typename ...Args>
inline output(const Args(&... args)) : T(sizeof(_Type), output::PARAM_TYPE), S(args...) { }
@@ -440,16 +438,16 @@
*/
template<typename T, typename S, int ParamIndex>
struct C2_HIDE C2PortParam<T, S, ParamIndex, IF_FLEXIBLE(S)>
- : public T, public C2FlexStructCheck<S, ParamIndex, T::PARAM_KIND | T::Type::DIR_UNDEFINED> {
+ : public T, public _C2FlexStructCheck<S, ParamIndex, T::PARAM_KIND | T::Type::DIR_UNDEFINED> {
private:
typedef C2PortParam<T, S, ParamIndex> _Type;
/// Default constructor for basic allocation: new(flexCount) P.
- inline C2PortParam(size_t flexCount) : T(_Type::calcSize(flexCount), _Type::PARAM_TYPE) { }
+ inline C2PortParam(size_t flexCount) : T(_Type::CalcSize(flexCount), _Type::PARAM_TYPE) { }
template<typename ...Args>
/// Wrapper around base structure's constructor while also specifying port/direction.
inline C2PortParam(size_t flexCount, bool _output, const Args(&... args))
- : T(_Type::calcSize(flexCount), _output ? output::PARAM_TYPE : input::PARAM_TYPE),
+ : T(_Type::CalcSize(flexCount), _output ? output::PARAM_TYPE : input::PARAM_TYPE),
m(flexCount, args...) { }
public:
@@ -463,12 +461,12 @@
/// Specialization for an input port parameter.
struct input : public T,
- public C2FlexStructCheck<S, ParamIndex, T::PARAM_KIND | T::Index::DIR_INPUT> {
+ public _C2FlexStructCheck<S, ParamIndex, T::PARAM_KIND | T::Index::DIR_INPUT> {
private:
/// Wrapper around base structure's constructor while also specifying port/direction.
template<typename ...Args>
inline input(size_t flexCount, const Args(&... args))
- : T(_Type::calcSize(flexCount), input::PARAM_TYPE), m(flexCount, args...) { }
+ : T(_Type::CalcSize(flexCount), input::PARAM_TYPE), m(flexCount, args...) { }
public:
S m; ///< wrapped flexible structure
@@ -479,12 +477,12 @@
/// Specialization for an output port parameter.
struct output : public T,
- public C2FlexStructCheck<S, ParamIndex, T::PARAM_KIND | T::Index::DIR_OUTPUT> {
+ public _C2FlexStructCheck<S, ParamIndex, T::PARAM_KIND | T::Index::DIR_OUTPUT> {
private:
/// Wrapper around base structure's constructor while also specifying port/direction.
template<typename ...Args>
inline output(size_t flexCount, const Args(&... args))
- : T(_Type::calcSize(flexCount), output::PARAM_TYPE), m(flexCount, args...) { }
+ : T(_Type::CalcSize(flexCount), output::PARAM_TYPE), m(flexCount, args...) { }
public:
S m; ///< wrapped flexible structure
@@ -516,8 +514,8 @@
* parameter to the constructor.
*/
template<typename T, typename S, int ParamIndex=S::CORE_INDEX, class Flex=void>
-struct C2_HIDE C2StreamParam : public T, public S, public C2CoreIndexOverride<S, ParamIndex>,
- private C2StructCheck<S, ParamIndex,
+struct C2_HIDE C2StreamParam : public T, public S, public _C2CoreIndexOverride<S, ParamIndex>,
+ private _C2StructCheck<S, ParamIndex,
T::PARAM_KIND | T::Index::IS_STREAM_FLAG | T::Index::DIR_UNDEFINED> {
private:
typedef C2StreamParam<T, S, ParamIndex> _Type;
@@ -539,8 +537,8 @@
DEFINE_CAST_OPERATORS(_Type)
/// Specialization for an input stream parameter.
- struct input : public T, public S, public C2CoreIndexOverride<S, ParamIndex>,
- public C2StructCheck<S, ParamIndex,
+ struct input : public T, public S, public _C2CoreIndexOverride<S, ParamIndex>,
+ public _C2StructCheck<S, ParamIndex,
T::PARAM_KIND | T::Index::IS_STREAM_FLAG | T::Type::DIR_INPUT> {
/// Default constructor. Stream-ID is undefined.
inline input() : T(sizeof(_Type), input::PARAM_TYPE) { }
@@ -555,8 +553,8 @@
};
/// Specialization for an output stream parameter.
- struct output : public T, public S, public C2CoreIndexOverride<S, ParamIndex>,
- public C2StructCheck<S, ParamIndex,
+ struct output : public T, public S, public _C2CoreIndexOverride<S, ParamIndex>,
+ public _C2StructCheck<S, ParamIndex,
T::PARAM_KIND | T::Index::IS_STREAM_FLAG | T::Type::DIR_OUTPUT> {
/// Default constructor. Stream-ID is undefined.
inline output() : T(sizeof(_Type), output::PARAM_TYPE) { }
@@ -595,17 +593,17 @@
template<typename T, typename S, int ParamIndex>
struct C2_HIDE C2StreamParam<T, S, ParamIndex, IF_FLEXIBLE(S)>
: public T,
- public C2FlexStructCheck<S, ParamIndex,
+ public _C2FlexStructCheck<S, ParamIndex,
T::PARAM_KIND | T::Index::IS_STREAM_FLAG | T::Index::DIR_UNDEFINED> {
private:
typedef C2StreamParam<T, S, ParamIndex> _Type;
/// Default constructor. Port/direction and stream-ID is undefined.
- inline C2StreamParam(size_t flexCount) : T(_Type::calcSize(flexCount), _Type::PARAM_TYPE, 0u) { }
+ inline C2StreamParam(size_t flexCount) : T(_Type::CalcSize(flexCount), _Type::PARAM_TYPE, 0u) { }
/// Wrapper around base structure's constructor while also specifying port/direction and
/// stream-ID.
template<typename ...Args>
inline C2StreamParam(size_t flexCount, bool _output, unsigned stream, const Args(&... args))
- : T(_Type::calcSize(flexCount), _output ? output::PARAM_TYPE : input::PARAM_TYPE, stream),
+ : T(_Type::CalcSize(flexCount), _output ? output::PARAM_TYPE : input::PARAM_TYPE, stream),
m(flexCount, args...) { }
public:
@@ -621,15 +619,15 @@
/// Specialization for an input stream parameter.
struct input : public T,
- public C2FlexStructCheck<S, ParamIndex,
+ public _C2FlexStructCheck<S, ParamIndex,
T::PARAM_KIND | T::Index::IS_STREAM_FLAG | T::Type::DIR_INPUT> {
private:
/// Default constructor. Stream-ID is undefined.
- inline input(size_t flexCount) : T(_Type::calcSize(flexCount), input::PARAM_TYPE) { }
+ inline input(size_t flexCount) : T(_Type::CalcSize(flexCount), input::PARAM_TYPE) { }
/// Wrapper around base structure's constructor while also specifying stream-ID.
template<typename ...Args>
inline input(size_t flexCount, unsigned stream, const Args(&... args))
- : T(_Type::calcSize(flexCount), input::PARAM_TYPE, stream), m(flexCount, args...) { }
+ : T(_Type::CalcSize(flexCount), input::PARAM_TYPE, stream), m(flexCount, args...) { }
public:
S m; ///< wrapped flexible structure
@@ -643,15 +641,15 @@
/// Specialization for an output stream parameter.
struct output : public T,
- public C2FlexStructCheck<S, ParamIndex,
+ public _C2FlexStructCheck<S, ParamIndex,
T::PARAM_KIND | T::Index::IS_STREAM_FLAG | T::Type::DIR_OUTPUT> {
private:
/// Default constructor. Stream-ID is undefined.
- inline output(size_t flexCount) : T(_Type::calcSize(flexCount), output::PARAM_TYPE) { }
+ inline output(size_t flexCount) : T(_Type::CalcSize(flexCount), output::PARAM_TYPE) { }
/// Wrapper around base structure's constructor while also specifying stream-ID.
template<typename ...Args>
inline output(size_t flexCount, unsigned stream, const Args(&... args))
- : T(_Type::calcSize(flexCount), output::PARAM_TYPE, stream), m(flexCount, args...) { }
+ : T(_Type::CalcSize(flexCount), output::PARAM_TYPE, stream), m(flexCount, args...) { }
public:
S m; ///< wrapped flexible structure
@@ -820,19 +818,19 @@
private:
/// Construct from a C2MemoryBlock.
- /// Used only by the flexible parameter allocators (alloc_unique & alloc_shared).
+ /// Used only by the flexible parameter allocators (AllocUnique & AllocShared).
inline C2SimpleArrayStruct(size_t flexCount, const C2MemoryBlock<T> &block) {
_C2ValueArrayHelper::init(values, flexCount, block);
}
/// Construct from an initializer list.
- /// Used only by the flexible parameter allocators (alloc_unique & alloc_shared).
+ /// Used only by the flexible parameter allocators (AllocUnique & AllocShared).
inline C2SimpleArrayStruct(size_t flexCount, const std::initializer_list<T> &init) {
_C2ValueArrayHelper::init(values, flexCount, init);
}
/// Construct from another flexible array.
- /// Used only by the flexible parameter allocators (alloc_unique & alloc_shared).
+ /// Used only by the flexible parameter allocators (AllocUnique & AllocShared).
template<unsigned N>
inline C2SimpleArrayStruct(size_t flexCount, const T(&init)[N]) {
_C2ValueArrayHelper::init(values, flexCount, init);
@@ -905,6 +903,4 @@
/// @}
-} // namespace android
-
#endif // C2PARAM_DEF_H_
diff --git a/media/libstagefright/codec2/include/C2Work.h b/media/libstagefright/codec2/include/C2Work.h
index b6c5814..1a35519 100644
--- a/media/libstagefright/codec2/include/C2Work.h
+++ b/media/libstagefright/codec2/include/C2Work.h
@@ -28,8 +28,6 @@
#include <list>
#include <vector>
-namespace android {
-
/// \defgroup work Work and data processing
/// @{
@@ -39,17 +37,20 @@
*/
struct C2SettingResult {
enum Failure : uint32_t {
- READ_ONLY, ///< parameter is read-only and cannot be set
- MISMATCH, ///< parameter mismatches input data
- BAD_VALUE, ///< parameter does not accept value
+ /* parameter failures below */
BAD_TYPE, ///< parameter is not supported
BAD_PORT, ///< parameter is not supported on the specific port
BAD_INDEX, ///< parameter is not supported on the specific stream
- CONFLICT, ///< parameter is in conflict with an/other setting(s)
- /// parameter is out of range due to other settings (this failure mode
- /// can only be used for strict parameters)
- UNSUPPORTED,
+ READ_ONLY, ///< parameter is read-only and cannot be set
+ MISMATCH, ///< parameter mismatches input data
+ /* field failures below */
+ BAD_VALUE, ///< parameter does not accept value for the field at all
+ CONFLICT, ///< parameter field value is in conflict with an/other setting(s)
+
+ /// parameter field is out of range due to other settings (this failure mode
+ /// can only be used for strict calculated parameters)
+ UNSUPPORTED,
/// requested parameter value is in conflict with an/other setting(s)
/// and has been corrected to the closest supported value. This failure
@@ -60,14 +61,15 @@
Failure failure; ///< failure code
- /// Failing (or corrected) field. Currently supported values for the field. This is set if
+ /// Failing (or corrected) field or parameterand optionally, currently supported values for the
+ /// field. Values must only be set for field failures other than BAD_VALUE, and only if they are
/// different from the globally supported values (e.g. due to restrictions by another param or
- /// input data)
- /// \todo need to define suggestions for masks to be set and unset.
+ /// input data).
C2ParamFieldValues field;
/// Conflicting parameters or fields with optional suggestions with (optional) suggested values
- /// for any conflicting fields to avoid the conflict.
+ /// for any conflicting fields to avoid the conflict. Must only be set for CONFLICT, UNSUPPORTED
+ /// and INFO_CONFLICT failure codes.
std::vector<C2ParamFieldValues> conflicts;
};
@@ -75,9 +77,8 @@
// WORK
// ================================================================================================
-// c2_node_id_t-s
+/** Unique ID for a processing node. */
typedef uint32_t c2_node_id_t;
-typedef c2_node_id_t c2_node_id_t;
enum {
kParamIndexWorkOrdinal,
@@ -211,6 +212,4 @@
/// @}
-} // namespace android
-
#endif // C2WORK_H_
diff --git a/media/libstagefright/codec2/include/SimpleC2Interface.h b/media/libstagefright/codec2/include/SimpleC2Interface.h
index b934f12..310096f 100644
--- a/media/libstagefright/codec2/include/SimpleC2Interface.h
+++ b/media/libstagefright/codec2/include/SimpleC2Interface.h
@@ -27,13 +27,9 @@
public:
inline Builder(
const char *name,
- c2_node_id_t id)
- : mIntf(new SimpleC2Interface(name, id)) {}
-
- inline Builder(
- const char *name,
c2_node_id_t id,
- std::function<void(::android::SimpleC2Interface*)> deleter)
+ std::function<void(C2ComponentInterface*)> deleter =
+ std::default_delete<C2ComponentInterface>())
: mIntf(new SimpleC2Interface(name, id), deleter) {}
inline Builder &inputFormat(C2FormatKind input) {
@@ -46,6 +42,28 @@
return *this;
}
+ inline Builder &inputMediaType(const char *mediaType, size_t maxLen = 128) {
+ mIntf->mInputMediaType = C2PortMimeConfig::input::AllocShared(maxLen);
+ std::strncpy(mIntf->mInputMediaType->m.value, mediaType, maxLen);
+ return *this;
+ }
+
+ inline Builder &outputMediaType(const char *mediaType, size_t maxLen = 128) {
+ mIntf->mOutputMediaType = C2PortMimeConfig::output::AllocShared(maxLen);
+ std::strncpy(mIntf->mOutputMediaType->m.value, mediaType, maxLen);
+ return *this;
+ }
+
+ template<size_t N>
+ inline Builder &inputMediaType(const char mediaType[N]) {
+ return inputMediaType(mediaType, N);
+ }
+
+ template<size_t N>
+ inline Builder &outputMediaType(const char mediaType[N]) {
+ return outputMediaType(mediaType, N);
+ }
+
inline std::shared_ptr<SimpleC2Interface> build() {
return mIntf;
}
@@ -89,6 +107,8 @@
const c2_node_id_t mId;
C2StreamFormatConfig::input mInputFormat;
C2StreamFormatConfig::output mOutputFormat;
+ std::shared_ptr<C2PortMimeConfig::input> mInputMediaType;
+ std::shared_ptr<C2PortMimeConfig::output> mOutputMediaType;
SimpleC2Interface() = delete;
};
diff --git a/media/libstagefright/codec2/include/android-C2Buffer.h b/media/libstagefright/codec2/include/android-C2Buffer.h
new file mode 100644
index 0000000..c71f2cf
--- /dev/null
+++ b/media/libstagefright/codec2/include/android-C2Buffer.h
@@ -0,0 +1,65 @@
+/*
+ * 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_C2BUFFER_H_
+#define ANDROID_C2BUFFER_H_
+
+#include <cutils/native_handle.h>
+#include <hardware/gralloc.h>
+
+/* Use android native handle for C2Handle */
+typedef ::native_handle_t C2Handle;
+
+namespace android {
+
+/**
+ * Android platform buffer/memory usage bits.
+ */
+struct C2AndroidMemoryUsage : public C2MemoryUsage {
+// public:
+ /**
+ * Reuse gralloc flags where possible, as Codec 2.0 API only uses bits 0 and 1.
+ */
+ enum Consumer : uint64_t {
+ RENDERSCRIPT_READ = GRALLOC_USAGE_RENDERSCRIPT,
+ HW_TEXTURE_READ = GRALLOC_USAGE_HW_TEXTURE,
+ HW_COMPOSER_READ = GRALLOC_USAGE_HW_COMPOSER,
+ HW_CODEC_READ = GRALLOC_USAGE_HW_VIDEO_ENCODER,
+ READ_PROTECTED = GRALLOC_USAGE_PROTECTED,
+ };
+
+ enum Producer : uint64_t {
+ RENDERSCRIPT_WRITE = GRALLOC_USAGE_RENDERSCRIPT,
+ HW_TEXTURE_WRITE = GRALLOC_USAGE_HW_RENDER,
+ HW_COMPOSER_WRITE = GRALLOC_USAGE_HW_COMPOSER | GRALLOC_USAGE_HW_RENDER,
+ HW_CODEC_WRITE = GRALLOC_USAGE_HW_VIDEO_ENCODER,
+ WRITE_PROTECTED = GRALLOC_USAGE_PROTECTED,
+ };
+
+ /**
+ * Convert from gralloc usage.
+ */
+ static C2MemoryUsage FromGrallocUsage(uint64_t usage);
+
+ /**
+ * Convert to gralloc usage.
+ */
+ uint64_t asGrallocUsage() const;
+};
+
+} // namespace android
+
+#endif // ANDROID_C2BUFFER_H_
diff --git a/media/libstagefright/codec2/include/media/stagefright/codec2/1.0/InputSurface.h b/media/libstagefright/codec2/include/media/stagefright/codec2/1.0/InputSurface.h
index e46d03c..b011a06 100644
--- a/media/libstagefright/codec2/include/media/stagefright/codec2/1.0/InputSurface.h
+++ b/media/libstagefright/codec2/include/media/stagefright/codec2/1.0/InputSurface.h
@@ -48,7 +48,7 @@
// Methods from IInputSurface
sp<InputSurfaceConnection> connectToComponent(
- const std::shared_ptr<::android::C2Component> &comp);
+ const std::shared_ptr<::C2Component> &comp);
// TODO: intf()
static sp<InputSurface> Create();
diff --git a/media/libstagefright/codec2/include/media/stagefright/codec2/1.0/InputSurfaceConnection.h b/media/libstagefright/codec2/include/media/stagefright/codec2/1.0/InputSurfaceConnection.h
index fc19acd..b24a416 100644
--- a/media/libstagefright/codec2/include/media/stagefright/codec2/1.0/InputSurfaceConnection.h
+++ b/media/libstagefright/codec2/include/media/stagefright/codec2/1.0/InputSurfaceConnection.h
@@ -25,9 +25,6 @@
#include <media/stagefright/codec2/1.0/InputSurfaceConnection.h>
namespace android {
-
-class C2Allocator;
-
namespace hardware {
namespace media {
namespace c2 {
diff --git a/media/libstagefright/codec2/tests/C2ComponentInterface_test.cpp b/media/libstagefright/codec2/tests/C2ComponentInterface_test.cpp
index 7da824b..e555e8c 100644
--- a/media/libstagefright/codec2/tests/C2ComponentInterface_test.cpp
+++ b/media/libstagefright/codec2/tests/C2ComponentInterface_test.cpp
@@ -37,7 +37,7 @@
template <class T> std::unique_ptr<T> alloc_unique_cstr(const char *cstr) {
size_t len = strlen(cstr);
- std::unique_ptr<T> ptr = T::alloc_unique(len);
+ std::unique_ptr<T> ptr = T::AllocUnique(len);
memcpy(ptr->m.value, cstr, len);
return ptr;
}
@@ -182,7 +182,7 @@
template <> std::unique_ptr<C2PortMimeConfig::input> makeParam() {
// TODO(hiroh): Set more precise length.
- return C2PortMimeConfig::input::alloc_unique(100);
+ return C2PortMimeConfig::input::AllocUnique(100);
}
#define TRACED_FAILURE(func) \
diff --git a/media/libstagefright/codec2/tests/C2Param_test.cpp b/media/libstagefright/codec2/tests/C2Param_test.cpp
index 1a29add..fcfbafd 100644
--- a/media/libstagefright/codec2/tests/C2Param_test.cpp
+++ b/media/libstagefright/codec2/tests/C2Param_test.cpp
@@ -23,8 +23,6 @@
#include <util/C2ParamUtils.h>
#include <C2ParamDef.h>
-namespace android {
-
void PrintTo(const _C2FieldId &id, ::std::ostream* os) {
*os << "@" << id._mOffset << "+" << id._mSize;
}
@@ -47,14 +45,14 @@
}
}
*os << " " << fd.name();
- if (fd.length() > 1) {
- *os << "[" << fd.length() << "]";
- } else if (fd.length() == 0) {
+ if (fd.extent() > 1) {
+ *os << "[" << fd.extent() << "]";
+ } else if (fd.extent() == 0) {
*os << "[]";
}
*os << " (";
PrintTo(fd._mFieldId, os);
- *os << "*" << fd.length() << ")";
+ *os << "*" << fd.extent() << ")";
}
enum C2ParamIndexType : C2Param::type_index_t {
@@ -104,7 +102,7 @@
bool operator==(const C2FieldDescriptor &a, const C2FieldDescriptor &b) {
return a.type() == b.type()
- && a.length() == b.length()
+ && a.extent() == b.extent()
&& strcmp(a.name(), b.name()) == 0
&& a._mFieldId == b._mFieldId;
}
@@ -142,7 +140,7 @@
// verify first field descriptor
EXPECT_EQ(FD::INT32, fields[0].type());
EXPECT_STREQ("s32", fields[0].name());
- EXPECT_EQ(1u, fields[0].length());
+ EXPECT_EQ(1u, fields[0].extent());
EXPECT_EQ(_C2FieldId(0, 4), fields[0]._mFieldId);
EXPECT_EQ(expected[0], fields[0]);
@@ -738,7 +736,7 @@
s.value = 11;
s = 12;
(void)C2NumberConfig3::FIELD_LIST;
- std::shared_ptr<C2VideoNameConfig> n = C2VideoNameConfig::alloc_shared(25);
+ std::shared_ptr<C2VideoNameConfig> n = C2VideoNameConfig::AllocShared(25);
strcpy(n->m.value, "lajos");
C2NumberConfig4 t(false, 0, 11);
t.value = 15;
@@ -766,10 +764,10 @@
std::list<const C2FieldDescriptor> myList = C2NumberConfig::FIELD_LIST;
- std::unique_ptr<android::C2ParamDescriptor> __test_describe(uint32_t paramType) {
+ std::unique_ptr<C2ParamDescriptor> __test_describe(uint32_t paramType) {
std::list<const C2FieldDescriptor> fields = describeC2Params<C2NumberConfig>();
- auto widths = C2NumbersInfo::alloc_shared(5);
+ auto widths = C2NumbersInfo::AllocShared(5);
widths->flexCount();
widths->m.mNumbers[4] = 1;
@@ -1112,7 +1110,7 @@
{
C2NumberInfo inf(100);
- std::unique_ptr<C2NumbersTuning> tun_ = C2NumbersTuning::alloc_unique(1);
+ std::unique_ptr<C2NumbersTuning> tun_ = C2NumbersTuning::AllocUnique(1);
EXPECT_EQ(tun.coreIndex(), inf.coreIndex());
EXPECT_NE(tun.coreIndex(), tun_->coreIndex());
@@ -1663,8 +1661,8 @@
void StaticTestAddCoreIndex() {
struct nobase {};
struct base { enum : uint32_t { CORE_INDEX = 1 }; };
- static_assert(C2AddCoreIndex<nobase, 2>::CORE_INDEX == 2, "should be 2");
- static_assert(C2AddCoreIndex<base, 1>::CORE_INDEX == 1, "should be 1");
+ static_assert(_C2AddCoreIndex<nobase, 2>::CORE_INDEX == 2, "should be 2");
+ static_assert(_C2AddCoreIndex<base, 1>::CORE_INDEX == 1, "should be 1");
}
class TestFlexHelper {
@@ -1711,10 +1709,10 @@
EXPECT_EQ(index.typeIndex(), kParamIndexNumbers);
}
- std::unique_ptr<C2NumbersTuning> tun_ = C2NumbersTuning::alloc_unique(1);
+ std::unique_ptr<C2NumbersTuning> tun_ = C2NumbersTuning::AllocUnique(1);
tun_->m.mNumbers[0] = 100;
std::unique_ptr<const C2NumbersTuning> tun = std::move(tun_);
- std::shared_ptr<C2NumbersTuning> btun = C2NumbersTuning::alloc_shared(1);
+ std::shared_ptr<C2NumbersTuning> btun = C2NumbersTuning::AllocShared(1);
{
// flags & invariables
@@ -1776,24 +1774,24 @@
EXPECT_EQ(*(C2Param::Copy(*tun)), *tun);
}
- std::unique_ptr<C2NumbersPortTuning> outp1_(C2NumbersPortTuning::alloc_unique(1, true)),
- inp1_ = C2NumbersPortTuning::alloc_unique(1, false);
+ std::unique_ptr<C2NumbersPortTuning> outp1_(C2NumbersPortTuning::AllocUnique(1, true)),
+ inp1_ = C2NumbersPortTuning::AllocUnique(1, false);
outp1_->m.mNumbers[0] = 100;
inp1_->m.mNumbers[0] = 100;
std::unique_ptr<const C2NumbersPortTuning> outp1 = std::move(outp1_);
std::unique_ptr<const C2NumbersPortTuning> inp1 = std::move(inp1_);
- std::shared_ptr<C2NumbersPortTuning> boutp1(C2NumbersPortTuning::alloc_shared(1)),
- binp1 = C2NumbersPortTuning::alloc_shared(1),
- binp3 = C2NumbersPortTuning::alloc_shared(1, false);
+ std::shared_ptr<C2NumbersPortTuning> boutp1(C2NumbersPortTuning::AllocShared(1)),
+ binp1 = C2NumbersPortTuning::AllocShared(1),
+ binp3 = C2NumbersPortTuning::AllocShared(1, false);
binp3->m.mNumbers[0] = 100;
- std::unique_ptr<C2NumbersPortTuning::input> inp2_(C2NumbersPortTuning::input::alloc_unique(1));
+ std::unique_ptr<C2NumbersPortTuning::input> inp2_(C2NumbersPortTuning::input::AllocUnique(1));
inp2_->m.mNumbers[0] = 100;
std::unique_ptr<const C2NumbersPortTuning::input> inp2 = std::move(inp2_);
- std::shared_ptr<C2NumbersPortTuning::input> binp2(C2NumbersPortTuning::input::alloc_shared(1));
- std::unique_ptr<C2NumbersPortTuning::output> outp2_(C2NumbersPortTuning::output::alloc_unique(1));
+ std::shared_ptr<C2NumbersPortTuning::input> binp2(C2NumbersPortTuning::input::AllocShared(1));
+ std::unique_ptr<C2NumbersPortTuning::output> outp2_(C2NumbersPortTuning::output::AllocUnique(1));
outp2_->m.mNumbers[0] = 100;
std::unique_ptr<const C2NumbersPortTuning::output> outp2 = std::move(outp2_);
- std::shared_ptr<C2NumbersPortTuning::output> boutp2(C2NumbersPortTuning::output::alloc_shared(1));
+ std::shared_ptr<C2NumbersPortTuning::output> boutp2(C2NumbersPortTuning::output::AllocShared(1));
{
static_assert(canCallSetPort(*binp3), "should be able to");
@@ -2001,24 +1999,24 @@
EXPECT_EQ(*(C2Param::Copy(*outp2)), *outp2);
}
- std::unique_ptr<C2NumbersStreamTuning> outs1_(C2NumbersStreamTuning::alloc_unique(1, true, 1u));
+ std::unique_ptr<C2NumbersStreamTuning> outs1_(C2NumbersStreamTuning::AllocUnique(1, true, 1u));
outs1_->m.mNumbers[0] = 100;
std::unique_ptr<const C2NumbersStreamTuning> outs1 = std::move(outs1_);
- std::unique_ptr<C2NumbersStreamTuning> ins1_(C2NumbersStreamTuning::alloc_unique(1, false, 1u));
+ std::unique_ptr<C2NumbersStreamTuning> ins1_(C2NumbersStreamTuning::AllocUnique(1, false, 1u));
ins1_->m.mNumbers[0] = 100;
std::unique_ptr<const C2NumbersStreamTuning> ins1 = std::move(ins1_);
- std::shared_ptr<C2NumbersStreamTuning> bouts1(C2NumbersStreamTuning::alloc_shared(1));
- std::shared_ptr<C2NumbersStreamTuning> bins1(C2NumbersStreamTuning::alloc_shared(1));
- std::shared_ptr<C2NumbersStreamTuning> bins3(C2NumbersStreamTuning::alloc_shared(1, false, 1u));
+ std::shared_ptr<C2NumbersStreamTuning> bouts1(C2NumbersStreamTuning::AllocShared(1));
+ std::shared_ptr<C2NumbersStreamTuning> bins1(C2NumbersStreamTuning::AllocShared(1));
+ std::shared_ptr<C2NumbersStreamTuning> bins3(C2NumbersStreamTuning::AllocShared(1, false, 1u));
bins3->m.mNumbers[0] = 100;
- std::unique_ptr<C2NumbersStreamTuning::input> ins2_(C2NumbersStreamTuning::input::alloc_unique(1, 1u));
+ std::unique_ptr<C2NumbersStreamTuning::input> ins2_(C2NumbersStreamTuning::input::AllocUnique(1, 1u));
ins2_->m.mNumbers[0] = 100;
std::unique_ptr<const C2NumbersStreamTuning::input> ins2 = std::move(ins2_);
- std::shared_ptr<C2NumbersStreamTuning::input> bins2(C2NumbersStreamTuning::input::alloc_shared(1));
- std::unique_ptr<C2NumbersStreamTuning::output> outs2_(C2NumbersStreamTuning::output::alloc_unique(1, 1u));
+ std::shared_ptr<C2NumbersStreamTuning::input> bins2(C2NumbersStreamTuning::input::AllocShared(1));
+ std::unique_ptr<C2NumbersStreamTuning::output> outs2_(C2NumbersStreamTuning::output::AllocUnique(1, 1u));
outs2_->m.mNumbers[0] = 100;
std::unique_ptr<const C2NumbersStreamTuning::output> outs2 = std::move(outs2_);
- std::shared_ptr<C2NumbersStreamTuning::output> bouts2(C2NumbersStreamTuning::output::alloc_shared(1));
+ std::shared_ptr<C2NumbersStreamTuning::output> bouts2(C2NumbersStreamTuning::output::AllocShared(1));
{
static_assert(canCallSetPort(*bins3), "should be able to");
@@ -2241,7 +2239,7 @@
std::list<const C2FieldDescriptor> fields = int32Value.FIELD_LIST;
EXPECT_EQ(1u, fields.size());
EXPECT_EQ(FD::INT32, fields.cbegin()->type());
- EXPECT_EQ(1u, fields.cbegin()->length());
+ EXPECT_EQ(1u, fields.cbegin()->extent());
EXPECT_EQ(C2String("value"), fields.cbegin()->name());
}
@@ -2252,7 +2250,7 @@
std::list<const C2FieldDescriptor> fields = uint32Value.FIELD_LIST;
EXPECT_EQ(1u, fields.size());
EXPECT_EQ(FD::UINT32, fields.cbegin()->type());
- EXPECT_EQ(1u, fields.cbegin()->length());
+ EXPECT_EQ(1u, fields.cbegin()->extent());
EXPECT_EQ(C2String("value"), fields.cbegin()->name());
}
@@ -2263,7 +2261,7 @@
std::list<const C2FieldDescriptor> fields = int64Value.FIELD_LIST;
EXPECT_EQ(1u, fields.size());
EXPECT_EQ(FD::INT64, fields.cbegin()->type());
- EXPECT_EQ(1u, fields.cbegin()->length());
+ EXPECT_EQ(1u, fields.cbegin()->extent());
EXPECT_EQ(C2String("value"), fields.cbegin()->name());
}
@@ -2274,7 +2272,7 @@
std::list<const C2FieldDescriptor> fields = uint64Value.FIELD_LIST;
EXPECT_EQ(1u, fields.size());
EXPECT_EQ(FD::UINT64, fields.cbegin()->type());
- EXPECT_EQ(1u, fields.cbegin()->length());
+ EXPECT_EQ(1u, fields.cbegin()->extent());
EXPECT_EQ(C2String("value"), fields.cbegin()->name());
}
@@ -2285,24 +2283,24 @@
std::list<const C2FieldDescriptor> fields = floatValue.FIELD_LIST;
EXPECT_EQ(1u, fields.size());
EXPECT_EQ(FD::FLOAT, fields.cbegin()->type());
- EXPECT_EQ(1u, fields.cbegin()->length());
+ EXPECT_EQ(1u, fields.cbegin()->extent());
EXPECT_EQ(C2String("value"), fields.cbegin()->name());
}
{
uint8_t initValue[] = "ABCD";
typedef C2GlobalParam<C2Setting, C2BlobValue, 0> BlobSetting;
- std::unique_ptr<BlobSetting> blobValue = BlobSetting::alloc_unique(6, C2ConstMemoryBlock<uint8_t>(initValue));
+ std::unique_ptr<BlobSetting> blobValue = BlobSetting::AllocUnique(6, C2ConstMemoryBlock<uint8_t>(initValue));
static_assert(std::is_same<decltype(blobValue->m.value), uint8_t[]>::value, "should be uint8_t[]");
EXPECT_EQ(0, memcmp(blobValue->m.value, "ABCD\0", 6));
EXPECT_EQ(6u, blobValue->flexCount());
std::list<const C2FieldDescriptor> fields = blobValue->FIELD_LIST;
EXPECT_EQ(1u, fields.size());
EXPECT_EQ(FD::BLOB, fields.cbegin()->type());
- EXPECT_EQ(0u, fields.cbegin()->length());
+ EXPECT_EQ(0u, fields.cbegin()->extent());
EXPECT_EQ(C2String("value"), fields.cbegin()->name());
- blobValue = BlobSetting::alloc_unique(3, C2ConstMemoryBlock<uint8_t>(initValue));
+ blobValue = BlobSetting::AllocUnique(3, C2ConstMemoryBlock<uint8_t>(initValue));
EXPECT_EQ(0, memcmp(blobValue->m.value, "ABC", 3));
EXPECT_EQ(3u, blobValue->flexCount());
}
@@ -2310,30 +2308,30 @@
{
constexpr char initValue[] = "ABCD";
typedef C2GlobalParam<C2Setting, C2StringValue, 0> StringSetting;
- std::unique_ptr<StringSetting> stringValue = StringSetting::alloc_unique(6, C2ConstMemoryBlock<char>(initValue));
- stringValue = StringSetting::alloc_unique(6, initValue);
+ std::unique_ptr<StringSetting> stringValue = StringSetting::AllocUnique(6, C2ConstMemoryBlock<char>(initValue));
+ stringValue = StringSetting::AllocUnique(6, initValue);
static_assert(std::is_same<decltype(stringValue->m.value), char[]>::value, "should be char[]");
EXPECT_EQ(0, memcmp(stringValue->m.value, "ABCD\0", 6));
EXPECT_EQ(6u, stringValue->flexCount());
std::list<const C2FieldDescriptor> fields = stringValue->FIELD_LIST;
EXPECT_EQ(1u, fields.size());
EXPECT_EQ(FD::STRING, fields.cbegin()->type());
- EXPECT_EQ(0u, fields.cbegin()->length());
+ EXPECT_EQ(0u, fields.cbegin()->extent());
EXPECT_EQ(C2String("value"), fields.cbegin()->name());
- stringValue = StringSetting::alloc_unique(3, C2ConstMemoryBlock<char>(initValue));
+ stringValue = StringSetting::AllocUnique(3, C2ConstMemoryBlock<char>(initValue));
EXPECT_EQ(0, memcmp(stringValue->m.value, "AB", 3));
EXPECT_EQ(3u, stringValue->flexCount());
- stringValue = StringSetting::alloc_unique(11, "initValue");
+ stringValue = StringSetting::AllocUnique(11, "initValue");
EXPECT_EQ(0, memcmp(stringValue->m.value, "initValue\0", 11));
EXPECT_EQ(11u, stringValue->flexCount());
- stringValue = StringSetting::alloc_unique(initValue);
+ stringValue = StringSetting::AllocUnique(initValue);
EXPECT_EQ(0, memcmp(stringValue->m.value, "ABCD", 5));
EXPECT_EQ(5u, stringValue->flexCount());
- stringValue = StringSetting::alloc_unique({ 'A', 'B', 'C', 'D' });
+ stringValue = StringSetting::AllocUnique({ 'A', 'B', 'C', 'D' });
EXPECT_EQ(0, memcmp(stringValue->m.value, "ABC", 4));
EXPECT_EQ(4u, stringValue->flexCount());
}
@@ -2397,15 +2395,11 @@
// ***********************
-}
-
#include <util/C2ParamUtils.h>
#include <C2Config.h>
#include <C2Component.h>
#include <unordered_map>
-namespace android {
-
C2ENUM(
MetadataType, int32_t,
kInvalid = -1,
@@ -2519,7 +2513,7 @@
public:
MyParamReflector(const MyComponentInstance *i) : instance(i) { }
- virtual std::unique_ptr<C2StructDescriptor> describe(C2Param::CoreIndex paramIndex) override {
+ virtual std::unique_ptr<C2StructDescriptor> describe(C2Param::CoreIndex paramIndex) const override {
switch (paramIndex.typeIndex()) {
case decltype(instance->mDomainInfo)::CORE_INDEX:
default:
@@ -2537,7 +2531,7 @@
c2_blocking_t mayBlock) const override {
(void)mayBlock;
for (C2FieldSupportedValuesQuery &query : fields) {
- if (query.field == C2ParamField(&mDomainInfo, &C2ComponentDomainInfo::value)) {
+ if (query.field() == C2ParamField(&mDomainInfo, &C2ComponentDomainInfo::value)) {
query.values = C2FieldSupportedValues(
false /* flag */,
&mDomainInfo.value
@@ -2708,7 +2702,7 @@
if (f.namedValues().size()) {
cout << ".named(";
const char *sep = "";
- for (const FD::named_value_type &p : f.namedValues()) {
+ for (const FD::NamedValueType &p : f.namedValues()) {
cout << sep << p.first << "=";
switch (f.type()) {
case C2Value::INT32: cout << get(p.second, (int32_t *)0); break;
@@ -2935,4 +2929,3 @@
EXPECT_EQ(15.25f, fp);
}
-} // namespace android
diff --git a/media/libstagefright/codec2/tests/vndk/C2BufferTest.cpp b/media/libstagefright/codec2/tests/vndk/C2BufferTest.cpp
index f0e57e2..038be48 100644
--- a/media/libstagefright/codec2/tests/vndk/C2BufferTest.cpp
+++ b/media/libstagefright/codec2/tests/vndk/C2BufferTest.cpp
@@ -312,13 +312,15 @@
addr[C2PlanarLayout::PLANE_V] = nullptr;
FAIL() << "C2GraphicAllocation::map() failed: " << err;
}
+ mMappedRect = rect;
+ memcpy(mAddrGraphic, addr, sizeof(uint8_t*) * C2PlanarLayout::MAX_NUM_PLANES);
}
void unmapGraphic() {
ASSERT_TRUE(mGraphicAllocation);
// TODO: fence
- ASSERT_EQ(C2_OK, mGraphicAllocation->unmap(nullptr));
+ ASSERT_EQ(C2_OK, mGraphicAllocation->unmap(mAddrGraphic, mMappedRect, nullptr));
}
std::shared_ptr<C2BlockPool> makeGraphicBlockPool() {
@@ -330,6 +332,8 @@
std::shared_ptr<C2LinearAllocation> mLinearAllocation;
size_t mSize;
void *mAddr;
+ C2Rect mMappedRect;
+ uint8_t* mAddrGraphic[C2PlanarLayout::MAX_NUM_PLANES];
std::shared_ptr<C2Allocator> mGraphicAllocator;
std::shared_ptr<C2GraphicAllocation> mGraphicAllocation;
diff --git a/media/libstagefright/codec2/vndk/Android.bp b/media/libstagefright/codec2/vndk/Android.bp
index d6cbe96..d7709a0 100644
--- a/media/libstagefright/codec2/vndk/Android.bp
+++ b/media/libstagefright/codec2/vndk/Android.bp
@@ -54,3 +54,7 @@
"-std=c++14",
],
}
+
+subdirs = [
+ "bufferpool",
+]
diff --git a/media/libstagefright/codec2/vndk/C2AllocatorGralloc.cpp b/media/libstagefright/codec2/vndk/C2AllocatorGralloc.cpp
index b287ca8..3d7fc8d 100644
--- a/media/libstagefright/codec2/vndk/C2AllocatorGralloc.cpp
+++ b/media/libstagefright/codec2/vndk/C2AllocatorGralloc.cpp
@@ -29,6 +29,36 @@
namespace android {
+namespace {
+ enum : uint64_t {
+ /**
+ * Usage mask that is passed through from gralloc to Codec 2.0 usage.
+ */
+ PASSTHROUGH_USAGE_MASK =
+ ~(GRALLOC_USAGE_SW_READ_MASK | GRALLOC_USAGE_SW_WRITE_MASK | GRALLOC_USAGE_PROTECTED)
+ };
+
+ // verify that passthrough mask is within the platform mask
+ static_assert((~C2MemoryUsage::PLATFORM_MASK & PASSTHROUGH_USAGE_MASK) == 0, "");
+}
+
+C2MemoryUsage C2AndroidMemoryUsage::FromGrallocUsage(uint64_t usage) {
+ // gralloc does not support WRITE_PROTECTED
+ return C2MemoryUsage(
+ ((usage & GRALLOC_USAGE_SW_READ_MASK) ? C2MemoryUsage::CPU_READ : 0) |
+ ((usage & GRALLOC_USAGE_SW_WRITE_MASK) ? C2MemoryUsage::CPU_WRITE : 0) |
+ ((usage & GRALLOC_USAGE_PROTECTED) ? C2MemoryUsage::READ_PROTECTED : 0) |
+ (usage & PASSTHROUGH_USAGE_MASK));
+}
+
+uint64_t C2AndroidMemoryUsage::asGrallocUsage() const {
+ // gralloc does not support WRITE_PROTECTED
+ return (((expected & C2MemoryUsage::CPU_READ) ? GRALLOC_USAGE_SW_READ_MASK : 0) |
+ ((expected & C2MemoryUsage::CPU_WRITE) ? GRALLOC_USAGE_SW_WRITE_MASK : 0) |
+ ((expected & C2MemoryUsage::READ_PROTECTED) ? GRALLOC_USAGE_PROTECTED : 0) |
+ (expected & PASSTHROUGH_USAGE_MASK));
+}
+
using ::android::hardware::graphics::allocator::V2_0::IAllocator;
using ::android::hardware::graphics::common::V1_0::BufferUsage;
using ::android::hardware::graphics::common::V1_0::PixelFormat;
@@ -183,9 +213,10 @@
virtual ~C2AllocationGralloc() override;
virtual c2_status_t map(
- C2Rect rect, C2MemoryUsage usage, int *fenceFd,
+ C2Rect rect, C2MemoryUsage usage, C2Fence *fence,
C2PlanarLayout *layout /* nonnull */, uint8_t **addr /* nonnull */) override;
- virtual c2_status_t unmap(C2Fence *fenceFd /* nullable */) override;
+ virtual c2_status_t unmap(
+ uint8_t **addr /* nonnull */, C2Rect rect, C2Fence *fence /* nullable */) override;
virtual C2Allocator::id_t getAllocatorId() const override { return mAllocatorId; }
virtual const C2Handle *handle() const override { return mLockedHandle ? : mHandle; }
virtual bool equals(const std::shared_ptr<const C2GraphicAllocation> &other) const override;
@@ -233,16 +264,18 @@
return;
}
if (mLocked) {
- unmap(nullptr);
+ // implementation ignores addresss and rect
+ uint8_t* addr[C2PlanarLayout::MAX_NUM_PLANES] = {};
+ unmap(addr, C2Rect(), nullptr);
}
mMapper->freeBuffer(const_cast<native_handle_t *>(mBuffer));
}
c2_status_t C2AllocationGralloc::map(
- C2Rect rect, C2MemoryUsage usage, int *fenceFd,
+ C2Rect rect, C2MemoryUsage usage, C2Fence *fence,
C2PlanarLayout *layout /* nonnull */, uint8_t **addr /* nonnull */) {
// TODO
- (void) fenceFd;
+ (void) fence;
(void) usage;
if (mBuffer && mLocked) {
@@ -296,6 +329,7 @@
addr[C2PlanarLayout::PLANE_V] = (uint8_t *)ycbcrLayout.cr;
layout->type = C2PlanarLayout::TYPE_YUV;
layout->numPlanes = 3;
+ layout->rootPlanes = 3;
layout->planes[C2PlanarLayout::PLANE_Y] = {
C2PlaneInfo::CHANNEL_Y, // channel
1, // colInc
@@ -306,6 +340,8 @@
8, // bitDepth
0, // rightShift
C2PlaneInfo::NATIVE, // endianness
+ C2PlanarLayout::PLANE_Y, // rootIx
+ 0, // offset
};
layout->planes[C2PlanarLayout::PLANE_U] = {
C2PlaneInfo::CHANNEL_CB, // channel
@@ -317,6 +353,8 @@
8, // bitDepth
0, // rightShift
C2PlaneInfo::NATIVE, // endianness
+ C2PlanarLayout::PLANE_U, // rootIx
+ 0, // offset
};
layout->planes[C2PlanarLayout::PLANE_V] = {
C2PlaneInfo::CHANNEL_CR, // channel
@@ -328,7 +366,20 @@
8, // bitDepth
0, // rightShift
C2PlaneInfo::NATIVE, // endianness
+ C2PlanarLayout::PLANE_V, // rootIx
+ 0, // offset
};
+ // handle interleaved formats
+ intptr_t uvOffset = addr[C2PlanarLayout::PLANE_V] - addr[C2PlanarLayout::PLANE_U];
+ if (uvOffset > 0 && uvOffset < (intptr_t)ycbcrLayout.chromaStep) {
+ layout->rootPlanes = 2;
+ layout->planes[C2PlanarLayout::PLANE_V].rootIx = C2PlanarLayout::PLANE_U;
+ layout->planes[C2PlanarLayout::PLANE_V].offset = uvOffset;
+ } else if (uvOffset < 0 && uvOffset > -(intptr_t)ycbcrLayout.chromaStep) {
+ layout->rootPlanes = 2;
+ layout->planes[C2PlanarLayout::PLANE_U].rootIx = C2PlanarLayout::PLANE_V;
+ layout->planes[C2PlanarLayout::PLANE_U].offset = -uvOffset;
+ }
break;
}
@@ -357,6 +408,7 @@
addr[C2PlanarLayout::PLANE_B] = (uint8_t *)pointer + 2;
layout->type = C2PlanarLayout::TYPE_RGB;
layout->numPlanes = 3;
+ layout->rootPlanes = 1;
layout->planes[C2PlanarLayout::PLANE_R] = {
C2PlaneInfo::CHANNEL_R, // channel
4, // colInc
@@ -367,6 +419,8 @@
8, // bitDepth
0, // rightShift
C2PlaneInfo::NATIVE, // endianness
+ C2PlanarLayout::PLANE_R, // rootIx
+ 0, // offset
};
layout->planes[C2PlanarLayout::PLANE_G] = {
C2PlaneInfo::CHANNEL_G, // channel
@@ -378,6 +432,8 @@
8, // bitDepth
0, // rightShift
C2PlaneInfo::NATIVE, // endianness
+ C2PlanarLayout::PLANE_R, // rootIx
+ 1, // offset
};
layout->planes[C2PlanarLayout::PLANE_B] = {
C2PlaneInfo::CHANNEL_B, // channel
@@ -389,6 +445,8 @@
8, // bitDepth
0, // rightShift
C2PlaneInfo::NATIVE, // endianness
+ C2PlanarLayout::PLANE_R, // rootIx
+ 2, // offset
};
break;
}
@@ -401,14 +459,17 @@
return C2_OK;
}
-c2_status_t C2AllocationGralloc::unmap(C2Fence *fenceFd /* nullable */) {
- // TODO: fence
+c2_status_t C2AllocationGralloc::unmap(
+ uint8_t **addr, C2Rect rect, C2Fence *fence /* nullable */) {
+ // TODO: check addr and size, use fence
+ (void)addr;
+ (void)rect;
c2_status_t err = C2_OK;
mMapper->unlock(
const_cast<native_handle_t *>(mBuffer),
- [&err, &fenceFd](const auto &maperr, const auto &releaseFence) {
+ [&err, &fence](const auto &maperr, const auto &releaseFence) {
// TODO
- (void) fenceFd;
+ (void) fence;
(void) releaseFence;
err = maperr2error(maperr);
if (err == C2_OK) {
diff --git a/media/libstagefright/codec2/vndk/C2AllocatorIon.cpp b/media/libstagefright/codec2/vndk/C2AllocatorIon.cpp
index 4328a8d..6b0cffb 100644
--- a/media/libstagefright/codec2/vndk/C2AllocatorIon.cpp
+++ b/media/libstagefright/codec2/vndk/C2AllocatorIon.cpp
@@ -20,6 +20,7 @@
#include <ion/ion.h>
#include <sys/mman.h>
+#include <unistd.h>
#include <C2AllocatorIon.h>
#include <C2Buffer.h>
@@ -116,9 +117,9 @@
public:
/* Interface methods */
virtual c2_status_t map(
- size_t offset, size_t size, C2MemoryUsage usage, int *fence,
+ size_t offset, size_t size, C2MemoryUsage usage, C2Fence *fence,
void **addr /* nonnull */) override;
- virtual c2_status_t unmap(void *addr, size_t size, int *fenceFd) override;
+ virtual c2_status_t unmap(void *addr, size_t size, C2Fence *fenceFd) override;
virtual ~C2AllocationIon() override;
virtual const C2Handle *handle() const override;
virtual id_t getAllocatorId() const override;
@@ -217,8 +218,8 @@
return new Impl(ionFd, size, bufferFd, buffer, id, ret);
}
- c2_status_t map(size_t offset, size_t size, C2MemoryUsage usage, int *fenceFd, void **addr) {
- (void)fenceFd; // TODO: wait for fence
+ c2_status_t map(size_t offset, size_t size, C2MemoryUsage usage, C2Fence *fence, void **addr) {
+ (void)fence; // TODO: wait for fence
*addr = nullptr;
if (mMapSize > 0) {
// TODO: technically we should return DUPLICATE here, but our block views don't
@@ -232,10 +233,10 @@
int prot = PROT_NONE;
int flags = MAP_PRIVATE;
- if (usage.consumer & C2MemoryUsage::CPU_READ) {
+ if (usage.expected & C2MemoryUsage::CPU_READ) {
prot |= PROT_READ;
}
- if (usage.producer & C2MemoryUsage::CPU_WRITE) {
+ if (usage.expected & C2MemoryUsage::CPU_WRITE) {
prot |= PROT_WRITE;
flags = MAP_SHARED;
}
@@ -271,7 +272,7 @@
return err;
}
- c2_status_t unmap(void *addr, size_t size, int *fenceFd) {
+ c2_status_t unmap(void *addr, size_t size, C2Fence *fence) {
if (mMapFd < 0 || mMapSize == 0) {
return C2_NOT_FOUND;
}
@@ -283,8 +284,8 @@
if (err != 0) {
return c2_map_errno<EINVAL>(errno);
}
- if (fenceFd) {
- *fenceFd = -1; // not using fences
+ if (fence) {
+ *fence = C2Fence(); // not using fences
}
mMapSize = 0;
return C2_OK;
@@ -333,12 +334,12 @@
};
c2_status_t C2AllocationIon::map(
- size_t offset, size_t size, C2MemoryUsage usage, int *fenceFd, void **addr) {
- return mImpl->map(offset, size, usage, fenceFd, addr);
+ size_t offset, size_t size, C2MemoryUsage usage, C2Fence *fence, void **addr) {
+ return mImpl->map(offset, size, usage, fence, addr);
}
-c2_status_t C2AllocationIon::unmap(void *addr, size_t size, int *fenceFd) {
- return mImpl->unmap(addr, size, fenceFd);
+c2_status_t C2AllocationIon::unmap(void *addr, size_t size, C2Fence *fence) {
+ return mImpl->unmap(addr, size, fence);
}
c2_status_t C2AllocationIon::status() const {
diff --git a/media/libstagefright/codec2/vndk/C2Buffer.cpp b/media/libstagefright/codec2/vndk/C2Buffer.cpp
index 0de8cde..dc765f5 100644
--- a/media/libstagefright/codec2/vndk/C2Buffer.cpp
+++ b/media/libstagefright/codec2/vndk/C2Buffer.cpp
@@ -18,14 +18,13 @@
#define LOG_TAG "C2Buffer"
#include <utils/Log.h>
+#include <list>
#include <map>
#include <mutex>
#include <C2BufferPriv.h>
#include <C2BlockInternal.h>
-namespace android {
-
namespace {
// This anonymous namespace contains the helper classes that allow our implementation to create
@@ -34,63 +33,63 @@
// Inherit from the parent, share with the friend.
class ReadViewBuddy : public C2ReadView {
using C2ReadView::C2ReadView;
- friend class ::android::C2ConstLinearBlock;
+ friend class ::C2ConstLinearBlock;
};
class WriteViewBuddy : public C2WriteView {
using C2WriteView::C2WriteView;
- friend class ::android::C2LinearBlock;
+ friend class ::C2LinearBlock;
};
class ConstLinearBlockBuddy : public C2ConstLinearBlock {
using C2ConstLinearBlock::C2ConstLinearBlock;
- friend class ::android::C2LinearBlock;
+ friend class ::C2LinearBlock;
};
class LinearBlockBuddy : public C2LinearBlock {
using C2LinearBlock::C2LinearBlock;
- friend class ::android::C2BasicLinearBlockPool;
+ friend class ::C2BasicLinearBlockPool;
};
class AcquirableReadViewBuddy : public C2Acquirable<C2ReadView> {
using C2Acquirable::C2Acquirable;
- friend class ::android::C2ConstLinearBlock;
+ friend class ::C2ConstLinearBlock;
};
class AcquirableWriteViewBuddy : public C2Acquirable<C2WriteView> {
using C2Acquirable::C2Acquirable;
- friend class ::android::C2LinearBlock;
+ friend class ::C2LinearBlock;
};
class GraphicViewBuddy : public C2GraphicView {
using C2GraphicView::C2GraphicView;
- friend class ::android::C2ConstGraphicBlock;
- friend class ::android::C2GraphicBlock;
+ friend class ::C2ConstGraphicBlock;
+ friend class ::C2GraphicBlock;
};
class AcquirableConstGraphicViewBuddy : public C2Acquirable<const C2GraphicView> {
using C2Acquirable::C2Acquirable;
- friend class ::android::C2ConstGraphicBlock;
+ friend class ::C2ConstGraphicBlock;
};
class AcquirableGraphicViewBuddy : public C2Acquirable<C2GraphicView> {
using C2Acquirable::C2Acquirable;
- friend class ::android::C2GraphicBlock;
+ friend class ::C2GraphicBlock;
};
class ConstGraphicBlockBuddy : public C2ConstGraphicBlock {
using C2ConstGraphicBlock::C2ConstGraphicBlock;
- friend class ::android::C2GraphicBlock;
+ friend class ::C2GraphicBlock;
};
class GraphicBlockBuddy : public C2GraphicBlock {
using C2GraphicBlock::C2GraphicBlock;
- friend class ::android::C2BasicGraphicBlockPool;
+ friend class ::C2BasicGraphicBlockPool;
};
class BufferDataBuddy : public C2BufferData {
using C2BufferData::C2BufferData;
- friend class ::android::C2Buffer;
+ friend class ::C2Buffer;
};
} // namespace
@@ -271,6 +270,7 @@
new ReadViewBuddy::Impl(*mImpl, (uint8_t *)base, offset(), len),
[base, len](ReadViewBuddy::Impl *i) {
(void)i->getAllocation()->unmap(base, len, nullptr);
+ delete i;
});
return AcquirableReadViewBuddy(error, C2Fence(), ReadViewBuddy(rvi, 0, len));
} else {
@@ -300,6 +300,7 @@
new WriteViewBuddy::Impl(*mImpl, (uint8_t *)base, 0, len),
[base, len](WriteViewBuddy::Impl *i) {
(void)i->getAllocation()->unmap(base, len, nullptr);
+ delete i;
});
return AcquirableWriteViewBuddy(error, C2Fence(), WriteViewBuddy(rvi));
} else {
@@ -413,6 +414,7 @@
if (mError != C2_OK) {
memset(&mLayout, 0, sizeof(mLayout));
memset(mData, 0, sizeof(mData));
+ memset(mOffsetData, 0, sizeof(mData));
} else {
// TODO: validate plane layout and
// adjust data pointers to the crop region's top left corner.
@@ -423,14 +425,16 @@
if (crop.left % colSampling || crop.right() % colSampling
|| crop.top % rowSampling || crop.bottom() % rowSampling) {
// cannot calculate data pointer
- mImpl->getAllocation()->unmap(nullptr);
+ mImpl->getAllocation()->unmap(mData, crop, nullptr);
memset(&mLayout, 0, sizeof(mLayout));
memset(mData, 0, sizeof(mData));
+ memset(mOffsetData, 0, sizeof(mData));
mError = C2_BAD_VALUE;
return;
}
- mData[planeIx] += (ssize_t)crop.left * mLayout.planes[planeIx].colInc
- + (ssize_t)crop.top * mLayout.planes[planeIx].rowInc;
+ mOffsetData[planeIx] =
+ mData[planeIx] + (ssize_t)crop.left * mLayout.planes[planeIx].colInc
+ + (ssize_t)crop.top * mLayout.planes[planeIx].rowInc;
}
}
}
@@ -440,12 +444,13 @@
// CHECK(error != C2_OK);
memset(&mLayout, 0, sizeof(mLayout));
memset(mData, 0, sizeof(mData));
+ memset(mOffsetData, 0, sizeof(mData));
}
public:
~Mapped() {
if (mData[0] != nullptr) {
- mImpl->getAllocation()->unmap(nullptr);
+ mImpl->getAllocation()->unmap(mData, mImpl->crop(), nullptr);
}
}
@@ -453,7 +458,7 @@
c2_status_t error() const { return mError; }
/** returns data pointer */
- uint8_t *const *data() const { return mData; }
+ uint8_t *const *data() const { return mOffsetData; }
/** returns the plane layout */
C2PlanarLayout layout() const { return mLayout; }
@@ -466,6 +471,7 @@
bool mWritable;
c2_status_t mError;
uint8_t *mData[C2PlanarLayout::MAX_NUM_PLANES];
+ uint8_t *mOffsetData[C2PlanarLayout::MAX_NUM_PLANES];
C2PlanarLayout mLayout;
};
@@ -791,4 +797,13 @@
return mImpl->removeInfo(index);
}
-} // namespace android
+// static
+std::shared_ptr<C2Buffer> C2Buffer::CreateLinearBuffer(const C2ConstLinearBlock &block) {
+ return std::shared_ptr<C2Buffer>(new C2Buffer({ block }));
+}
+
+// static
+std::shared_ptr<C2Buffer> C2Buffer::CreateGraphicBuffer(const C2ConstGraphicBlock &block) {
+ return std::shared_ptr<C2Buffer>(new C2Buffer({ block }));
+}
+
diff --git a/media/libstagefright/codec2/vndk/C2Store.cpp b/media/libstagefright/codec2/vndk/C2Store.cpp
index 05b46c3..f612b4f 100644
--- a/media/libstagefright/codec2/vndk/C2Store.cpp
+++ b/media/libstagefright/codec2/vndk/C2Store.cpp
@@ -136,13 +136,13 @@
switch (id) {
case C2BlockPool::BASIC_LINEAR:
res = allocatorStore->fetchAllocator(C2AllocatorStore::DEFAULT_LINEAR, &allocator);
- if (res == OK) {
+ if (res == C2_OK) {
*pool = std::make_shared<C2BasicLinearBlockPool>(allocator);
}
break;
case C2BlockPool::BASIC_GRAPHIC:
res = allocatorStore->fetchAllocator(C2AllocatorStore::DEFAULT_GRAPHIC, &allocator);
- if (res == OK) {
+ if (res == C2_OK) {
*pool = std::make_shared<C2BasicGraphicBlockPool>(allocator);
}
break;
@@ -203,9 +203,17 @@
/**
* Creates an uninitialized component module.
*
+ * \param name[in] component name.
+ *
* \note Only used by ComponentLoader.
*/
- ComponentModule() : mInit(C2_NO_INIT) {}
+ ComponentModule()
+ : mInit(C2_NO_INIT),
+ mLibHandle(nullptr),
+ createFactory(nullptr),
+ destroyFactory(nullptr),
+ mComponentFactory(nullptr) {
+ }
/**
* Initializes a component module with a given library path. Must be called exactly once.
@@ -345,7 +353,7 @@
c2_status_t C2PlatformComponentStore::ComponentModule::createInterface(
c2_node_id_t id, std::shared_ptr<C2ComponentInterface> *interface,
- std::function<void(::android::C2ComponentInterface*)> deleter) {
+ std::function<void(::C2ComponentInterface*)> deleter) {
interface->reset();
if (mInit != C2_OK) {
return mInit;
@@ -362,7 +370,7 @@
c2_status_t C2PlatformComponentStore::ComponentModule::createComponent(
c2_node_id_t id, std::shared_ptr<C2Component> *component,
- std::function<void(::android::C2Component*)> deleter) {
+ std::function<void(::C2Component*)> deleter) {
component->reset();
if (mInit != C2_OK) {
return mInit;
@@ -383,12 +391,35 @@
std::shared_ptr<C2ComponentInterface> intf;
c2_status_t res = createInterface(0, &intf);
if (res != C2_OK) {
+ ALOGD("failed to create interface: %d", res);
return nullptr;
}
std::shared_ptr<C2Component::Traits> traits(new (std::nothrow) C2Component::Traits);
if (traits) {
- // traits->name = intf->getName();
+ traits->name = intf->getName();
+ // TODO: get this from interface properly.
+ bool encoder = (traits->name.find("encoder") != std::string::npos);
+ uint32_t mediaTypeIndex = encoder ? C2PortMimeConfig::output::PARAM_TYPE
+ : C2PortMimeConfig::input::PARAM_TYPE;
+ std::vector<std::unique_ptr<C2Param>> params;
+ res = intf->query_vb({}, { mediaTypeIndex }, C2_MAY_BLOCK, ¶ms);
+ if (res != C2_OK) {
+ ALOGD("failed to query interface: %d", res);
+ return nullptr;
+ }
+ if (params.size() != 1u) {
+ ALOGD("failed to query interface: unexpected vector size: %zu", params.size());
+ return nullptr;
+ }
+ C2PortMimeConfig *mediaTypeConfig = (C2PortMimeConfig *)(params[0].get());
+ if (mediaTypeConfig == nullptr) {
+ ALOGD("failed to query media type");
+ return nullptr;
+ }
+ traits->mediaType = mediaTypeConfig->m.value;
+ // TODO: get this properly.
+ traits->rank = 0x200;
}
mTraits = traits;
diff --git a/media/libstagefright/codec2/vndk/bufferpool/Accessor.cpp b/media/libstagefright/codec2/vndk/bufferpool/Accessor.cpp
new file mode 100644
index 0000000..3571ed5
--- /dev/null
+++ b/media/libstagefright/codec2/vndk/bufferpool/Accessor.cpp
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+#include "Accessor.h"
+#include "AccessorImpl.h"
+#include "Connection.h"
+
+namespace android {
+namespace hardware {
+namespace media {
+namespace bufferpool {
+namespace V1_0 {
+namespace implementation {
+
+// Methods from ::android::hardware::media::bufferpool::V1_0::IAccessor follow.
+Return<void> Accessor::connect(connect_cb _hidl_cb) {
+ sp<Connection> connection;
+ ConnectionId connectionId;
+ const QueueDescriptor* fmqDesc;
+
+ ResultStatus status = connect(&connection, &connectionId, &fmqDesc);
+ if (status == ResultStatus::OK) {
+ _hidl_cb(status, connection, connectionId, *fmqDesc);
+ } else {
+ _hidl_cb(status, nullptr, -1LL,
+ android::hardware::MQDescriptorSync<BufferStatusMessage>(
+ std::vector<android::hardware::GrantorDescriptor>(),
+ nullptr /* nhandle */, 0 /* size */));
+ }
+ return Void();
+}
+
+Accessor::Accessor(const std::shared_ptr<C2Allocator> &allocator, bool linear)
+ : mImpl(new Impl(allocator, linear)) {}
+
+Accessor::~Accessor() {
+}
+
+bool Accessor::isValid() {
+ return (bool)mImpl;
+}
+
+ResultStatus Accessor::allocate(
+ ConnectionId connectionId,
+ const std::vector<uint8_t> ¶ms,
+ BufferId *bufferId, const native_handle_t** handle) {
+ if (mImpl) {
+ return mImpl->allocate(connectionId, params, bufferId, handle);
+ }
+ return ResultStatus::CRITICAL_ERROR;
+}
+
+ResultStatus Accessor::fetch(
+ ConnectionId connectionId, TransactionId transactionId,
+ BufferId bufferId, const native_handle_t** handle) {
+ if (mImpl) {
+ return mImpl->fetch(connectionId, transactionId, bufferId, handle);
+ }
+ return ResultStatus::CRITICAL_ERROR;
+}
+
+ResultStatus Accessor::connect(
+ sp<Connection> *connection, ConnectionId *pConnectionId,
+ const QueueDescriptor** fmqDescPtr) {
+ if (mImpl) {
+ return mImpl->connect(this, connection, pConnectionId, fmqDescPtr);
+ }
+ return ResultStatus::CRITICAL_ERROR;
+}
+
+ResultStatus Accessor::close(ConnectionId connectionId) {
+ if (mImpl) {
+ return mImpl->close(connectionId);
+ }
+ return ResultStatus::CRITICAL_ERROR;
+}
+
+//IAccessor* HIDL_FETCH_IAccessor(const char* /* name */) {
+// return new Accessor();
+//}
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace bufferpool
+} // namespace media
+} // namespace hardware
+} // namespace android
diff --git a/media/libstagefright/codec2/vndk/bufferpool/Accessor.h b/media/libstagefright/codec2/vndk/bufferpool/Accessor.h
new file mode 100644
index 0000000..6fd82ba
--- /dev/null
+++ b/media/libstagefright/codec2/vndk/bufferpool/Accessor.h
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V1_0_ACCESSOR_H
+#define ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V1_0_ACCESSOR_H
+
+#include <android/hardware/media/bufferpool/1.0/IAccessor.h>
+#include <hidl/MQDescriptor.h>
+#include <hidl/Status.h>
+#include <C2Buffer.h>
+#include <BufferPoolTypes.h>
+#include "BufferStatus.h"
+
+namespace android {
+namespace hardware {
+namespace media {
+namespace bufferpool {
+namespace V1_0 {
+namespace implementation {
+
+using ::android::hardware::hidl_array;
+using ::android::hardware::hidl_memory;
+using ::android::hardware::hidl_string;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+using ::android::sp;
+
+struct Connection;
+
+/**
+ * A buffer pool accessor which enables a buffer pool to communicate with buffer
+ * pool clients. 1:1 correspondense holds between a buffer pool and an accessor.
+ */
+struct Accessor : public IAccessor {
+ // Methods from ::android::hardware::media::bufferpool::V1_0::IAccessor follow.
+ Return<void> connect(connect_cb _hidl_cb) override;
+
+ /**
+ * Creates a buffer pool accessor which uses the specified allocator.
+ *
+ * @param allocator buffer allocator.
+ * @param linear whether the allocator is linear or not.
+ */
+ Accessor(const std::shared_ptr<C2Allocator> &allocator, bool linear);
+
+ /** Destructs a buffer pool accessor. */
+ ~Accessor();
+
+ /** Returns whether the accessor is valid. */
+ bool isValid();
+
+ /** Allocates a buffer form a buffer pool.
+ *
+ * @param connectionId the connection id of the client.
+ * @param params the allocation parameters.
+ * @param bufferId the id of the allocated buffer.
+ * @param handle the native handle of the allocated buffer.
+ *
+ * @return OK when a buffer is successfully allocated.
+ * NO_MEMORY when there is no memory.
+ * CRITICAL_ERROR otherwise.
+ */
+ ResultStatus allocate(
+ ConnectionId connectionId,
+ const std::vector<uint8_t>& params,
+ BufferId *bufferId,
+ const native_handle_t** handle);
+
+ /**
+ * Fetches a buffer for the specified transaction.
+ *
+ * @param connectionId the id of receiving connection(client).
+ * @param transactionId the id of the transfer transaction.
+ * @param bufferId the id of the buffer to be fetched.
+ * @param handle the native handle of the fetched buffer.
+ *
+ * @return OK when a buffer is successfully fetched.
+ * NO_MEMORY when there is no memory.
+ * CRITICAL_ERROR otherwise.
+ */
+ ResultStatus fetch(
+ ConnectionId connectionId,
+ TransactionId transactionId,
+ BufferId bufferId,
+ const native_handle_t** handle);
+
+ /**
+ * Makes a connection to the buffer pool. The buffer pool client uses the
+ * created connection in order to communicate with the buffer pool. An
+ * FMQ for buffer status message is also created for the client.
+ *
+ * @param connection created connection
+ * @param pConnectionId the id of the created connection
+ * @param fmqDescPtr FMQ descriptor for shared buffer status message
+ * queue between a buffer pool and the client.
+ *
+ * @return OK when a connection is successfully made.
+ * NO_MEMORY when there is no memory.
+ * CRITICAL_ERROR otherwise.
+ */
+ ResultStatus connect(
+ sp<Connection> *connection, ConnectionId *pConnectionId,
+ const QueueDescriptor** fmqDescPtr);
+
+ /**
+ * Closes the specified connection to the client.
+ *
+ * @param connectionId the id of the connection.
+ *
+ * @return OK when the connection is closed.
+ * CRITICAL_ERROR otherwise.
+ */
+ ResultStatus close(ConnectionId connectionId);
+
+private:
+ class Impl;
+ std::unique_ptr<Impl> mImpl;
+};
+
+// FIXME: most likely delete, this is only for passthrough implementations
+// extern "C" IAccessor* HIDL_FETCH_IAccessor(const char* name);
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace bufferpool
+} // namespace media
+} // namespace hardware
+} // namespace android
+
+#endif // ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V1_0_ACCESSOR_H
diff --git a/media/libstagefright/codec2/vndk/bufferpool/AccessorImpl.cpp b/media/libstagefright/codec2/vndk/bufferpool/AccessorImpl.cpp
new file mode 100644
index 0000000..f8aec53
--- /dev/null
+++ b/media/libstagefright/codec2/vndk/bufferpool/AccessorImpl.cpp
@@ -0,0 +1,542 @@
+/*
+ * Copyright (C) 2018 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 "bufferpool"
+//#define LOG_NDEBUG 0
+
+#include <inttypes.h>
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+#include <utils/Log.h>
+#include <C2Buffer.h>
+#include "AccessorImpl.h"
+#include "Connection.h"
+
+namespace android {
+namespace hardware {
+namespace media {
+namespace bufferpool {
+namespace V1_0 {
+namespace implementation {
+
+// Buffer structure in bufferpool process
+struct InternalBuffer {
+ BufferId mId;
+ size_t mOwnerCount;
+ size_t mTransactionCount;
+ const bool mLinear;
+ const std::shared_ptr<C2LinearAllocation> mLinearAllocation;
+ const std::shared_ptr<C2GraphicAllocation> mGraphicAllocation;
+ const std::vector<uint8_t> mConfig;
+
+ InternalBuffer(
+ BufferId id,
+ const std::shared_ptr<C2LinearAllocation> &alloc,
+ const std::vector<uint8_t> &allocConfig)
+ : mId(id), mOwnerCount(0), mTransactionCount(0),
+ mLinear(true), mLinearAllocation(alloc),
+ mGraphicAllocation(nullptr),
+ mConfig(allocConfig) {}
+
+ InternalBuffer(
+ BufferId id,
+ const std::shared_ptr<C2GraphicAllocation> &alloc,
+ const std::vector<uint8_t> &allocConfig)
+ : mId(id), mOwnerCount(0), mTransactionCount(0),
+ mLinear(false), mLinearAllocation(nullptr),
+ mGraphicAllocation(alloc),
+ mConfig(allocConfig) {}
+
+ const native_handle_t *handle() {
+ if (mLinear) {
+ return mLinearAllocation->handle();
+ } else {
+ return mGraphicAllocation->handle();
+ }
+ }
+
+ // TODO : support non exact matching. e.g) capacity
+ bool isRecyclable(const std::vector<uint8_t> &config) {
+ if (mConfig.size() == config.size()) {
+ for (size_t i = 0; i < config.size(); ++i) {
+ if (mConfig[i] != config[i]) {
+ return false;
+ }
+ }
+ return true;
+ }
+ return false;
+ }
+};
+
+struct TransactionStatus {
+ TransactionId mId;
+ BufferId mBufferId;
+ ConnectionId mSender;
+ ConnectionId mReceiver;
+ BufferStatus mStatus;
+ int64_t mTimestampUs;
+ bool mSenderValidated;
+
+ TransactionStatus(const BufferStatusMessage &message, int64_t timestampUs) {
+ mId = message.transactionId;
+ mBufferId = message.bufferId;
+ mStatus = message.newStatus;
+ mTimestampUs = timestampUs;
+ if (mStatus == BufferStatus::TRANSFER_TO) {
+ mSender = message.connectionId;
+ mReceiver = message.targetConnectionId;
+ mSenderValidated = true;
+ } else {
+ mSender = -1LL;
+ mReceiver = message.connectionId;
+ mSenderValidated = false;
+ }
+ }
+};
+
+// Helper template methods for handling map of set.
+template<class T, class U>
+bool insert(std::map<T, std::set<U>> *mapOfSet, T key, U value) {
+ auto iter = mapOfSet->find(key);
+ if (iter == mapOfSet->end()) {
+ std::set<U> valueSet{value};
+ mapOfSet->insert(std::make_pair(key, valueSet));
+ return true;
+ } else if (iter->second.find(value) == iter->second.end()) {
+ iter->second.insert(value);
+ return true;
+ }
+ return false;
+}
+
+template<class T, class U>
+bool erase(std::map<T, std::set<U>> *mapOfSet, T key, U value) {
+ bool ret = false;
+ auto iter = mapOfSet->find(key);
+ if (iter != mapOfSet->end()) {
+ if (iter->second.erase(value) > 0) {
+ ret = true;
+ }
+ if (iter->second.size() == 0) {
+ mapOfSet->erase(iter);
+ }
+ }
+ return ret;
+}
+
+template<class T, class U>
+bool contains(std::map<T, std::set<U>> *mapOfSet, T key, U value) {
+ auto iter = mapOfSet->find(key);
+ if (iter != mapOfSet->end()) {
+ auto setIter = iter->second.find(value);
+ return setIter != iter->second.end();
+ }
+ return false;
+}
+
+
+int32_t Accessor::Impl::sPid = getpid();
+uint32_t Accessor::Impl::sSeqId = time(NULL);
+
+Accessor::Impl::Impl(
+ const std::shared_ptr<C2Allocator> &allocator, bool linear)
+ : mAllocator(allocator), mLinear(linear) {}
+
+Accessor::Impl::~Impl() {
+}
+
+ResultStatus Accessor::Impl::connect(
+ const sp<Accessor> &accessor, sp<Connection> *connection,
+ ConnectionId *pConnectionId, const QueueDescriptor** fmqDescPtr) {
+ sp<Connection> newConnection = new Connection();
+ ResultStatus status = ResultStatus::CRITICAL_ERROR;
+ if (newConnection) {
+ std::lock_guard<std::mutex> lock(mBufferPool.mMutex);
+ ConnectionId id = (int64_t)sPid << 32 | sSeqId;
+ status = mBufferPool.mObserver.open(id, fmqDescPtr);
+ if (status == ResultStatus::OK) {
+ newConnection->initialize(accessor, id);
+ *connection = newConnection;
+ *pConnectionId = id;
+ ++sSeqId;
+ }
+ }
+ return status;
+}
+
+ResultStatus Accessor::Impl::close(ConnectionId connectionId) {
+ std::lock_guard<std::mutex> lock(mBufferPool.mMutex);
+ mBufferPool.processStatusMessages();
+ mBufferPool.handleClose(connectionId);
+ mBufferPool.mObserver.close(connectionId);
+ return ResultStatus::OK;
+}
+
+ResultStatus Accessor::Impl::allocate(
+ ConnectionId connectionId, const std::vector<uint8_t>& params,
+ BufferId *bufferId, const native_handle_t** handle) {
+ std::lock_guard<std::mutex> lock(mBufferPool.mMutex);
+ mBufferPool.processStatusMessages();
+ ResultStatus status = ResultStatus::OK;
+ if (!mBufferPool.getFreeBuffer(params, bufferId, handle)) {
+ if (mLinear) {
+ status = mBufferPool.getNewLinearBuffer(
+ mAllocator, params, bufferId, handle);
+ } else {
+ status = mBufferPool.getNewGraphicBuffer(
+ mAllocator, params, bufferId, handle);
+ }
+ ALOGV("create a buffer %d : %u %p",
+ status == ResultStatus::OK, *bufferId, *handle);
+ }
+ if (status == ResultStatus::OK) {
+ // TODO: handle ownBuffer failure
+ mBufferPool.handleOwnBuffer(connectionId, *bufferId);
+ }
+ return status;
+}
+
+ResultStatus Accessor::Impl::fetch(
+ ConnectionId connectionId, TransactionId transactionId,
+ BufferId bufferId, const native_handle_t** handle) {
+ std::lock_guard<std::mutex> lock(mBufferPool.mMutex);
+ mBufferPool.processStatusMessages();
+ auto found = mBufferPool.mTransactions.find(transactionId);
+ if (found != mBufferPool.mTransactions.end() &&
+ contains(&mBufferPool.mPendingTransactions,
+ connectionId, transactionId)) {
+ if (found->second->mSenderValidated &&
+ found->second->mStatus == BufferStatus::TRANSFER_FROM &&
+ found->second->mBufferId == bufferId) {
+ found->second->mStatus = BufferStatus::TRANSFER_FETCH;
+ auto bufferIt = mBufferPool.mBuffers.find(bufferId);
+ if (bufferIt != mBufferPool.mBuffers.end()) {
+ *handle = bufferIt->second->handle();
+ return ResultStatus::OK;
+ }
+ }
+ }
+ return ResultStatus::CRITICAL_ERROR;
+}
+
+void Accessor::Impl::sync() {
+ // TODO: periodic jobs
+ // transaction timeout, buffer cacheing TTL handling
+ std::lock_guard<std::mutex> lock(mBufferPool.mMutex);
+ mBufferPool.processStatusMessages();
+}
+
+Accessor::Impl::Impl::BufferPool::BufferPool()
+ : mTimestampUs(getTimestampNow()), mSeq(0) {}
+
+bool Accessor::Impl::BufferPool::handleOwnBuffer(
+ ConnectionId connectionId, BufferId bufferId) {
+
+ bool added = insert(&mUsingBuffers, connectionId, bufferId);
+ if (added) {
+ auto iter = mBuffers.find(bufferId);
+ iter->second->mOwnerCount++;
+ }
+ insert(&mUsingConnections, bufferId, connectionId);
+ return added;
+}
+
+bool Accessor::Impl::BufferPool::handleReleaseBuffer(
+ ConnectionId connectionId, BufferId bufferId) {
+ bool deleted = erase(&mUsingBuffers, connectionId, bufferId);
+ if (deleted) {
+ auto iter = mBuffers.find(bufferId);
+ iter->second->mOwnerCount--;
+ if (iter->second->mOwnerCount == 0 &&
+ iter->second->mTransactionCount == 0) {
+ mFreeBuffers.insert(bufferId);
+ }
+ }
+ erase(&mUsingConnections, bufferId, connectionId);
+ ALOGV("release buffer %u : %d", bufferId, deleted);
+ return deleted;
+}
+
+bool Accessor::Impl::BufferPool::handleTransferTo(const BufferStatusMessage &message) {
+ auto completed = mCompletedTransactions.find(
+ message.transactionId);
+ if (completed != mCompletedTransactions.end()) {
+ // already completed
+ mCompletedTransactions.erase(completed);
+ return true;
+ }
+ // the buffer should exist and be owned.
+ auto bufferIter = mBuffers.find(message.bufferId);
+ if (bufferIter == mBuffers.end() ||
+ !contains(&mUsingBuffers, message.connectionId, message.bufferId)) {
+ return false;
+ }
+ auto found = mTransactions.find(message.transactionId);
+ if (found != mTransactions.end()) {
+ // transfer_from was received earlier.
+ found->second->mSender = message.connectionId;
+ found->second->mSenderValidated = true;
+ return true;
+ }
+ // TODO: verify there is target connection Id
+ mTransactions.insert(std::make_pair(
+ message.transactionId,
+ std::make_unique<TransactionStatus>(message, mTimestampUs)));
+ insert(&mPendingTransactions, message.targetConnectionId,
+ message.transactionId);
+ bufferIter->second->mTransactionCount++;
+ return true;
+}
+
+bool Accessor::Impl::BufferPool::handleTransferFrom(const BufferStatusMessage &message) {
+ auto found = mTransactions.find(message.transactionId);
+ if (found == mTransactions.end()) {
+ // TODO: is it feasible to check ownership here?
+ mTransactions.insert(std::make_pair(
+ message.transactionId,
+ std::make_unique<TransactionStatus>(message, mTimestampUs)));
+ insert(&mPendingTransactions, message.connectionId,
+ message.transactionId);
+ auto bufferIter = mBuffers.find(message.bufferId);
+ bufferIter->second->mTransactionCount++;
+ } else {
+ if (message.connectionId == found->second->mReceiver) {
+ found->second->mStatus = BufferStatus::TRANSFER_FROM;
+ }
+ }
+ return true;
+}
+
+bool Accessor::Impl::BufferPool::handleTransferResult(const BufferStatusMessage &message) {
+ auto found = mTransactions.find(message.transactionId);
+ if (found != mTransactions.end()) {
+ bool deleted = erase(&mPendingTransactions, message.connectionId,
+ message.transactionId);
+ if (deleted) {
+ if (!found->second->mSenderValidated) {
+ mCompletedTransactions.insert(message.transactionId);
+ }
+ auto bufferIter = mBuffers.find(message.bufferId);
+ if (message.newStatus == BufferStatus::TRANSFER_OK) {
+ handleOwnBuffer(message.connectionId, message.bufferId);
+ }
+ bufferIter->second->mTransactionCount--;
+ if (bufferIter->second->mOwnerCount == 0
+ && bufferIter->second->mTransactionCount == 0) {
+ mFreeBuffers.insert(message.bufferId);
+ }
+ }
+ ALOGV("transfer finished %" PRIu64 " %u - %d", message.transactionId,
+ message.bufferId, deleted);
+ return deleted;
+ }
+ ALOGV("transfer not found %" PRIu64 " %u", message.transactionId,
+ message.bufferId);
+ return false;
+}
+
+void Accessor::Impl::BufferPool::processStatusMessages() {
+ std::vector<BufferStatusMessage> messages;
+ mObserver.getBufferStatusChanges(messages);
+ mTimestampUs = getTimestampNow();
+ for (BufferStatusMessage& message: messages) {
+ bool ret = false;
+ switch (message.newStatus) {
+ case BufferStatus::NOT_USED:
+ ret = handleReleaseBuffer(
+ message.connectionId, message.bufferId);
+ break;
+ case BufferStatus::USED:
+ // not happening
+ break;
+ case BufferStatus::TRANSFER_TO:
+ ret = handleTransferTo(message);
+ break;
+ case BufferStatus::TRANSFER_FROM:
+ ret = handleTransferFrom(message);
+ break;
+ case BufferStatus::TRANSFER_TIMEOUT:
+ // TODO
+ break;
+ case BufferStatus::TRANSFER_LOST:
+ // TODO
+ break;
+ case BufferStatus::TRANSFER_FETCH:
+ // not happening
+ break;
+ case BufferStatus::TRANSFER_OK:
+ case BufferStatus::TRANSFER_ERROR:
+ ret = handleTransferResult(message);
+ break;
+ }
+ if (ret == false) {
+ ALOGW("buffer status message processing failure - message : %d "
+ "connection : %" PRId64,
+ message.newStatus, message.connectionId);
+ }
+ }
+ messages.clear();
+}
+
+bool Accessor::Impl::BufferPool::handleClose(ConnectionId connectionId) {
+ // Cleaning buffers
+ auto buffers = mUsingBuffers.find(connectionId);
+ if (buffers != mUsingBuffers.end()) {
+ for (const BufferId& bufferId : buffers->second) {
+ bool deleted = erase(&mUsingConnections, bufferId, connectionId);
+ if (deleted) {
+ auto bufferIter = mBuffers.find(bufferId);
+ bufferIter->second->mOwnerCount--;
+ if (bufferIter->second->mOwnerCount == 0 &&
+ bufferIter->second->mTransactionCount == 0) {
+ // TODO: handle freebuffer insert fail
+ mFreeBuffers.insert(bufferId);
+ }
+ }
+ }
+ mUsingBuffers.erase(buffers);
+ }
+
+ // Cleaning transactions
+ auto pending = mPendingTransactions.find(connectionId);
+ if (pending != mPendingTransactions.end()) {
+ for (const TransactionId& transactionId : pending->second) {
+ auto iter = mTransactions.find(transactionId);
+ if (iter != mTransactions.end()) {
+ if (!iter->second->mSenderValidated) {
+ mCompletedTransactions.insert(transactionId);
+ }
+ BufferId bufferId = iter->second->mBufferId;
+ auto bufferIter = mBuffers.find(bufferId);
+ bufferIter->second->mTransactionCount--;
+ if (bufferIter->second->mOwnerCount == 0 &&
+ bufferIter->second->mTransactionCount == 0) {
+ // TODO: handle freebuffer insert fail
+ mFreeBuffers.insert(bufferId);
+ }
+ mTransactions.erase(iter);
+ }
+ }
+ }
+ return true;
+}
+
+bool Accessor::Impl::BufferPool::getFreeBuffer(
+ const std::vector<uint8_t> ¶ms, BufferId *pId,
+ const native_handle_t** handle) {
+ auto bufferIt = mFreeBuffers.begin();
+ for (;bufferIt != mFreeBuffers.end(); ++bufferIt) {
+ BufferId bufferId = *bufferIt;
+ if (mBuffers[bufferId]->isRecyclable(params)) {
+ break;
+ }
+ }
+ if (bufferIt != mFreeBuffers.end()) {
+ BufferId id = *bufferIt;
+ mFreeBuffers.erase(bufferIt);
+ *handle = mBuffers[id]->handle();
+ *pId = id;
+ ALOGV("recycle a buffer %u %p", id, *handle);
+ return true;
+ }
+ return false;
+}
+
+ResultStatus Accessor::Impl::BufferPool::getNewLinearBuffer(
+ const std::shared_ptr<C2Allocator> &allocator,
+ const std::vector<uint8_t> ¶ms, BufferId *pId,
+ const native_handle_t** handle) {
+ union LinearParam {
+ struct {
+ uint32_t capacity;
+ C2MemoryUsage usage;
+ } data;
+ uint8_t array[0];
+ LinearParam() : data{0, {0, 0}} {}
+ } linearParam;
+ memcpy(&linearParam, params.data(),
+ std::min(sizeof(linearParam), params.size()));
+ std::shared_ptr<C2LinearAllocation> linearAlloc;
+ c2_status_t status = allocator->newLinearAllocation(
+ linearParam.data.capacity, linearParam.data.usage, &linearAlloc);
+ if (status == C2_OK) {
+ BufferId bufferId = mSeq++;
+ std::unique_ptr<InternalBuffer> buffer =
+ std::make_unique<InternalBuffer>(
+ bufferId, linearAlloc, params);
+ if (buffer) {
+ auto res = mBuffers.insert(std::make_pair(
+ bufferId, std::move(buffer)));
+ if (res.second) {
+ *handle = linearAlloc->handle();
+ *pId = bufferId;
+ return ResultStatus::OK;
+ }
+ }
+ return ResultStatus::NO_MEMORY;
+ }
+ // TODO: map C2 error code
+ return ResultStatus::CRITICAL_ERROR;
+}
+
+ResultStatus Accessor::Impl::BufferPool::getNewGraphicBuffer(
+ const std::shared_ptr<C2Allocator> &allocator,
+ const std::vector<uint8_t> ¶ms, BufferId *pId,
+ const native_handle_t** handle) {
+ union GraphicParam {
+ struct {
+ uint32_t width;
+ uint32_t height;
+ uint32_t format;
+ C2MemoryUsage usage;
+ } data;
+ uint8_t array[0];
+ GraphicParam() : data{0, 0, 0, {0, 0}} {}
+ } graphicParam;
+ memcpy(&graphicParam, params.data(),
+ std::min(sizeof(graphicParam), params.size()));
+ std::shared_ptr<C2GraphicAllocation> graphicAlloc;
+ c2_status_t status = allocator->newGraphicAllocation(
+ graphicParam.data.width, graphicParam.data.height,
+ graphicParam.data.format, graphicParam.data.usage, &graphicAlloc);
+ if (status == C2_OK) {
+ BufferId bufferId = mSeq;
+ std::unique_ptr<InternalBuffer> buffer =
+ std::make_unique<InternalBuffer>(
+ bufferId, graphicAlloc, params);
+ if (buffer) {
+ auto res = mBuffers.insert(std::make_pair(
+ bufferId, std::move(buffer)));
+ if (res.second) {
+ *handle = graphicAlloc->handle();
+ *pId = bufferId;
+ return ResultStatus::OK;
+ }
+ }
+ return ResultStatus::NO_MEMORY;
+ }
+ // TODO: map C2 error code
+ return ResultStatus::CRITICAL_ERROR;
+}
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace bufferpool
+} // namespace media
+} // namespace hardware
+} // namespace android
diff --git a/media/libstagefright/codec2/vndk/bufferpool/AccessorImpl.h b/media/libstagefright/codec2/vndk/bufferpool/AccessorImpl.h
new file mode 100644
index 0000000..92d926c
--- /dev/null
+++ b/media/libstagefright/codec2/vndk/bufferpool/AccessorImpl.h
@@ -0,0 +1,230 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V1_0_ACCESSORIMPL_H
+#define ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V1_0_ACCESSORIMPL_H
+
+#include <map>
+#include <set>
+#include "Accessor.h"
+
+namespace android {
+namespace hardware {
+namespace media {
+namespace bufferpool {
+namespace V1_0 {
+namespace implementation {
+
+struct InternalBuffer;
+struct TransactionStatus;
+
+/**
+ * An implementation of a buffer pool accessor(or a buffer pool implementation.) */
+class Accessor::Impl {
+public:
+ Impl(const std::shared_ptr<C2Allocator> &allocator, bool linear);
+
+ ~Impl();
+
+ ResultStatus connect(
+ const sp<Accessor> &accessor, sp<Connection> *connection,
+ ConnectionId *pConnectionId, const QueueDescriptor** fmqDescPtr);
+
+ ResultStatus close(ConnectionId connectionId);
+
+ ResultStatus allocate(ConnectionId connectionId,
+ const std::vector<uint8_t>& params,
+ BufferId *bufferId,
+ const native_handle_t** handle);
+
+ ResultStatus fetch(ConnectionId connectionId,
+ TransactionId transactionId,
+ BufferId bufferId,
+ const native_handle_t** handle);
+
+ /** Processes pending buffer status messages */
+ void sync();
+
+private:
+ // ConnectionId = pid : (timestamp_created + seqId)
+ // in order to guarantee uniqueness for each connection
+ static uint32_t sSeqId;
+ static int32_t sPid;
+
+ const std::shared_ptr<C2Allocator> mAllocator;
+ bool mLinear;
+
+ /**
+ * Buffer pool implementation.
+ *
+ * Handles buffer status messages. Handles buffer allocation/recycling.
+ * Handles buffer transfer between buffer pool clients.
+ */
+ struct BufferPool {
+ private:
+ std::mutex mMutex;
+ int64_t mTimestampUs;
+ BufferId mSeq;
+ BufferStatusObserver mObserver;
+
+ std::map<ConnectionId, std::set<BufferId>> mUsingBuffers;
+ std::map<BufferId, std::set<ConnectionId>> mUsingConnections;
+
+ std::map<ConnectionId, std::set<TransactionId>> mPendingTransactions;
+ // Transactions completed before TRANSFER_TO message arrival.
+ // Fetch does not occur for the transactions.
+ // Only transaction id is kept for the transactions in short duration.
+ std::set<TransactionId> mCompletedTransactions;
+ // Currently active(pending) transations' status & information.
+ std::map<TransactionId, std::unique_ptr<TransactionStatus>>
+ mTransactions;
+
+ std::map<BufferId, std::unique_ptr<InternalBuffer>> mBuffers;
+ std::set<BufferId> mFreeBuffers;
+
+ public:
+ /** Creates a buffer pool. */
+ BufferPool();
+
+ /**
+ * Processes all pending buffer status messages, and returns the result.
+ * Each status message is handled by methods with 'handle' prefix.
+ */
+ void processStatusMessages();
+
+ /**
+ * Handles a buffer being owned by a connection.
+ *
+ * @param connectionId the id of the buffer owning connection.
+ * @param bufferId the id of the buffer.
+ *
+ * @return {@code true} when the buffer is owned,
+ * {@code false} otherwise.
+ */
+ bool handleOwnBuffer(ConnectionId connectionId, BufferId bufferId);
+
+ /**
+ * Handles a buffer being released by a connection.
+ *
+ * @param connectionId the id of the buffer owning connection.
+ * @param bufferId the id of the buffer.
+ *
+ * @return {@code true} when the buffer ownership is released,
+ * {@code false} otherwise.
+ */
+ bool handleReleaseBuffer(ConnectionId connectionId, BufferId bufferId);
+
+ /**
+ * Handles a transfer transaction start message from the sender.
+ *
+ * @param message a buffer status message for the transaction.
+ *
+ * @result {@code true} when transfer_to message is acknowledged,
+ * {@code false} otherwise.
+ */
+ bool handleTransferTo(const BufferStatusMessage &message);
+
+ /**
+ * Handles a transfer transaction being acked by the receiver.
+ *
+ * @param message a buffer status message for the transaction.
+ *
+ * @result {@code true} when transfer_from message is acknowledged,
+ * {@code false} otherwise.
+ */
+ bool handleTransferFrom(const BufferStatusMessage &message);
+
+ /**
+ * Handles a transfer transaction result message from the receiver.
+ *
+ * @param message a buffer status message for the transaction.
+ *
+ * @result {@code true} when the exisitng transaction is finished,
+ * {@code false} otherwise.
+ */
+ bool handleTransferResult(const BufferStatusMessage &message);
+
+ /**
+ * Handles a connection being closed, and returns the result. All the
+ * buffers and transactions owned by the connection will be cleaned up.
+ * The related FMQ will be cleaned up too.
+ *
+ * @param connectionId the id of the connection.
+ *
+ * @result {@code true} when the connection existed,
+ * {@code false} otherwise.
+ */
+ bool handleClose(ConnectionId connectionId);
+
+ /**
+ * Recycles a existing free buffer if it is possible.
+ *
+ * @param params the allocation parameters.
+ * @param pId the id of the recycled buffer.
+ * @param handle the native handle of the recycled buffer.
+ *
+ * @return {@code true} when a buffer is recycled, {@code false}
+ * otherwise.
+ */
+ bool getFreeBuffer(const std::vector<uint8_t> ¶ms, BufferId *pId,
+ const native_handle_t **handle);
+
+ /**
+ * Creates a new linear buffer.
+ *
+ * @param allocator the linear buffer allocator
+ * @param params the allocator parameters
+ * @param pId the buffer id for the newly allocated buffer.
+ * @param handle the native handle for the newly allocated buffer.
+ *
+ * @return OK when a linear allocation is successfully allocated.
+ * NO_MEMORY when there is no memory.
+ * CRITICAL_ERROR otherwise.
+ */
+ ResultStatus getNewLinearBuffer(
+ const std::shared_ptr<C2Allocator> &allocator,
+ const std::vector<uint8_t> ¶ms, BufferId *pId,
+ const native_handle_t **handle);
+
+ /**
+ * Creates a new graphic buffer.
+ *
+ * @param allocator the graphic buffer allocator
+ * @param params the allocator parameters
+ * @param pId the buffer id for the newly allocated buffer.
+ * @param handle the native handle for the newly allocated buffer.
+ *
+ * @return OK when a graphic allocation is successfully allocated.
+ * NO_MEMORY when there is no memory.
+ * CRITICAL_ERROR otherwise.
+ */
+ ResultStatus getNewGraphicBuffer(
+ const std::shared_ptr<C2Allocator> &allocator,
+ const std::vector<uint8_t> ¶ms, BufferId *pId,
+ const native_handle_t **handle);
+
+ friend class Accessor::Impl;
+ } mBufferPool;
+};
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace ufferpool
+} // namespace media
+} // namespace hardware
+} // namespace android
+
+#endif // ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V1_0_ACCESSORIMPL_H
diff --git a/media/libstagefright/codec2/vndk/bufferpool/Android.bp b/media/libstagefright/codec2/vndk/bufferpool/Android.bp
new file mode 100644
index 0000000..f0588d5
--- /dev/null
+++ b/media/libstagefright/codec2/vndk/bufferpool/Android.bp
@@ -0,0 +1,31 @@
+cc_library_shared {
+ name: "android.hardware.media.bufferpool@1.0-impl",
+ vendor_available: true,
+ vndk: {
+ enabled: true,
+ },
+ srcs: [
+ "Accessor.cpp",
+ "AccessorImpl.cpp",
+ "BufferPoolClient.cpp",
+ "BufferStatus.cpp",
+ "ClientManager.cpp",
+ "Connection.cpp",
+ ],
+ export_include_dirs: [
+ "include",
+ ],
+ include_dirs: [
+ "frameworks/av/media/libstagefright/codec2/vndk/bufferpool/include",
+ ],
+ shared_libs: [
+ "libcutils",
+ "libfmq",
+ "libhidlbase",
+ "libhidltransport",
+ "liblog",
+ "libstagefright_codec2",
+ "libutils",
+ "android.hardware.media.bufferpool@1.0",
+ ],
+}
diff --git a/media/libstagefright/codec2/vndk/bufferpool/BufferPoolClient.cpp b/media/libstagefright/codec2/vndk/bufferpool/BufferPoolClient.cpp
new file mode 100644
index 0000000..7cb4ba9
--- /dev/null
+++ b/media/libstagefright/codec2/vndk/bufferpool/BufferPoolClient.cpp
@@ -0,0 +1,566 @@
+/*
+ * Copyright (C) 2018 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 "bufferpool"
+//#define LOG_NDEBUG 0
+
+#include <inttypes.h>
+#include <thread>
+#include <utils/Log.h>
+#include "BufferPoolClient.h"
+#include "Connection.h"
+
+namespace android {
+namespace hardware {
+namespace media {
+namespace bufferpool {
+namespace V1_0 {
+namespace implementation {
+
+static constexpr int64_t kReceiveTimeoutUs = 5000; // 5ms
+static constexpr int kPostMaxRetry = 3;
+static constexpr int kCacheTtlUs = 500000; // TODO: tune
+
+class BufferPoolClient::Impl
+ : public std::enable_shared_from_this<BufferPoolClient::Impl> {
+public:
+ explicit Impl(const sp<Accessor> &accessor);
+
+ explicit Impl(const sp<IAccessor> &accessor);
+
+ bool isValid() {
+ return mValid;
+ }
+
+ ConnectionId getConnectionId() {
+ return mConnectionId;
+ }
+
+ sp<IAccessor> &getAccessor() {
+ return mAccessor;
+ }
+
+ ResultStatus allocate(const std::vector<uint8_t> ¶ms,
+ std::shared_ptr<_C2BlockPoolData> *buffer);
+
+ ResultStatus receive(
+ TransactionId transactionId, BufferId bufferId,
+ int64_t timestampUs, std::shared_ptr<_C2BlockPoolData> *buffer);
+
+ void postBufferRelease(BufferId bufferId);
+
+ bool postSend(
+ BufferId bufferId, ConnectionId receiver,
+ TransactionId *transactionId, int64_t *timestampUs);
+private:
+
+ bool postReceive(
+ BufferId bufferId, TransactionId transactionId,
+ int64_t timestampUs);
+
+ bool postReceiveResult(
+ BufferId bufferId, TransactionId transactionId, bool result);
+
+ bool syncReleased();
+
+ ResultStatus allocateBufferHandle(
+ const std::vector<uint8_t>& params, BufferId *bufferId,
+ native_handle_t **handle);
+
+ ResultStatus fetchBufferHandle(
+ TransactionId transactionId, BufferId bufferId,
+ native_handle_t **handle);
+
+
+ struct BlockPoolDataDtor;
+ struct ClientBuffer;
+
+ bool mLocal;
+ bool mValid;
+ sp<IAccessor> mAccessor;
+ sp<Connection> mLocalConnection;
+ sp<IConnection> mRemoteConnection;
+ uint32_t mSeqId;
+ ConnectionId mConnectionId;
+
+ // CachedBuffers
+ struct {
+ std::mutex mLock;
+ bool creating;
+ std::condition_variable mCreateCv;
+ std::map<BufferId, std::unique_ptr<ClientBuffer>> mBuffers;
+ } mCache;
+
+ // FMQ - release notifier
+ struct {
+ std::mutex mLock;
+ // TODO: use only one list?(using one list may dealy sending messages?)
+ std::list<BufferId> mReleasingIds;
+ std::list<BufferId> mReleasedIds;
+ std::unique_ptr<BufferStatusChannel> mStatusChannel;
+ } mReleasing;
+};
+
+struct BufferPoolClient::Impl::BlockPoolDataDtor {
+ BlockPoolDataDtor(const std::shared_ptr<BufferPoolClient::Impl> &impl)
+ : mImpl(impl) {}
+
+ void operator()(_C2BlockPoolData *buffer) {
+ BufferId id = buffer->mId;
+ delete buffer;
+
+ auto impl = mImpl.lock();
+ if (impl && impl->isValid()) {
+ impl->postBufferRelease(id);
+ }
+ }
+ const std::weak_ptr<BufferPoolClient::Impl> mImpl;
+};
+
+struct BufferPoolClient::Impl::ClientBuffer {
+private:
+ bool mInvalidated; // TODO: implement
+ int64_t mExpireUs;
+ bool mHasCache;
+ _C2BlockPoolData mBuffer;
+ std::weak_ptr<_C2BlockPoolData> mCache;
+
+ void updateExpire() {
+ mExpireUs = getTimestampNow() + kCacheTtlUs;
+ }
+
+public:
+ ClientBuffer(BufferId id, native_handle_t *handle)
+ : mInvalidated(false), mHasCache(false), mBuffer(id, handle) {
+ (void)mInvalidated;
+ mExpireUs = getTimestampNow() + kCacheTtlUs;
+ }
+
+ bool expire() const {
+ int64_t now = getTimestampNow();
+ return now >= mExpireUs;
+ }
+
+ bool hasCache() const {
+ return mHasCache;
+ }
+
+ std::shared_ptr<_C2BlockPoolData> fetchCache() {
+ if (mHasCache) {
+ std::shared_ptr<_C2BlockPoolData> cache = mCache.lock();
+ if (cache) {
+ updateExpire();
+ }
+ return cache;
+ }
+ return nullptr;
+ }
+
+ std::shared_ptr<_C2BlockPoolData> createCache(
+ const std::shared_ptr<BufferPoolClient::Impl> &impl) {
+ if (!mHasCache) {
+ // Allocates a raw ptr in order to avoid sending #postBufferRelease
+ // from deleter, in case of native_handle_clone failure.
+ _C2BlockPoolData *ptr = new _C2BlockPoolData(
+ mBuffer.mId, native_handle_clone(mBuffer.mHandle));
+ if (ptr && ptr->mHandle != NULL) {
+ std::shared_ptr<_C2BlockPoolData>
+ cache(ptr, BlockPoolDataDtor(impl));
+ if (cache) {
+ mCache = cache;
+ mHasCache = true;
+ updateExpire();
+ return cache;
+ }
+ }
+ if (ptr) {
+ delete ptr;
+ }
+ }
+ return nullptr;
+ }
+
+ bool onCacheRelease() {
+ if (mHasCache) {
+ // TODO: verify mCache is not valid;
+ mHasCache = false;
+ return true;
+ }
+ return false;
+ }
+};
+
+BufferPoolClient::Impl::Impl(const sp<Accessor> &accessor)
+ : mLocal(true), mAccessor(accessor), mSeqId(0) {
+ mValid = false;
+ const QueueDescriptor *fmqDesc;
+ ResultStatus status = accessor->connect(
+ &mLocalConnection, &mConnectionId, &fmqDesc);
+ if (status == ResultStatus::OK) {
+ mReleasing.mStatusChannel =
+ std::make_unique<BufferStatusChannel>(*fmqDesc);
+ mValid = mReleasing.mStatusChannel &&
+ mReleasing.mStatusChannel->isValid();
+ }
+}
+
+BufferPoolClient::Impl::Impl(const sp<IAccessor> &accessor)
+ : mLocal(false), mAccessor(accessor), mSeqId(0) {
+ mValid = false;
+ bool& valid = mValid;
+ sp<IConnection>& outConnection = mRemoteConnection;
+ ConnectionId& id = mConnectionId;
+ std::unique_ptr<BufferStatusChannel>& outChannel =
+ mReleasing.mStatusChannel;
+ accessor->connect(
+ [&valid, &outConnection, &id, &outChannel]
+ (ResultStatus status, sp<IConnection> connection,
+ ConnectionId connectionId, const QueueDescriptor& desc) {
+ if (status == ResultStatus::OK) {
+ outConnection = connection;
+ id = connectionId;
+ outChannel = std::make_unique<BufferStatusChannel>(desc);
+ if (outChannel && outChannel->isValid()) {
+ valid = true;
+ }
+ }
+ });
+}
+
+ResultStatus BufferPoolClient::Impl::allocate(
+ const std::vector<uint8_t> ¶ms,
+ std::shared_ptr<_C2BlockPoolData> *buffer) {
+ if (!mLocal || !mLocalConnection || !mValid) {
+ return ResultStatus::CRITICAL_ERROR;
+ }
+ BufferId bufferId;
+ native_handle_t *handle = NULL;
+ buffer->reset();
+ ResultStatus status = allocateBufferHandle(params, &bufferId, &handle);
+ if (status == ResultStatus::OK) {
+ if (handle) {
+ std::unique_lock<std::mutex> lock(mCache.mLock);
+ syncReleased();
+ auto cacheIt = mCache.mBuffers.find(bufferId);
+ if (cacheIt != mCache.mBuffers.end()) {
+ // TODO: verify it is recycled. (not having active ref)
+ mCache.mBuffers.erase(cacheIt);
+ }
+ auto clientBuffer = std::make_unique<ClientBuffer>(
+ bufferId, handle);
+ if (clientBuffer) {
+ auto result = mCache.mBuffers.insert(std::make_pair(
+ bufferId, std::move(clientBuffer)));
+ if (result.second) {
+ *buffer = result.first->second->createCache(
+ shared_from_this());
+ }
+ }
+ }
+ if (!*buffer) {
+ ALOGV("client cache creation failure %d: %" PRId64,
+ handle != NULL, mConnectionId);
+ status = ResultStatus::NO_MEMORY;
+ postBufferRelease(bufferId);
+ }
+ }
+ return status;
+}
+
+ResultStatus BufferPoolClient::Impl::receive(
+ TransactionId transactionId, BufferId bufferId, int64_t timestampUs,
+ std::shared_ptr<_C2BlockPoolData> *buffer) {
+ if (!mValid) {
+ return ResultStatus::CRITICAL_ERROR;
+ }
+ if (timestampUs != 0) {
+ timestampUs += kReceiveTimeoutUs;
+ }
+ if (!postReceive(bufferId, transactionId, timestampUs)) {
+ return ResultStatus::CRITICAL_ERROR;
+ }
+ ResultStatus status = ResultStatus::CRITICAL_ERROR;
+ buffer->reset();
+ while(1) {
+ std::unique_lock<std::mutex> lock(mCache.mLock);
+ syncReleased();
+ auto cacheIt = mCache.mBuffers.find(bufferId);
+ if (cacheIt != mCache.mBuffers.end()) {
+ if (cacheIt->second->hasCache()) {
+ *buffer = cacheIt->second->fetchCache();
+ if (!*buffer) {
+ // check transfer time_out
+ lock.unlock();
+ std::this_thread::yield();
+ continue;
+ }
+ ALOGV("client receive from reference %" PRId64, mConnectionId);
+ break;
+ } else {
+ *buffer = cacheIt->second->createCache(shared_from_this());
+ ALOGV("client receive from cache %" PRId64, mConnectionId);
+ break;
+ }
+ } else {
+ if (!mCache.creating) {
+ mCache.creating = true;
+ lock.unlock();
+ native_handle_t* handle = NULL;
+ status = fetchBufferHandle(transactionId, bufferId, &handle);
+ lock.lock();
+ if (status == ResultStatus::OK) {
+ if (handle) {
+ auto clientBuffer = std::make_unique<ClientBuffer>(
+ bufferId, handle);
+ if (clientBuffer) {
+ auto result = mCache.mBuffers.insert(
+ std::make_pair(bufferId, std::move(
+ clientBuffer)));
+ if (result.second) {
+ *buffer = result.first->second->createCache(
+ shared_from_this());
+ }
+ }
+ }
+ if (!*buffer) {
+ status = ResultStatus::NO_MEMORY;
+ }
+ }
+ mCache.creating = false;
+ lock.unlock();
+ mCache.mCreateCv.notify_all();
+ break;
+ }
+ mCache.mCreateCv.wait(lock);
+ }
+ }
+ bool posted = postReceiveResult(bufferId, transactionId,
+ *buffer ? true : false);
+ ALOGV("client receive %" PRId64 " - %u : %s (%d)", mConnectionId, bufferId,
+ *buffer ? "ok" : "fail", posted);
+ if (*buffer) {
+ if (!posted) {
+ buffer->reset();
+ return ResultStatus::CRITICAL_ERROR;
+ }
+ return ResultStatus::OK;
+ }
+ return status;
+}
+
+
+void BufferPoolClient::Impl::postBufferRelease(BufferId bufferId) {
+ std::lock_guard<std::mutex> lock(mReleasing.mLock);
+ mReleasing.mReleasingIds.push_back(bufferId);
+ mReleasing.mStatusChannel->postBufferRelease(
+ mConnectionId, mReleasing.mReleasingIds, mReleasing.mReleasedIds);
+}
+
+// TODO: revise ad-hoc posting data structure
+bool BufferPoolClient::Impl::postSend(
+ BufferId bufferId, ConnectionId receiver,
+ TransactionId *transactionId, int64_t *timestampUs) {
+ std::lock_guard<std::mutex> lock(mReleasing.mLock);
+ *timestampUs = getTimestampNow();
+ *transactionId = (mConnectionId << 32) | mSeqId++;
+ // TODO: retry, add timeout, target?
+ return mReleasing.mStatusChannel->postBufferStatusMessage(
+ *transactionId, bufferId, BufferStatus::TRANSFER_TO, mConnectionId,
+ receiver, mReleasing.mReleasingIds, mReleasing.mReleasedIds);
+}
+
+bool BufferPoolClient::Impl::postReceive(
+ BufferId bufferId, TransactionId transactionId, int64_t timestampUs) {
+ for (int i = 0; i < kPostMaxRetry; ++i) {
+ std::unique_lock<std::mutex> lock(mReleasing.mLock);
+ int64_t now = getTimestampNow();
+ if (timestampUs == 0 || now < timestampUs) {
+ bool result = mReleasing.mStatusChannel->postBufferStatusMessage(
+ transactionId, bufferId, BufferStatus::TRANSFER_FROM,
+ mConnectionId, -1, mReleasing.mReleasingIds,
+ mReleasing.mReleasedIds);
+ if (result) {
+ return true;
+ }
+ lock.unlock();
+ std::this_thread::yield();
+ } else {
+ mReleasing.mStatusChannel->postBufferStatusMessage(
+ transactionId, bufferId, BufferStatus::TRANSFER_TIMEOUT,
+ mConnectionId, -1, mReleasing.mReleasingIds,
+ mReleasing.mReleasedIds);
+ return false;
+ }
+ }
+ return false;
+}
+
+bool BufferPoolClient::Impl::postReceiveResult(
+ BufferId bufferId, TransactionId transactionId, bool result) {
+ std::lock_guard<std::mutex> lock(mReleasing.mLock);
+ // TODO: retry, add timeout
+ return mReleasing.mStatusChannel->postBufferStatusMessage(
+ transactionId, bufferId,
+ result ? BufferStatus::TRANSFER_OK : BufferStatus::TRANSFER_ERROR,
+ mConnectionId, -1, mReleasing.mReleasingIds,
+ mReleasing.mReleasedIds);
+}
+
+// should have mCache.mLock
+bool BufferPoolClient::Impl::syncReleased() {
+ std::lock_guard<std::mutex> lock(mReleasing.mLock);
+ if (mReleasing.mReleasingIds.size() > 0) {
+ mReleasing.mStatusChannel->postBufferRelease(
+ mConnectionId, mReleasing.mReleasingIds,
+ mReleasing.mReleasedIds);
+ }
+ if (mReleasing.mReleasedIds.size() > 0) {
+ for (BufferId& id: mReleasing.mReleasedIds) {
+ ALOGV("client release buffer %" PRId64 " - %u", mConnectionId, id);
+ auto found = mCache.mBuffers.find(id);
+ if (found != mCache.mBuffers.end()) {
+ if (!found->second->onCacheRelease()) {
+ // should not happen!
+ ALOGW("client %" PRId64 "cache release status inconsitent!",
+ mConnectionId);
+ }
+ if (found->second->expire()) {
+ ALOGV("client evict buffer from cache %" PRId64 " - %u",
+ mConnectionId, id);
+ mCache.mBuffers.erase(found);
+ }
+ } else {
+ // should not happen!
+ ALOGW("client %" PRId64 "cache status inconsitent!",
+ mConnectionId);
+ }
+ }
+ mReleasing.mReleasedIds.clear();
+ return true;
+ }
+ return false;
+}
+
+ResultStatus BufferPoolClient::Impl::allocateBufferHandle(
+ const std::vector<uint8_t>& params, BufferId *bufferId,
+ native_handle_t** handle) {
+ if (mLocalConnection) {
+ const native_handle_t* allocHandle = NULL;
+ ResultStatus status = mLocalConnection->allocate(
+ params, bufferId, &allocHandle);
+ if (status == ResultStatus::OK) {
+ *handle = native_handle_clone(allocHandle);
+ }
+ ALOGV("client allocate result %" PRId64 "%d : %u clone %p",
+ mConnectionId, status == ResultStatus::OK,
+ *handle ? *bufferId : 0 , *handle);
+ return status;
+ }
+ return ResultStatus::CRITICAL_ERROR;
+}
+
+ResultStatus BufferPoolClient::Impl::fetchBufferHandle(
+ TransactionId transactionId, BufferId bufferId,
+ native_handle_t **handle) {
+ sp<IConnection> connection;
+ if (mLocal) {
+ connection = mLocalConnection;
+ } else {
+ connection = mRemoteConnection;
+ }
+ ResultStatus status;
+ connection->fetch(
+ transactionId, bufferId,
+ [&status, &handle]
+ (ResultStatus outStatus, Buffer outBuffer) {
+ status = outStatus;
+ if (status == ResultStatus::OK) {
+ *handle = native_handle_clone(
+ outBuffer.buffer.getNativeHandle());
+ }
+ });
+ return status;
+}
+
+
+BufferPoolClient::BufferPoolClient(const sp<Accessor> &accessor) {
+ mImpl = std::make_shared<Impl>(accessor);
+}
+
+BufferPoolClient::BufferPoolClient(const sp<IAccessor> &accessor) {
+ mImpl = std::make_shared<Impl>(accessor);
+}
+
+BufferPoolClient::~BufferPoolClient() {
+ // TODO: how to handle orphaned buffers?
+}
+
+bool BufferPoolClient::isValid() {
+ return mImpl && mImpl->isValid();
+}
+
+ConnectionId BufferPoolClient::getConnectionId() {
+ if (isValid()) {
+ return mImpl->getConnectionId();
+ }
+ return -1;
+}
+
+ResultStatus BufferPoolClient::getAccessor(sp<IAccessor> *accessor) {
+ if (isValid()) {
+ *accessor = mImpl->getAccessor();
+ return ResultStatus::OK;
+ }
+ return ResultStatus::CRITICAL_ERROR;
+}
+
+ResultStatus BufferPoolClient::allocate(
+ const std::vector<uint8_t> ¶ms,
+ std::shared_ptr<_C2BlockPoolData> *buffer) {
+ if (isValid()) {
+ return mImpl->allocate(params, buffer);
+ }
+ return ResultStatus::CRITICAL_ERROR;
+}
+
+ResultStatus BufferPoolClient::receive(
+ TransactionId transactionId, BufferId bufferId, int64_t timestampUs,
+ std::shared_ptr<_C2BlockPoolData> *buffer) {
+ if (isValid()) {
+ return mImpl->receive(transactionId, bufferId, timestampUs, buffer);
+ }
+ return ResultStatus::CRITICAL_ERROR;
+}
+
+ResultStatus BufferPoolClient::postSend(
+ ConnectionId receiverId,
+ const std::shared_ptr<_C2BlockPoolData> &buffer,
+ TransactionId *transactionId,
+ int64_t *timestampUs) {
+ if (isValid()) {
+ bool result = mImpl->postSend(
+ buffer->mId, receiverId, transactionId, timestampUs);
+ return result ? ResultStatus::OK : ResultStatus::CRITICAL_ERROR;
+ }
+ return ResultStatus::CRITICAL_ERROR;
+}
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace bufferpool
+} // namespace media
+} // namespace hardware
+} // namespace android
diff --git a/media/libstagefright/codec2/vndk/bufferpool/BufferPoolClient.h b/media/libstagefright/codec2/vndk/bufferpool/BufferPoolClient.h
new file mode 100644
index 0000000..a6111b5
--- /dev/null
+++ b/media/libstagefright/codec2/vndk/bufferpool/BufferPoolClient.h
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V1_0_BUFFERPOOLCLIENT_H
+#define ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V1_0_BUFFERPOOLCLIENT_H
+
+#include <memory>
+#include <cutils/native_handle.h>
+#include <android/hardware/media/bufferpool/1.0/IAccessor.h>
+#include <android/hardware/media/bufferpool/1.0/IConnection.h>
+#include <BufferPoolTypes.h>
+#include "Accessor.h"
+
+namespace android {
+namespace hardware {
+namespace media {
+namespace bufferpool {
+namespace V1_0 {
+namespace implementation {
+
+using ::android::hardware::media::bufferpool::V1_0::IAccessor;
+using ::android::hardware::media::bufferpool::V1_0::IConnection;
+using ::android::hardware::media::bufferpool::V1_0::ResultStatus;
+using ::android::sp;
+
+/**
+ * A buffer pool client for a buffer pool. For a specific buffer pool, at most
+ * one buffer pool client exists per process. This class will not be exposed
+ * outside. A buffer pool client will be used via ClientManager.
+ */
+class BufferPoolClient {
+public:
+ /**
+ * Creates a buffer pool client from a local buffer pool
+ * (via ClientManager#create).
+ */
+ explicit BufferPoolClient(const sp<Accessor> &accessor);
+
+ /**
+ * Creates a buffer pool client from a remote buffer pool
+ * (via ClientManager#registerSender).
+ * Note: A buffer pool client created with remote buffer pool cannot
+ * allocate a buffer.
+ */
+ explicit BufferPoolClient(const sp<IAccessor> &accessor);
+
+ /** Destructs a buffer pool client. */
+ ~BufferPoolClient();
+
+private:
+ bool isValid();
+
+ ConnectionId getConnectionId();
+
+ ResultStatus getAccessor(sp<IAccessor> *accessor);
+
+ ResultStatus allocate(const std::vector<uint8_t> ¶ms,
+ std::shared_ptr<_C2BlockPoolData> *buffer);
+
+ ResultStatus receive(TransactionId transactionId,
+ BufferId bufferId,
+ int64_t timestampUs,
+ std::shared_ptr<_C2BlockPoolData> *buffer);
+
+ ResultStatus postSend(ConnectionId receiver,
+ const std::shared_ptr<_C2BlockPoolData> &buffer,
+ TransactionId *transactionId,
+ int64_t *timestampUs);
+
+ class Impl;
+ std::shared_ptr<Impl> mImpl;
+
+ friend struct ClientManager;
+};
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace bufferpool
+} // namespace media
+} // namespace hardware
+} // namespace android
+
+#endif // ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V1_0_BUFFERPOOLCLIENT_H
diff --git a/media/libstagefright/codec2/vndk/bufferpool/BufferStatus.cpp b/media/libstagefright/codec2/vndk/bufferpool/BufferStatus.cpp
new file mode 100644
index 0000000..4fc88c6
--- /dev/null
+++ b/media/libstagefright/codec2/vndk/bufferpool/BufferStatus.cpp
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) 2018 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 "bufferpool"
+//#define LOG_NDEBUG 0
+
+#include <inttypes.h>
+#include <time.h>
+#include "BufferStatus.h"
+
+namespace android {
+namespace hardware {
+namespace media {
+namespace bufferpool {
+namespace V1_0 {
+namespace implementation {
+
+int64_t getTimestampNow() {
+ int64_t stamp;
+ struct timespec ts;
+ // TODO: CLOCK_MONOTONIC_COARSE?
+ clock_gettime(CLOCK_MONOTONIC, &ts);
+ stamp = ts.tv_nsec / 1000;
+ stamp += (ts.tv_sec * 1000000LL);
+ return stamp;
+}
+
+static constexpr int kNumElementsInQueue = 1024*16;
+
+ResultStatus BufferStatusObserver::open(
+ ConnectionId id, const QueueDescriptor** fmqDescPtr) {
+ if (mBufferStatusQueues.find(id) != mBufferStatusQueues.end()) {
+ // TODO: id collision log?
+ return ResultStatus::CRITICAL_ERROR;
+ }
+ std::unique_ptr<BufferStatusQueue> queue =
+ std::make_unique<BufferStatusQueue>(kNumElementsInQueue);
+ if (!queue || queue->isValid() == false) {
+ *fmqDescPtr = NULL;
+ return ResultStatus::NO_MEMORY;
+ } else {
+ *fmqDescPtr = queue->getDesc();
+ }
+ auto result = mBufferStatusQueues.insert(
+ std::make_pair(id, std::move(queue)));
+ if (!result.second) {
+ *fmqDescPtr = NULL;
+ return ResultStatus::NO_MEMORY;
+ }
+ return ResultStatus::OK;
+}
+
+ResultStatus BufferStatusObserver::close(ConnectionId id) {
+ if (mBufferStatusQueues.find(id) == mBufferStatusQueues.end()) {
+ return ResultStatus::CRITICAL_ERROR;
+ }
+ mBufferStatusQueues.erase(id);
+ return ResultStatus::OK;
+}
+
+void BufferStatusObserver::getBufferStatusChanges(std::vector<BufferStatusMessage> &messages) {
+ for (auto it = mBufferStatusQueues.begin(); it != mBufferStatusQueues.end(); ++it) {
+ BufferStatusMessage message;
+ size_t avail = it->second->availableToRead();
+ while (avail > 0) {
+ if (!it->second->read(&message, 1)) {
+ // Since avaliable # of reads are already confiremd,
+ // this should not happen.
+ // TODO: error handling (supurious client?)
+ ALOGW("FMQ message cannot be read from %" PRId64, it->first);
+ return;
+ }
+ message.connectionId = it->first;
+ messages.push_back(message);
+ --avail;
+ }
+ }
+}
+
+BufferStatusChannel::BufferStatusChannel(
+ const QueueDescriptor &fmqDesc) {
+ std::unique_ptr<BufferStatusQueue> queue =
+ std::make_unique<BufferStatusQueue>(fmqDesc);
+ if (!queue || queue->isValid() == false) {
+ mValid = false;
+ return;
+ }
+ mValid = true;
+ mBufferStatusQueue = std::move(queue);
+}
+
+bool BufferStatusChannel::isValid() {
+ return mValid;
+}
+
+void BufferStatusChannel::postBufferRelease(
+ ConnectionId connectionId,
+ std::list<BufferId> &pending, std::list<BufferId> &posted) {
+ if (mValid && pending.size() > 0) {
+ size_t avail = mBufferStatusQueue->availableToWrite();
+ avail = std::min(avail, pending.size());
+ BufferStatusMessage message;
+ for (size_t i = 0 ; i < avail; ++i) {
+ BufferId id = pending.front();
+ message.newStatus = BufferStatus::NOT_USED;
+ message.bufferId = id;
+ message.connectionId = connectionId;
+ if (!mBufferStatusQueue->write(&message, 1)) {
+ // Since avaliable # of writes are already confiremd,
+ // this should not happen.
+ // TODO: error handing?
+ ALOGW("FMQ message cannot be sent from %" PRId64, connectionId);
+ return;
+ }
+ pending.pop_front();
+ posted.push_back(id);
+ }
+ }
+}
+
+bool BufferStatusChannel::postBufferStatusMessage(
+ TransactionId transactionId, BufferId bufferId,
+ BufferStatus status, ConnectionId connectionId, ConnectionId targetId,
+ std::list<BufferId> &pending, std::list<BufferId> &posted) {
+ if (mValid) {
+ size_t avail = mBufferStatusQueue->availableToWrite();
+ size_t numPending = pending.size();
+ if (avail >= numPending + 1) {
+ BufferStatusMessage release, message;
+ for (size_t i = 0; i < numPending; ++i) {
+ BufferId id = pending.front();
+ release.newStatus = BufferStatus::NOT_USED;
+ release.bufferId = id;
+ release.connectionId = connectionId;
+ if (!mBufferStatusQueue->write(&release, 1)) {
+ // Since avaliable # of writes are already confiremd,
+ // this should not happen.
+ // TODO: error handling?
+ ALOGW("FMQ message cannot be sent from %" PRId64,
+ connectionId);
+ return false;
+ }
+ pending.pop_front();
+ posted.push_back(id);
+ }
+ message.transactionId = transactionId;
+ message.bufferId = bufferId;
+ message.newStatus = status;
+ message.connectionId = connectionId;
+ message.targetConnectionId = targetId;
+ // TODO : timesatamp
+ message.timestampUs = 0;
+ if (!mBufferStatusQueue->write(&message, 1)) {
+ // Since avaliable # of writes are already confiremd,
+ // this should not happen.
+ ALOGW("FMQ message cannot be sent from %" PRId64, connectionId);
+ return false;
+ }
+ return true;
+ }
+ }
+ return false;
+}
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace bufferpool
+} // namespace media
+} // namespace hardware
+} // namespace android
+
diff --git a/media/libstagefright/codec2/vndk/bufferpool/BufferStatus.h b/media/libstagefright/codec2/vndk/bufferpool/BufferStatus.h
new file mode 100644
index 0000000..82303cb
--- /dev/null
+++ b/media/libstagefright/codec2/vndk/bufferpool/BufferStatus.h
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V1_0_BUFFERSTATUS_H
+#define ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V1_0_BUFFERSTATUS_H
+
+#include <android/hardware/media/bufferpool/1.0/types.h>
+#include <fmq/MessageQueue.h>
+#include <hidl/MQDescriptor.h>
+#include <hidl/Status.h>
+#include <memory>
+#include <mutex>
+#include <vector>
+#include <list>
+#include <BufferPoolTypes.h>
+
+namespace android {
+namespace hardware {
+namespace media {
+namespace bufferpool {
+namespace V1_0 {
+namespace implementation {
+
+/** Returns monotonic timestamp in Us since fixed point in time. */
+int64_t getTimestampNow();
+
+/**
+ * A collection of FMQ for a buffer pool. buffer ownership/status change
+ * messages are sent via the FMQs from the clients.
+ */
+class BufferStatusObserver {
+private:
+ std::map<ConnectionId, std::unique_ptr<BufferStatusQueue>>
+ mBufferStatusQueues;
+
+public:
+ /** Creates an FMQ for the specified connection(client).
+ *
+ * @param connectionId connection Id of the specified client.
+ * @param fmqDescPtr double ptr of created FMQ's descriptor.
+ *
+ * @return OK if FMQ is created successfully.
+ * NO_MEMORY when there is no memory.
+ * CRITICAL_ERROR otherwise.
+ */
+ ResultStatus open(ConnectionId id, const QueueDescriptor** fmqDescPtr);
+
+ /** Closes an FMQ for the specified connection(client).
+ *
+ * @param connectionId connection Id of the specified client.
+ *
+ * @return OK if the specified connection is closed successfully.
+ * CRITICAL_ERROR otherwise.
+ */
+ ResultStatus close(ConnectionId id);
+
+ /** Retrieves all pending FMQ buffer status messages from clients.
+ *
+ * @param messages retrieved pending messages.
+ */
+ void getBufferStatusChanges(std::vector<BufferStatusMessage> &messages);
+};
+
+/**
+ * An FMQ for a buffer pool client. buffer ownership/status change messages
+ * are sent via the fmq to the buffer pool.
+ */
+class BufferStatusChannel {
+private:
+ bool mValid;
+ std::unique_ptr<BufferStatusQueue> mBufferStatusQueue;
+
+public:
+ /**
+ * Connects to an FMQ from a descriptor of the created FMQ.
+ *
+ * @param fmqDesc Descriptor of the created FMQ.
+ */
+ BufferStatusChannel(const QueueDescriptor &fmqDesc);
+
+ /** Returns whether the FMQ is connected successfully. */
+ bool isValid();
+
+ /**
+ * Posts a buffer release message to the buffer pool.
+ *
+ * @param connectionId connection Id of the client.
+ * @param pending currently pending buffer release messages.
+ * @param posted posted buffer release messages.
+ */
+ void postBufferRelease(
+ ConnectionId connectionId,
+ std::list<BufferId> &pending, std::list<BufferId> &posted);
+
+ /**
+ * Posts a buffer status message regarding the specified buffer
+ * transfer transaction.
+ *
+ * @param transactionId Id of the specified transaction.
+ * @param bufferId buffer Id of the specified transaction.
+ * @param status new status of the buffer.
+ * @param connectionId connection Id of the client.
+ * @param targetId connection Id of the receiver(only when the sender
+ * posts a status message).
+ * @param pending currently pending buffer release messages.
+ * @param posted posted buffer release messages.
+ *
+ * @return {@code true} when the specified message is posted,
+ * {@code false} otherwise.
+ */
+ bool postBufferStatusMessage(
+ TransactionId transactionId,
+ BufferId bufferId,
+ BufferStatus status,
+ ConnectionId connectionId,
+ ConnectionId targetId,
+ std::list<BufferId> &pending, std::list<BufferId> &posted);
+};
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace bufferpool
+} // namespace media
+} // namespace hardware
+} // namespace android
+
+#endif // ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V1_0_BUFFERSTATUS_H
diff --git a/media/libstagefright/codec2/vndk/bufferpool/ClientManager.cpp b/media/libstagefright/codec2/vndk/bufferpool/ClientManager.cpp
new file mode 100644
index 0000000..97efee4
--- /dev/null
+++ b/media/libstagefright/codec2/vndk/bufferpool/ClientManager.cpp
@@ -0,0 +1,340 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+#include <ClientManager.h>
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+#include "BufferPoolClient.h"
+
+namespace android {
+namespace hardware {
+namespace media {
+namespace bufferpool {
+namespace V1_0 {
+namespace implementation {
+
+static constexpr int64_t kRegisterTimeoutUs = 500000; // 0.5 sec
+
+class ClientManager::Impl {
+public:
+ Impl();
+
+ ResultStatus registerSender(const sp<IAccessor> &accessor,
+ ConnectionId *pConnectionId);
+
+ ResultStatus create(const std::shared_ptr<C2Allocator> &allocator,
+ bool linear,
+ ConnectionId *pConnectionId);
+
+ ResultStatus close(ConnectionId connectionId);
+
+ ResultStatus allocate(ConnectionId connectionId,
+ const std::vector<uint8_t> ¶ms,
+ std::shared_ptr<_C2BlockPoolData> *buffer);
+
+ ResultStatus receive(ConnectionId connectionId,
+ TransactionId transactionId,
+ BufferId bufferId,
+ int64_t timestampUs,
+ std::shared_ptr<_C2BlockPoolData> *buffer);
+
+ ResultStatus postSend(ConnectionId connectionId,
+ ConnectionId receiverId,
+ const std::shared_ptr<_C2BlockPoolData> &buffer,
+ TransactionId *transactionId,
+ int64_t *timestampUs);
+
+ ResultStatus getAccessor(ConnectionId connectionId,
+ sp<IAccessor> *accessor);
+
+private:
+ // In order to prevent deadlock between multiple locks,
+ // Always lock ClientCache.lock before locking ActiveClients.lock.
+ struct ClientCache {
+ // This lock is held for brief duration.
+ // Blocking operation is not performed holding the lock.
+ std::mutex mMutex;
+ std::map<const wp<IAccessor>, const std::weak_ptr<BufferPoolClient>>
+ mClients;
+ std::condition_variable mConnectCv;
+ bool mConnecting;
+
+ ClientCache() : mConnecting(false) {}
+ } mCache;
+
+ // Active clients which can be retrieved via ConnectionId
+ struct ActiveClients {
+ // This lock is held for brief duration.
+ // Blocking operation is not performed holding the lock.
+ std::mutex mMutex;
+ std::map<ConnectionId, const std::shared_ptr<BufferPoolClient>>
+ mClients;
+ } mActive;
+
+};
+
+ClientManager::Impl::Impl() {}
+
+ResultStatus ClientManager::Impl::registerSender(
+ const sp<IAccessor> &accessor, ConnectionId *pConnectionId) {
+ int64_t timeoutUs = getTimestampNow() + kRegisterTimeoutUs;
+ do {
+ std::unique_lock<std::mutex> lock(mCache.mMutex);
+ auto it = mCache.mClients.find(accessor);
+ if (it != mCache.mClients.end()) {
+ const std::shared_ptr<BufferPoolClient> client = it->second.lock();
+ if (client) {
+ *pConnectionId = client->getConnectionId();
+ return ResultStatus::ALREADY_EXISTS;
+ }
+ mCache.mClients.erase(it);
+ }
+ if (!mCache.mConnecting) {
+ mCache.mConnecting = true;
+ lock.unlock();
+ ResultStatus result = ResultStatus::OK;
+ const std::shared_ptr<BufferPoolClient> client =
+ std::make_shared<BufferPoolClient>(accessor);
+ lock.lock();
+ if (!client) {
+ result = ResultStatus::NO_MEMORY;
+ } else if (!client->isValid()) {
+ result = ResultStatus::CRITICAL_ERROR;
+ }
+ if (result == ResultStatus::OK) {
+ // TODO: handle insert fail. (malloc fail)
+ const std::weak_ptr<BufferPoolClient> wclient = client;
+ mCache.mClients.insert(std::make_pair(accessor, wclient));
+ ConnectionId conId = client->getConnectionId();
+ {
+ std::lock_guard<std::mutex> lock(mActive.mMutex);
+ mActive.mClients.insert(std::make_pair(conId, client));
+ }
+ *pConnectionId = conId;
+ }
+ mCache.mConnecting = false;
+ lock.unlock();
+ mCache.mConnectCv.notify_all();
+ return result;
+ }
+ mCache.mConnectCv.wait_for(
+ lock, std::chrono::microseconds(kRegisterTimeoutUs));
+ } while (getTimestampNow() < timeoutUs);
+ // TODO: return timeout error
+ return ResultStatus::CRITICAL_ERROR;
+}
+
+ResultStatus ClientManager::Impl::create(
+ const std::shared_ptr<C2Allocator> &allocator,
+ bool linear,
+ ConnectionId *pConnectionId) {
+ const sp<Accessor> accessor = new Accessor(allocator, linear);
+ if (!accessor || !accessor->isValid()) {
+ return ResultStatus::CRITICAL_ERROR;
+ }
+ std::shared_ptr<BufferPoolClient> client =
+ std::make_shared<BufferPoolClient>(accessor);
+ if (!client || !client->isValid()) {
+ return ResultStatus::CRITICAL_ERROR;
+ }
+ {
+ // TODO: handle insert fail. (malloc fail)
+ std::lock_guard<std::mutex> lock(mCache.mMutex);
+ const wp<Accessor> waccessor = accessor;
+ const std::weak_ptr<BufferPoolClient> wclient = client;
+ mCache.mClients.insert(std::make_pair(waccessor, wclient));
+ ConnectionId conId = client->getConnectionId();
+ {
+ std::lock_guard<std::mutex> lock(mActive.mMutex);
+ mActive.mClients.insert(std::make_pair(conId, client));
+ }
+ *pConnectionId = conId;
+ }
+ return ResultStatus::OK;
+}
+
+ResultStatus ClientManager::Impl::close(ConnectionId connectionId) {
+ std::lock_guard<std::mutex> lock1(mCache.mMutex);
+ std::lock_guard<std::mutex> lock2(mActive.mMutex);
+ auto it = mActive.mClients.find(connectionId);
+ if (it != mActive.mClients.end()) {
+ sp<IAccessor> accessor;
+ if (it->second->getAccessor(&accessor) == ResultStatus::OK) {
+ mCache.mClients.erase(accessor);
+ }
+ mActive.mClients.erase(connectionId);
+ return ResultStatus::OK;
+ }
+ return ResultStatus::NOT_FOUND;
+}
+
+ResultStatus ClientManager::Impl::allocate(
+ ConnectionId connectionId, const std::vector<uint8_t> ¶ms,
+ std::shared_ptr<_C2BlockPoolData> *buffer) {
+ std::shared_ptr<BufferPoolClient> client;
+ {
+ std::lock_guard<std::mutex> lock(mActive.mMutex);
+ auto it = mActive.mClients.find(connectionId);
+ if (it == mActive.mClients.end()) {
+ return ResultStatus::NOT_FOUND;
+ }
+ client = it->second;
+ }
+ return client->allocate(params, buffer);
+}
+
+ResultStatus ClientManager::Impl::receive(
+ ConnectionId connectionId, TransactionId transactionId,
+ BufferId bufferId, int64_t timestampUs,
+ std::shared_ptr<_C2BlockPoolData> *buffer) {
+ std::shared_ptr<BufferPoolClient> client;
+ {
+ std::lock_guard<std::mutex> lock(mActive.mMutex);
+ auto it = mActive.mClients.find(connectionId);
+ if (it == mActive.mClients.end()) {
+ return ResultStatus::NOT_FOUND;
+ }
+ client = it->second;
+ }
+ return client->receive(transactionId, bufferId, timestampUs, buffer);
+}
+
+ResultStatus ClientManager::Impl::postSend(
+ ConnectionId connectionId, ConnectionId receiverId,
+ const std::shared_ptr<_C2BlockPoolData> &buffer,
+ TransactionId *transactionId, int64_t *timestampUs) {
+ std::shared_ptr<BufferPoolClient> client;
+ {
+ std::lock_guard<std::mutex> lock(mActive.mMutex);
+ auto it = mActive.mClients.find(connectionId);
+ if (it == mActive.mClients.end()) {
+ return ResultStatus::NOT_FOUND;
+ }
+ client = it->second;
+ }
+ return client->postSend(receiverId, buffer, transactionId, timestampUs);
+}
+
+ResultStatus ClientManager::Impl::getAccessor(
+ ConnectionId connectionId, sp<IAccessor> *accessor) {
+ std::shared_ptr<BufferPoolClient> client;
+ {
+ std::lock_guard<std::mutex> lock(mActive.mMutex);
+ auto it = mActive.mClients.find(connectionId);
+ if (it == mActive.mClients.end()) {
+ return ResultStatus::NOT_FOUND;
+ }
+ client = it->second;
+ }
+ return client->getAccessor(accessor);
+}
+
+// Methods from ::android::hardware::media::bufferpool::V1_0::IClientManager follow.
+Return<void> ClientManager::registerSender(const sp<::android::hardware::media::bufferpool::V1_0::IAccessor>& bufferPool, registerSender_cb _hidl_cb) {
+ if (mImpl) {
+ ConnectionId connectionId = -1;
+ ResultStatus status = mImpl->registerSender(bufferPool, &connectionId);
+ _hidl_cb(status, connectionId);
+ } else {
+ _hidl_cb(ResultStatus::CRITICAL_ERROR, -1);
+ }
+ return Void();
+}
+
+// Methods for local use.
+sp<ClientManager> ClientManager::sInstance;
+std::mutex ClientManager::sInstanceLock;
+
+sp<ClientManager> ClientManager::getInstance() {
+ std::lock_guard<std::mutex> lock(sInstanceLock);
+ if (!sInstance) {
+ sInstance = new ClientManager();
+ }
+ return sInstance;
+}
+
+ClientManager::ClientManager() : mImpl(new Impl()) {}
+
+ClientManager::~ClientManager() {
+}
+
+ResultStatus ClientManager::create(
+ const std::shared_ptr<C2Allocator> &allocator,
+ bool linear,
+ ConnectionId *pConnectionId) {
+ if (mImpl) {
+ return mImpl->create(allocator, linear, pConnectionId);
+ }
+ return ResultStatus::CRITICAL_ERROR;
+}
+
+ResultStatus ClientManager::close(ConnectionId connectionId) {
+ if (mImpl) {
+ return mImpl->close(connectionId);
+ }
+ return ResultStatus::CRITICAL_ERROR;
+}
+
+ResultStatus ClientManager::allocate(
+ ConnectionId connectionId, const std::vector<uint8_t> ¶ms,
+ std::shared_ptr<_C2BlockPoolData> *buffer) {
+ if (mImpl) {
+ return mImpl->allocate(connectionId, params, buffer);
+ }
+ return ResultStatus::CRITICAL_ERROR;
+}
+
+ResultStatus ClientManager::receive(
+ ConnectionId connectionId, TransactionId transactionId,
+ BufferId bufferId, int64_t timestampUs,
+ std::shared_ptr<_C2BlockPoolData> *buffer) {
+ if (mImpl) {
+ return mImpl->receive(connectionId, transactionId, bufferId,
+ timestampUs, buffer);
+ }
+ return ResultStatus::CRITICAL_ERROR;
+}
+
+ResultStatus ClientManager::postSend(
+ ConnectionId connectionId, ConnectionId receiverId,
+ const std::shared_ptr<_C2BlockPoolData> &buffer,
+ TransactionId *transactionId, int64_t* timestampUs) {
+ if (mImpl) {
+ return mImpl->postSend(connectionId, receiverId, buffer,
+ transactionId, timestampUs);
+ }
+ return ResultStatus::CRITICAL_ERROR;
+}
+
+ResultStatus ClientManager::getAccessor(
+ ConnectionId connectionId, sp<IAccessor> *accessor) {
+ if (mImpl) {
+ return mImpl->getAccessor(connectionId, accessor);
+ }
+ return ResultStatus::CRITICAL_ERROR;
+}
+
+//IClientManager* HIDL_FETCH_IClientManager(const char* /* name */) {
+// return new ClientManager();
+//}
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace bufferpool
+} // namespace media
+} // namespace hardware
+} // namespace android
diff --git a/media/libstagefright/codec2/vndk/bufferpool/Connection.cpp b/media/libstagefright/codec2/vndk/bufferpool/Connection.cpp
new file mode 100644
index 0000000..7b385b8
--- /dev/null
+++ b/media/libstagefright/codec2/vndk/bufferpool/Connection.cpp
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+#include "Connection.h"
+
+namespace android {
+namespace hardware {
+namespace media {
+namespace bufferpool {
+namespace V1_0 {
+namespace implementation {
+
+// Methods from ::android::hardware::media::bufferpool::V1_0::IConnection follow.
+Return<void> Connection::fetch(uint64_t transactionId, uint32_t bufferId, fetch_cb _hidl_cb) {
+ ResultStatus status = ResultStatus::CRITICAL_ERROR;
+ if (mInitialized && mAccessor) {
+ const native_handle_t *handle = NULL;
+ status = mAccessor->fetch(
+ mConnectionId, transactionId, bufferId, &handle);
+ if (status == ResultStatus::OK) {
+ _hidl_cb(status, Buffer{bufferId, handle});
+ return Void();
+ }
+ }
+ _hidl_cb(status, Buffer{0, nullptr});
+ return Void();
+}
+
+Connection::Connection() : mInitialized(false), mConnectionId(-1LL) {}
+
+Connection::~Connection() {
+ if (mInitialized && mAccessor) {
+ mAccessor->close(mConnectionId);
+ }
+}
+
+void Connection::initialize(
+ const sp<Accessor>& accessor, ConnectionId connectionId) {
+ if (!mInitialized) {
+ mAccessor = accessor;
+ mConnectionId = connectionId;
+ mInitialized = true;
+ }
+}
+
+ResultStatus Connection::allocate(
+ const std::vector<uint8_t> ¶ms, BufferId *bufferId,
+ const native_handle_t **handle) {
+ if (mInitialized && mAccessor) {
+ return mAccessor->allocate(mConnectionId, params, bufferId, handle);
+ }
+ return ResultStatus::CRITICAL_ERROR;
+}
+
+
+// Methods from ::android::hidl::base::V1_0::IBase follow.
+
+//IConnection* HIDL_FETCH_IConnection(const char* /* name */) {
+// return new Connection();
+//}
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace bufferpool
+} // namespace media
+} // namespace hardware
+} // namespace android
diff --git a/media/libstagefright/codec2/vndk/bufferpool/Connection.h b/media/libstagefright/codec2/vndk/bufferpool/Connection.h
new file mode 100644
index 0000000..47ddbcb
--- /dev/null
+++ b/media/libstagefright/codec2/vndk/bufferpool/Connection.h
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V1_0_CONNECTION_H
+#define ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V1_0_CONNECTION_H
+
+#include <android/hardware/media/bufferpool/1.0/IConnection.h>
+#include <hidl/MQDescriptor.h>
+#include <hidl/Status.h>
+#include <BufferPoolTypes.h>
+#include "Accessor.h"
+
+namespace android {
+namespace hardware {
+namespace media {
+namespace bufferpool {
+namespace V1_0 {
+namespace implementation {
+
+using ::android::hardware::hidl_array;
+using ::android::hardware::hidl_memory;
+using ::android::hardware::hidl_string;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::media::bufferpool::V1_0::implementation::Accessor;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+using ::android::sp;
+
+struct Connection : public IConnection {
+ // Methods from ::android::hardware::media::bufferpool::V1_0::IConnection follow.
+ Return<void> fetch(uint64_t transactionId, uint32_t bufferId, fetch_cb _hidl_cb) override;
+
+ /**
+ * Allocates a buffer using the specified parameters. Recycles a buffer if
+ * it is possible. The returned buffer can be transferred to other remote
+ * clients(Connection).
+ *
+ * @param params allocation parameters.
+ * @param bufferId Id of the allocated buffer.
+ * @param handle native handle of the allocated buffer.
+ *
+ * @return OK if a buffer is successfully allocated.
+ * NO_MEMORY when there is no memory.
+ * CRITICAL_ERROR otherwise.
+ */
+ ResultStatus allocate(const std::vector<uint8_t> ¶ms,
+ BufferId *bufferId, const native_handle_t **handle);
+
+ /** Destructs a connection. */
+ ~Connection();
+
+ /** Creates a connection. */
+ Connection();
+
+ /**
+ * Initializes with the specified buffer pool and the connection id.
+ * The connection id should be unique in the whole system.
+ *
+ * @param accessor the specified buffer pool.
+ * @param connectionId Id.
+ */
+ void initialize(const sp<Accessor> &accessor, ConnectionId connectionId);
+
+private:
+ bool mInitialized;
+ sp<Accessor> mAccessor;
+ ConnectionId mConnectionId;
+};
+
+// FIXME: most likely delete, this is only for passthrough implementations
+// extern "C" IConnection* HIDL_FETCH_IConnection(const char* name);
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace bufferpool
+} // namespace media
+} // namespace hardware
+} // namespace android
+
+#endif // ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V1_0_CONNECTION_H
diff --git a/media/libstagefright/codec2/vndk/bufferpool/include/BufferPoolTypes.h b/media/libstagefright/codec2/vndk/bufferpool/include/BufferPoolTypes.h
new file mode 100644
index 0000000..4b7363f
--- /dev/null
+++ b/media/libstagefright/codec2/vndk/bufferpool/include/BufferPoolTypes.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V1_0_BUFFERPOOLTYPES_H
+#define ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V1_0_BUFFERPOOLTYPES_H
+
+#include <android/hardware/media/bufferpool/1.0/types.h>
+#include <cutils/native_handle.h>
+#include <fmq/MessageQueue.h>
+#include <hidl/MQDescriptor.h>
+#include <hidl/Status.h>
+#include <C2Buffer.h>
+
+struct C2_HIDE _C2BlockPoolData {
+ uint32_t mId; //BufferId
+ native_handle_t *mHandle;
+
+ _C2BlockPoolData() : mId(0), mHandle(NULL) {}
+
+ _C2BlockPoolData(uint32_t id, native_handle_t *handle)
+ : mId(id), mHandle(handle) {}
+
+ ~_C2BlockPoolData() {
+ if (mHandle != NULL) {
+ native_handle_close(mHandle);
+ native_handle_delete(mHandle);
+ }
+ }
+};
+
+namespace android {
+namespace hardware {
+namespace media {
+namespace bufferpool {
+namespace V1_0 {
+namespace implementation {
+
+using ::android::hardware::kSynchronizedReadWrite;
+
+typedef uint32_t BufferId;
+typedef uint64_t TransactionId;
+typedef int64_t ConnectionId;
+
+typedef android::hardware::MessageQueue<BufferStatusMessage, kSynchronizedReadWrite> BufferStatusQueue;
+typedef BufferStatusQueue::Descriptor QueueDescriptor;
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace bufferpool
+} // namespace media
+} // namespace hardware
+} // namespace android
+
+#endif // ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V1_0_BUFFERPOOLTYPES_H
diff --git a/media/libstagefright/codec2/vndk/bufferpool/include/ClientManager.h b/media/libstagefright/codec2/vndk/bufferpool/include/ClientManager.h
new file mode 100644
index 0000000..412fa59
--- /dev/null
+++ b/media/libstagefright/codec2/vndk/bufferpool/include/ClientManager.h
@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V1_0_CLIENTMANAGER_H
+#define ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V1_0_CLIENTMANAGER_H
+
+#include <android/hardware/media/bufferpool/1.0/IClientManager.h>
+#include <hidl/MQDescriptor.h>
+#include <hidl/Status.h>
+#include <C2Buffer.h>
+#include <memory>
+#include <BufferPoolTypes.h>
+
+namespace android {
+namespace hardware {
+namespace media {
+namespace bufferpool {
+namespace V1_0 {
+namespace implementation {
+
+using ::android::hardware::hidl_array;
+using ::android::hardware::hidl_memory;
+using ::android::hardware::hidl_string;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::media::bufferpool::V1_0::IAccessor;
+using ::android::hardware::media::bufferpool::V1_0::ResultStatus;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+using ::android::sp;
+
+struct ClientManager : public IClientManager {
+ // Methods from ::android::hardware::media::bufferpool::V1_0::IClientManager follow.
+ Return<void> registerSender(const sp<::android::hardware::media::bufferpool::V1_0::IAccessor>& bufferPool, registerSender_cb _hidl_cb) override;
+
+ /** Gets an instance. */
+ static sp<ClientManager> getInstance();
+
+ /**
+ * Creates a local connection with a newly created buffer pool.
+ *
+ * @param allocator for new buffer allocation.
+ * @param linear whether the allocator is linear or not.
+ * @param pConnectionId Id of the created connection. This is
+ * system-wide unique.
+ *
+ * @return OK when a buffer pool and a local connection is successfully
+ * created.
+ * NO_MEMORY when there is no memory.
+ * CRITICAL_ERROR otherwise.
+ */
+ ResultStatus create(const std::shared_ptr<C2Allocator> &allocator,
+ bool linear,
+ ConnectionId *pConnectionId);
+
+ /**
+ * Closes the specified connection.
+ *
+ * @param connectionId The id of the connection.
+ *
+ * @return OK when the connection is closed.
+ * NOT_FOUND when the specified connection was not found.
+ * CRITICAL_ERROR otherwise.
+ */
+ ResultStatus close(ConnectionId connectionId);
+
+ /**
+ * Allocates a buffer from the specified connection.
+ *
+ * @param connectionId The id of the connection.
+ * @param params The allocation parameters.
+ * @param buffer The allocated buffer.
+ *
+ * @return OK when a buffer was allocated successfully.
+ * NOT_FOUND when the specified connection was not found.
+ * NO_MEMORY when there is no memory.
+ * CRITICAL_ERROR otherwise.
+ */
+ ResultStatus allocate(ConnectionId connectionId,
+ const std::vector<uint8_t> ¶ms,
+ std::shared_ptr<_C2BlockPoolData> *buffer);
+
+ /**
+ * Receives a buffer for the transaction.
+ *
+ * @param connectionId The id of the receiving connection.
+ * @param transactionId The id for the transaction.
+ * @param bufferId The id for the buffer.
+ * @param timestampUs The timestamp of the buffer is being sent.
+ * @param buffer The received buffer.
+ *
+ * @return OK when a buffer was received successfully.
+ * NOT_FOUND when the specified connection was not found.
+ * NO_MEMORY when there is no memory.
+ * CRITICAL_ERROR otherwise.
+ */
+ ResultStatus receive(ConnectionId connectionId,
+ TransactionId transactionId,
+ BufferId bufferId,
+ int64_t timestampUs,
+ std::shared_ptr<_C2BlockPoolData> *buffer);
+
+ /**
+ * Posts a buffer transfer transaction to the buffer pool. Sends a buffer
+ * to other remote clients(connection) after this call has been succeeded.
+ *
+ * @param connectionId The id of the sending connection.
+ * @param receiverId The id of the receiving connection.
+ * @param buffer to transfer
+ * @param transactionId Id of the transfer transaction.
+ * @param timestampUs The timestamp of the buffer transaction is being
+ * posted.
+ *
+ * @return OK when a buffer transaction was posted successfully.
+ * NOT_FOUND when the sending connection was not found.
+ * CRITICAL_ERROR otherwise.
+ */
+ ResultStatus postSend(ConnectionId connectionId,
+ ConnectionId receiverId,
+ const std::shared_ptr<_C2BlockPoolData> &buffer,
+ TransactionId *transactionId,
+ int64_t *timestampUs);
+
+ /** Gets a buffer pool for the specified connection.
+ *
+ * @param connectionId The id of the connection.
+ * @param accessor The buffer pool for the specified connection.
+ * @return OK when a buffer pool was found for the connection.
+ * NOT_FOUND when the specified connection was not found.
+ * CRITICAL_ERROR otherwise.
+ */
+ ResultStatus getAccessor(ConnectionId connectionId,
+ sp<IAccessor> *accessor);
+
+ /** Destructs the manager of buffer pool clients. */
+ ~ClientManager();
+private:
+ static sp<ClientManager> sInstance;
+ static std::mutex sInstanceLock;
+
+ class Impl;
+ const std::unique_ptr<Impl> mImpl;
+
+ ClientManager();
+};
+
+// FIXME: most likely delete, this is only for passthrough implementations
+// extern "C" IClientManager* HIDL_FETCH_IClientManager(const char* name);
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace bufferpool
+} // namespace media
+} // namespace hardware
+} // namespace android
+
+#endif // ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V1_0_CLIENTMANAGER_H
diff --git a/media/libstagefright/codec2/vndk/include/C2BufferPriv.h b/media/libstagefright/codec2/vndk/include/C2BufferPriv.h
index 875a8c2..977cf7b 100644
--- a/media/libstagefright/codec2/vndk/include/C2BufferPriv.h
+++ b/media/libstagefright/codec2/vndk/include/C2BufferPriv.h
@@ -21,8 +21,6 @@
#include <C2Buffer.h>
-namespace android {
-
class C2BasicLinearBlockPool : public C2BlockPool {
public:
explicit C2BasicLinearBlockPool(const std::shared_ptr<C2Allocator> &allocator);
@@ -73,6 +71,4 @@
const std::shared_ptr<C2Allocator> mAllocator;
};
-} // namespace android
-
#endif // STAGEFRIGHT_CODEC2_BUFFER_PRIV_H_
diff --git a/media/libstagefright/codec2/vndk/include/C2ComponentFactory.h b/media/libstagefright/codec2/vndk/include/C2ComponentFactory.h
index cfea104..f6d8b98 100644
--- a/media/libstagefright/codec2/vndk/include/C2ComponentFactory.h
+++ b/media/libstagefright/codec2/vndk/include/C2ComponentFactory.h
@@ -22,8 +22,6 @@
#include <functional>
#include <memory>
-namespace android {
-
/**
* Component factory object that enables to create a component and/or interface from a dynamically
* linked library. This is needed because the component/interfaces are managed objects, but we
@@ -36,8 +34,8 @@
*/
class C2ComponentFactory {
public:
- typedef std::function<void(::android::C2Component*)> ComponentDeleter;
- typedef std::function<void(::android::C2ComponentInterface*)> InterfaceDeleter;
+ typedef std::function<void(::C2Component*)> ComponentDeleter;
+ typedef std::function<void(::C2ComponentInterface*)> InterfaceDeleter;
/**
* Creates a component.
@@ -81,9 +79,9 @@
virtual ~C2ComponentFactory() = default;
- typedef ::android::C2ComponentFactory* (*CreateCodec2FactoryFunc)(void);
- typedef void (*DestroyCodec2FactoryFunc)(::android::C2ComponentFactory*);
+ typedef ::C2ComponentFactory* (*CreateCodec2FactoryFunc)(void);
+ typedef void (*DestroyCodec2FactoryFunc)(::C2ComponentFactory*);
};
-} // namespace android
+
#endif // STAGEFRIGHT_CODEC2_COMPONENT_FACTORY_H_
diff --git a/media/libstagefright/codec2/vndk/include/C2ErrnoUtils.h b/media/libstagefright/codec2/vndk/include/C2ErrnoUtils.h
index 41132b9..5b995f6 100644
--- a/media/libstagefright/codec2/vndk/include/C2ErrnoUtils.h
+++ b/media/libstagefright/codec2/vndk/include/C2ErrnoUtils.h
@@ -20,8 +20,6 @@
#include <errno.h>
#include <C2.h>
-namespace android {
-
// standard ERRNO mappings
template<int N> constexpr c2_status_t _c2_errno2status_impl();
template<> constexpr c2_status_t _c2_errno2status_impl<0>() { return C2_OK; }
@@ -52,7 +50,5 @@
return _c2_map_errno_impl<N...>::map(result);
}
-} // namespace android
-
#endif // STAGEFRIGHT_CODEC2_ERRNO_UTILS_H_
diff --git a/media/libstagefright/codec2/vndk/include/C2PlatformSupport.h b/media/libstagefright/codec2/vndk/include/C2PlatformSupport.h
index 76b02ed..afa51ee 100644
--- a/media/libstagefright/codec2/vndk/include/C2PlatformSupport.h
+++ b/media/libstagefright/codec2/vndk/include/C2PlatformSupport.h
@@ -86,6 +86,7 @@
* \retval nullptr if the platform component store could not be obtained
*/
std::shared_ptr<C2ComponentStore> GetCodec2PlatformComponentStore();
+
} // namespace android
#endif // STAGEFRIGHT_CODEC2_PLATFORM_SUPPORT_H_
diff --git a/media/libstagefright/codec2/vndk/include/util/C2ParamUtils.h b/media/libstagefright/codec2/vndk/include/util/C2ParamUtils.h
index 3168248..710e74b 100644
--- a/media/libstagefright/codec2/vndk/include/util/C2ParamUtils.h
+++ b/media/libstagefright/codec2/vndk/include/util/C2ParamUtils.h
@@ -21,13 +21,14 @@
#include <util/_C2MacroUtils.h>
#include <iostream>
+#include <list>
+#include <utility>
+#include <vector>
/** \file
* Utilities for parameter handling to be used by Codec2 implementations.
*/
-namespace android {
-
/// \cond INTERNAL
/* ---------------------------- UTILITIES FOR ENUMERATION REFLECTION ---------------------------- */
@@ -61,7 +62,7 @@
#undef DEFINE_C2_ENUM_VALUE_AUTO_HELPER
#define DEFINE_C2_ENUM_VALUE_AUTO_HELPER(name, type, prefix, ...) \
-template<> C2FieldDescriptor::named_values_type C2FieldDescriptor::namedValuesFor(const name &r __unused) { \
+template<> C2FieldDescriptor::NamedValuesType C2FieldDescriptor::namedValuesFor(const name &r __unused) { \
return C2ParamUtils::sanitizeEnumValues( \
std::vector<C2Value::Primitive> { _C2_MAP(_C2_GET_ENUM_VALUE, type, __VA_ARGS__) }, \
{ _C2_MAP(_C2_GET_ENUM_NAME, type, __VA_ARGS__) }, \
@@ -70,7 +71,7 @@
#undef DEFINE_C2_ENUM_VALUE_CUSTOM_HELPER
#define DEFINE_C2_ENUM_VALUE_CUSTOM_HELPER(name, type, names, ...) \
-template<> C2FieldDescriptor::named_values_type C2FieldDescriptor::namedValuesFor(const name &r __unused) { \
+template<> C2FieldDescriptor::NamedValuesType C2FieldDescriptor::namedValuesFor(const name &r __unused) { \
return C2ParamUtils::customEnumValues( \
std::vector<std::pair<C2StringLiteral, name>> names); \
}
@@ -241,11 +242,11 @@
}
template<typename T>
- static C2FieldDescriptor::named_values_type sanitizeEnumValues(
+ static C2FieldDescriptor::NamedValuesType sanitizeEnumValues(
std::vector<T> values,
std::vector<C2StringLiteral> names,
C2StringLiteral prefix = NULL) {
- C2FieldDescriptor::named_values_type namedValues;
+ C2FieldDescriptor::NamedValuesType namedValues;
std::vector<C2String> sanitizedNames = sanitizeEnumValueNames(names, prefix);
for (size_t i = 0; i < values.size() && i < sanitizedNames.size(); ++i) {
namedValues.emplace_back(sanitizedNames[i], values[i]);
@@ -254,9 +255,9 @@
}
template<typename E>
- static C2FieldDescriptor::named_values_type customEnumValues(
+ static C2FieldDescriptor::NamedValuesType customEnumValues(
std::vector<std::pair<C2StringLiteral, E>> items) {
- C2FieldDescriptor::named_values_type namedValues;
+ C2FieldDescriptor::NamedValuesType namedValues;
for (auto &item : items) {
namedValues.emplace_back(item.first, item.second);
}
@@ -313,7 +314,5 @@
/* ---------------------------- UTILITIES FOR ENUMERATION REFLECTION ---------------------------- */
-} // namespace android
-
#endif // C2UTILS_PARAM_UTILS_H_
diff --git a/media/libstagefright/codec2/vndk/internal/C2BlockInternal.h b/media/libstagefright/codec2/vndk/internal/C2BlockInternal.h
index 9c68369..25003cb 100644
--- a/media/libstagefright/codec2/vndk/internal/C2BlockInternal.h
+++ b/media/libstagefright/codec2/vndk/internal/C2BlockInternal.h
@@ -19,8 +19,6 @@
#include <C2Buffer.h>
-namespace android {
-
struct _C2BlockPoolData;
/**
@@ -64,7 +62,5 @@
const C2Rect &allottedCrop = C2Rect(~0u, ~0u));
};
-}
-
#endif // ANDROID_STAGEFRIGHT_C2BLOCK_INTERNAL_H_
diff --git a/media/libstagefright/codec2/vndk/internal/C2ParamInternal.h b/media/libstagefright/codec2/vndk/internal/C2ParamInternal.h
index 5bf3009..34797a9 100644
--- a/media/libstagefright/codec2/vndk/internal/C2ParamInternal.h
+++ b/media/libstagefright/codec2/vndk/internal/C2ParamInternal.h
@@ -19,22 +19,20 @@
#include <C2Param.h>
-namespace android {
-
struct C2_HIDE _C2ParamInspector {
- inline static uint32_t getIndex(const C2ParamField &pf) {
+ inline static uint32_t GetIndex(const C2ParamField &pf) {
return pf._mIndex;
}
- inline static uint32_t getOffset(const C2ParamField &pf) {
+ inline static uint32_t GetOffset(const C2ParamField &pf) {
return pf._mFieldId._mOffset;
}
- inline static uint32_t getSize(const C2ParamField &pf) {
+ inline static uint32_t GetSize(const C2ParamField &pf) {
return pf._mFieldId._mSize;
}
- inline static uint32_t getAttrib(const C2ParamDescriptor &pd) {
+ inline static uint32_t GetAttrib(const C2ParamDescriptor &pd) {
return pd._mAttrib;
}
@@ -42,9 +40,15 @@
C2ParamField CreateParamField(C2Param::Index index, uint32_t offset, uint32_t size) {
return C2ParamField(index, offset, size);
}
-};
-}
+ inline static
+ C2ParamField CreateParamField(C2Param::Index index, _C2FieldId field) {
+ return C2ParamField(index, field._mOffset, field._mSize);
+ }
+
+ // expose attributes
+ typedef C2ParamDescriptor::attrib_t attrib_t;
+};
#endif // ANDROID_STAGEFRIGHT_C2PARAM_INTERNAL_H_
diff --git a/media/libstagefright/codecs/aacdec/C2SoftAac.cpp b/media/libstagefright/codecs/aacdec/C2SoftAac.cpp
index b57c2aa..c82ea45 100644
--- a/media/libstagefright/codecs/aacdec/C2SoftAac.cpp
+++ b/media/libstagefright/codecs/aacdec/C2SoftAac.cpp
@@ -25,6 +25,7 @@
#include <cutils/properties.h>
#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/foundation/MediaDefs.h>
#include <media/stagefright/foundation/hexdump.h>
#include <media/stagefright/MediaErrors.h>
#include <utils/misc.h>
@@ -50,12 +51,22 @@
namespace android {
-C2SoftAac::C2SoftAac(const char *name, c2_node_id_t id)
- : SimpleC2Component(
- SimpleC2Interface::Builder(name, id)
+constexpr char kComponentName[] = "c2.google.aac.decoder";
+
+static std::shared_ptr<C2ComponentInterface> BuildIntf(
+ const char *name, c2_node_id_t id,
+ std::function<void(C2ComponentInterface*)> deleter =
+ std::default_delete<C2ComponentInterface>()) {
+ return SimpleC2Interface::Builder(name, id, deleter)
.inputFormat(C2FormatCompressed)
.outputFormat(C2FormatAudio)
- .build()),
+ .inputMediaType(MEDIA_MIMETYPE_AUDIO_AAC)
+ .outputMediaType(MEDIA_MIMETYPE_AUDIO_RAW)
+ .build();
+}
+
+C2SoftAac::C2SoftAac(const char *name, c2_node_id_t id)
+ : SimpleC2Component(BuildIntf(name, id)),
mAACDecoder(NULL),
mStreamInfo(NULL),
mIsADTS(false),
@@ -333,6 +344,7 @@
const std::unique_ptr<C2Work> &work,
const std::shared_ptr<C2BlockPool> &pool) {
work->workletsProcessed = 0u;
+ work->result = C2_OK;
if (mSignalledError) {
return;
}
@@ -675,20 +687,18 @@
class C2SoftAacDecFactory : public C2ComponentFactory {
public:
virtual c2_status_t createComponent(
- c2_node_id_t id, std::shared_ptr<C2Component>* const component,
- std::function<void(::android::C2Component*)> deleter) override {
- *component = std::shared_ptr<C2Component>(new C2SoftAac("aac", id), deleter);
+ c2_node_id_t id,
+ std::shared_ptr<C2Component>* const component,
+ std::function<void(C2Component*)> deleter) override {
+ *component = std::shared_ptr<C2Component>(new C2SoftAac(kComponentName, id), deleter);
return C2_OK;
}
virtual c2_status_t createInterface(
- c2_node_id_t id, std::shared_ptr<C2ComponentInterface>* const interface,
- std::function<void(::android::C2ComponentInterface*)> deleter) override {
- *interface =
- SimpleC2Interface::Builder("aac", id, deleter)
- .inputFormat(C2FormatCompressed)
- .outputFormat(C2FormatVideo)
- .build();
+ c2_node_id_t id,
+ std::shared_ptr<C2ComponentInterface>* const interface,
+ std::function<void(C2ComponentInterface*)> deleter) override {
+ *interface = BuildIntf(kComponentName, id, deleter);
return C2_OK;
}
@@ -697,12 +707,12 @@
} // namespace android
-extern "C" ::android::C2ComponentFactory* CreateCodec2Factory() {
+extern "C" ::C2ComponentFactory* CreateCodec2Factory() {
ALOGV("in %s", __func__);
return new ::android::C2SoftAacDecFactory();
}
-extern "C" void DestroyCodec2Factory(::android::C2ComponentFactory* factory) {
+extern "C" void DestroyCodec2Factory(::C2ComponentFactory* factory) {
ALOGV("in %s", __func__);
delete factory;
}
diff --git a/media/libstagefright/codecs/aacenc/C2SoftAacEnc.cpp b/media/libstagefright/codecs/aacenc/C2SoftAacEnc.cpp
index 6f1b325..70f6817 100644
--- a/media/libstagefright/codecs/aacenc/C2SoftAacEnc.cpp
+++ b/media/libstagefright/codecs/aacenc/C2SoftAacEnc.cpp
@@ -22,20 +22,31 @@
#include <C2PlatformSupport.h>
#include <SimpleC2Interface.h>
+#include <media/stagefright/foundation/MediaDefs.h>
#include <media/stagefright/foundation/hexdump.h>
#include "C2SoftAacEnc.h"
namespace android {
+constexpr char kComponentName[] = "c2.google.aac.encoder";
+
+static std::shared_ptr<C2ComponentInterface> BuildIntf(
+ const char *name, c2_node_id_t id,
+ std::function<void(C2ComponentInterface*)> deleter =
+ std::default_delete<C2ComponentInterface>()) {
+ return SimpleC2Interface::Builder(name, id, deleter)
+ .inputFormat(C2FormatAudio)
+ .outputFormat(C2FormatCompressed)
+ .inputMediaType(MEDIA_MIMETYPE_AUDIO_AAC)
+ .outputMediaType(MEDIA_MIMETYPE_AUDIO_RAW)
+ .build();
+}
+
C2SoftAacEnc::C2SoftAacEnc(
const char *name,
c2_node_id_t id)
- : SimpleC2Component(
- SimpleC2Interface::Builder(name, id)
- .inputFormat(C2FormatAudio)
- .outputFormat(C2FormatCompressed)
- .build()),
+ : SimpleC2Component(BuildIntf(name, id)),
mAACEncoder(NULL),
mNumChannels(1),
mSampleRate(44100),
@@ -176,6 +187,7 @@
void C2SoftAacEnc::process(
const std::unique_ptr<C2Work> &work,
const std::shared_ptr<C2BlockPool> &pool) {
+ work->result = C2_OK;
work->workletsProcessed = 0u;
if (mSignalledError) {
@@ -208,7 +220,7 @@
}
std::unique_ptr<C2StreamCsdInfo::output> csd =
- C2StreamCsdInfo::output::alloc_unique(encInfo.confSize, 0u);
+ C2StreamCsdInfo::output::AllocUnique(encInfo.confSize, 0u);
// TODO: check NO_MEMORY
memcpy(csd->m.value, encInfo.confBuf, encInfo.confSize);
ALOGV("put csd");
@@ -381,20 +393,17 @@
class C2SoftAacEncFactory : public C2ComponentFactory {
public:
virtual c2_status_t createComponent(
- c2_node_id_t id, std::shared_ptr<C2Component>* const component,
- std::function<void(::android::C2Component*)> deleter) override {
- *component = std::shared_ptr<C2Component>(new C2SoftAacEnc("aacenc", id), deleter);
+ c2_node_id_t id,
+ std::shared_ptr<C2Component>* const component,
+ std::function<void(C2Component*)> deleter) override {
+ *component = std::shared_ptr<C2Component>(new C2SoftAacEnc(kComponentName, id), deleter);
return C2_OK;
}
virtual c2_status_t createInterface(
c2_node_id_t id, std::shared_ptr<C2ComponentInterface>* const interface,
- std::function<void(::android::C2ComponentInterface*)> deleter) override {
- *interface =
- SimpleC2Interface::Builder("aacenc", id, deleter)
- .inputFormat(C2FormatAudio)
- .outputFormat(C2FormatCompressed)
- .build();
+ std::function<void(C2ComponentInterface*)> deleter) override {
+ *interface = BuildIntf(kComponentName, id, deleter);
return C2_OK;
}
@@ -403,12 +412,12 @@
} // namespace android
-extern "C" ::android::C2ComponentFactory* CreateCodec2Factory() {
+extern "C" ::C2ComponentFactory* CreateCodec2Factory() {
ALOGV("in %s", __func__);
return new ::android::C2SoftAacEncFactory();
}
-extern "C" void DestroyCodec2Factory(::android::C2ComponentFactory* factory) {
+extern "C" void DestroyCodec2Factory(::C2ComponentFactory* factory) {
ALOGV("in %s", __func__);
delete factory;
}
diff --git a/media/libstagefright/codecs/avcdec/C2SoftAvcDec.cpp b/media/libstagefright/codecs/avcdec/C2SoftAvcDec.cpp
index 306d0a5..b9ba251 100644
--- a/media/libstagefright/codecs/avcdec/C2SoftAvcDec.cpp
+++ b/media/libstagefright/codecs/avcdec/C2SoftAvcDec.cpp
@@ -54,7 +54,7 @@
#define PRINT_TIME ALOGV
-#define componentName "video_decoder.avc"
+constexpr char kComponentName[] = "c2.google.avc.decoder";
// #define codingType OMX_VIDEO_CodingAVC
#define CODEC_MIME_TYPE MEDIA_MIMETYPE_VIDEO_AVC
@@ -71,6 +71,18 @@
(IVD_CONTROL_API_COMMAND_TYPE_T)IH264D_CMD_CTL_SET_NUM_CORES
namespace {
+std::shared_ptr<C2ComponentInterface> BuildIntf(
+ const char *name, c2_node_id_t id,
+ std::function<void(C2ComponentInterface*)> deleter =
+ std::default_delete<C2ComponentInterface>()) {
+ return SimpleC2Interface::Builder(name, id, deleter)
+ .inputFormat(C2FormatCompressed)
+ .outputFormat(C2FormatVideo)
+ .inputMediaType(MEDIA_MIMETYPE_VIDEO_AVC)
+ .outputMediaType(MEDIA_MIMETYPE_VIDEO_RAW)
+ .build();
+}
+
#if 0
using SupportedValuesWithFields = C2SoftAvcDecIntf::SupportedValuesWithFields;
@@ -266,9 +278,9 @@
mBlocksPerSecond(0u, 0),
mParamReflector(new ParamReflector) {
ALOGV("in %s", __func__);
- mInputPortMime = C2PortMimeConfig::input::alloc_unique(strlen(CODEC_MIME_TYPE) + 1);
+ mInputPortMime = C2PortMimeConfig::input::AllocUnique(strlen(CODEC_MIME_TYPE) + 1);
strcpy(mInputPortMime->m.value, CODEC_MIME_TYPE);
- mOutputPortMime = C2PortMimeConfig::output::alloc_unique(strlen(MEDIA_MIMETYPE_VIDEO_RAW) + 1);
+ mOutputPortMime = C2PortMimeConfig::output::AllocUnique(strlen(MEDIA_MIMETYPE_VIDEO_RAW) + 1);
strcpy(mOutputPortMime->m.value, MEDIA_MIMETYPE_VIDEO_RAW);
mVideoSize.width = 320;
@@ -281,7 +293,7 @@
mMaxVideoSizeHint.width = H264_MAX_FRAME_WIDTH;
mMaxVideoSizeHint.height = H264_MAX_FRAME_HEIGHT;
- mOutputBlockPools = C2PortBlockPoolsTuning::output::alloc_unique({});
+ mOutputBlockPools = C2PortBlockPoolsTuning::output::AllocUnique({});
auto insertParam = [¶ms = mParams] (C2Param *param) {
params[param->index()] = param;
@@ -614,11 +626,7 @@
C2SoftAvcDec::C2SoftAvcDec(
const char *name,
c2_node_id_t id)
- : SimpleC2Component(
- SimpleC2Interface::Builder(name, id)
- .inputFormat(C2FormatCompressed)
- .outputFormat(C2FormatVideo)
- .build()),
+ : SimpleC2Component(BuildIntf(name, id)),
mCodecCtx(NULL),
mFlushOutBuffer(NULL),
mIvColorFormat(IV_YUV_420P),
@@ -1327,21 +1335,18 @@
class C2SoftAvcDecFactory : public C2ComponentFactory {
public:
virtual c2_status_t createComponent(
- c2_node_id_t id, std::shared_ptr<C2Component>* const component,
- std::function<void(::android::C2Component*)> deleter) override {
- *component = std::shared_ptr<C2Component>(new C2SoftAvcDec("avc", id), deleter);
+ c2_node_id_t id,
+ std::shared_ptr<C2Component>* const component,
+ std::function<void(C2Component*)> deleter) override {
+ *component = std::shared_ptr<C2Component>(new C2SoftAvcDec(kComponentName, id), deleter);
return C2_OK;
}
virtual c2_status_t createInterface(
- c2_node_id_t id, std::shared_ptr<C2ComponentInterface>* const interface,
- std::function<void(::android::C2ComponentInterface*)> deleter) override {
- *interface =
- SimpleC2Interface::Builder("avc", id, deleter)
- .inputFormat(C2FormatCompressed)
- .outputFormat(C2FormatVideo)
- .build();
-// std::shared_ptr<C2ComponentInterface>(new C2SoftAvcDecIntf("avc", id), deleter);
+ c2_node_id_t id,
+ std::shared_ptr<C2ComponentInterface>* const interface,
+ std::function<void(C2ComponentInterface*)> deleter) override {
+ *interface = BuildIntf(kComponentName, id, deleter);
return C2_OK;
}
@@ -1350,12 +1355,12 @@
} // namespace android
-extern "C" ::android::C2ComponentFactory* CreateCodec2Factory() {
+extern "C" ::C2ComponentFactory* CreateCodec2Factory() {
ALOGV("in %s", __func__);
return new ::android::C2SoftAvcDecFactory();
}
-extern "C" void DestroyCodec2Factory(::android::C2ComponentFactory* factory) {
+extern "C" void DestroyCodec2Factory(::C2ComponentFactory* factory) {
ALOGV("in %s", __func__);
delete factory;
}
diff --git a/media/libstagefright/codecs/avcenc/C2SoftAvcEnc.cpp b/media/libstagefright/codecs/avcenc/C2SoftAvcEnc.cpp
index 9ea3589..8fb8122 100644
--- a/media/libstagefright/codecs/avcenc/C2SoftAvcEnc.cpp
+++ b/media/libstagefright/codecs/avcenc/C2SoftAvcEnc.cpp
@@ -35,7 +35,9 @@
namespace android {
-#define ive_api_function ih264e_api_function
+#define ive_api_function ih264e_api_function
+
+constexpr char kComponentName[] = "c2.google.avc.encoder";
namespace {
@@ -55,6 +57,18 @@
return (size_t)cpuCoreCount;
}
+std::shared_ptr<C2ComponentInterface> BuildIntf(
+ const char *name, c2_node_id_t id,
+ std::function<void(C2ComponentInterface*)> deleter =
+ std::default_delete<C2ComponentInterface>()) {
+ return SimpleC2Interface::Builder(name, id, deleter)
+ .inputFormat(C2FormatVideo)
+ .outputFormat(C2FormatCompressed)
+ .inputMediaType(MEDIA_MIMETYPE_VIDEO_RAW)
+ .outputMediaType(MEDIA_MIMETYPE_VIDEO_AVC)
+ .build();
+}
+
void ConvertRGBToPlanarYUV(
uint8_t *dstY, size_t dstStride, size_t dstVStride,
const C2GraphicView &src) {
@@ -115,11 +129,7 @@
} // namespace
C2SoftAvcEnc::C2SoftAvcEnc(const char *name, c2_node_id_t id)
- : SimpleC2Component(
- SimpleC2Interface::Builder(name, id)
- .inputFormat(C2FormatVideo)
- .outputFormat(C2FormatCompressed)
- .build()),
+ : SimpleC2Component(BuildIntf(name, id)),
mUpdateFlag(0),
mIvVideoColorFormat(IV_YUV_420P),
mAVCEncProfile(IV_PROFILE_BASE),
@@ -1044,7 +1054,7 @@
mSpsPpsHeaderReceived = true;
std::unique_ptr<C2StreamCsdInfo::output> csd =
- C2StreamCsdInfo::output::alloc_unique(s_encode_op.s_out_buf.u4_bytes, 0u);
+ C2StreamCsdInfo::output::AllocUnique(s_encode_op.s_out_buf.u4_bytes, 0u);
memcpy(csd->m.value, header, s_encode_op.s_out_buf.u4_bytes);
work->worklets.front()->output.configUpdate.push_back(std::move(csd));
@@ -1209,20 +1219,18 @@
class C2SoftAvcEncFactory : public C2ComponentFactory {
public:
virtual c2_status_t createComponent(
- c2_node_id_t id, std::shared_ptr<C2Component>* const component,
- std::function<void(::android::C2Component*)> deleter) override {
- *component = std::shared_ptr<C2Component>(new C2SoftAvcEnc("avcenc", id), deleter);
+ c2_node_id_t id,
+ std::shared_ptr<C2Component>* const component,
+ std::function<void(C2Component*)> deleter) override {
+ *component = std::shared_ptr<C2Component>(new C2SoftAvcEnc(kComponentName, id), deleter);
return C2_OK;
}
virtual c2_status_t createInterface(
- c2_node_id_t id, std::shared_ptr<C2ComponentInterface>* const interface,
- std::function<void(::android::C2ComponentInterface*)> deleter) override {
- *interface =
- SimpleC2Interface::Builder("avcenc", id, deleter)
- .inputFormat(C2FormatVideo)
- .outputFormat(C2FormatCompressed)
- .build();
+ c2_node_id_t id,
+ std::shared_ptr<C2ComponentInterface>* const interface,
+ std::function<void(C2ComponentInterface*)> deleter) override {
+ *interface = BuildIntf(kComponentName, id, deleter);
return C2_OK;
}
@@ -1231,12 +1239,12 @@
} // namespace android
-extern "C" ::android::C2ComponentFactory* CreateCodec2Factory() {
+extern "C" ::C2ComponentFactory* CreateCodec2Factory() {
ALOGV("in %s", __func__);
return new ::android::C2SoftAvcEncFactory();
}
-extern "C" void DestroyCodec2Factory(::android::C2ComponentFactory* factory) {
+extern "C" void DestroyCodec2Factory(::C2ComponentFactory* factory) {
ALOGV("in %s", __func__);
delete factory;
}
diff --git a/media/libstagefright/codecs/cmds/codec2.cpp b/media/libstagefright/codecs/cmds/codec2.cpp
index d95bb07..0ec1a77 100644
--- a/media/libstagefright/codecs/cmds/codec2.cpp
+++ b/media/libstagefright/codecs/cmds/codec2.cpp
@@ -59,11 +59,6 @@
#include <C2PlatformSupport.h>
#include <C2Work.h>
-extern "C" ::android::C2ComponentFactory *CreateCodec2Factory();
-extern "C" void DestroyCodec2Factory(::android::C2ComponentFactory *);
-
-#include "../avcdec/C2SoftAvcDec.h"
-
using namespace android;
using namespace std::chrono_literals;
@@ -72,7 +67,7 @@
class LinearBuffer : public C2Buffer {
public:
explicit LinearBuffer(const std::shared_ptr<C2LinearBlock> &block)
- : C2Buffer({ block->share(block->offset(), block->size(), ::android::C2Fence()) }) {}
+ : C2Buffer({ block->share(block->offset(), block->size(), ::C2Fence()) }) {}
};
class Listener;
@@ -220,7 +215,7 @@
(void)component->setListener_vb(mListener, C2_DONT_BLOCK);
std::unique_ptr<C2PortBlockPoolsTuning::output> pools =
- C2PortBlockPoolsTuning::output::alloc_unique({ (uint64_t)C2BlockPool::BASIC_GRAPHIC });
+ C2PortBlockPoolsTuning::output::AllocUnique({ (uint64_t)C2BlockPool::BASIC_GRAPHIC });
std::vector<std::unique_ptr<C2SettingResult>> result;
(void)component->intf()->config_vb({pools.get()}, C2_DONT_BLOCK, &result);
component->start();
@@ -295,7 +290,7 @@
size_t size = 0u;
void *data = nullptr;
int64_t timestamp = 0u;
- MediaBuffer *buffer = nullptr;
+ MediaBufferBase *buffer = nullptr;
sp<ABuffer> csd;
if (csd0 != nullptr) {
csd = csd0;
@@ -314,8 +309,8 @@
break;
}
- sp<MetaData> meta = buffer->meta_data();
- CHECK(meta->findInt64(kKeyTime, ×tamp));
+ MetaDataBase &meta = buffer->meta_data();
+ CHECK(meta.findInt64(kKeyTime, ×tamp));
size = buffer->size();
data = buffer->data();
diff --git a/media/libstagefright/codecs/g711/dec/C2SoftG711.cpp b/media/libstagefright/codecs/g711/dec/C2SoftG711.cpp
index a26dbb9..d296a3d 100644
--- a/media/libstagefright/codecs/g711/dec/C2SoftG711.cpp
+++ b/media/libstagefright/codecs/g711/dec/C2SoftG711.cpp
@@ -24,21 +24,37 @@
#include <SimpleC2Interface.h>
#include <media/stagefright/foundation/ADebug.h>
-
-#ifdef ALAW
-#define COMPONENT_NAME "g711a"
-#else
-#define COMPONENT_NAME "g711m"
-#endif
+#include <media/stagefright/foundation/MediaDefs.h>
namespace android {
-C2SoftG711::C2SoftG711(const char *name, c2_node_id_t id)
- : SimpleC2Component(
- SimpleC2Interface::Builder(name, id)
+#ifdef ALAW
+constexpr char kComponentName[] = "c2.google.g711.alaw.decoder";
+#else
+constexpr char kComponentName[] = "c2.google.g711.mlaw.decoder";
+#endif
+
+static std::shared_ptr<C2ComponentInterface> BuildIntf(
+ const char *name, c2_node_id_t id,
+ std::function<void(C2ComponentInterface*)> deleter =
+ std::default_delete<C2ComponentInterface>()) {
+ return SimpleC2Interface::Builder(name, id, deleter)
.inputFormat(C2FormatCompressed)
.outputFormat(C2FormatAudio)
- .build()) {
+ .inputMediaType(
+#ifdef ALAW
+ MEDIA_MIMETYPE_AUDIO_G711_ALAW
+#else
+ MEDIA_MIMETYPE_AUDIO_G711_MLAW
+#endif
+ )
+ .outputMediaType(MEDIA_MIMETYPE_AUDIO_RAW)
+ .build();
+}
+
+
+C2SoftG711::C2SoftG711(const char *name, c2_node_id_t id)
+ : SimpleC2Component(BuildIntf(name, id)) {
}
C2SoftG711::~C2SoftG711() {
@@ -206,20 +222,17 @@
class C2SoftG711DecFactory : public C2ComponentFactory {
public:
virtual c2_status_t createComponent(
- c2_node_id_t id, std::shared_ptr<C2Component>* const component,
- std::function<void(::android::C2Component*)> deleter) override {
- *component = std::shared_ptr<C2Component>(new C2SoftG711(COMPONENT_NAME, id), deleter);
+ c2_node_id_t id,
+ std::shared_ptr<C2Component>* const component,
+ std::function<void(C2Component*)> deleter) override {
+ *component = std::shared_ptr<C2Component>(new C2SoftG711(kComponentName, id), deleter);
return C2_OK;
}
virtual c2_status_t createInterface(
c2_node_id_t id, std::shared_ptr<C2ComponentInterface>* const interface,
- std::function<void(::android::C2ComponentInterface*)> deleter) override {
- *interface =
- SimpleC2Interface::Builder(COMPONENT_NAME, id, deleter)
- .inputFormat(C2FormatCompressed)
- .outputFormat(C2FormatAudio)
- .build();
+ std::function<void(C2ComponentInterface*)> deleter) override {
+ *interface = BuildIntf(kComponentName, id, deleter);
return C2_OK;
}
@@ -228,12 +241,12 @@
} // namespace android
-extern "C" ::android::C2ComponentFactory* CreateCodec2Factory() {
+extern "C" ::C2ComponentFactory* CreateCodec2Factory() {
ALOGV("in %s", __func__);
return new ::android::C2SoftG711DecFactory();
}
-extern "C" void DestroyCodec2Factory(::android::C2ComponentFactory* factory) {
+extern "C" void DestroyCodec2Factory(::C2ComponentFactory* factory) {
ALOGV("in %s", __func__);
delete factory;
}
diff --git a/media/libstagefright/codecs/mp3dec/C2SoftMP3.cpp b/media/libstagefright/codecs/mp3dec/C2SoftMP3.cpp
index c34a6f0..9cac87e 100644
--- a/media/libstagefright/codecs/mp3dec/C2SoftMP3.cpp
+++ b/media/libstagefright/codecs/mp3dec/C2SoftMP3.cpp
@@ -26,17 +26,28 @@
#include <SimpleC2Interface.h>
#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/foundation/MediaDefs.h>
#include <numeric>
namespace android {
-C2SoftMP3::C2SoftMP3(const char *name, c2_node_id_t id)
- : SimpleC2Component(
- SimpleC2Interface::Builder(name, id)
+constexpr char kComponentName[] = "c2.google.aac.encoder";
+
+static std::shared_ptr<C2ComponentInterface> BuildIntf(
+ const char *name, c2_node_id_t id,
+ std::function<void(C2ComponentInterface*)> deleter =
+ std::default_delete<C2ComponentInterface>()) {
+ return SimpleC2Interface::Builder(name, id, deleter)
.inputFormat(C2FormatCompressed)
.outputFormat(C2FormatAudio)
- .build()),
+ .inputMediaType(MEDIA_MIMETYPE_AUDIO_MPEG)
+ .outputMediaType(MEDIA_MIMETYPE_AUDIO_RAW)
+ .build();
+}
+
+C2SoftMP3::C2SoftMP3(const char *name, c2_node_id_t id)
+ : SimpleC2Component(BuildIntf(name, id)),
mConfig(nullptr),
mDecoderBuf(nullptr) {
}
@@ -397,20 +408,18 @@
class C2SoftMp3DecFactory : public C2ComponentFactory {
public:
virtual c2_status_t createComponent(
- c2_node_id_t id, std::shared_ptr<C2Component>* const component,
- std::function<void(::android::C2Component*)> deleter) override {
- *component = std::shared_ptr<C2Component>(new C2SoftMP3("mp3", id), deleter);
+ c2_node_id_t id,
+ std::shared_ptr<C2Component>* const component,
+ std::function<void(C2Component*)> deleter) override {
+ *component = std::shared_ptr<C2Component>(new C2SoftMP3(kComponentName, id), deleter);
return C2_OK;
}
virtual c2_status_t createInterface(
- c2_node_id_t id, std::shared_ptr<C2ComponentInterface>* const interface,
- std::function<void(::android::C2ComponentInterface*)> deleter) override {
- *interface =
- SimpleC2Interface::Builder("mp3", id, deleter)
- .inputFormat(C2FormatCompressed)
- .outputFormat(C2FormatAudio)
- .build();
+ c2_node_id_t id,
+ std::shared_ptr<C2ComponentInterface>* const interface,
+ std::function<void(C2ComponentInterface*)> deleter) override {
+ *interface = BuildIntf(kComponentName, id, deleter);
return C2_OK;
}
@@ -419,12 +428,12 @@
} // namespace android
-extern "C" ::android::C2ComponentFactory* CreateCodec2Factory() {
+extern "C" ::C2ComponentFactory* CreateCodec2Factory() {
ALOGV("in %s", __func__);
return new ::android::C2SoftMp3DecFactory();
}
-extern "C" void DestroyCodec2Factory(::android::C2ComponentFactory* factory) {
+extern "C" void DestroyCodec2Factory(::C2ComponentFactory* factory) {
ALOGV("in %s", __func__);
delete factory;
}
diff --git a/media/libstagefright/foundation/avc_utils.cpp b/media/libstagefright/foundation/avc_utils.cpp
index 8414af3..e8a6083 100644
--- a/media/libstagefright/foundation/avc_utils.cpp
+++ b/media/libstagefright/foundation/avc_utils.cpp
@@ -537,49 +537,6 @@
return layerId;
}
-sp<ABuffer> MakeAACCodecSpecificData(
- unsigned profile, unsigned sampling_freq_index,
- unsigned channel_configuration, int32_t *sampleRate,
- int32_t *channelCount) {
- CHECK_LE(sampling_freq_index, 11u);
- static const int32_t kSamplingFreq[] = {
- 96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050,
- 16000, 12000, 11025, 8000
- };
- *sampleRate = kSamplingFreq[sampling_freq_index];
- *channelCount = channel_configuration;
-
- static const uint8_t kStaticESDS[] = {
- 0x03, 22,
- 0x00, 0x00, // ES_ID
- 0x00, // streamDependenceFlag, URL_Flag, OCRstreamFlag
-
- 0x04, 17,
- 0x40, // Audio ISO/IEC 14496-3
- 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00,
-
- 0x05, 2,
- // AudioSpecificInfo follows
-
- // oooo offf fccc c000
- // o - audioObjectType
- // f - samplingFreqIndex
- // c - channelConfig
- };
- sp<ABuffer> csd = new ABuffer(sizeof(kStaticESDS) + 2);
- memcpy(csd->data(), kStaticESDS, sizeof(kStaticESDS));
-
- csd->data()[sizeof(kStaticESDS)] =
- ((profile + 1) << 3) | (sampling_freq_index >> 1);
-
- csd->data()[sizeof(kStaticESDS) + 1] =
- ((sampling_freq_index << 7) & 0x80) | (channel_configuration << 3);
-
- return csd;
-}
-
bool ExtractDimensionsFromVOLHeader(
const uint8_t *data, size_t size, int32_t *width, int32_t *height) {
ABitReader br(&data[4], size - 4);
diff --git a/media/libstagefright/foundation/include/media/stagefright/foundation/avc_utils.h b/media/libstagefright/foundation/include/media/stagefright/foundation/avc_utils.h
index 2ca66fb..c287559 100644
--- a/media/libstagefright/foundation/include/media/stagefright/foundation/avc_utils.h
+++ b/media/libstagefright/foundation/include/media/stagefright/foundation/avc_utils.h
@@ -90,11 +90,6 @@
const char *AVCProfileToString(uint8_t profile);
-sp<ABuffer> MakeAACCodecSpecificData(
- unsigned profile, unsigned sampling_freq_index,
- unsigned channel_configuration, int32_t *sampleRate,
- int32_t *channelCount);
-
// Given an MPEG4 video VOL-header chunk (starting with 0x00 0x00 0x01 0x2?)
// parse it and fill in dimensions, returns true iff successful.
bool ExtractDimensionsFromVOLHeader(
diff --git a/media/libstagefright/httplive/Android.bp b/media/libstagefright/httplive/Android.bp
index f64e437..8a77401 100644
--- a/media/libstagefright/httplive/Android.bp
+++ b/media/libstagefright/httplive/Android.bp
@@ -40,6 +40,7 @@
"libstagefright",
"libstagefright_foundation",
"libutils",
+ "libhidlallocatorutils",
"libhidlbase",
"android.hardware.cas@1.0",
"android.hardware.cas.native@1.0",
diff --git a/media/libstagefright/httplive/PlaylistFetcher.cpp b/media/libstagefright/httplive/PlaylistFetcher.cpp
index 55fc680..5624f4a 100644
--- a/media/libstagefright/httplive/PlaylistFetcher.cpp
+++ b/media/libstagefright/httplive/PlaylistFetcher.cpp
@@ -2066,7 +2066,8 @@
CHECK_NE(channel_configuration, 0u);
bits.skipBits(2); // original_copy, home
- sp<MetaData> meta = MakeAACCodecSpecificData(
+ sp<MetaData> meta = new MetaData();
+ MakeAACCodecSpecificData(*meta,
profile, sampling_freq_index, channel_configuration);
meta->setInt32(kKeyIsADTS, true);
diff --git a/media/libstagefright/include/ACodecBufferChannel.h b/media/libstagefright/include/ACodecBufferChannel.h
index f253a52..7c01e45 100644
--- a/media/libstagefright/include/ACodecBufferChannel.h
+++ b/media/libstagefright/include/ACodecBufferChannel.h
@@ -29,8 +29,10 @@
#include <media/IOMX.h>
namespace android {
-
-using hardware::hidl_memory;
+namespace hardware {
+class HidlMemory;
+};
+using hardware::HidlMemory;
/**
* BufferChannelBase implementation for ACodec.
@@ -119,7 +121,7 @@
sp<MemoryDealer> mDealer;
sp<IMemory> mDecryptDestination;
int32_t mHeapSeqNum;
- hidl_memory mHidlMemory;
+ sp<HidlMemory> mHidlMemory;
// These should only be accessed via std::atomic_* functions.
//
diff --git a/media/libstagefright/include/Codec2Buffer.h b/media/libstagefright/include/Codec2Buffer.h
index 6d85e83..9766b41 100644
--- a/media/libstagefright/include/Codec2Buffer.h
+++ b/media/libstagefright/include/Codec2Buffer.h
@@ -24,17 +24,100 @@
namespace android {
+class Codec2Buffer : public MediaCodecBuffer {
+public:
+ using MediaCodecBuffer::MediaCodecBuffer;
+ ~Codec2Buffer() override = default;
+
+ /**
+ * \return C2Buffer object represents this buffer.
+ */
+ virtual std::shared_ptr<C2Buffer> asC2Buffer() = 0;
+
+ /**
+ * Test if we can copy the content of |buffer| into this object.
+ *
+ * \param buffer C2Buffer object to copy.
+ * \return true if the content of buffer can be copied over to this buffer
+ * false otherwise.
+ */
+ virtual bool canCopy(const std::shared_ptr<C2Buffer> &buffer) const {
+ (void)buffer;
+ return false;
+ }
+
+ /**
+ * Copy the content of |buffer| into this object. This method assumes that
+ * canCopy() check already passed.
+ *
+ * \param buffer C2Buffer object to copy.
+ * \return true if successful
+ * false otherwise.
+ */
+ virtual bool copy(const std::shared_ptr<C2Buffer> &buffer) {
+ (void)buffer;
+ return false;
+ }
+
+protected:
+ /**
+ * canCopy() implementation for linear buffers.
+ */
+ bool canCopyLinear(const std::shared_ptr<C2Buffer> &buffer) const;
+
+ /**
+ * copy() implementation for linear buffers.
+ */
+ bool copyLinear(const std::shared_ptr<C2Buffer> &buffer);
+};
+
+/**
+ * MediaCodecBuffer implementation on top of local linear buffer. This cannot
+ * cross process boundary so asC2Buffer() returns only nullptr.
+ */
+class LocalLinearBuffer : public Codec2Buffer {
+public:
+ using Codec2Buffer::Codec2Buffer;
+
+ std::shared_ptr<C2Buffer> asC2Buffer() override { return nullptr; }
+ bool canCopy(const std::shared_ptr<C2Buffer> &buffer) const override;
+ bool copy(const std::shared_ptr<C2Buffer> &buffer) override;
+};
+
+/**
+ * MediaCodecBuffer implementation to be used only as a dummy wrapper around a
+ * C2Buffer object.
+ */
+class DummyContainerBuffer : public Codec2Buffer {
+public:
+ DummyContainerBuffer(
+ const sp<AMessage> &format,
+ const std::shared_ptr<C2Buffer> &buffer = nullptr);
+
+ std::shared_ptr<C2Buffer> asC2Buffer() override;
+ bool canCopy(const std::shared_ptr<C2Buffer> &buffer) const override;
+ bool copy(const std::shared_ptr<C2Buffer> &buffer) override;
+
+private:
+ std::shared_ptr<C2Buffer> mBufferRef;
+};
+
/**
* MediaCodecBuffer implementation wraps around C2LinearBlock.
*/
-class LinearBlockBuffer : public MediaCodecBuffer {
+class LinearBlockBuffer : public Codec2Buffer {
public:
- static sp<LinearBlockBuffer> allocate(
+ /**
+ * Allocate a new LinearBufferBlock wrapping around C2LinearBlock object.
+ */
+ static sp<LinearBlockBuffer> Allocate(
const sp<AMessage> &format, const std::shared_ptr<C2LinearBlock> &block);
virtual ~LinearBlockBuffer() = default;
- C2ConstLinearBlock share();
+ std::shared_ptr<C2Buffer> asC2Buffer() override;
+ bool canCopy(const std::shared_ptr<C2Buffer> &buffer) const override;
+ bool copy(const std::shared_ptr<C2Buffer> &buffer) override;
private:
LinearBlockBuffer(
@@ -50,20 +133,27 @@
/**
* MediaCodecBuffer implementation wraps around C2ConstLinearBlock.
*/
-class ConstLinearBlockBuffer : public MediaCodecBuffer {
+class ConstLinearBlockBuffer : public Codec2Buffer {
public:
- static sp<ConstLinearBlockBuffer> allocate(
- const sp<AMessage> &format, const C2ConstLinearBlock &block);
+ /**
+ * Allocate a new ConstLinearBlockBuffer wrapping around C2Buffer object.
+ */
+ static sp<ConstLinearBlockBuffer> Allocate(
+ const sp<AMessage> &format, const std::shared_ptr<C2Buffer> &buffer);
virtual ~ConstLinearBlockBuffer() = default;
+ std::shared_ptr<C2Buffer> asC2Buffer() override;
+
private:
ConstLinearBlockBuffer(
const sp<AMessage> &format,
- C2ReadView &&readView);
+ C2ReadView &&readView,
+ const std::shared_ptr<C2Buffer> &buffer);
ConstLinearBlockBuffer() = delete;
C2ReadView mReadView;
+ std::shared_ptr<C2Buffer> mBufferRef;
};
} // namespace android
diff --git a/media/libstagefright/include/FrameDecoder.h b/media/libstagefright/include/FrameDecoder.h
index c40324b..dfbe2cd 100644
--- a/media/libstagefright/include/FrameDecoder.h
+++ b/media/libstagefright/include/FrameDecoder.h
@@ -68,7 +68,7 @@
virtual status_t onInputReceived(
const sp<MediaCodecBuffer> &codecBuffer,
- const sp<MetaData> &sampleMeta,
+ MetaDataBase &sampleMeta,
bool firstSample,
uint32_t *flags) = 0;
@@ -123,7 +123,7 @@
virtual status_t onInputReceived(
const sp<MediaCodecBuffer> &codecBuffer,
- const sp<MetaData> &sampleMeta,
+ MetaDataBase &sampleMeta,
bool firstSample,
uint32_t *flags) override;
@@ -158,7 +158,7 @@
virtual status_t onInputReceived(
const sp<MediaCodecBuffer> &codecBuffer __unused,
- const sp<MetaData> &sampleMeta __unused,
+ MetaDataBase &sampleMeta __unused,
bool firstSample __unused,
uint32_t *flags __unused) override { return OK; }
diff --git a/media/libstagefright/include/media/stagefright/AudioPlayer.h b/media/libstagefright/include/media/stagefright/AudioPlayer.h
index e971762..7c2c36f 100644
--- a/media/libstagefright/include/media/stagefright/AudioPlayer.h
+++ b/media/libstagefright/include/media/stagefright/AudioPlayer.h
@@ -69,7 +69,7 @@
sp<MediaSource> mSource;
sp<AudioTrack> mAudioTrack;
- MediaBuffer *mInputBuffer;
+ MediaBufferBase *mInputBuffer;
int mSampleRate;
int64_t mLatencyUs;
@@ -91,7 +91,7 @@
bool mIsFirstBuffer;
status_t mFirstBufferResult;
- MediaBuffer *mFirstBuffer;
+ MediaBufferBase *mFirstBuffer;
sp<MediaPlayerBase::AudioSink> mAudioSink;
diff --git a/media/libstagefright/include/media/stagefright/AudioSource.h b/media/libstagefright/include/media/stagefright/AudioSource.h
index f66b92d..206d322 100644
--- a/media/libstagefright/include/media/stagefright/AudioSource.h
+++ b/media/libstagefright/include/media/stagefright/AudioSource.h
@@ -56,11 +56,11 @@
int16_t getMaxAmplitude();
virtual status_t read(
- MediaBuffer **buffer, const ReadOptions *options = NULL);
+ MediaBufferBase **buffer, const ReadOptions *options = NULL);
virtual status_t setStopTimeUs(int64_t stopTimeUs);
status_t dataCallback(const AudioRecord::Buffer& buffer);
- virtual void signalBufferReturned(MediaBuffer *buffer);
+ virtual void signalBufferReturned(MediaBufferBase *buffer);
status_t setInputDevice(audio_port_handle_t deviceId);
status_t getRoutedDeviceId(audio_port_handle_t* deviceId);
diff --git a/media/libstagefright/include/media/stagefright/CCodec.h b/media/libstagefright/include/media/stagefright/CCodec.h
index 24ee0a3..3a2670d 100644
--- a/media/libstagefright/include/media/stagefright/CCodec.h
+++ b/media/libstagefright/include/media/stagefright/CCodec.h
@@ -83,7 +83,7 @@
void setInputSurface(const sp<PersistentSurface> &surface);
status_t setupInputSurface(const std::shared_ptr<InputSurfaceWrapper> &surface);
- void setDeadline(const TimePoint &deadline);
+ void setDeadline(const TimePoint &deadline, const char *name);
enum {
kWhatAllocate,
@@ -126,10 +126,25 @@
sp<AMessage> outputFormat;
};
+ struct NamedTimePoint {
+ inline void set(
+ const TimePoint &timePoint,
+ const char *name) {
+ mTimePoint = timePoint;
+ mName = name;
+ }
+
+ inline TimePoint get() const { return mTimePoint; }
+ inline const char *getName() const { return mName; }
+ private:
+ TimePoint mTimePoint;
+ const char *mName;
+ };
+
Mutexed<State> mState;
std::shared_ptr<CCodecBufferChannel> mChannel;
std::shared_ptr<C2Component::Listener> mListener;
- Mutexed<TimePoint> mDeadline;
+ Mutexed<NamedTimePoint> mDeadline;
Mutexed<Formats> mFormats;
Mutexed<std::list<std::unique_ptr<C2Work>>> mWorkDoneQueue;
diff --git a/media/libstagefright/include/media/stagefright/CallbackMediaSource.h b/media/libstagefright/include/media/stagefright/CallbackMediaSource.h
index 944d951..33453fa 100644
--- a/media/libstagefright/include/media/stagefright/CallbackMediaSource.h
+++ b/media/libstagefright/include/media/stagefright/CallbackMediaSource.h
@@ -33,7 +33,7 @@
virtual status_t stop();
virtual sp<MetaData> getFormat();
virtual status_t read(
- MediaBuffer **buffer, const ReadOptions *options = NULL);
+ MediaBufferBase **buffer, const ReadOptions *options = NULL);
virtual status_t pause();
private:
diff --git a/media/libstagefright/include/media/stagefright/CameraSource.h b/media/libstagefright/include/media/stagefright/CameraSource.h
index 945e1be..475976b 100644
--- a/media/libstagefright/include/media/stagefright/CameraSource.h
+++ b/media/libstagefright/include/media/stagefright/CameraSource.h
@@ -97,7 +97,7 @@
virtual status_t start(MetaData *params = NULL);
virtual status_t stop() { return reset(); }
virtual status_t read(
- MediaBuffer **buffer, const ReadOptions *options = NULL);
+ MediaBufferBase **buffer, const ReadOptions *options = NULL);
virtual status_t setStopTimeUs(int64_t stopTimeUs);
/**
@@ -127,7 +127,7 @@
*/
MetadataBufferType metaDataStoredInVideoBuffers() const;
- virtual void signalBufferReturned(MediaBuffer* buffer);
+ virtual void signalBufferReturned(MediaBufferBase* buffer);
protected:
diff --git a/media/libstagefright/include/media/stagefright/CameraSourceTimeLapse.h b/media/libstagefright/include/media/stagefright/CameraSourceTimeLapse.h
index b066f9a..533e33b 100644
--- a/media/libstagefright/include/media/stagefright/CameraSourceTimeLapse.h
+++ b/media/libstagefright/include/media/stagefright/CameraSourceTimeLapse.h
@@ -107,7 +107,7 @@
// Stores a copy of the MediaBuffer read in the last read() call after
// mQuickStop was true.
- MediaBuffer* mLastReadBufferCopy;
+ MediaBufferBase* mLastReadBufferCopy;
// Status code for last read.
status_t mLastReadStatus;
@@ -128,10 +128,10 @@
// Wrapper over CameraSource::signalBufferReturned() to implement quick stop.
// It only handles the case when mLastReadBufferCopy is signalled. Otherwise
// it calls the base class' function.
- virtual void signalBufferReturned(MediaBuffer* buffer);
+ virtual void signalBufferReturned(MediaBufferBase* buffer);
// Wrapper over CameraSource::read() to implement quick stop.
- virtual status_t read(MediaBuffer **buffer, const ReadOptions *options = NULL);
+ virtual status_t read(MediaBufferBase **buffer, const ReadOptions *options = NULL);
// mSkipCurrentFrame is set to true in dataCallbackTimestamp() if the current
// frame needs to be skipped and this function just returns the value of mSkipCurrentFrame.
@@ -170,7 +170,7 @@
// Convenience function to fill mLastReadBufferCopy from the just read
// buffer.
- void fillLastReadBufferCopy(MediaBuffer& sourceBuffer);
+ void fillLastReadBufferCopy(MediaBufferBase& sourceBuffer);
// If the passed in size (width x height) is a supported video/preview size,
// the function sets the camera's video/preview size to it and returns true.
diff --git a/media/libstagefright/include/media/stagefright/Codec2InfoBuilder.h b/media/libstagefright/include/media/stagefright/Codec2InfoBuilder.h
new file mode 100644
index 0000000..ea0b5c4
--- /dev/null
+++ b/media/libstagefright/include/media/stagefright/Codec2InfoBuilder.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2018 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 CODEC2_INFO_BUILDER_H_
+#define CODEC2_INFO_BUILDER_H_
+
+#include <media/stagefright/MediaCodecList.h>
+#include <utils/Errors.h>
+
+namespace android {
+
+class Codec2InfoBuilder : public MediaCodecListBuilderBase {
+public:
+ Codec2InfoBuilder() = default;
+ status_t buildMediaCodecList(MediaCodecListWriter* writer) override;
+};
+
+} // namespace android
+
+#endif // CODEC2_INFO_BUILDER_H_
diff --git a/media/libstagefright/include/media/stagefright/InterfaceUtils.h b/media/libstagefright/include/media/stagefright/InterfaceUtils.h
index 568c735..f0ebd48 100644
--- a/media/libstagefright/include/media/stagefright/InterfaceUtils.h
+++ b/media/libstagefright/include/media/stagefright/InterfaceUtils.h
@@ -51,7 +51,7 @@
// Creates an IMediaSource wrapper to the given MediaSource.
sp<IMediaSource> CreateIMediaSourceFromMediaSourceBase(
const sp<RemoteMediaExtractor> &extractor,
- MediaSourceBase *source, const sp<RefBase> &plugin);
+ MediaTrack *source, const sp<RefBase> &plugin);
} // namespace android
diff --git a/media/libstagefright/include/media/stagefright/JPEGSource.h b/media/libstagefright/include/media/stagefright/JPEGSource.h
index 9fcbfc2..8ab3d11 100644
--- a/media/libstagefright/include/media/stagefright/JPEGSource.h
+++ b/media/libstagefright/include/media/stagefright/JPEGSource.h
@@ -33,7 +33,7 @@
virtual sp<MetaData> getFormat();
virtual status_t read(
- MediaBuffer **buffer, const ReadOptions *options = NULL);
+ MediaBufferBase **buffer, const ReadOptions *options = NULL);
protected:
virtual ~JPEGSource();
diff --git a/media/libstagefright/include/media/stagefright/MediaAdapter.h b/media/libstagefright/include/media/stagefright/MediaAdapter.h
index 4b47160..589c827 100644
--- a/media/libstagefright/include/media/stagefright/MediaAdapter.h
+++ b/media/libstagefright/include/media/stagefright/MediaAdapter.h
@@ -40,13 +40,13 @@
virtual status_t stop();
virtual sp<MetaData> getFormat();
virtual status_t read(
- MediaBuffer **buffer, const ReadOptions *options = NULL);
+ MediaBufferBase **buffer, const ReadOptions *options = NULL);
/////////////////////////////////////////////////
// Inherited functions from MediaBufferObserver
/////////////////////////////////////////////////
- virtual void signalBufferReturned(MediaBuffer *buffer);
+ virtual void signalBufferReturned(MediaBufferBase *buffer);
/////////////////////////////////////////////////
// Non-inherited functions:
diff --git a/media/libstagefright/include/media/stagefright/MediaBufferBase.h b/media/libstagefright/include/media/stagefright/MediaBufferBase.h
new file mode 120000
index 0000000..80e49b0
--- /dev/null
+++ b/media/libstagefright/include/media/stagefright/MediaBufferBase.h
@@ -0,0 +1 @@
+../../../../libmediaextractor/include/media/stagefright/MediaBufferBase.h
\ No newline at end of file
diff --git a/media/libstagefright/include/media/stagefright/MediaCodec.h b/media/libstagefright/include/media/stagefright/MediaCodec.h
index 0bc02af..e7faea5 100644
--- a/media/libstagefright/include/media/stagefright/MediaCodec.h
+++ b/media/libstagefright/include/media/stagefright/MediaCodec.h
@@ -334,8 +334,6 @@
// initial create parameters
AString mInitName;
- bool mInitNameIsType;
- bool mInitIsEncoder;
// configure parameter
sp<AMessage> mConfigureMsg;
@@ -370,14 +368,14 @@
MediaCodec(const sp<ALooper> &looper, pid_t pid, uid_t uid);
- static sp<CodecBase> GetCodecBase(const AString &name, bool nameIsType = false);
+ static sp<CodecBase> GetCodecBase(const AString &name);
static status_t PostAndAwaitResponse(
const sp<AMessage> &msg, sp<AMessage> *response);
void PostReplyWithError(const sp<AReplyToken> &replyID, int32_t err);
- status_t init(const AString &name, bool nameIsType, bool encoder);
+ status_t init(const AString &name);
void setState(State newState);
void returnBuffersToCodec(bool isReclaim = false);
diff --git a/media/libstagefright/include/media/stagefright/MediaCodecList.h b/media/libstagefright/include/media/stagefright/MediaCodecList.h
index f2bd496..d46fe85 100644
--- a/media/libstagefright/include/media/stagefright/MediaCodecList.h
+++ b/media/libstagefright/include/media/stagefright/MediaCodecList.h
@@ -18,6 +18,8 @@
#define MEDIA_CODEC_LIST_H_
+#include <initializer_list>
+
#include <media/stagefright/foundation/ABase.h>
#include <media/stagefright/foundation/AString.h>
#include <media/IMediaCodecList.h>
@@ -94,9 +96,9 @@
/**
* This constructor will call `buildMediaCodecList()` from the given
- * `MediaCodecListBuilderBase` object.
+ * `MediaCodecListBuilderBase` objects.
*/
- MediaCodecList(MediaCodecListBuilderBase* builder);
+ MediaCodecList(std::initializer_list<MediaCodecListBuilderBase*> builders);
~MediaCodecList();
diff --git a/media/libstagefright/include/media/stagefright/MediaCodecSource.h b/media/libstagefright/include/media/stagefright/MediaCodecSource.h
index eec115e..a68cc19 100644
--- a/media/libstagefright/include/media/stagefright/MediaCodecSource.h
+++ b/media/libstagefright/include/media/stagefright/MediaCodecSource.h
@@ -57,13 +57,13 @@
virtual status_t pause(MetaData *params);
virtual sp<MetaData> getFormat();
virtual status_t read(
- MediaBuffer **buffer,
+ MediaBufferBase **buffer,
const ReadOptions *options = NULL);
virtual status_t setStopTimeUs(int64_t stopTimeUs);
// MediaBufferObserver
- virtual void signalBufferReturned(MediaBuffer *buffer);
+ virtual void signalBufferReturned(MediaBufferBase *buffer);
// for AHandlerReflector
void onMessageReceived(const sp<AMessage> &msg);
@@ -136,7 +136,7 @@
sp<AMessage> mEncoderActivityNotify;
sp<IGraphicBufferProducer> mGraphicBufferProducer;
sp<PersistentSurface> mPersistentSurface;
- List<MediaBuffer *> mInputBufferQueue;
+ List<MediaBufferBase *> mInputBufferQueue;
List<size_t> mAvailEncoderInputIndices;
List<int64_t> mDecodingTimeQueue; // decoding time (us) for video
int64_t mInputBufferTimeOffsetUs;
@@ -149,7 +149,7 @@
struct Output {
Output();
- List<MediaBuffer*> mBufferQueue;
+ List<MediaBufferBase*> mBufferQueue;
bool mEncoderReachedEOS;
status_t mErrorCode;
Condition mCond;
diff --git a/media/libstagefright/include/media/stagefright/MetaDataBase.h b/media/libstagefright/include/media/stagefright/MetaDataBase.h
new file mode 120000
index 0000000..1e12193
--- /dev/null
+++ b/media/libstagefright/include/media/stagefright/MetaDataBase.h
@@ -0,0 +1 @@
+../../../../libmediaextractor/include/media/stagefright/MetaDataBase.h
\ No newline at end of file
diff --git a/media/libstagefright/include/media/stagefright/MetaDataUtils.h b/media/libstagefright/include/media/stagefright/MetaDataUtils.h
index 7c18bc2..3af2218 100644
--- a/media/libstagefright/include/media/stagefright/MetaDataUtils.h
+++ b/media/libstagefright/include/media/stagefright/MetaDataUtils.h
@@ -23,8 +23,8 @@
namespace android {
struct ABuffer;
-sp<MetaData> MakeAVCCodecSpecificData(const sp<ABuffer> &accessUnit);
-sp<MetaData> MakeAACCodecSpecificData(unsigned profile, unsigned sampling_freq_index,
+bool MakeAVCCodecSpecificData(MetaDataBase &meta, const sp<ABuffer> &accessUnit);
+bool MakeAACCodecSpecificData(MetaDataBase &meta, unsigned profile, unsigned sampling_freq_index,
unsigned channel_configuration);
} // namespace android
diff --git a/media/libstagefright/include/media/stagefright/NuMediaExtractor.h b/media/libstagefright/include/media/stagefright/NuMediaExtractor.h
index 6a2e39b..5e5ef6e 100644
--- a/media/libstagefright/include/media/stagefright/NuMediaExtractor.h
+++ b/media/libstagefright/include/media/stagefright/NuMediaExtractor.h
@@ -107,8 +107,8 @@
struct Sample {
Sample();
- Sample(MediaBuffer *buffer, int64_t timeUs);
- MediaBuffer *mBuffer;
+ Sample(MediaBufferBase *buffer, int64_t timeUs);
+ MediaBufferBase *mBuffer;
int64_t mSampleTimeUs;
};
@@ -150,7 +150,7 @@
bool getTotalBitrate(int64_t *bitRate) const;
status_t updateDurationAndBitrate();
- status_t appendVorbisNumPageSamples(MediaBuffer *mbuf, const sp<ABuffer> &buffer);
+ status_t appendVorbisNumPageSamples(MediaBufferBase *mbuf, const sp<ABuffer> &buffer);
DISALLOW_EVIL_CONSTRUCTORS(NuMediaExtractor);
};
diff --git a/media/libstagefright/include/media/stagefright/RemoteMediaExtractor.h b/media/libstagefright/include/media/stagefright/RemoteMediaExtractor.h
index 22a8210..509e669 100644
--- a/media/libstagefright/include/media/stagefright/RemoteMediaExtractor.h
+++ b/media/libstagefright/include/media/stagefright/RemoteMediaExtractor.h
@@ -40,8 +40,6 @@
virtual sp<MetaData> getMetaData();
virtual status_t getMetrics(Parcel *reply);
virtual uint32_t flags() const;
- virtual char* getDrmTrackInfo(size_t trackID, int * len);
- virtual void setUID(uid_t uid);
virtual status_t setMediaCas(const HInterfaceToken &casToken);
virtual const char * name();
diff --git a/media/libstagefright/include/media/stagefright/RemoteMediaSource.h b/media/libstagefright/include/media/stagefright/RemoteMediaSource.h
index d1afa6a..1d720af 100644
--- a/media/libstagefright/include/media/stagefright/RemoteMediaSource.h
+++ b/media/libstagefright/include/media/stagefright/RemoteMediaSource.h
@@ -28,26 +28,26 @@
public:
static sp<IMediaSource> wrap(
const sp<RemoteMediaExtractor> &extractor,
- MediaSourceBase *source,
+ MediaTrack *source,
const sp<RefBase> &plugin);
virtual ~RemoteMediaSource();
virtual status_t start(MetaData *params = NULL);
virtual status_t stop();
virtual sp<MetaData> getFormat();
virtual status_t read(
- MediaBuffer **buffer,
+ MediaBufferBase **buffer,
const MediaSource::ReadOptions *options = NULL);
virtual status_t pause();
virtual status_t setStopTimeUs(int64_t stopTimeUs);
private:
sp<RemoteMediaExtractor> mExtractor;
- MediaSourceBase *mSource;
+ MediaTrack *mSource;
sp<RefBase> mExtractorPlugin;
explicit RemoteMediaSource(
const sp<RemoteMediaExtractor> &extractor,
- MediaSourceBase *source,
+ MediaTrack *source,
const sp<RefBase> &plugin);
DISALLOW_EVIL_CONSTRUCTORS(RemoteMediaSource);
diff --git a/media/libstagefright/include/media/stagefright/SimpleDecodingSource.h b/media/libstagefright/include/media/stagefright/SimpleDecodingSource.h
index 3006b45..23defb4 100644
--- a/media/libstagefright/include/media/stagefright/SimpleDecodingSource.h
+++ b/media/libstagefright/include/media/stagefright/SimpleDecodingSource.h
@@ -65,7 +65,7 @@
virtual sp<MetaData> getFormat();
// reads from the source. This call always blocks.
- virtual status_t read(MediaBuffer **buffer, const ReadOptions *options);
+ virtual status_t read(MediaBufferBase **buffer, const ReadOptions *options);
// unsupported methods
virtual status_t pause() { return INVALID_OPERATION; }
@@ -104,7 +104,8 @@
// do the actual reading
status_t doRead(
- Mutexed<ProtectedState>::Locked &me, MediaBuffer **buffer, const ReadOptions *options);
+ Mutexed<ProtectedState>::Locked &me, MediaBufferBase **buffer,
+ const ReadOptions *options);
};
} // namespace android
diff --git a/media/libstagefright/include/media/stagefright/SurfaceMediaSource.h b/media/libstagefright/include/media/stagefright/SurfaceMediaSource.h
deleted file mode 100644
index 2e495f9..0000000
--- a/media/libstagefright/include/media/stagefright/SurfaceMediaSource.h
+++ /dev/null
@@ -1,248 +0,0 @@
-/*
- * Copyright (C) 2011 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_GUI_SURFACEMEDIASOURCE_H
-#define ANDROID_GUI_SURFACEMEDIASOURCE_H
-
-#include <gui/IGraphicBufferProducer.h>
-#include <gui/BufferQueue.h>
-
-#include <utils/threads.h>
-#include <utils/Vector.h>
-#include <media/MediaSource.h>
-#include <media/stagefright/MediaBuffer.h>
-
-#include <media/hardware/MetadataBufferType.h>
-
-#include "foundation/ABase.h"
-
-namespace android {
-// ----------------------------------------------------------------------------
-
-class String8;
-class GraphicBuffer;
-
-// ASSUMPTIONS
-// 1. SurfaceMediaSource is initialized with width*height which
-// can never change. However, deqeueue buffer does not currently
-// enforce this as in BufferQueue, dequeue can be used by Surface
-// which can modify the default width and heght. Also neither the width
-// nor height can be 0.
-// 2. setSynchronousMode is never used (basically no one should call
-// setSynchronousMode(false)
-// 3. setCrop, setTransform, setScalingMode should never be used
-// 4. queueBuffer returns a filled buffer to the SurfaceMediaSource. In addition, a
-// timestamp must be provided for the buffer. The timestamp is in
-// nanoseconds, and must be monotonically increasing. Its other semantics
-// (zero point, etc) are client-dependent and should be documented by the
-// client.
-// 5. Once disconnected, SurfaceMediaSource can be reused (can not
-// connect again)
-// 6. Stop is a hard stop, the last few frames held by the encoder
-// may be dropped. It is possible to wait for the buffers to be
-// returned (but not implemented)
-
-#define DEBUG_PENDING_BUFFERS 0
-
-class SurfaceMediaSource : public MediaSource,
- public MediaBufferObserver,
- protected ConsumerListener {
-public:
- enum { MIN_UNDEQUEUED_BUFFERS = 4};
-
- struct FrameAvailableListener : public virtual RefBase {
- // onFrameAvailable() is called from queueBuffer() is the FIFO is
- // empty. You can use SurfaceMediaSource::getQueuedCount() to
- // figure out if there are more frames waiting.
- // This is called without any lock held can be called concurrently by
- // multiple threads.
- virtual void onFrameAvailable() = 0;
- };
-
- SurfaceMediaSource(uint32_t bufferWidth, uint32_t bufferHeight);
-
- virtual ~SurfaceMediaSource();
-
- // For the MediaSource interface for use by StageFrightRecorder:
- virtual status_t start(MetaData *params = NULL);
- virtual status_t stop();
- virtual status_t read(MediaBuffer **buffer,
- const ReadOptions *options = NULL);
- virtual sp<MetaData> getFormat();
-
- // Get / Set the frame rate used for encoding. Default fps = 30
- status_t setFrameRate(int32_t fps) ;
- int32_t getFrameRate( ) const;
-
- // The call for the StageFrightRecorder to tell us that
- // it is done using the MediaBuffer data so that its state
- // can be set to FREE for dequeuing
- virtual void signalBufferReturned(MediaBuffer* buffer);
- // end of MediaSource interface
-
- // getTimestamp retrieves the timestamp associated with the image
- // set by the most recent call to read()
- //
- // The timestamp is in nanoseconds, and is monotonically increasing. Its
- // other semantics (zero point, etc) are source-dependent and should be
- // documented by the source.
- int64_t getTimestamp();
-
- // setFrameAvailableListener sets the listener object that will be notified
- // when a new frame becomes available.
- void setFrameAvailableListener(const sp<FrameAvailableListener>& listener);
-
- // dump our state in a String
- void dumpState(String8& result) const;
- void dumpState(String8& result, const char* prefix, char* buffer,
- size_t SIZE) const;
-
- // metaDataStoredInVideoBuffers tells the encoder what kind of metadata
- // is passed through the buffers. Currently, it is set to ANWBuffer
- MetadataBufferType metaDataStoredInVideoBuffers() const;
-
- sp<IGraphicBufferProducer> getProducer() const { return mProducer; }
-
- // To be called before start()
- status_t setMaxAcquiredBufferCount(size_t count);
-
- // To be called before start()
- status_t setUseAbsoluteTimestamps();
-
-protected:
-
- // Implementation of the BufferQueue::ConsumerListener interface. These
- // calls are used to notify the Surface of asynchronous events in the
- // BufferQueue.
- virtual void onFrameAvailable(const BufferItem& item);
-
- // Used as a hook to BufferQueue::disconnect()
- // This is called by the client side when it is done
- // TODO: Currently, this also sets mStopped to true which
- // is needed for unblocking the encoder which might be
- // waiting to read more frames. So if on the client side,
- // the same thread supplies the frames and also calls stop
- // on the encoder, the client has to call disconnect before
- // it calls stop.
- // In the case of the camera,
- // that need not be required since the thread supplying the
- // frames is separate than the one calling stop.
- virtual void onBuffersReleased();
-
- // SurfaceMediaSource can't handle sideband streams, so this is not expected
- // to ever be called. Does nothing.
- virtual void onSidebandStreamChanged();
-
- static bool isExternalFormat(uint32_t format);
-
-private:
- // A BufferQueue, represented by these interfaces, is the exchange point
- // between the producer and this consumer
- sp<IGraphicBufferProducer> mProducer;
- sp<IGraphicBufferConsumer> mConsumer;
-
- struct SlotData {
- sp<GraphicBuffer> mGraphicBuffer;
- uint64_t mFrameNumber;
- };
-
- // mSlots caches GraphicBuffers and frameNumbers from the buffer queue
- SlotData mSlots[BufferQueue::NUM_BUFFER_SLOTS];
-
- // The permenent width and height of SMS buffers
- int mWidth;
- int mHeight;
-
- // mCurrentSlot is the buffer slot index of the buffer that is currently
- // being used by buffer consumer
- // (e.g. StageFrightRecorder in the case of SurfaceMediaSource or GLTexture
- // in the case of Surface).
- // It is initialized to INVALID_BUFFER_SLOT,
- // indicating that no buffer slot is currently bound to the texture. Note,
- // however, that a value of INVALID_BUFFER_SLOT does not necessarily mean
- // that no buffer is bound to the texture. A call to setBufferCount will
- // reset mCurrentTexture to INVALID_BUFFER_SLOT.
- int mCurrentSlot;
-
- // mCurrentBuffers is a list of the graphic buffers that are being used by
- // buffer consumer (i.e. the video encoder). It's possible that these
- // buffers are not associated with any buffer slots, so we must track them
- // separately. Buffers are added to this list in read, and removed from
- // this list in signalBufferReturned
- Vector<sp<GraphicBuffer> > mCurrentBuffers;
-
- size_t mNumPendingBuffers;
-
-#if DEBUG_PENDING_BUFFERS
- Vector<MediaBuffer *> mPendingBuffers;
-#endif
-
- // mCurrentTimestamp is the timestamp for the current texture. It
- // gets set to mLastQueuedTimestamp each time updateTexImage is called.
- int64_t mCurrentTimestamp;
-
- // mFrameAvailableListener is the listener object that will be called when a
- // new frame becomes available. If it is not NULL it will be called from
- // queueBuffer.
- sp<FrameAvailableListener> mFrameAvailableListener;
-
- // mMutex is the mutex used to prevent concurrent access to the member
- // variables of SurfaceMediaSource objects. It must be locked whenever the
- // member variables are accessed.
- mutable Mutex mMutex;
-
- ////////////////////////// For MediaSource
- // Set to a default of 30 fps if not specified by the client side
- int32_t mFrameRate;
-
- // mStarted is a flag to check if the recording is going on
- bool mStarted;
-
- // mNumFramesReceived indicates the number of frames recieved from
- // the client side
- int mNumFramesReceived;
- // mNumFramesEncoded indicates the number of frames passed on to the
- // encoder
- int mNumFramesEncoded;
-
- // mFirstFrameTimestamp is the timestamp of the first received frame.
- // It is used to offset the output timestamps so recording starts at time 0.
- int64_t mFirstFrameTimestamp;
- // mStartTimeNs is the start time passed into the source at start, used to
- // offset timestamps.
- int64_t mStartTimeNs;
-
- size_t mMaxAcquiredBufferCount;
-
- bool mUseAbsoluteTimestamps;
-
- // mFrameAvailableCondition condition used to indicate whether there
- // is a frame available for dequeuing
- Condition mFrameAvailableCondition;
-
- Condition mMediaBuffersAvailableCondition;
-
- // Allocate and return a new MediaBuffer and pass the ANW buffer as metadata into it.
- void passMetadataBuffer_l(MediaBuffer **buffer, ANativeWindowBuffer *bufferHandle) const;
-
- // Avoid copying and equating and default constructor
- DISALLOW_EVIL_CONSTRUCTORS(SurfaceMediaSource);
-};
-
-// ----------------------------------------------------------------------------
-}; // namespace android
-
-#endif // ANDROID_GUI_SURFACEMEDIASOURCE_H
diff --git a/media/libstagefright/mpeg2ts/ATSParser.cpp b/media/libstagefright/mpeg2ts/ATSParser.cpp
index 9b12b2d..bef2db4 100644
--- a/media/libstagefright/mpeg2ts/ATSParser.cpp
+++ b/media/libstagefright/mpeg2ts/ATSParser.cpp
@@ -26,6 +26,7 @@
#include <binder/IMemory.h>
#include <binder/MemoryDealer.h>
#include <cutils/native_handle.h>
+#include <hidlmemory/FrameworkUtils.h>
#include <media/stagefright/foundation/ABitReader.h>
#include <media/stagefright/foundation/ABuffer.h>
#include <media/stagefright/foundation/ADebug.h>
@@ -44,10 +45,10 @@
#include <inttypes.h>
namespace android {
-using hardware::hidl_handle;
-using hardware::hidl_memory;
+using hardware::fromHeap;
using hardware::hidl_string;
using hardware::hidl_vec;
+using hardware::HidlMemory;
using namespace hardware::cas::V1_0;
using namespace hardware::cas::native::V1_0;
@@ -210,6 +211,7 @@
sp<AMessage> mSampleAesKeyItem;
sp<IMemory> mMem;
sp<MemoryDealer> mDealer;
+ sp<HidlMemory> mHidlMemory;
hardware::cas::native::V1_0::SharedBuffer mDescramblerSrcBuffer;
sp<ABuffer> mDescrambledBuffer;
List<SubSampleInfo> mSubSamples;
@@ -852,14 +854,9 @@
if (heap == NULL) {
return false;
}
- native_handle_t* nativeHandle = native_handle_create(1, 0);
- if (!nativeHandle) {
- ALOGE("[stream %d] failed to create native handle", mElementaryPID);
- return false;
- }
- nativeHandle->data[0] = heap->getHeapID();
- mDescramblerSrcBuffer.heapBase = hidl_memory("ashmem",
- hidl_handle(nativeHandle), heap->getSize());
+
+ mHidlMemory = fromHeap(heap);
+ mDescramblerSrcBuffer.heapBase = *mHidlMemory;
mDescramblerSrcBuffer.offset = (uint64_t) offset;
mDescramblerSrcBuffer.size = (uint64_t) size;
diff --git a/media/libstagefright/mpeg2ts/Android.bp b/media/libstagefright/mpeg2ts/Android.bp
index 0b2a48f..fbf1496 100644
--- a/media/libstagefright/mpeg2ts/Android.bp
+++ b/media/libstagefright/mpeg2ts/Android.bp
@@ -33,7 +33,7 @@
shared_libs: [
"libcrypto",
"libmedia",
- "libhidlmemory",
+ "libhidlallocatorutils",
"android.hardware.cas.native@1.0",
"android.hidl.memory@1.0",
],
diff --git a/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp b/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp
index 1dac171..8488d10 100644
--- a/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp
+++ b/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp
@@ -163,7 +163,7 @@
}
status_t AnotherPacketSource::read(
- MediaBuffer **out, const ReadOptions *) {
+ MediaBufferBase **out, const ReadOptions *) {
*out = NULL;
Mutex::Autolock autoLock(mLock);
@@ -202,24 +202,24 @@
seg.mMaxDequeTimeUs = timeUs;
}
- MediaBuffer *mediaBuffer = new MediaBuffer(buffer);
- sp<MetaData> bufmeta = mediaBuffer->meta_data();
+ MediaBufferBase *mediaBuffer = new MediaBuffer(buffer);
+ MetaDataBase &bufmeta = mediaBuffer->meta_data();
- bufmeta->setInt64(kKeyTime, timeUs);
+ bufmeta.setInt64(kKeyTime, timeUs);
int32_t isSync;
if (buffer->meta()->findInt32("isSync", &isSync)) {
- bufmeta->setInt32(kKeyIsSyncFrame, isSync);
+ bufmeta.setInt32(kKeyIsSyncFrame, isSync);
}
sp<ABuffer> sei;
if (buffer->meta()->findBuffer("sei", &sei) && sei != NULL) {
- bufmeta->setData(kKeySEI, 0, sei->data(), sei->size());
+ bufmeta.setData(kKeySEI, 0, sei->data(), sei->size());
}
sp<ABuffer> mpegUserData;
if (buffer->meta()->findBuffer("mpegUserData", &mpegUserData) && mpegUserData != NULL) {
- bufmeta->setData(
+ bufmeta.setData(
kKeyMpegUserData, 0, mpegUserData->data(), mpegUserData->size());
}
@@ -234,18 +234,18 @@
CHECK(buffer->meta()->findBuffer("encBytes", &encBytesBuffer)
&& encBytesBuffer != NULL);
- bufmeta->setInt32(kKeyCryptoMode, cryptoMode);
+ bufmeta.setInt32(kKeyCryptoMode, cryptoMode);
uint8_t array[16] = {0};
- bufmeta->setData(kKeyCryptoIV, 0, array, 16);
+ bufmeta.setData(kKeyCryptoIV, 0, array, 16);
array[0] = (uint8_t) (cryptoKey & 0xff);
- bufmeta->setData(kKeyCryptoKey, 0, array, 16);
+ bufmeta.setData(kKeyCryptoKey, 0, array, 16);
- bufmeta->setData(kKeyPlainSizes, 0,
+ bufmeta.setData(kKeyPlainSizes, 0,
clearBytesBuffer->data(), clearBytesBuffer->size());
- bufmeta->setData(kKeyEncryptedSizes, 0,
+ bufmeta.setData(kKeyEncryptedSizes, 0,
encBytesBuffer->data(), encBytesBuffer->size());
}
diff --git a/media/libstagefright/mpeg2ts/AnotherPacketSource.h b/media/libstagefright/mpeg2ts/AnotherPacketSource.h
index 3abd573..f4a6acb 100644
--- a/media/libstagefright/mpeg2ts/AnotherPacketSource.h
+++ b/media/libstagefright/mpeg2ts/AnotherPacketSource.h
@@ -39,7 +39,7 @@
virtual sp<MetaData> getFormat();
virtual status_t read(
- MediaBuffer **buffer, const ReadOptions *options = NULL);
+ MediaBufferBase **buffer, const ReadOptions *options = NULL);
void clear();
diff --git a/media/libstagefright/mpeg2ts/ESQueue.cpp b/media/libstagefright/mpeg2ts/ESQueue.cpp
index 850face..59fba1a 100644
--- a/media/libstagefright/mpeg2ts/ESQueue.cpp
+++ b/media/libstagefright/mpeg2ts/ESQueue.cpp
@@ -633,7 +633,10 @@
mBuffer->setRange(0, mBuffer->size() - info.mLength);
if (mFormat == NULL) {
- mFormat = MakeAVCCodecSpecificData(accessUnit);
+ mFormat = new MetaData;
+ if (!MakeAVCCodecSpecificData(*mFormat, accessUnit)) {
+ mFormat.clear();
+ }
}
return accessUnit;
@@ -862,7 +865,7 @@
}
bits.skipBits(2); // original_copy, home
- mFormat = MakeAACCodecSpecificData(
+ MakeAACCodecSpecificData(*mFormat,
profile, sampling_freq_index, channel_configuration);
mFormat->setInt32(kKeyIsADTS, true);
@@ -1005,9 +1008,9 @@
return NULL;
}
if (mFormat == NULL) {
- mFormat = MakeAVCCodecSpecificData(mBuffer);
- if (mFormat == NULL) {
- ALOGI("Creating dummy AVC format for scrambled content");
+ mFormat = new MetaData;
+ if (!MakeAVCCodecSpecificData(*mFormat, mBuffer)) {
+ ALOGW("Creating dummy AVC format for scrambled content");
mFormat = new MetaData;
mFormat->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_AVC);
mFormat->setInt32(kKeyWidth, 1280);
@@ -1167,7 +1170,10 @@
}
if (mFormat == NULL) {
- mFormat = MakeAVCCodecSpecificData(accessUnit);
+ mFormat = new MetaData;
+ if (!MakeAVCCodecSpecificData(*mFormat, accessUnit)) {
+ mFormat.clear();
+ }
}
if (mSampleDecryptor != NULL && shrunkBytes > 0) {
diff --git a/media/libstagefright/omx/tests/OMXHarness.cpp b/media/libstagefright/omx/tests/OMXHarness.cpp
index 86c7211..895a4ce 100644
--- a/media/libstagefright/omx/tests/OMXHarness.cpp
+++ b/media/libstagefright/omx/tests/OMXHarness.cpp
@@ -658,7 +658,7 @@
requestedSeekTimeUs, requestedSeekTimeUs / 1E6);
}
- MediaBuffer *buffer = NULL;
+ MediaBufferBase *buffer = NULL;
options.setSeekTo(
requestedSeekTimeUs, MediaSource::ReadOptions::SEEK_NEXT_SYNC);
@@ -667,7 +667,7 @@
actualSeekTimeUs = -1;
} else {
CHECK(buffer != NULL);
- CHECK(buffer->meta_data()->findInt64(kKeyTime, &actualSeekTimeUs));
+ CHECK(buffer->meta_data().findInt64(kKeyTime, &actualSeekTimeUs));
CHECK(actualSeekTimeUs >= 0);
buffer->release();
@@ -679,7 +679,7 @@
}
status_t err;
- MediaBuffer *buffer;
+ MediaBufferBase *buffer;
for (;;) {
err = codec->read(&buffer, &options);
options.clearSeekTo();
@@ -728,7 +728,7 @@
CHECK(buffer != NULL);
int64_t bufferTimeUs;
- CHECK(buffer->meta_data()->findInt64(kKeyTime, &bufferTimeUs));
+ CHECK(buffer->meta_data().findInt64(kKeyTime, &bufferTimeUs));
if (!CloseEnough(bufferTimeUs, actualSeekTimeUs)) {
printf("\n * Attempted seeking to %" PRId64 " us (%.2f secs)",
requestedSeekTimeUs, requestedSeekTimeUs / 1E6);
diff --git a/media/libstagefright/rtsp/APacketSource.cpp b/media/libstagefright/rtsp/APacketSource.cpp
index 68f8bdd..201a5df 100644
--- a/media/libstagefright/rtsp/APacketSource.cpp
+++ b/media/libstagefright/rtsp/APacketSource.cpp
@@ -219,7 +219,7 @@
return csd;
}
-sp<ABuffer> MakeAACCodecSpecificData(const char *params) {
+static sp<ABuffer> MakeAACCodecSpecificData(const char *params) {
AString val;
CHECK(GetAttribute(params, "config", &val));
@@ -257,7 +257,7 @@
}
// From mpeg4-generic configuration data.
-sp<ABuffer> MakeAACCodecSpecificData2(const char *params) {
+static sp<ABuffer> MakeAACCodecSpecificData2(const char *params) {
AString val;
unsigned long objectType;
if (GetAttribute(params, "objectType", &val)) {
diff --git a/media/libstagefright/rtsp/ARTPWriter.cpp b/media/libstagefright/rtsp/ARTPWriter.cpp
index 8604b69..0667df1 100644
--- a/media/libstagefright/rtsp/ARTPWriter.cpp
+++ b/media/libstagefright/rtsp/ARTPWriter.cpp
@@ -173,7 +173,7 @@
return OK;
}
-static void StripStartcode(MediaBuffer *buffer) {
+static void StripStartcode(MediaBufferBase *buffer) {
if (buffer->range_length() < 4) {
return;
}
@@ -195,7 +195,7 @@
#if 0
if (mMode == H264) {
- MediaBuffer *buffer;
+ MediaBufferBase *buffer;
CHECK_EQ(mSource->read(&buffer), (status_t)OK);
StripStartcode(buffer);
@@ -265,7 +265,7 @@
}
void ARTPWriter::onRead(const sp<AMessage> &msg) {
- MediaBuffer *mediaBuf;
+ MediaBufferBase *mediaBuf;
status_t err = mSource->read(&mediaBuf);
if (err != OK) {
@@ -523,7 +523,7 @@
ALOGI("%s", sdp.c_str());
}
-void ARTPWriter::makeH264SPropParamSets(MediaBuffer *buffer) {
+void ARTPWriter::makeH264SPropParamSets(MediaBufferBase *buffer) {
static const char kStartCode[] = "\x00\x00\x00\x01";
const uint8_t *data =
@@ -567,12 +567,12 @@
send(buffer, true /* isRTCP */);
}
-void ARTPWriter::sendAVCData(MediaBuffer *mediaBuf) {
+void ARTPWriter::sendAVCData(MediaBufferBase *mediaBuf) {
// 12 bytes RTP header + 2 bytes for the FU-indicator and FU-header.
CHECK_GE(kMaxPacketSize, 12u + 2u);
int64_t timeUs;
- CHECK(mediaBuf->meta_data()->findInt64(kKeyTime, &timeUs));
+ CHECK(mediaBuf->meta_data().findInt64(kKeyTime, &timeUs));
uint32_t rtpTime = mRTPTimeBase + (timeUs * 9 / 100ll);
@@ -663,11 +663,11 @@
mLastNTPTime = GetNowNTP();
}
-void ARTPWriter::sendH263Data(MediaBuffer *mediaBuf) {
+void ARTPWriter::sendH263Data(MediaBufferBase *mediaBuf) {
CHECK_GE(kMaxPacketSize, 12u + 2u);
int64_t timeUs;
- CHECK(mediaBuf->meta_data()->findInt64(kKeyTime, &timeUs));
+ CHECK(mediaBuf->meta_data().findInt64(kKeyTime, &timeUs));
uint32_t rtpTime = mRTPTimeBase + (timeUs * 9 / 100ll);
@@ -741,7 +741,7 @@
return frameSize;
}
-void ARTPWriter::sendAMRData(MediaBuffer *mediaBuf) {
+void ARTPWriter::sendAMRData(MediaBufferBase *mediaBuf) {
const uint8_t *mediaData =
(const uint8_t *)mediaBuf->data() + mediaBuf->range_offset();
@@ -752,7 +752,7 @@
const bool isWide = (mMode == AMR_WB);
int64_t timeUs;
- CHECK(mediaBuf->meta_data()->findInt64(kKeyTime, &timeUs));
+ CHECK(mediaBuf->meta_data().findInt64(kKeyTime, &timeUs));
uint32_t rtpTime = mRTPTimeBase + (timeUs / (isWide ? 250 : 125));
// hexdump(mediaData, mediaLength);
diff --git a/media/libstagefright/rtsp/ARTPWriter.h b/media/libstagefright/rtsp/ARTPWriter.h
index 92a64f2..2f13486 100644
--- a/media/libstagefright/rtsp/ARTPWriter.h
+++ b/media/libstagefright/rtsp/ARTPWriter.h
@@ -110,13 +110,13 @@
void addSR(const sp<ABuffer> &buffer);
void addSDES(const sp<ABuffer> &buffer);
- void makeH264SPropParamSets(MediaBuffer *buffer);
+ void makeH264SPropParamSets(MediaBufferBase *buffer);
void dumpSessionDesc();
void sendBye();
- void sendAVCData(MediaBuffer *mediaBuf);
- void sendH263Data(MediaBuffer *mediaBuf);
- void sendAMRData(MediaBuffer *mediaBuf);
+ void sendAVCData(MediaBufferBase *mediaBuf);
+ void sendH263Data(MediaBufferBase *mediaBuf);
+ void sendAMRData(MediaBufferBase *mediaBuf);
void send(const sp<ABuffer> &buffer, bool isRTCP);
diff --git a/media/libstagefright/tests/Android.bp b/media/libstagefright/tests/Android.bp
index e67a949..be10fdc 100644
--- a/media/libstagefright/tests/Android.bp
+++ b/media/libstagefright/tests/Android.bp
@@ -1,46 +1,6 @@
// Build the unit tests.
cc_test {
- name: "SurfaceMediaSource_test",
-
- srcs: [
- "SurfaceMediaSource_test.cpp",
- "DummyRecorder.cpp",
- ],
-
- shared_libs: [
- "libEGL",
- "libGLESv2",
- "libbinder",
- "libcutils",
- "libgui",
- "libmedia",
- "libmediaextractor",
- "libstagefright",
- "libstagefright_foundation",
- "libstagefright_omx",
- "libsync",
- "libui",
- "libutils",
- "liblog",
- ],
-
- include_dirs: [
- "frameworks/av/media/libstagefright",
- "frameworks/av/media/libstagefright/include",
- "frameworks/native/include/media/openmax",
- "frameworks/native/include/media/hardware",
- ],
-
- cflags: [
- "-Werror",
- "-Wall",
- ],
-
- compile_multilib: "32",
-}
-
-cc_test {
name: "MediaCodecListOverrides_test",
srcs: ["MediaCodecListOverrides_test.cpp"],
diff --git a/media/libstagefright/tests/DummyRecorder.cpp b/media/libstagefright/tests/DummyRecorder.cpp
deleted file mode 100644
index 4f560cb..0000000
--- a/media/libstagefright/tests/DummyRecorder.cpp
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * Copyright (C) 2011 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 "DummyRecorder"
-// #define LOG_NDEBUG 0
-
-#include <media/MediaSource.h>
-#include <media/stagefright/MediaBuffer.h>
-#include "DummyRecorder.h"
-
-#include <utils/Log.h>
-
-namespace android {
-
-// static
-void *DummyRecorder::threadWrapper(void *pthis) {
- ALOGV("ThreadWrapper: %p", pthis);
- DummyRecorder *writer = static_cast<DummyRecorder *>(pthis);
- writer->readFromSource();
- return NULL;
-}
-
-
-status_t DummyRecorder::start() {
- ALOGV("Start");
- mStarted = true;
-
- mSource->start();
-
- pthread_attr_t attr;
- pthread_attr_init(&attr);
- pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
- int err = pthread_create(&mThread, &attr, threadWrapper, this);
- pthread_attr_destroy(&attr);
-
- if (err) {
- ALOGE("Error creating thread!");
- return -ENODEV;
- }
- return OK;
-}
-
-
-status_t DummyRecorder::stop() {
- ALOGV("Stop");
- mStarted = false;
-
- mSource->stop();
- void *dummy;
- pthread_join(mThread, &dummy);
- status_t err = static_cast<status_t>(reinterpret_cast<uintptr_t>(dummy));
-
- ALOGV("Ending the reading thread");
- return err;
-}
-
-// pretend to read the source buffers
-void DummyRecorder::readFromSource() {
- ALOGV("ReadFromSource");
- if (!mStarted) {
- return;
- }
-
- status_t err = OK;
- MediaBuffer *buffer;
- ALOGV("A fake writer accessing the frames");
- while (mStarted && (err = mSource->read(&buffer)) == OK){
- // if not getting a valid buffer from source, then exit
- if (buffer == NULL) {
- return;
- }
- buffer->release();
- buffer = NULL;
- }
-}
-
-
-} // end of namespace android
diff --git a/media/libstagefright/tests/DummyRecorder.h b/media/libstagefright/tests/DummyRecorder.h
deleted file mode 100644
index 0759777..0000000
--- a/media/libstagefright/tests/DummyRecorder.h
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright (C) 2011 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 DUMMY_RECORDER_H_
-#define DUMMY_RECORDER_H_
-
-#include <pthread.h>
-#include <utils/String8.h>
-#include <media/stagefright/foundation/ABase.h>
-
-
-namespace android {
-
-struct MediaSource;
-class MediaBuffer;
-
-class DummyRecorder {
- public:
- // The media source from which this will receive frames
- sp<MediaSource> mSource;
- bool mStarted;
- pthread_t mThread;
-
- status_t start();
- status_t stop();
-
- // actual entry point for the thread
- void readFromSource();
-
- // static function to wrap the actual thread entry point
- static void *threadWrapper(void *pthis);
-
- explicit DummyRecorder(const sp<MediaSource> &source) : mSource(source)
- , mStarted(false) {}
- ~DummyRecorder( ) {}
-
- private:
-
- DISALLOW_EVIL_CONSTRUCTORS(DummyRecorder);
-};
-
-} // end of namespace android
-#endif
-
-
diff --git a/media/libstagefright/tests/SurfaceMediaSource_test.cpp b/media/libstagefright/tests/SurfaceMediaSource_test.cpp
deleted file mode 100644
index 051108f..0000000
--- a/media/libstagefright/tests/SurfaceMediaSource_test.cpp
+++ /dev/null
@@ -1,944 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-//#define LOG_NDEBUG 0
-#define LOG_TAG "SurfaceMediaSource_test"
-
-#include <gtest/gtest.h>
-#include <utils/String8.h>
-#include <utils/String16.h>
-#include <utils/Errors.h>
-#include <fcntl.h>
-#include <unistd.h>
-
-#include <GLES2/gl2.h>
-
-#include <media/stagefright/SurfaceMediaSource.h>
-#include <media/mediarecorder.h>
-
-#include <ui/GraphicBuffer.h>
-#include <gui/Surface.h>
-#include <gui/ISurfaceComposer.h>
-#include <gui/Surface.h>
-#include <gui/SurfaceComposerClient.h>
-
-#include <binder/ProcessState.h>
-
-#include <media/stagefright/foundation/ADebug.h>
-#include <OMX_Component.h>
-
-#include "DummyRecorder.h"
-
-
-namespace android {
-
-class GLTest : public ::testing::Test {
-protected:
-
- GLTest():
- mEglDisplay(EGL_NO_DISPLAY),
- mEglSurface(EGL_NO_SURFACE),
- mEglContext(EGL_NO_CONTEXT) {
- }
-
- virtual void SetUp() {
- ALOGV("GLTest::SetUp()");
- mEglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
- ASSERT_EQ(EGL_SUCCESS, eglGetError());
- ASSERT_NE(EGL_NO_DISPLAY, mEglDisplay);
-
- EGLint majorVersion;
- EGLint minorVersion;
- EXPECT_TRUE(eglInitialize(mEglDisplay, &majorVersion, &minorVersion));
- ASSERT_EQ(EGL_SUCCESS, eglGetError());
- RecordProperty("EglVersionMajor", majorVersion);
- RecordProperty("EglVersionMajor", minorVersion);
-
- EGLint numConfigs = 0;
- EXPECT_TRUE(eglChooseConfig(mEglDisplay, getConfigAttribs(), &mGlConfig,
- 1, &numConfigs));
- ASSERT_EQ(EGL_SUCCESS, eglGetError());
-
- char* displaySecsEnv = getenv("GLTEST_DISPLAY_SECS");
- if (displaySecsEnv != NULL) {
- mDisplaySecs = atoi(displaySecsEnv);
- if (mDisplaySecs < 0) {
- mDisplaySecs = 0;
- }
- } else {
- mDisplaySecs = 0;
- }
-
- if (mDisplaySecs > 0) {
- mComposerClient = new SurfaceComposerClient;
- ASSERT_EQ(NO_ERROR, mComposerClient->initCheck());
-
- mSurfaceControl = mComposerClient->createSurface(
- String8("Test Surface"),
- getSurfaceWidth(), getSurfaceHeight(),
- PIXEL_FORMAT_RGB_888, 0);
-
- ASSERT_TRUE(mSurfaceControl != NULL);
- ASSERT_TRUE(mSurfaceControl->isValid());
-
- SurfaceComposerClient::Transaction{}
- .setLayer(mSurfaceControl, 0x7FFFFFFF)
- .show(mSurfaceControl)
- .apply();
-
- sp<ANativeWindow> window = mSurfaceControl->getSurface();
- mEglSurface = eglCreateWindowSurface(mEglDisplay, mGlConfig,
- window.get(), NULL);
- } else {
- ALOGV("No actual display. Choosing EGLSurface based on SurfaceMediaSource");
- sp<IGraphicBufferProducer> sms = (new SurfaceMediaSource(
- getSurfaceWidth(), getSurfaceHeight()))->getProducer();
- sp<Surface> stc = new Surface(sms);
- sp<ANativeWindow> window = stc;
-
- mEglSurface = eglCreateWindowSurface(mEglDisplay, mGlConfig,
- window.get(), NULL);
- }
- ASSERT_EQ(EGL_SUCCESS, eglGetError());
- ASSERT_NE(EGL_NO_SURFACE, mEglSurface);
-
- mEglContext = eglCreateContext(mEglDisplay, mGlConfig, EGL_NO_CONTEXT,
- getContextAttribs());
- ASSERT_EQ(EGL_SUCCESS, eglGetError());
- ASSERT_NE(EGL_NO_CONTEXT, mEglContext);
-
- EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface,
- mEglContext));
- ASSERT_EQ(EGL_SUCCESS, eglGetError());
-
- EGLint w, h;
- EXPECT_TRUE(eglQuerySurface(mEglDisplay, mEglSurface, EGL_WIDTH, &w));
- ASSERT_EQ(EGL_SUCCESS, eglGetError());
- EXPECT_TRUE(eglQuerySurface(mEglDisplay, mEglSurface, EGL_HEIGHT, &h));
- ASSERT_EQ(EGL_SUCCESS, eglGetError());
- RecordProperty("EglSurfaceWidth", w);
- RecordProperty("EglSurfaceHeight", h);
-
- glViewport(0, 0, w, h);
- ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError());
- }
-
- virtual void TearDown() {
- // Display the result
- if (mDisplaySecs > 0 && mEglSurface != EGL_NO_SURFACE) {
- eglSwapBuffers(mEglDisplay, mEglSurface);
- sleep(mDisplaySecs);
- }
-
- if (mComposerClient != NULL) {
- mComposerClient->dispose();
- }
- if (mEglContext != EGL_NO_CONTEXT) {
- eglDestroyContext(mEglDisplay, mEglContext);
- }
- if (mEglSurface != EGL_NO_SURFACE) {
- eglDestroySurface(mEglDisplay, mEglSurface);
- }
- if (mEglDisplay != EGL_NO_DISPLAY) {
- eglTerminate(mEglDisplay);
- }
- ASSERT_EQ(EGL_SUCCESS, eglGetError());
- }
-
- virtual EGLint const* getConfigAttribs() {
- ALOGV("GLTest getConfigAttribs");
- static EGLint sDefaultConfigAttribs[] = {
- EGL_SURFACE_TYPE, EGL_PBUFFER_BIT,
- EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
- EGL_RED_SIZE, 8,
- EGL_GREEN_SIZE, 8,
- EGL_BLUE_SIZE, 8,
- EGL_ALPHA_SIZE, 8,
- EGL_DEPTH_SIZE, 16,
- EGL_STENCIL_SIZE, 8,
- EGL_NONE };
-
- return sDefaultConfigAttribs;
- }
-
- virtual EGLint const* getContextAttribs() {
- static EGLint sDefaultContextAttribs[] = {
- EGL_CONTEXT_CLIENT_VERSION, 2,
- EGL_NONE };
-
- return sDefaultContextAttribs;
- }
-
- virtual EGLint getSurfaceWidth() {
- return 512;
- }
-
- virtual EGLint getSurfaceHeight() {
- return 512;
- }
-
- void loadShader(GLenum shaderType, const char* pSource, GLuint* outShader) {
- GLuint shader = glCreateShader(shaderType);
- ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError());
- if (shader) {
- glShaderSource(shader, 1, &pSource, NULL);
- ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError());
- glCompileShader(shader);
- ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError());
- GLint compiled = 0;
- glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);
- ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError());
- if (!compiled) {
- GLint infoLen = 0;
- glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLen);
- ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError());
- if (infoLen) {
- char* buf = (char*) malloc(infoLen);
- if (buf) {
- glGetShaderInfoLog(shader, infoLen, NULL, buf);
- printf("Shader compile log:\n%s\n", buf);
- free(buf);
- FAIL();
- }
- } else {
- char* buf = (char*) malloc(0x1000);
- if (buf) {
- glGetShaderInfoLog(shader, 0x1000, NULL, buf);
- printf("Shader compile log:\n%s\n", buf);
- free(buf);
- FAIL();
- }
- }
- glDeleteShader(shader);
- shader = 0;
- }
- }
- ASSERT_TRUE(shader != 0);
- *outShader = shader;
- }
-
- void createProgram(const char* pVertexSource, const char* pFragmentSource,
- GLuint* outPgm) {
- GLuint vertexShader, fragmentShader;
- {
- SCOPED_TRACE("compiling vertex shader");
- loadShader(GL_VERTEX_SHADER, pVertexSource, &vertexShader);
- if (HasFatalFailure()) {
- return;
- }
- }
- {
- SCOPED_TRACE("compiling fragment shader");
- loadShader(GL_FRAGMENT_SHADER, pFragmentSource, &fragmentShader);
- if (HasFatalFailure()) {
- return;
- }
- }
-
- GLuint program = glCreateProgram();
- ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError());
- if (program) {
- glAttachShader(program, vertexShader);
- ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError());
- glAttachShader(program, fragmentShader);
- ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError());
- glLinkProgram(program);
- GLint linkStatus = GL_FALSE;
- glGetProgramiv(program, GL_LINK_STATUS, &linkStatus);
- if (linkStatus != GL_TRUE) {
- GLint bufLength = 0;
- glGetProgramiv(program, GL_INFO_LOG_LENGTH, &bufLength);
- if (bufLength) {
- char* buf = (char*) malloc(bufLength);
- if (buf) {
- glGetProgramInfoLog(program, bufLength, NULL, buf);
- printf("Program link log:\n%s\n", buf);
- free(buf);
- FAIL();
- }
- }
- glDeleteProgram(program);
- program = 0;
- }
- }
- glDeleteShader(vertexShader);
- glDeleteShader(fragmentShader);
- ASSERT_TRUE(program != 0);
- *outPgm = program;
- }
-
- static int abs(int value) {
- return value > 0 ? value : -value;
- }
-
- ::testing::AssertionResult checkPixel(int x, int y, int r,
- int g, int b, int a, int tolerance=2) {
- GLubyte pixel[4];
- String8 msg;
- glReadPixels(x, y, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, pixel);
- GLenum err = glGetError();
- if (err != GL_NO_ERROR) {
- msg += String8::format("error reading pixel: %#x", err);
- while ((err = glGetError()) != GL_NO_ERROR) {
- msg += String8::format(", %#x", err);
- }
- fprintf(stderr, "pixel check failure: %s\n", msg.string());
- return ::testing::AssertionFailure(
- ::testing::Message(msg.string()));
- }
- if (r >= 0 && abs(r - int(pixel[0])) > tolerance) {
- msg += String8::format("r(%d isn't %d)", pixel[0], r);
- }
- if (g >= 0 && abs(g - int(pixel[1])) > tolerance) {
- if (!msg.isEmpty()) {
- msg += " ";
- }
- msg += String8::format("g(%d isn't %d)", pixel[1], g);
- }
- if (b >= 0 && abs(b - int(pixel[2])) > tolerance) {
- if (!msg.isEmpty()) {
- msg += " ";
- }
- msg += String8::format("b(%d isn't %d)", pixel[2], b);
- }
- if (a >= 0 && abs(a - int(pixel[3])) > tolerance) {
- if (!msg.isEmpty()) {
- msg += " ";
- }
- msg += String8::format("a(%d isn't %d)", pixel[3], a);
- }
- if (!msg.isEmpty()) {
- fprintf(stderr, "pixel check failure: %s\n", msg.string());
- return ::testing::AssertionFailure(
- ::testing::Message(msg.string()));
- } else {
- return ::testing::AssertionSuccess();
- }
- }
-
- int mDisplaySecs;
- sp<SurfaceComposerClient> mComposerClient;
- sp<SurfaceControl> mSurfaceControl;
-
- EGLDisplay mEglDisplay;
- EGLSurface mEglSurface;
- EGLContext mEglContext;
- EGLConfig mGlConfig;
-};
-
-///////////////////////////////////////////////////////////////////////
-// Class for the NON-GL tests
-///////////////////////////////////////////////////////////////////////
-class SurfaceMediaSourceTest : public ::testing::Test {
-public:
-
- SurfaceMediaSourceTest( ): mYuvTexWidth(176), mYuvTexHeight(144) { }
- void oneBufferPass(int width, int height );
- void oneBufferPassNoFill(int width, int height );
- static void fillYV12Buffer(uint8_t* buf, int w, int h, int stride) ;
- static void fillYV12BufferRect(uint8_t* buf, int w, int h,
- int stride, const android_native_rect_t& rect) ;
-protected:
-
- virtual void SetUp() {
- android::ProcessState::self()->startThreadPool();
- mSMS = new SurfaceMediaSource(mYuvTexWidth, mYuvTexHeight);
- mSTC = new Surface(mSMS->getProducer());
- mANW = mSTC;
- }
-
- virtual void TearDown() {
- mSMS.clear();
- mSTC.clear();
- mANW.clear();
- }
-
- const int mYuvTexWidth;
- const int mYuvTexHeight;
-
- sp<SurfaceMediaSource> mSMS;
- sp<Surface> mSTC;
- sp<ANativeWindow> mANW;
-};
-
-///////////////////////////////////////////////////////////////////////
-// Class for the GL tests
-///////////////////////////////////////////////////////////////////////
-class SurfaceMediaSourceGLTest : public GLTest {
-public:
-
- SurfaceMediaSourceGLTest( ): mYuvTexWidth(176), mYuvTexHeight(144) { }
- virtual EGLint const* getConfigAttribs();
- void oneBufferPassGL(int num = 0);
- static sp<MediaRecorder> setUpMediaRecorder(int fileDescriptor, int videoSource,
- int outputFormat, int videoEncoder, int width, int height, int fps);
-protected:
-
- virtual void SetUp() {
- ALOGV("SMS-GLTest::SetUp()");
- android::ProcessState::self()->startThreadPool();
- mSMS = new SurfaceMediaSource(mYuvTexWidth, mYuvTexHeight);
- mSTC = new Surface(mSMS->getProducer());
- mANW = mSTC;
-
- // Doing the setup related to the GL Side
- GLTest::SetUp();
- }
-
- virtual void TearDown() {
- mSMS.clear();
- mSTC.clear();
- mANW.clear();
- GLTest::TearDown();
- }
-
- void setUpEGLSurfaceFromMediaRecorder(sp<MediaRecorder>& mr);
-
- const int mYuvTexWidth;
- const int mYuvTexHeight;
-
- sp<SurfaceMediaSource> mSMS;
- sp<Surface> mSTC;
- sp<ANativeWindow> mANW;
-};
-
-/////////////////////////////////////////////////////////////////////
-// Methods in SurfaceMediaSourceGLTest
-/////////////////////////////////////////////////////////////////////
-EGLint const* SurfaceMediaSourceGLTest::getConfigAttribs() {
- ALOGV("SurfaceMediaSourceGLTest getConfigAttribs");
- static EGLint sDefaultConfigAttribs[] = {
- EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
- EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
- EGL_RED_SIZE, 8,
- EGL_GREEN_SIZE, 8,
- EGL_BLUE_SIZE, 8,
- EGL_RECORDABLE_ANDROID, EGL_TRUE,
- EGL_NONE };
-
- return sDefaultConfigAttribs;
-}
-
-// One pass of dequeuing and queuing a GLBuffer
-void SurfaceMediaSourceGLTest::oneBufferPassGL(int num) {
- int d = num % 50;
- float f = 0.2f; // 0.1f * d;
-
- glClearColor(0, 0.3, 0, 0.6);
- glClear(GL_COLOR_BUFFER_BIT);
-
- glEnable(GL_SCISSOR_TEST);
- glScissor(4 + d, 4 + d, 4, 4);
- glClearColor(1.0 - f, f, f, 1.0);
- glClear(GL_COLOR_BUFFER_BIT);
-
- glScissor(24 + d, 48 + d, 4, 4);
- glClearColor(f, 1.0 - f, f, 1.0);
- glClear(GL_COLOR_BUFFER_BIT);
-
- glScissor(37 + d, 17 + d, 4, 4);
- glClearColor(f, f, 1.0 - f, 1.0);
- glClear(GL_COLOR_BUFFER_BIT);
-
- // The following call dequeues and queues the buffer
- eglSwapBuffers(mEglDisplay, mEglSurface);
- ASSERT_EQ(EGL_SUCCESS, eglGetError());
- glDisable(GL_SCISSOR_TEST);
-}
-
-// Set up the MediaRecorder which runs in the same process as mediaserver
-sp<MediaRecorder> SurfaceMediaSourceGLTest::setUpMediaRecorder(int fd, int videoSource,
- int outputFormat, int videoEncoder, int width, int height, int fps) {
- sp<MediaRecorder> mr = new MediaRecorder(String16());
- mr->setVideoSource(videoSource);
- mr->setOutputFormat(outputFormat);
- mr->setVideoEncoder(videoEncoder);
- mr->setOutputFile(fd);
- mr->setVideoSize(width, height);
- mr->setVideoFrameRate(fps);
- mr->prepare();
- ALOGV("Starting MediaRecorder...");
- CHECK_EQ((status_t)OK, mr->start());
- return mr;
-}
-
-// query the mediarecorder for a surfacemeidasource and create an egl surface with that
-void SurfaceMediaSourceGLTest::setUpEGLSurfaceFromMediaRecorder(sp<MediaRecorder>& mr) {
- sp<IGraphicBufferProducer> iST = mr->querySurfaceMediaSourceFromMediaServer();
- mSTC = new Surface(iST);
- mANW = mSTC;
-
- if (mEglSurface != EGL_NO_SURFACE) {
- EXPECT_TRUE(eglDestroySurface(mEglDisplay, mEglSurface));
- mEglSurface = EGL_NO_SURFACE;
- }
- mEglSurface = eglCreateWindowSurface(mEglDisplay, mGlConfig,
- mANW.get(), NULL);
- ASSERT_EQ(EGL_SUCCESS, eglGetError());
- ASSERT_NE(EGL_NO_SURFACE, mEglSurface) ;
-
- EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface,
- mEglContext));
- ASSERT_EQ(EGL_SUCCESS, eglGetError());
-}
-
-
-/////////////////////////////////////////////////////////////////////
-// Methods in SurfaceMediaSourceTest
-/////////////////////////////////////////////////////////////////////
-
-// One pass of dequeuing and queuing the buffer. Fill it in with
-// cpu YV12 buffer
-void SurfaceMediaSourceTest::oneBufferPass(int width, int height ) {
- ANativeWindowBuffer* anb;
- ASSERT_EQ(NO_ERROR, native_window_dequeue_buffer_and_wait(mANW.get(), &anb));
- ASSERT_TRUE(anb != NULL);
-
-
- // Fill the buffer with the a checkerboard pattern
- uint8_t* img = NULL;
- sp<GraphicBuffer> buf(GraphicBuffer::from(anb));
- buf->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, (void**)(&img));
- SurfaceMediaSourceTest::fillYV12Buffer(img, width, height, buf->getStride());
- buf->unlock();
-
- ASSERT_EQ(NO_ERROR, mANW->queueBuffer(mANW.get(), buf->getNativeBuffer(),
- -1));
-}
-
-// Dequeuing and queuing the buffer without really filling it in.
-void SurfaceMediaSourceTest::oneBufferPassNoFill(
- int /* width */, int /* height */) {
- ANativeWindowBuffer* anb;
- ASSERT_EQ(NO_ERROR, native_window_dequeue_buffer_and_wait(mANW.get(), &anb));
- ASSERT_TRUE(anb != NULL);
-
- // We do not fill the buffer in. Just queue it back.
- sp<GraphicBuffer> buf(GraphicBuffer::from(anb));
- ASSERT_EQ(NO_ERROR, mANW->queueBuffer(mANW.get(), buf->getNativeBuffer(),
- -1));
-}
-
-// Fill a YV12 buffer with a multi-colored checkerboard pattern
-void SurfaceMediaSourceTest::fillYV12Buffer(uint8_t* buf, int w, int h, int stride) {
- const int blockWidth = w > 16 ? w / 16 : 1;
- const int blockHeight = h > 16 ? h / 16 : 1;
- const int yuvTexOffsetY = 0;
- int yuvTexStrideY = stride;
- int yuvTexOffsetV = yuvTexStrideY * h;
- int yuvTexStrideV = (yuvTexStrideY/2 + 0xf) & ~0xf;
- int yuvTexOffsetU = yuvTexOffsetV + yuvTexStrideV * h/2;
- int yuvTexStrideU = yuvTexStrideV;
- for (int x = 0; x < w; x++) {
- for (int y = 0; y < h; y++) {
- int parityX = (x / blockWidth) & 1;
- int parityY = (y / blockHeight) & 1;
- unsigned char intensity = (parityX ^ parityY) ? 63 : 191;
- buf[yuvTexOffsetY + (y * yuvTexStrideY) + x] = intensity;
- if (x < w / 2 && y < h / 2) {
- buf[yuvTexOffsetU + (y * yuvTexStrideU) + x] = intensity;
- if (x * 2 < w / 2 && y * 2 < h / 2) {
- buf[yuvTexOffsetV + (y*2 * yuvTexStrideV) + x*2 + 0] =
- buf[yuvTexOffsetV + (y*2 * yuvTexStrideV) + x*2 + 1] =
- buf[yuvTexOffsetV + ((y*2+1) * yuvTexStrideV) + x*2 + 0] =
- buf[yuvTexOffsetV + ((y*2+1) * yuvTexStrideV) + x*2 + 1] =
- intensity;
- }
- }
- }
- }
-}
-
-// Fill a YV12 buffer with red outside a given rectangle and green inside it.
-void SurfaceMediaSourceTest::fillYV12BufferRect(uint8_t* buf, int w,
- int h, int stride, const android_native_rect_t& rect) {
- const int yuvTexOffsetY = 0;
- int yuvTexStrideY = stride;
- int yuvTexOffsetV = yuvTexStrideY * h;
- int yuvTexStrideV = (yuvTexStrideY/2 + 0xf) & ~0xf;
- int yuvTexOffsetU = yuvTexOffsetV + yuvTexStrideV * h/2;
- int yuvTexStrideU = yuvTexStrideV;
- for (int x = 0; x < w; x++) {
- for (int y = 0; y < h; y++) {
- bool inside = rect.left <= x && x < rect.right &&
- rect.top <= y && y < rect.bottom;
- buf[yuvTexOffsetY + (y * yuvTexStrideY) + x] = inside ? 240 : 64;
- if (x < w / 2 && y < h / 2) {
- bool inside = rect.left <= 2*x && 2*x < rect.right &&
- rect.top <= 2*y && 2*y < rect.bottom;
- buf[yuvTexOffsetU + (y * yuvTexStrideU) + x] = 16;
- buf[yuvTexOffsetV + (y * yuvTexStrideV) + x] =
- inside ? 16 : 255;
- }
- }
- }
-} ///////// End of class SurfaceMediaSourceTest
-
-///////////////////////////////////////////////////////////////////
-// Class to imitate the recording /////////////////////////////
-// ////////////////////////////////////////////////////////////////
-struct SimpleDummyRecorder {
- sp<MediaSource> mSource;
-
- explicit SimpleDummyRecorder
- (const sp<MediaSource> &source): mSource(source) {}
-
- status_t start() { return mSource->start();}
- status_t stop() { return mSource->stop();}
-
- // fakes reading from a media source
- status_t readFromSource() {
- MediaBuffer *buffer;
- status_t err = mSource->read(&buffer);
- if (err != OK) {
- return err;
- }
- buffer->release();
- buffer = NULL;
- return OK;
- }
-};
-///////////////////////////////////////////////////////////////////
-// TESTS
-// SurfaceMediaSourceTest class contains tests that fill the buffers
-// using the cpu calls
-// SurfaceMediaSourceGLTest class contains tests that fill the buffers
-// using the GL calls.
-// TODO: None of the tests actually verify the encoded images.. so at this point,
-// these are mostly functionality tests + visual inspection
-//////////////////////////////////////////////////////////////////////
-
-// Just pass one buffer from the native_window to the SurfaceMediaSource
-// Dummy Encoder
-static int testId = 1;
-TEST_F(SurfaceMediaSourceTest, DISABLED_DummyEncodingFromCpuFilledYV12BufferNpotOneBufferPass) {
- ALOGV("Test # %d", testId++);
- ALOGV("Testing OneBufferPass ******************************");
-
- ASSERT_EQ(NO_ERROR, native_window_set_buffers_format(mANW.get(),
- HAL_PIXEL_FORMAT_YV12));
- oneBufferPass(mYuvTexWidth, mYuvTexHeight);
-}
-
-// Pass the buffer with the wrong height and weight and should not be accepted
-// Dummy Encoder
-TEST_F(SurfaceMediaSourceTest, DISABLED_DummyEncodingFromCpuFilledYV12BufferNpotWrongSizeBufferPass) {
- ALOGV("Test # %d", testId++);
- ALOGV("Testing Wrong size BufferPass ******************************");
-
- // setting the client side buffer size different than the server size
- ASSERT_EQ(NO_ERROR, native_window_set_buffers_dimensions(mANW.get(),
- 10, 10));
- ASSERT_EQ(NO_ERROR, native_window_set_buffers_format(mANW.get(),
- HAL_PIXEL_FORMAT_YV12));
-
- ANativeWindowBuffer* anb;
-
- // Note: make sure we get an ERROR back when dequeuing!
- ASSERT_NE(NO_ERROR, native_window_dequeue_buffer_and_wait(mANW.get(), &anb));
-}
-
-// pass multiple buffers from the native_window the SurfaceMediaSource
-// Dummy Encoder
-TEST_F(SurfaceMediaSourceTest, DISABLED_DummyEncodingFromCpuFilledYV12BufferNpotMultiBufferPass) {
- ALOGV("Test # %d", testId++);
- ALOGV("Testing MultiBufferPass, Dummy Recorder *********************");
- ASSERT_EQ(NO_ERROR, native_window_set_buffers_format(mANW.get(),
- HAL_PIXEL_FORMAT_YV12));
-
- SimpleDummyRecorder writer(mSMS);
- writer.start();
-
- int32_t nFramesCount = 0;
- while (nFramesCount < 300) {
- oneBufferPass(mYuvTexWidth, mYuvTexHeight);
-
- ASSERT_EQ(NO_ERROR, writer.readFromSource());
-
- nFramesCount++;
- }
- writer.stop();
-}
-
-// Delayed pass of multiple buffers from the native_window the SurfaceMediaSource
-// Dummy Encoder
-TEST_F(SurfaceMediaSourceTest, DummyLagEncodingFromCpuFilledYV12BufferNpotMultiBufferPass) {
- ALOGV("Test # %d", testId++);
- ALOGV("Testing MultiBufferPass, Dummy Recorder Lagging **************");
-
- ASSERT_EQ(NO_ERROR, native_window_set_buffers_format(mANW.get(),
- HAL_PIXEL_FORMAT_YV12));
-
- SimpleDummyRecorder writer(mSMS);
- writer.start();
-
- int32_t nFramesCount = 1;
- const int FRAMES_LAG = SurfaceMediaSource::MIN_UNDEQUEUED_BUFFERS;
-
- while (nFramesCount <= 300) {
- ALOGV("Frame: %d", nFramesCount);
- oneBufferPass(mYuvTexWidth, mYuvTexHeight);
- // Forcing the writer to lag behind a few frames
- if (nFramesCount > FRAMES_LAG) {
- ASSERT_EQ(NO_ERROR, writer.readFromSource());
- }
- nFramesCount++;
- }
- writer.stop();
-}
-
-// pass multiple buffers from the native_window the SurfaceMediaSource
-// A dummy writer (MULTITHREADED) is used to simulate actual MPEG4Writer
-TEST_F(SurfaceMediaSourceTest, DummyThreadedEncodingFromCpuFilledYV12BufferNpotMultiBufferPass) {
- ALOGV("Test # %d", testId++);
- ALOGV("Testing MultiBufferPass, Dummy Recorder Multi-Threaded **********");
- ASSERT_EQ(NO_ERROR, native_window_set_buffers_format(mANW.get(),
- HAL_PIXEL_FORMAT_YV12));
-
- DummyRecorder writer(mSMS);
- writer.start();
-
- int32_t nFramesCount = 0;
- while (nFramesCount <= 300) {
- ALOGV("Frame: %d", nFramesCount);
- oneBufferPass(mYuvTexWidth, mYuvTexHeight);
-
- nFramesCount++;
- }
- writer.stop();
-}
-
-// Test to examine actual encoding using mediarecorder
-// We use the mediaserver to create a mediarecorder and send
-// it back to us. So SurfaceMediaSource lives in the same process
-// as the mediaserver.
-// Very close to the actual camera, except that the
-// buffers are filled and queueud by the CPU instead of GL.
-TEST_F(SurfaceMediaSourceTest, DISABLED_EncodingFromCpuYV12BufferNpotWriteMediaServer) {
- ALOGV("Test # %d", testId++);
- ALOGV("************** Testing the whole pipeline with actual MediaRecorder ***********");
- ALOGV("************** SurfaceMediaSource is same process as mediaserver ***********");
-
- const char *fileName = "/sdcard/outputSurfEncMSource.mp4";
- int fd = open(fileName, O_RDWR | O_CREAT, 0744);
- if (fd < 0) {
- ALOGE("ERROR: Could not open the the file %s, fd = %d !!", fileName, fd);
- }
- CHECK(fd >= 0);
-
- sp<MediaRecorder> mr = SurfaceMediaSourceGLTest::setUpMediaRecorder(fd,
- VIDEO_SOURCE_SURFACE, OUTPUT_FORMAT_MPEG_4, VIDEO_ENCODER_H264,
- mYuvTexWidth, mYuvTexHeight, 30);
- // get the reference to the surfacemediasource living in
- // mediaserver that is created by stagefrightrecorder
- sp<IGraphicBufferProducer> iST = mr->querySurfaceMediaSourceFromMediaServer();
- mSTC = new Surface(iST);
- mANW = mSTC;
- ASSERT_EQ(NO_ERROR, native_window_api_connect(mANW.get(), NATIVE_WINDOW_API_CPU));
- ASSERT_EQ(NO_ERROR, native_window_set_buffers_format(mANW.get(),
- HAL_PIXEL_FORMAT_YV12));
-
- int32_t nFramesCount = 0;
- while (nFramesCount <= 300) {
- oneBufferPassNoFill(mYuvTexWidth, mYuvTexHeight);
- nFramesCount++;
- ALOGV("framesCount = %d", nFramesCount);
- }
-
- ASSERT_EQ(NO_ERROR, native_window_api_disconnect(mANW.get(), NATIVE_WINDOW_API_CPU));
- ALOGV("Stopping MediaRecorder...");
- CHECK_EQ((status_t)OK, mr->stop());
- mr.clear();
- close(fd);
-}
-
-//////////////////////////////////////////////////////////////////////
-// GL tests
-/////////////////////////////////////////////////////////////////////
-
-// Test to examine whether we can choose the Recordable Android GLConfig
-// DummyRecorder used- no real encoding here
-TEST_F(SurfaceMediaSourceGLTest, ChooseAndroidRecordableEGLConfigDummyWriter) {
- ALOGV("Test # %d", testId++);
- ALOGV("Verify creating a surface w/ right config + dummy writer*********");
-
- mSMS = new SurfaceMediaSource(mYuvTexWidth, mYuvTexHeight);
- mSTC = new Surface(mSMS->getProducer());
- mANW = mSTC;
-
- DummyRecorder writer(mSMS);
- writer.start();
-
- if (mEglSurface != EGL_NO_SURFACE) {
- EXPECT_TRUE(eglDestroySurface(mEglDisplay, mEglSurface));
- mEglSurface = EGL_NO_SURFACE;
- }
-
- mEglSurface = eglCreateWindowSurface(mEglDisplay, mGlConfig,
- mANW.get(), NULL);
- ASSERT_EQ(EGL_SUCCESS, eglGetError());
- ASSERT_NE(EGL_NO_SURFACE, mEglSurface) ;
-
- EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface,
- mEglContext));
- ASSERT_EQ(EGL_SUCCESS, eglGetError());
-
- int32_t nFramesCount = 0;
- while (nFramesCount <= 300) {
- oneBufferPassGL();
- nFramesCount++;
- ALOGV("framesCount = %d", nFramesCount);
- }
-
- EXPECT_TRUE(eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE,
- EGL_NO_CONTEXT));
- ASSERT_EQ(EGL_SUCCESS, eglGetError());
- eglDestroySurface(mEglDisplay, mEglSurface);
- mEglSurface = EGL_NO_SURFACE;
-
- writer.stop();
-}
-// Test to examine whether we can render GL buffers in to the surface
-// created with the native window handle
-TEST_F(SurfaceMediaSourceGLTest, RenderingToRecordableEGLSurfaceWorks) {
- ALOGV("Test # %d", testId++);
- ALOGV("RenderingToRecordableEGLSurfaceWorks *********************");
- // Do the producer side of things
- glClearColor(0.6, 0.6, 0.6, 0.6);
- glClear(GL_COLOR_BUFFER_BIT);
-
- glEnable(GL_SCISSOR_TEST);
- glScissor(4, 4, 4, 4);
- glClearColor(1.0, 0.0, 0.0, 1.0);
- glClear(GL_COLOR_BUFFER_BIT);
-
- glScissor(24, 48, 4, 4);
- glClearColor(0.0, 1.0, 0.0, 1.0);
- glClear(GL_COLOR_BUFFER_BIT);
-
- glScissor(37, 17, 4, 4);
- glClearColor(0.0, 0.0, 1.0, 1.0);
- glClear(GL_COLOR_BUFFER_BIT);
-
- EXPECT_TRUE(checkPixel( 0, 0, 153, 153, 153, 153));
- EXPECT_TRUE(checkPixel(63, 0, 153, 153, 153, 153));
- EXPECT_TRUE(checkPixel(63, 63, 153, 153, 153, 153));
- EXPECT_TRUE(checkPixel( 0, 63, 153, 153, 153, 153));
-
- EXPECT_TRUE(checkPixel( 4, 7, 255, 0, 0, 255));
- EXPECT_TRUE(checkPixel(25, 51, 0, 255, 0, 255));
- EXPECT_TRUE(checkPixel(40, 19, 0, 0, 255, 255));
- EXPECT_TRUE(checkPixel(29, 51, 153, 153, 153, 153));
- EXPECT_TRUE(checkPixel( 5, 32, 153, 153, 153, 153));
- EXPECT_TRUE(checkPixel(13, 8, 153, 153, 153, 153));
- EXPECT_TRUE(checkPixel(46, 3, 153, 153, 153, 153));
- EXPECT_TRUE(checkPixel(30, 33, 153, 153, 153, 153));
- EXPECT_TRUE(checkPixel( 6, 52, 153, 153, 153, 153));
- EXPECT_TRUE(checkPixel(55, 33, 153, 153, 153, 153));
- EXPECT_TRUE(checkPixel(16, 29, 153, 153, 153, 153));
- EXPECT_TRUE(checkPixel( 1, 30, 153, 153, 153, 153));
- EXPECT_TRUE(checkPixel(41, 37, 153, 153, 153, 153));
- EXPECT_TRUE(checkPixel(46, 29, 153, 153, 153, 153));
- EXPECT_TRUE(checkPixel(15, 25, 153, 153, 153, 153));
- EXPECT_TRUE(checkPixel( 3, 52, 153, 153, 153, 153));
-}
-
-// Test to examine the actual encoding with GL buffers
-// Actual encoder, Actual GL Buffers Filled SurfaceMediaSource
-// The same pattern is rendered every frame
-TEST_F(SurfaceMediaSourceGLTest, EncodingFromGLRgbaSameImageEachBufNpotWrite) {
- ALOGV("Test # %d", testId++);
- ALOGV("************** Testing the whole pipeline with actual Recorder ***********");
- ALOGV("************** GL Filling the buffers ***********");
- // Note: No need to set the colorformat for the buffers. The colorformat is
- // in the GRAlloc buffers itself.
-
- const char *fileName = "/sdcard/outputSurfEncMSourceGL.mp4";
- int fd = open(fileName, O_RDWR | O_CREAT, 0744);
- if (fd < 0) {
- ALOGE("ERROR: Could not open the the file %s, fd = %d !!", fileName, fd);
- }
- CHECK(fd >= 0);
-
- sp<MediaRecorder> mr = setUpMediaRecorder(fd, VIDEO_SOURCE_SURFACE,
- OUTPUT_FORMAT_MPEG_4, VIDEO_ENCODER_H264, mYuvTexWidth, mYuvTexHeight, 30);
-
- // get the reference to the surfacemediasource living in
- // mediaserver that is created by stagefrightrecorder
- setUpEGLSurfaceFromMediaRecorder(mr);
-
- int32_t nFramesCount = 0;
- while (nFramesCount <= 300) {
- oneBufferPassGL();
- nFramesCount++;
- ALOGV("framesCount = %d", nFramesCount);
- }
-
- EXPECT_TRUE(eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE,
- EGL_NO_CONTEXT));
- ASSERT_EQ(EGL_SUCCESS, eglGetError());
- eglDestroySurface(mEglDisplay, mEglSurface);
- mEglSurface = EGL_NO_SURFACE;
-
- ALOGV("Stopping MediaRecorder...");
- CHECK_EQ((status_t)OK, mr->stop());
- mr.clear();
- close(fd);
-}
-
-// Test to examine the actual encoding from the GL Buffers
-// Actual encoder, Actual GL Buffers Filled SurfaceMediaSource
-// A different pattern is rendered every frame
-TEST_F(SurfaceMediaSourceGLTest, EncodingFromGLRgbaDiffImageEachBufNpotWrite) {
- ALOGV("Test # %d", testId++);
- ALOGV("************** Testing the whole pipeline with actual Recorder ***********");
- ALOGV("************** Diff GL Filling the buffers ***********");
- // Note: No need to set the colorformat for the buffers. The colorformat is
- // in the GRAlloc buffers itself.
-
- const char *fileName = "/sdcard/outputSurfEncMSourceGLDiff.mp4";
- int fd = open(fileName, O_RDWR | O_CREAT, 0744);
- if (fd < 0) {
- ALOGE("ERROR: Could not open the the file %s, fd = %d !!", fileName, fd);
- }
- CHECK(fd >= 0);
-
- sp<MediaRecorder> mr = setUpMediaRecorder(fd, VIDEO_SOURCE_SURFACE,
- OUTPUT_FORMAT_MPEG_4, VIDEO_ENCODER_H264, mYuvTexWidth, mYuvTexHeight, 30);
-
- // get the reference to the surfacemediasource living in
- // mediaserver that is created by stagefrightrecorder
- setUpEGLSurfaceFromMediaRecorder(mr);
-
- int32_t nFramesCount = 0;
- while (nFramesCount <= 300) {
- oneBufferPassGL(nFramesCount);
- nFramesCount++;
- ALOGV("framesCount = %d", nFramesCount);
- }
-
- EXPECT_TRUE(eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE,
- EGL_NO_CONTEXT));
- ASSERT_EQ(EGL_SUCCESS, eglGetError());
- eglDestroySurface(mEglDisplay, mEglSurface);
- mEglSurface = EGL_NO_SURFACE;
-
- ALOGV("Stopping MediaRecorder...");
- CHECK_EQ((status_t)OK, mr->stop());
- mr.clear();
- close(fd);
-}
-} // namespace android
diff --git a/media/libstagefright/webm/WebmFrame.cpp b/media/libstagefright/webm/WebmFrame.cpp
index e5134ed..4b0d47c 100644
--- a/media/libstagefright/webm/WebmFrame.cpp
+++ b/media/libstagefright/webm/WebmFrame.cpp
@@ -27,7 +27,7 @@
using namespace webm;
namespace {
-sp<ABuffer> toABuffer(MediaBuffer *mbuf) {
+sp<ABuffer> toABuffer(MediaBufferBase *mbuf) {
sp<ABuffer> abuf = new ABuffer(mbuf->range_length());
memcpy(abuf->data(), (uint8_t*) mbuf->data() + mbuf->range_offset(), mbuf->range_length());
return abuf;
@@ -46,7 +46,7 @@
mEos(true) {
}
-WebmFrame::WebmFrame(int type, bool key, uint64_t absTimecode, MediaBuffer *mbuf)
+WebmFrame::WebmFrame(int type, bool key, uint64_t absTimecode, MediaBufferBase *mbuf)
: mType(type),
mKey(key),
mAbsTimecode(absTimecode),
diff --git a/media/libstagefright/webm/WebmFrame.h b/media/libstagefright/webm/WebmFrame.h
index 4f0b055..a410a87 100644
--- a/media/libstagefright/webm/WebmFrame.h
+++ b/media/libstagefright/webm/WebmFrame.h
@@ -30,7 +30,7 @@
const bool mEos;
WebmFrame();
- WebmFrame(int type, bool key, uint64_t absTimecode, MediaBuffer *buf);
+ WebmFrame(int type, bool key, uint64_t absTimecode, MediaBufferBase *buf);
~WebmFrame() {}
sp<WebmElement> SimpleBlock(uint64_t baseTimecode) const;
diff --git a/media/libstagefright/webm/WebmFrameThread.cpp b/media/libstagefright/webm/WebmFrameThread.cpp
index 420890b..23269af 100644
--- a/media/libstagefright/webm/WebmFrameThread.cpp
+++ b/media/libstagefright/webm/WebmFrameThread.cpp
@@ -337,7 +337,7 @@
mStartTimeUs = kUninitialized;
status_t err = OK;
- MediaBuffer *buffer;
+ MediaBufferBase *buffer;
while (!mDone && (err = mSource->read(&buffer, NULL)) == OK) {
if (buffer->range_length() == 0) {
buffer->release();
@@ -345,8 +345,8 @@
continue;
}
- sp<MetaData> md = buffer->meta_data();
- CHECK(md->findInt64(kKeyTime, ×tampUs));
+ MetaDataBase &md = buffer->meta_data();
+ CHECK(md.findInt64(kKeyTime, ×tampUs));
if (mStartTimeUs == kUninitialized) {
mStartTimeUs = timestampUs;
}
@@ -374,7 +374,7 @@
CHECK_GE(timestampUs, 0ll);
int32_t isSync = false;
- md->findInt32(kKeyIsSyncFrame, &isSync);
+ md.findInt32(kKeyIsSyncFrame, &isSync);
const sp<WebmFrame> f = new WebmFrame(
mType,
isSync,
diff --git a/packages/MediaComponents/Android.mk b/packages/MediaComponents/Android.mk
index 7c7718e..22eea25 100644
--- a/packages/MediaComponents/Android.mk
+++ b/packages/MediaComponents/Android.mk
@@ -16,6 +16,12 @@
LOCAL_PATH := $(call my-dir)
+ifneq ($(TARGET_BUILD_PDK),true)
+# Build MediaComponents only if this is not a PDK build. MediaComponents won't
+# build in PDK builds because frameworks/base/core/java is not available but
+# IMediaSession2.aidl and IMediaSession2Callback.aidl are using classes from
+# frameworks/base/core/java.
+
include $(CLEAR_VARS)
LOCAL_PACKAGE_NAME := MediaComponents
@@ -60,4 +66,6 @@
include $(BUILD_PACKAGE)
+endif # ifneq ($(TARGET_BUILD_PDK),true)
+
include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/packages/MediaComponents/res/drawable/ic_audiotrack.xml b/packages/MediaComponents/res/drawable/ic_audiotrack.xml
new file mode 100644
index 0000000..27c12b5
--- /dev/null
+++ b/packages/MediaComponents/res/drawable/ic_audiotrack.xml
@@ -0,0 +1,9 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M12,3v9.28c-0.47,-0.17 -0.97,-0.28 -1.5,-0.28C8.01,12 6,14.01 6,16.5S8.01,21 10.5,21c2.31,0 4.2,-1.75 4.45,-4H15V6h4V3h-7z"/>
+</vector>
diff --git a/packages/MediaComponents/res/drawable/ic_check.xml b/packages/MediaComponents/res/drawable/ic_check.xml
new file mode 100644
index 0000000..32f720b
--- /dev/null
+++ b/packages/MediaComponents/res/drawable/ic_check.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+
+ <path
+ android:pathData="M0 0h24v24H0z" />
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M9 16.2L4.8 12l-1.4 1.4L9 19 21 7l-1.4-1.4L9 16.2z" />
+</vector>
\ No newline at end of file
diff --git a/packages/MediaComponents/res/drawable/ic_closed_caption_off.xml b/packages/MediaComponents/res/drawable/ic_closed_caption_off.xml
new file mode 100644
index 0000000..a79cd11
--- /dev/null
+++ b/packages/MediaComponents/res/drawable/ic_closed_caption_off.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+
+ <path
+ android:pathData="M0,0h24v24H0V0z" />
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M19,5.5c0.27,0,0.5,0.23,0.5,0.5v12c0,0.27-0.23,0.5-0.5,0.5H5c-0.28,0-0.5-0.22-0.5-0.5V6c0-0.28,0.22-0.5,0.5-0.5H19
+M19,4H5C3.89,4,3,4.9,3,6v12c0,1.1,0.89,2,2,2h14c1.1,0,2-0.9,2-2V6C21,4.9,20.1,4,19,4L19,4z" />
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M11,11H9.5v-0.5h-2v3h2V13H11v1c0,0.55-0.45,1-1,1H7c-0.55,0-1-0.45-1-1v-4c0-0.55,0.45-1,1-1h3c0.55,0,1,0.45,1,1V11z" />
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M18,11h-1.5v-0.5h-2v3h2V13H18v1c0,0.55-0.45,1-1,1h-3c-0.55,0-1-0.45-1-1v-4c0-0.55,0.45-1,1-1h3c0.55,0,1,0.45,1,1V11z" />
+</vector>
\ No newline at end of file
diff --git a/packages/MediaComponents/res/drawable/ic_help.xml b/packages/MediaComponents/res/drawable/ic_help.xml
new file mode 100644
index 0000000..4d1d75d
--- /dev/null
+++ b/packages/MediaComponents/res/drawable/ic_help.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8"?>
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+
+ <path
+ android:pathData="M0 0h24v24H0z" />
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1
+17h-2v-2h2v2zm2.07-7.75l-.9 .92 C13.45 12.9 13 13.5 13 15h-2v-.5c0-1.1 .45 -2.1
+1.17-2.83l1.24-1.26c.37-.36 .59 -.86 .59 -1.41 0-1.1-.9-2-2-2s-2 .9-2 2H8c0-2.21
+1.79-4 4-4s4 1.79 4 4c0 .88-.36 1.68-.93 2.25z" />
+</vector>
\ No newline at end of file
diff --git a/packages/MediaComponents/res/drawable/ic_high_quality.xml b/packages/MediaComponents/res/drawable/ic_high_quality.xml
new file mode 100644
index 0000000..e27d3e2
--- /dev/null
+++ b/packages/MediaComponents/res/drawable/ic_high_quality.xml
@@ -0,0 +1,9 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M19,4L5,4c-1.11,0 -2,0.9 -2,2v12c0,1.1 0.89,2 2,2h14c1.1,0 2,-0.9 2,-2L21,6c0,-1.1 -0.9,-2 -2,-2zM11,15L9.5,15v-2h-2v2L6,15L6,9h1.5v2.5h2L9.5,9L11,9v6zM18,14c0,0.55 -0.45,1 -1,1h-0.75v1.5h-1.5L14.75,15L14,15c-0.55,0 -1,-0.45 -1,-1v-4c0,-0.55 0.45,-1 1,-1h3c0.55,0 1,0.45 1,1v4zM14.5,13.5h2v-3h-2v3z"/>
+</vector>
\ No newline at end of file
diff --git a/packages/MediaComponents/res/drawable/ic_replay.xml b/packages/MediaComponents/res/drawable/ic_replay.xml
deleted file mode 100644
index 2bde120..0000000
--- a/packages/MediaComponents/res/drawable/ic_replay.xml
+++ /dev/null
@@ -1,4 +0,0 @@
-<vector android:height="40dp" android:viewportHeight="48.0"
- android:viewportWidth="48.0" android:width="40dp" xmlns:android="http://schemas.android.com/apk/res/android">
- <path android:fillColor="#FFFFFF" android:pathData="M24,10V2L14,12l10,10v-8c6.63,0 12,5.37 12,12s-5.37,12 -12,12 -12,-5.37 -12,-12H8c0,8.84 7.16,16 16,16s16,-7.16 16,-16 -7.16,-16 -16,-16z"/>
-</vector>
diff --git a/packages/MediaComponents/res/drawable/ic_replay_circle_filled.xml b/packages/MediaComponents/res/drawable/ic_replay_circle_filled.xml
new file mode 100644
index 0000000..389396b
--- /dev/null
+++ b/packages/MediaComponents/res/drawable/ic_replay_circle_filled.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+
+ <path
+ android:pathData="M0,0h24v24H0V0z" />
+ <path
+ android:fillColor="#FFFFFF"
+ android:fillType="evenOdd"
+ android:pathData="M12,2C6.48,2,2,6.48,2,12c0,5.52,4.48,10,10,10c5.52,0,10-4.48,10-10
+C22,6.48,17.52,2,12,2z
+M18,12c0,3.31-2.69,6-6,6c-3.31,0-6-2.69-6-6h2c0,2.21,1.79,4,4,4s4-1.79,4-4s-1.79-4-4-4v3L8,7l4-4v3
+C15.31,6,18,8.69,18,12z" />
+</vector>
\ No newline at end of file
diff --git a/packages/MediaComponents/res/drawable/ic_sd.xml b/packages/MediaComponents/res/drawable/ic_sd.xml
new file mode 100644
index 0000000..decb6d2
--- /dev/null
+++ b/packages/MediaComponents/res/drawable/ic_sd.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+
+ <path
+ android:pathData="M0,0h24v24H0V0z" />
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M19,3H5C3.89,3,3,3.9,3,5v14c0,1.1,0.89,2,2,2h14c1.1,0,2-0.9,2-2V5C21,3.9,20.1,3,19,3z
+M13,9h4c0.55,0,1,0.45,1,1v4 c0,0.55-0.45,1-1,1h-4V9z
+M9.5,13.5v-1H7c-0.55,0-1-0.45-1-1V10c0-0.55,0.45-1,1-1h3c0.55,0,1,0.45,1,1v1H9.5v-0.5h-2v1H10
+c0.55,0,1,0.45,1,1V14c0,0.55-0.45,1-1,1H7c-0.55,0-1-0.45-1-1v-1h1.5v0.5H9.5z
+M14.5,13.5h2v-3h-2V13.5z" />
+</vector>
\ No newline at end of file
diff --git a/packages/MediaComponents/res/layout/media_controller.xml b/packages/MediaComponents/res/layout/media_controller.xml
index f9ebd44..38f139d 100644
--- a/packages/MediaComponents/res/layout/media_controller.xml
+++ b/packages/MediaComponents/res/layout/media_controller.xml
@@ -71,7 +71,7 @@
android:orientation="horizontal">
<LinearLayout
- android:id="@+id/ad"
+ android:id="@+id/ad_external_link"
android:clickable="true"
android:gravity="center"
android:layout_width="wrap_content"
@@ -80,7 +80,8 @@
android:layout_centerVertical="true"
android:paddingLeft="5dip"
android:paddingRight="10dip"
- android:orientation="horizontal">
+ android:orientation="horizontal"
+ android:visibility="gone">
<TextView
android:id="@+id/ad_text"
@@ -88,7 +89,7 @@
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:paddingRight="5dip"
- android:text="Visit Advertiser"
+ android:text="@string/MediaControlView2_ad_text"
android:textSize="10sp"
android:textColor="#FFFFFFFF" />
@@ -135,7 +136,7 @@
<RelativeLayout
android:layout_width="match_parent"
- android:layout_height="wrap_content"
+ android:layout_height="44dp"
android:paddingLeft="15dp"
android:orientation="horizontal">
@@ -163,14 +164,35 @@
android:textStyle="bold"
android:textColor="#BBBBBB" />
+ <TextView
+ android:id="@+id/ad_skip_time"
+ android:gravity="center"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_centerVertical="true"
+ android:textSize="12sp"
+ android:textColor="#FFFFFF"
+ android:visibility="gone" />
+
<LinearLayout
android:id="@+id/basic_controls"
+ android:gravity="center"
android:layout_alignParentRight="true"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:orientation="horizontal" >
+ <TextView
+ android:id="@+id/ad_remaining"
+ android:gravity="center"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_centerVertical="true"
+ android:textSize="12sp"
+ android:textColor="#FFFFFF"
+ android:visibility="gone" />
+
<ImageButton
android:id="@+id/subtitle"
android:scaleType="fitCenter"
@@ -213,6 +235,4 @@
</LinearLayout>
</RelativeLayout>
-
-</LinearLayout>
-
+</LinearLayout>
\ No newline at end of file
diff --git a/packages/MediaComponents/res/layout/settings_list.xml b/packages/MediaComponents/res/layout/settings_list.xml
new file mode 100644
index 0000000..37a3a60
--- /dev/null
+++ b/packages/MediaComponents/res/layout/settings_list.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2018 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.
+-->
+
+<ListView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="@dimen/MediaControlView2_settings_width"
+ android:layout_height="@dimen/MediaControlView2_settings_height"
+ android:divider="@null"
+ android:dividerHeight="0dp">
+</ListView>
\ No newline at end of file
diff --git a/packages/MediaComponents/res/layout/settings_list_item.xml b/packages/MediaComponents/res/layout/settings_list_item.xml
new file mode 100644
index 0000000..e7522b7
--- /dev/null
+++ b/packages/MediaComponents/res/layout/settings_list_item.xml
@@ -0,0 +1,71 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2018 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.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="@dimen/MediaControlView2_settings_width"
+ android:layout_height="@dimen/MediaControlView2_settings_height"
+ android:orientation="horizontal"
+ android:background="@color/black_transparent_70">
+
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="@dimen/MediaControlView2_settings_height"
+ android:paddingRight="2dp"
+ android:gravity="center"
+ android:orientation="horizontal">
+
+ <ImageView
+ android:id="@+id/check"
+ android:layout_width="@dimen/MediaControlView2_settings_icon_size"
+ android:layout_height="@dimen/MediaControlView2_settings_icon_size"
+ android:gravity="center"
+ android:paddingLeft="2dp"
+ android:src="@drawable/ic_check"/>
+
+ <ImageView
+ android:id="@+id/icon"
+ android:layout_width="@dimen/MediaControlView2_settings_icon_size"
+ android:layout_height="@dimen/MediaControlView2_settings_icon_size"
+ android:gravity="center"
+ android:paddingLeft="2dp"/>
+ </LinearLayout>
+
+ <RelativeLayout
+ android:layout_width="wrap_content"
+ android:layout_height="@dimen/MediaControlView2_settings_height"
+ android:gravity="center"
+ android:orientation="vertical">
+
+ <TextView
+ android:id="@+id/main_text"
+ android:layout_width="wrap_content"
+ android:layout_height="@dimen/MediaControlView2_text_width"
+ android:paddingLeft="2dp"
+ android:textColor="@color/white"
+ android:textSize="@dimen/MediaControlView2_settings_main_text_size"/>
+
+ <TextView
+ android:id="@+id/sub_text"
+ android:layout_width="wrap_content"
+ android:layout_height="@dimen/MediaControlView2_text_width"
+ android:layout_below="@id/main_text"
+ android:paddingLeft="2dp"
+ android:textColor="@color/white_transparent_70"
+ android:textSize="@dimen/MediaControlView2_settings_sub_text_size"/>
+ </RelativeLayout>
+
+</LinearLayout>
+
diff --git a/packages/MediaComponents/res/values/colors.xml b/packages/MediaComponents/res/values/colors.xml
index 9e071d7..8ba069c 100644
--- a/packages/MediaComponents/res/values/colors.xml
+++ b/packages/MediaComponents/res/values/colors.xml
@@ -15,5 +15,8 @@
-->
<resources>
- <integer name="gray">0xff444444</integer>
+ <color name="gray">#808080</color>
+ <color name="white">#ffffff</color>
+ <color name="white_transparent_70">#B3ffffff</color>
+ <color name="black_transparent_70">#B3000000</color>
</resources>
\ No newline at end of file
diff --git a/packages/MediaComponents/res/values/dimens.xml b/packages/MediaComponents/res/values/dimens.xml
index 91241cd..b5ef626 100644
--- a/packages/MediaComponents/res/values/dimens.xml
+++ b/packages/MediaComponents/res/values/dimens.xml
@@ -40,4 +40,13 @@
<integer name="mr_controller_volume_group_list_fade_in_duration_ms">400</integer>
<!-- Group list fade out animation duration. -->
<integer name="mr_controller_volume_group_list_fade_out_duration_ms">200</integer>
+
+ <dimen name="MediaControlView2_settings_width">200dp</dimen>
+ <dimen name="MediaControlView2_settings_height">36dp</dimen>
+ <dimen name="MediaControlView2_settings_icon_size">28dp</dimen>
+ <dimen name="MediaControlView2_settings_offset">8dp</dimen>
+ <dimen name="MediaControlView2_text_width">18dp</dimen>
+
+ <dimen name="MediaControlView2_settings_main_text_size">12sp</dimen>
+ <dimen name="MediaControlView2_settings_sub_text_size">10sp</dimen>
</resources>
diff --git a/packages/MediaComponents/res/values/strings.xml b/packages/MediaComponents/res/values/strings.xml
index 35db0e5..5f9c78d 100644
--- a/packages/MediaComponents/res/values/strings.xml
+++ b/packages/MediaComponents/res/values/strings.xml
@@ -14,7 +14,9 @@
limitations under the License.
-->
-<resources>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+
<!-- Name for the default system route prior to Jellybean. [CHAR LIMIT=30] -->
<string name="mr_system_route_name">System</string>
@@ -92,4 +94,22 @@
<string name="VideoView2_error_text_unknown">Can\'t play this video.</string>
<!-- Button to close error alert when a video cannot be played. -->
<string name="VideoView2_error_button">OK</string>
+
+ <!-- Text for displaying ad skip wait time. -->
+ <string name="MediaControlView2_ad_skip_wait_time">
+ You can skip Ad in <xliff:g id="wait_time" example="5">%1$d</xliff:g>s
+ </string>
+ <!-- Text for displaying ad total remaining time. -->
+ <string name="MediaControlView2_ad_remaining_time">
+ Ad · <xliff:g id="remaining_time" example="1:15">%1$s</xliff:g> remaining
+ </string>
+ <!-- Placeholder text indicating that the user can press the button to go to an external website. -->
+ <string name="MediaControlView2_ad_text">Visit Advertiser</string>
+ <string name="MediaControlView2_cc_text">Closed caption</string>
+ <string name="MediaControlView2_audio_track_text">Audio track</string>
+ <string name="MediaControlView2_video_quality_text">Video quality</string>
+ <string name="MediaControlView2_video_quality_auto_text">Auto</string>
+ <string name="MediaControlView2_playback_speed_text">Playback speed</string>
+ <string name="MediaControlView2_playback_speed_normal_text">Normal</string>
+ <string name="MediaControlView2_help_text">Help & feedback</string>
</resources>
diff --git a/packages/MediaComponents/res/values/style.xml b/packages/MediaComponents/res/values/style.xml
index db5e8f3..299f16b 100644
--- a/packages/MediaComponents/res/values/style.xml
+++ b/packages/MediaComponents/res/values/style.xml
@@ -81,5 +81,4 @@
<style name="BottomBarButton.Mute">
<item name="android:src">@drawable/ic_mute</item>
</style>
-
</resources>
diff --git a/packages/MediaComponents/src/com/android/media/IMediaSession2.aidl b/packages/MediaComponents/src/com/android/media/IMediaSession2.aidl
index 81e5547..63c0697 100644
--- a/packages/MediaComponents/src/com/android/media/IMediaSession2.aidl
+++ b/packages/MediaComponents/src/com/android/media/IMediaSession2.aidl
@@ -37,7 +37,7 @@
// not to expose other methods to the controller whose connection wasn't accepted.
// But this would be enough for now because it's the same as existing
// MediaBrowser and MediaBrowserService.
- void connect(String callingPackage, IMediaSession2Callback callback);
+ void connect(IMediaSession2Callback caller, String callingPackage);
void release(IMediaSession2Callback caller);
void setVolumeTo(IMediaSession2Callback caller, int value, int flags);
@@ -52,18 +52,24 @@
void sendCustomCommand(IMediaSession2Callback caller, in Bundle command, in Bundle args,
in ResultReceiver receiver);
- void prepareFromUri(IMediaSession2Callback caller, in Uri uri, in Bundle extra);
- void prepareFromSearch(IMediaSession2Callback caller, String query, in Bundle extra);
- void prepareFromMediaId(IMediaSession2Callback caller, String mediaId, in Bundle extra);
- void playFromUri(IMediaSession2Callback caller, in Uri uri, in Bundle extra);
- void playFromSearch(IMediaSession2Callback caller, String query, in Bundle extra);
- void playFromMediaId(IMediaSession2Callback caller, String mediaId, in Bundle extra);
+ void prepareFromUri(IMediaSession2Callback caller, in Uri uri, in Bundle extras);
+ void prepareFromSearch(IMediaSession2Callback caller, String query, in Bundle extras);
+ void prepareFromMediaId(IMediaSession2Callback caller, String mediaId, in Bundle extras);
+ void playFromUri(IMediaSession2Callback caller, in Uri uri, in Bundle extras);
+ void playFromSearch(IMediaSession2Callback caller, String query, in Bundle extras);
+ void playFromMediaId(IMediaSession2Callback caller, String mediaId, in Bundle extras);
+ void setRating(IMediaSession2Callback caller, String mediaId, in Bundle rating);
- //////////////////////////////////////////////////////////////////////////////////////////////
- // Get library service specific
//////////////////////////////////////////////////////////////////////////////////////////////
- void getBrowserRoot(IMediaSession2Callback callback, in Bundle rootHints);
- void getItem(IMediaSession2Callback callback, String mediaId);
- void getChildren(IMediaSession2Callback callback, String parentId, int page, int pageSize,
- in Bundle options);
+ // library service specific
+ //////////////////////////////////////////////////////////////////////////////////////////////
+ void getBrowserRoot(IMediaSession2Callback caller, in Bundle rootHints);
+ void getItem(IMediaSession2Callback caller, String mediaId);
+ void getChildren(IMediaSession2Callback caller, String parentId, int page, int pageSize,
+ in Bundle extras);
+ void search(IMediaSession2Callback caller, String query, in Bundle extras);
+ void getSearchResult(IMediaSession2Callback caller, String query, int page, int pageSize,
+ in Bundle extras);
+ void subscribe(IMediaSession2Callback caller, String parentId, in Bundle extras);
+ void unsubscribe(IMediaSession2Callback caller, String parentId);
}
diff --git a/packages/MediaComponents/src/com/android/media/IMediaSession2Callback.aidl b/packages/MediaComponents/src/com/android/media/IMediaSession2Callback.aidl
index 7e76d1d..6a03989 100644
--- a/packages/MediaComponents/src/com/android/media/IMediaSession2Callback.aidl
+++ b/packages/MediaComponents/src/com/android/media/IMediaSession2Callback.aidl
@@ -36,7 +36,7 @@
// TODO(jaewan): Handle when the playlist becomes too huge.
void onConnected(IMediaSession2 sessionBinder, in Bundle commandGroup, in Bundle playbackState,
- in Bundle playbackInfo, in Bundle params, in List<Bundle> playlist, int ratingType,
+ in Bundle playbackInfo, in Bundle params, in List<Bundle> playlist,
in PendingIntent sessionActivity);
void onDisconnected();
@@ -47,8 +47,12 @@
//////////////////////////////////////////////////////////////////////////////////////////////
// Browser sepcific
//////////////////////////////////////////////////////////////////////////////////////////////
- void onGetRootResult(in Bundle rootHints, String rootMediaId, in Bundle rootExtra);
- void onItemLoaded(String mediaId, in Bundle result);
- void onChildrenLoaded(String parentId, int page, int pageSize, in Bundle options,
- in List<Bundle> result);
+ void onGetLibraryRootDone(in Bundle rootHints, String rootMediaId, in Bundle rootExtra);
+ void onGetItemDone(String mediaId, in Bundle result);
+ void onChildrenChanged(String rootMediaId, int itemCount, in Bundle extras);
+ void onGetChildrenDone(String parentId, int page, int pageSize, in List<Bundle> result,
+ in Bundle extras);
+ void onSearchResultChanged(String query, int itemCount, in Bundle extras);
+ void onGetSearchResultDone(String query, int page, int pageSize, in List<Bundle> result,
+ in Bundle extras);
}
diff --git a/packages/MediaComponents/src/com/android/media/MediaBrowser2Impl.java b/packages/MediaComponents/src/com/android/media/MediaBrowser2Impl.java
index 5e88262..9190dfc 100644
--- a/packages/MediaComponents/src/com/android/media/MediaBrowser2Impl.java
+++ b/packages/MediaComponents/src/com/android/media/MediaBrowser2Impl.java
@@ -20,7 +20,6 @@
import android.media.MediaBrowser2;
import android.media.MediaBrowser2.BrowserCallback;
import android.media.MediaItem2;
-import android.media.MediaSession2.CommandButton;
import android.media.SessionToken2;
import android.media.update.MediaBrowser2Provider;
import android.os.Bundle;
@@ -63,13 +62,37 @@
}
@Override
- public void subscribe_impl(String parentId, Bundle options) {
- // TODO(jaewan): Implement
+ public void subscribe_impl(String parentId, Bundle extras) {
+ final IMediaSession2 binder = getSessionBinder();
+ if (binder != null) {
+ try {
+ binder.subscribe(getControllerStub(), parentId, extras);
+ } catch (RemoteException e) {
+ // TODO(jaewan): Handle disconnect.
+ if (DEBUG) {
+ Log.w(TAG, "Cannot connect to the service or the session is gone", e);
+ }
+ }
+ } else {
+ Log.w(TAG, "Session isn't active", new IllegalStateException());
+ }
}
@Override
- public void unsubscribe_impl(String parentId, Bundle options) {
- // TODO(jaewan): Implement
+ public void unsubscribe_impl(String parentId) {
+ final IMediaSession2 binder = getSessionBinder();
+ if (binder != null) {
+ try {
+ binder.unsubscribe(getControllerStub(), parentId);
+ } catch (RemoteException e) {
+ // TODO(jaewan): Handle disconnect.
+ if (DEBUG) {
+ Log.w(TAG, "Cannot connect to the service or the session is gone", e);
+ }
+ }
+ } else {
+ Log.w(TAG, "Session isn't active", new IllegalStateException());
+ }
}
@Override
@@ -94,7 +117,7 @@
}
@Override
- public void getChildren_impl(String parentId, int page, int pageSize, Bundle options) {
+ public void getChildren_impl(String parentId, int page, int pageSize, Bundle extras) {
if (parentId == null) {
throw new IllegalArgumentException("parentId shouldn't be null");
}
@@ -105,7 +128,7 @@
final IMediaSession2 binder = getSessionBinder();
if (binder != null) {
try {
- binder.getChildren(getControllerStub(), parentId, page, pageSize, options);
+ binder.getChildren(getControllerStub(), parentId, page, pageSize, extras);
} catch (RemoteException e) {
// TODO(jaewan): Handle disconnect.
if (DEBUG) {
@@ -118,27 +141,81 @@
}
@Override
- public void search_impl(String query, int page, int pageSize, Bundle extras) {
- // TODO(jaewan): Implement
+ public void search_impl(String query, Bundle extras) {
+ if (TextUtils.isEmpty(query)) {
+ throw new IllegalArgumentException("query shouldn't be empty");
+ }
+ final IMediaSession2 binder = getSessionBinder();
+ if (binder != null) {
+ try {
+ binder.search(getControllerStub(), query, extras);
+ } catch (RemoteException e) {
+ // TODO(jaewan): Handle disconnect.
+ if (DEBUG) {
+ Log.w(TAG, "Cannot connect to the service or the session is gone", e);
+ }
+ }
+ } else {
+ Log.w(TAG, "Session isn't active", new IllegalStateException());
+ }
}
- public void onGetRootResult(
+ @Override
+ public void getSearchResult_impl(String query, int page, int pageSize, Bundle extras) {
+ if (TextUtils.isEmpty(query)) {
+ throw new IllegalArgumentException("query shouldn't be empty");
+ }
+ final IMediaSession2 binder = getSessionBinder();
+ if (binder != null) {
+ try {
+ binder.getSearchResult(getControllerStub(), query, page, pageSize, extras);
+ } catch (RemoteException e) {
+ // TODO(jaewan): Handle disconnect.
+ if (DEBUG) {
+ Log.w(TAG, "Cannot connect to the service or the session is gone", e);
+ }
+ }
+ } else {
+ Log.w(TAG, "Session isn't active", new IllegalStateException());
+ }
+ }
+
+ public void onGetLibraryRootDone(
final Bundle rootHints, final String rootMediaId, final Bundle rootExtra) {
getCallbackExecutor().execute(() -> {
- mCallback.onGetRootResult(rootHints, rootMediaId, rootExtra);
+ mCallback.onGetLibraryRootDone(rootHints, rootMediaId, rootExtra);
});
}
- public void onItemLoaded(String mediaId, MediaItem2 item) {
+ public void onGetItemDone(String mediaId, MediaItem2 item) {
getCallbackExecutor().execute(() -> {
- mCallback.onItemLoaded(mediaId, item);
+ mCallback.onGetItemDone(mediaId, item);
});
}
- public void onChildrenLoaded(String parentId, int page, int pageSize, Bundle options,
- List<MediaItem2> result) {
+ public void onGetChildrenDone(String parentId, int page, int pageSize, List<MediaItem2> result,
+ Bundle extras) {
getCallbackExecutor().execute(() -> {
- mCallback.onChildrenLoaded(parentId, page, pageSize, options, result);
+ mCallback.onGetChildrenDone(parentId, page, pageSize, result, extras);
+ });
+ }
+
+ public void onSearchResultChanged(String query, int itemCount, Bundle extras) {
+ getCallbackExecutor().execute(() -> {
+ mCallback.onSearchResultChanged(query, itemCount, extras);
+ });
+ }
+
+ public void onGetSearchResultDone(String query, int page, int pageSize, List<MediaItem2> result,
+ Bundle extras) {
+ getCallbackExecutor().execute(() -> {
+ mCallback.onGetSearchResultDone(query, page, pageSize, result, extras);
+ });
+ }
+
+ public void onChildrenChanged(final String parentId, int itemCount, final Bundle extras) {
+ getCallbackExecutor().execute(() -> {
+ mCallback.onChildrenChanged(parentId, itemCount, extras);
});
}
}
diff --git a/packages/MediaComponents/src/com/android/media/MediaController2Impl.java b/packages/MediaComponents/src/com/android/media/MediaController2Impl.java
index 5af4240..4c447b5 100644
--- a/packages/MediaComponents/src/com/android/media/MediaController2Impl.java
+++ b/packages/MediaComponents/src/com/android/media/MediaController2Impl.java
@@ -75,8 +75,6 @@
@GuardedBy("mLock")
private PlaybackInfo mPlaybackInfo;
@GuardedBy("mLock")
- private int mRatingType;
- @GuardedBy("mLock")
private PendingIntent mSessionActivity;
@GuardedBy("mLock")
private CommandGroup mCommandGroup;
@@ -120,16 +118,15 @@
@Override
public void initialize() {
- SessionToken2Impl impl = SessionToken2Impl.from(mToken);
// TODO(jaewan): More sanity checks.
- if (impl.getSessionBinder() == null) {
+ if (mToken.getType() == SessionToken2.TYPE_SESSION) {
+ // Session
+ mServiceConnection = null;
+ connectToSession(SessionToken2Impl.from(mToken).getSessionBinder());
+ } else {
// Session service
mServiceConnection = new SessionServiceConnection();
connectToService();
- } else {
- // Session
- mServiceConnection = null;
- connectToSession(impl.getSessionBinder());
}
}
@@ -164,7 +161,7 @@
private void connectToSession(IMediaSession2 sessionBinder) {
try {
- sessionBinder.connect(mContext.getPackageName(), mSessionCallbackStub);
+ sessionBinder.connect(mSessionCallbackStub, mContext.getPackageName());
} catch (RemoteException e) {
Log.w(TAG, "Failed to call connection request. Framework will retry"
+ " automatically");
@@ -286,11 +283,6 @@
}
@Override
- public int getRatingType_impl() {
- return mRatingType;
- }
-
- @Override
public void setVolumeTo_impl(int value, int flags) {
// TODO(hdmoon): sanity check
final IMediaSession2 binder = mSessionBinder;
@@ -403,9 +395,26 @@
// TODO(jaewan): Handle.
}
}
+
@Override
- public void setRating_impl(Rating2 rating) {
- // TODO(jaewan): Implement
+ public void setRating_impl(String mediaId, Rating2 rating) {
+ if (mediaId == null) {
+ throw new IllegalArgumentException("mediaId shouldn't be null");
+ }
+ if (rating == null) {
+ throw new IllegalArgumentException("rating shouldn't be null");
+ }
+
+ final IMediaSession2 binder = mSessionBinder;
+ if (binder != null) {
+ try {
+ binder.setRating(mSessionCallbackStub, mediaId, rating.toBundle());
+ } catch (RemoteException e) {
+ Log.w(TAG, "Cannot connect to the service or the session is gone", e);
+ }
+ } else {
+ // TODO(jaewan): Handle.
+ }
}
@Override
@@ -563,7 +572,7 @@
// Should be used without a lock to prevent potential deadlock.
void onConnectedNotLocked(IMediaSession2 sessionBinder,
final CommandGroup commandGroup, final PlaybackState2 state, final PlaybackInfo info,
- final PlaylistParams params, final List<MediaItem2> playlist, final int ratingType,
+ final PlaylistParams params, final List<MediaItem2> playlist,
final PendingIntent sessionActivity) {
if (DEBUG) {
Log.d(TAG, "onConnectedNotLocked sessionBinder=" + sessionBinder
@@ -591,7 +600,6 @@
mPlaybackInfo = info;
mPlaylistParams = params;
mPlaylist = playlist;
- mRatingType = ratingType;
mSessionActivity = sessionActivity;
mSessionBinder = sessionBinder;
try {
diff --git a/packages/MediaComponents/src/com/android/media/MediaItem2Impl.java b/packages/MediaComponents/src/com/android/media/MediaItem2Impl.java
index f51e246..4c4ef24 100644
--- a/packages/MediaComponents/src/com/android/media/MediaItem2Impl.java
+++ b/packages/MediaComponents/src/com/android/media/MediaItem2Impl.java
@@ -52,7 +52,7 @@
throw new IllegalArgumentException("dsd shouldn't be null");
}
if (metadata != null && !TextUtils.equals(mediaId, metadata.getMediaId())) {
- throw new IllegalArgumentException("metadata's id should be match with the mediaid");
+ throw new IllegalArgumentException("metadata's id should be matched with the mediaid");
}
mContext = context;
@@ -71,9 +71,9 @@
throw new IllegalArgumentException("mediaId shouldn't be null");
}
if (metadata != null && !TextUtils.equals(mediaId, metadata.getMediaId())) {
- throw new IllegalArgumentException("metadata's id should be match with the mediaid");
+ throw new IllegalArgumentException("metadata's id should be matched with the mediaid");
}
- mContext =context;
+ mContext = context;
mId = mediaId;
mMetadata = metadata;
mFlags = flags;
@@ -136,24 +136,21 @@
}
@Override
- public void setMetadata_impl(@NonNull MediaMetadata2 metadata) {
- if (metadata == null) {
- throw new IllegalArgumentException("metadata shouldn't be null");
- }
- if (TextUtils.isEmpty(metadata.getMediaId())) {
- throw new IllegalArgumentException("metadata must have a non-empty media id");
+ public void setMetadata_impl(@Nullable MediaMetadata2 metadata) {
+ if (metadata != null && !TextUtils.equals(mId, metadata.getMediaId())) {
+ throw new IllegalArgumentException("metadata's id should be matched with the mediaId");
}
mMetadata = metadata;
}
@Override
- public MediaMetadata2 getMetadata_impl() {
+ public @Nullable MediaMetadata2 getMetadata_impl() {
return mMetadata;
}
@Override
- public @Nullable String getMediaId_impl() {
- return mMetadata.getMediaId();
+ public @NonNull String getMediaId_impl() {
+ return mId;
}
@Override
diff --git a/packages/MediaComponents/src/com/android/media/MediaLibraryService2Impl.java b/packages/MediaComponents/src/com/android/media/MediaLibraryService2Impl.java
index b9dffcf..1cce38a 100644
--- a/packages/MediaComponents/src/com/android/media/MediaLibraryService2Impl.java
+++ b/packages/MediaComponents/src/com/android/media/MediaLibraryService2Impl.java
@@ -26,12 +26,12 @@
import android.media.MediaPlayerInterface;
import android.media.MediaSession2;
import android.media.MediaSession2.ControllerInfo;
-import android.media.MediaSession2.SessionCallback;
import android.media.MediaSessionService2;
import android.media.SessionToken2;
import android.media.VolumeProvider2;
import android.media.update.MediaLibraryService2Provider;
import android.os.Bundle;
+import android.text.TextUtils;
import com.android.media.MediaSession2Impl.BuilderBaseImpl;
@@ -66,15 +66,16 @@
public static class MediaLibrarySessionImpl extends MediaSession2Impl
implements MediaLibrarySessionProvider {
- private final MediaLibrarySessionCallback mCallback;
-
public MediaLibrarySessionImpl(Context context,
MediaPlayerInterface player, String id, VolumeProvider2 volumeProvider,
- int ratingType, PendingIntent sessionActivity, Executor callbackExecutor,
+ PendingIntent sessionActivity, Executor callbackExecutor,
MediaLibrarySessionCallback callback) {
- super(context, player, id, volumeProvider, ratingType, sessionActivity,
- callbackExecutor, callback);
- mCallback = callback;
+ super(context, player, id, volumeProvider, sessionActivity, callbackExecutor, callback);
+ // Don't put any extra initialization here. Here's the reason.
+ // System service will recognize this session inside of the super constructor and would
+ // connect to this session assuming that initialization is finished. However, if any
+ // initialization logic is here, calls from the server would fail.
+ // see: MediaSession2Stub#connect()
}
@Override
@@ -89,19 +90,41 @@
@Override
MediaLibrarySessionCallback getCallback() {
- // Equivalent to the (MediaLibrarySessionCallback) super.getCallback().
- return mCallback;
+ return (MediaLibrarySessionCallback) super.getCallback();
}
@Override
public void notifyChildrenChanged_impl(ControllerInfo controller, String parentId,
- Bundle options) {
- // TODO(jaewan): Implements
+ int itemCount, Bundle extras) {
+ if (controller == null) {
+ throw new IllegalArgumentException("controller shouldn't be null");
+ }
+ if (parentId == null) {
+ throw new IllegalArgumentException("parentId shouldn't be null");
+ }
+ getSessionStub().notifyChildrenChangedNotLocked(controller, parentId, itemCount,
+ extras);
}
@Override
- public void notifyChildrenChanged_impl(String parentId, Bundle options) {
- // TODO(jaewan): Implements
+ public void notifyChildrenChanged_impl(String parentId, int itemCount, Bundle extras) {
+ if (parentId == null) {
+ throw new IllegalArgumentException("parentId shouldn't be null");
+ }
+ getSessionStub().notifyChildrenChangedNotLocked(parentId, itemCount, extras);
+ }
+
+ @Override
+ public void notifySearchResultChanged_impl(ControllerInfo controller, String query,
+ int itemCount, Bundle extras) {
+ ensureCallingThread();
+ if (controller == null) {
+ throw new IllegalArgumentException("controller shouldn't be null");
+ }
+ if (TextUtils.isEmpty(query)) {
+ throw new IllegalArgumentException("query shouldn't be empty");
+ }
+ getSessionStub().notifySearchResultChanged(controller, query, itemCount, extras);
}
}
@@ -116,7 +139,7 @@
@Override
public MediaLibrarySession build_impl() {
- return new MediaLibrarySessionImpl(mContext, mPlayer, mId, mVolumeProvider, mRatingType,
+ return new MediaLibrarySessionImpl(mContext, mPlayer, mId, mVolumeProvider,
mSessionActivity, mCallbackExecutor, mCallback).getInstance();
}
}
@@ -147,4 +170,4 @@
return mExtras;
}
}
-}
\ No newline at end of file
+}
diff --git a/packages/MediaComponents/src/com/android/media/MediaMetadata2Impl.java b/packages/MediaComponents/src/com/android/media/MediaMetadata2Impl.java
index 9088029..7b2ee32 100644
--- a/packages/MediaComponents/src/com/android/media/MediaMetadata2Impl.java
+++ b/packages/MediaComponents/src/com/android/media/MediaMetadata2Impl.java
@@ -38,16 +38,11 @@
public class MediaMetadata2Impl implements MediaMetadata2Provider {
private static final String TAG = "MediaMetadata2";
- /**
- * A {@link Bundle} extra.
- * @hide
- */
- public static final String METADATA_KEY_EXTRA = "android.media.metadata.EXTRA";
-
static final int METADATA_TYPE_LONG = 0;
static final int METADATA_TYPE_TEXT = 1;
static final int METADATA_TYPE_BITMAP = 2;
static final int METADATA_TYPE_RATING = 3;
+ static final int METADATA_TYPE_FLOAT = 4;
static final ArrayMap<String, Integer> METADATA_KEYS_TYPE;
static {
@@ -83,6 +78,8 @@
METADATA_KEYS_TYPE.put(METADATA_KEY_MEDIA_URI, METADATA_TYPE_TEXT);
METADATA_KEYS_TYPE.put(METADATA_KEY_ADVERTISEMENT, METADATA_TYPE_LONG);
METADATA_KEYS_TYPE.put(METADATA_KEY_DOWNLOAD_STATUS, METADATA_TYPE_LONG);
+ METADATA_KEYS_TYPE.put(METADATA_KEY_RADIO_FREQUENCY, METADATA_TYPE_FLOAT);
+ METADATA_KEYS_TYPE.put(METADATA_KEY_RADIO_CALLSIGN, METADATA_TYPE_TEXT);
}
private static final @TextKey
@@ -167,6 +164,11 @@
}
@Override
+ public float getFloat_impl(@FloatKey String key) {
+ return mBundle.getFloat(key);
+ }
+
+ @Override
public Bitmap getBitmap_impl(@BitmapKey String key) {
Bitmap bmp = null;
try {
@@ -179,9 +181,9 @@
}
@Override
- public Bundle getExtra_impl() {
+ public Bundle getExtras_impl() {
try {
- return mBundle.getBundle(METADATA_KEY_EXTRA);
+ return mBundle.getBundle(METADATA_KEY_EXTRAS);
} catch (Exception e) {
// ignore, value was not an bundle
Log.w(TAG, "Failed to retrieve an extra");
@@ -305,8 +307,20 @@
}
@Override
- public Builder setExtra_impl(Bundle bundle) {
- mBundle.putBundle(METADATA_KEY_EXTRA, bundle);
+ public Builder putFloat_impl(@FloatKey String key, float value) {
+ if (METADATA_KEYS_TYPE.containsKey(key)) {
+ if (METADATA_KEYS_TYPE.get(key) != METADATA_TYPE_FLOAT) {
+ throw new IllegalArgumentException("The " + key
+ + " key cannot be used to put a float");
+ }
+ }
+ mBundle.putFloat(key, value);
+ return mInstance;
+ }
+
+ @Override
+ public Builder setExtras_impl(Bundle bundle) {
+ mBundle.putBundle(METADATA_KEY_EXTRAS, bundle);
return mInstance;
}
diff --git a/packages/MediaComponents/src/com/android/media/MediaSession2CallbackStub.java b/packages/MediaComponents/src/com/android/media/MediaSession2CallbackStub.java
index 1f4d12b..2178ccd 100644
--- a/packages/MediaComponents/src/com/android/media/MediaSession2CallbackStub.java
+++ b/packages/MediaComponents/src/com/android/media/MediaSession2CallbackStub.java
@@ -125,7 +125,7 @@
@Override
public void onConnected(IMediaSession2 sessionBinder, Bundle commandGroup,
Bundle playbackState, Bundle playbackInfo, Bundle playlistParams, List<Bundle>
- playlist, int ratingType, PendingIntent sessionActivity) {
+ playlist, PendingIntent sessionActivity) {
final MediaController2Impl controller = mController.get();
if (controller == null) {
if (DEBUG) {
@@ -146,7 +146,7 @@
PlaybackState2.fromBundle(context, playbackState),
PlaybackInfoImpl.fromBundle(context, playbackInfo),
PlaylistParams.fromBundle(context, playlistParams),
- list, ratingType, sessionActivity);
+ list, sessionActivity);
}
@Override
@@ -209,7 +209,7 @@
// MediaBrowser specific
////////////////////////////////////////////////////////////////////////////////////////////
@Override
- public void onGetRootResult(Bundle rootHints, String rootMediaId, Bundle rootExtra)
+ public void onGetLibraryRootDone(Bundle rootHints, String rootMediaId, Bundle rootExtra)
throws RuntimeException {
final MediaBrowser2Impl browser;
try {
@@ -222,12 +222,12 @@
// TODO(jaewan): Revisit here. Could be a bug
return;
}
- browser.onGetRootResult(rootHints, rootMediaId, rootExtra);
+ browser.onGetLibraryRootDone(rootHints, rootMediaId, rootExtra);
}
@Override
- public void onItemLoaded(String mediaId, Bundle itemBundle) throws RuntimeException {
+ public void onGetItemDone(String mediaId, Bundle itemBundle) throws RuntimeException {
final MediaBrowser2Impl browser;
try {
browser = getBrowser();
@@ -239,13 +239,13 @@
// TODO(jaewan): Revisit here. Could be a bug
return;
}
- browser.onItemLoaded(mediaId,
+ browser.onGetItemDone(mediaId,
MediaItem2Impl.fromBundle(browser.getContext(), itemBundle));
}
@Override
- public void onChildrenLoaded(String parentId, int page, int pageSize, Bundle options,
- List<Bundle> itemBundleList) throws RuntimeException {
+ public void onGetChildrenDone(String parentId, int page, int pageSize,
+ List<Bundle> itemBundleList, Bundle extras) throws RuntimeException {
final MediaBrowser2Impl browser;
try {
browser = getBrowser();
@@ -265,6 +265,64 @@
result.add(MediaItem2.fromBundle(browser.getContext(), bundle));
}
}
- browser.onChildrenLoaded(parentId, page, pageSize, options, result);
+ browser.onGetChildrenDone(parentId, page, pageSize, result, extras);
+ }
+
+ @Override
+ public void onSearchResultChanged(String query, int itemCount, Bundle extras)
+ throws RuntimeException {
+ final MediaBrowser2Impl browser;
+ try {
+ browser = getBrowser();
+ } catch (IllegalStateException e) {
+ Log.w(TAG, "Don't fail silently here. Highly likely a bug");
+ return;
+ }
+ if (browser == null) {
+ // TODO(jaewan): Revisit here. Could be a bug
+ return;
+ }
+ browser.onSearchResultChanged(query, itemCount, extras);
+ }
+
+ @Override
+ public void onGetSearchResultDone(String query, int page, int pageSize,
+ List<Bundle> itemBundleList, Bundle extras) throws RuntimeException {
+ final MediaBrowser2Impl browser;
+ try {
+ browser = getBrowser();
+ } catch (IllegalStateException e) {
+ Log.w(TAG, "Don't fail silently here. Highly likely a bug");
+ return;
+ }
+ if (browser == null) {
+ // TODO(jaewan): Revisit here. Could be a bug
+ return;
+ }
+
+ List<MediaItem2> result = null;
+ if (itemBundleList != null) {
+ result = new ArrayList<>();
+ for (Bundle bundle : itemBundleList) {
+ result.add(MediaItem2.fromBundle(browser.getContext(), bundle));
+ }
+ }
+ browser.onGetSearchResultDone(query, page, pageSize, result, extras);
+ }
+
+ @Override
+ public void onChildrenChanged(String parentId, int itemCount, Bundle extras) {
+ final MediaBrowser2Impl browser;
+ try {
+ browser = getBrowser();
+ } catch (IllegalStateException e) {
+ Log.w(TAG, "Don't fail silently here. Highly likely a bug");
+ return;
+ }
+ if (browser == null) {
+ // TODO(jaewan): Revisit here. Could be a bug
+ return;
+ }
+ browser.onChildrenChanged(parentId, itemCount, extras);
}
}
diff --git a/packages/MediaComponents/src/com/android/media/MediaSession2Impl.java b/packages/MediaComponents/src/com/android/media/MediaSession2Impl.java
index 4a9a729..40a14a1 100644
--- a/packages/MediaComponents/src/com/android/media/MediaSession2Impl.java
+++ b/packages/MediaComponents/src/com/android/media/MediaSession2Impl.java
@@ -38,7 +38,7 @@
import android.media.MediaLibraryService2;
import android.media.MediaMetadata2;
import android.media.MediaPlayerInterface;
-import android.media.MediaPlayerInterface.PlaybackListener;
+import android.media.MediaPlayerInterface.EventCallback;
import android.media.MediaSession2;
import android.media.MediaSession2.Builder;
import android.media.MediaSession2.Command;
@@ -62,6 +62,7 @@
import android.os.ResultReceiver;
import android.support.annotation.GuardedBy;
import android.text.TextUtils;
+import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Log;
@@ -84,8 +85,7 @@
private final MediaSession2Stub mSessionStub;
private final SessionToken2 mSessionToken;
private final AudioManager mAudioManager;
- private final List<PlaybackListenerHolder> mListeners = new ArrayList<>();
- private final int mRatingType;
+ private final ArrayMap<EventCallback, Executor> mCallbacks = new ArrayMap<>();
private final PendingIntent mSessionActivity;
// mPlayer is set to null when the session is closed, and we shouldn't throw an exception
@@ -111,7 +111,7 @@
@GuardedBy("mLock")
private PlaybackInfo mPlaybackInfo;
@GuardedBy("mLock")
- private MyPlaybackListener mListener;
+ private MyEventCallback mEventCallback;
/**
* Can be only called by the {@link Builder#build()}.
@@ -119,13 +119,13 @@
* @param context
* @param player
* @param id
- * @param callback
* @param volumeProvider
- * @param ratingType
* @param sessionActivity
+ * @param callbackExecutor
+ * @param callback
*/
public MediaSession2Impl(Context context, MediaPlayerInterface player, String id,
- VolumeProvider2 volumeProvider, int ratingType, PendingIntent sessionActivity,
+ VolumeProvider2 volumeProvider, PendingIntent sessionActivity,
Executor callbackExecutor, SessionCallback callback) {
// TODO(jaewan): Keep other params.
mInstance = createInstance();
@@ -136,7 +136,6 @@
mId = id;
mCallback = callback;
mCallbackExecutor = callbackExecutor;
- mRatingType = ratingType;
mSessionActivity = sessionActivity;
mSessionStub = new MediaSession2Stub(this);
mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
@@ -149,10 +148,10 @@
+ " session services define the same id=" + id);
} else if (libraryService != null) {
mSessionToken = new SessionToken2Impl(context, Process.myUid(), TYPE_LIBRARY_SERVICE,
- mContext.getPackageName(), libraryService, id, mSessionStub).getInstance();
+ mContext.getPackageName(), libraryService, id, null).getInstance();
} else if (sessionService != null) {
mSessionToken = new SessionToken2Impl(context, Process.myUid(), TYPE_SESSION_SERVICE,
- mContext.getPackageName(), sessionService, id, mSessionStub).getInstance();
+ mContext.getPackageName(), sessionService, id, null).getInstance();
} else {
mSessionToken = new SessionToken2Impl(context, Process.myUid(), TYPE_SESSION,
mContext.getPackageName(), null, id, mSessionStub).getInstance();
@@ -167,7 +166,7 @@
// a session in another package.
MediaSessionManager manager =
(MediaSessionManager) mContext.getSystemService(Context.MEDIA_SESSION_SERVICE);
- if (!manager.onSessionCreated(mSessionToken)) {
+ if (!manager.createSession2(mSessionToken)) {
throw new IllegalStateException("Session with the same id is already used by"
+ " another process. Use MediaController2 instead.");
}
@@ -225,19 +224,20 @@
}
private void setPlayer(MediaPlayerInterface player, VolumeProvider2 volumeProvider) {
- PlaybackInfo info = createPlaybackInfo(volumeProvider, player.getAudioAttributes());
+ final PlaybackInfo info = createPlaybackInfo(volumeProvider, player.getAudioAttributes());
synchronized (mLock) {
- if (mPlayer != null && mListener != null) {
+ if (mPlayer != null && mEventCallback != null) {
// This might not work for a poorly implemented player.
- mPlayer.removePlaybackListener(mListener);
+ mPlayer.unregisterEventCallback(mEventCallback);
}
mPlayer = player;
- mListener = new MyPlaybackListener(this, player);
- player.addPlaybackListener(mCallbackExecutor, mListener);
+ mEventCallback = new MyEventCallback(this, player);
+ player.registerEventCallback(mCallbackExecutor, mEventCallback);
mVolumeProvider = volumeProvider;
mPlaybackInfo = info;
}
mSessionStub.notifyPlaybackInfoChanged(info);
+ notifyPlaybackStateChangedNotLocked(mInstance.getPlaybackState());
}
private PlaybackInfo createPlaybackInfo(VolumeProvider2 volumeProvider, AudioAttributes attrs) {
@@ -281,7 +281,7 @@
// Stop system service from listening this session first.
MediaSessionManager manager =
(MediaSessionManager) mContext.getSystemService(Context.MEDIA_SESSION_SERVICE);
- manager.onSessionDestroyed(mSessionToken);
+ manager.destroySession2(mSessionToken);
if (mSessionStub != null) {
if (DEBUG) {
@@ -293,7 +293,7 @@
synchronized (mLock) {
if (mPlayer != null) {
// close can be called multiple times
- mPlayer.removePlaybackListener(mListener);
+ mPlayer.unregisterEventCallback(mEventCallback);
mPlayer = null;
}
}
@@ -522,32 +522,31 @@
}
@Override
- public void addPlaybackListener_impl(Executor executor, PlaybackListener listener) {
+ public void registerPlayerEventCallback_impl(Executor executor, EventCallback callback) {
if (executor == null) {
throw new IllegalArgumentException("executor shouldn't be null");
}
- if (listener == null) {
- throw new IllegalArgumentException("listener shouldn't be null");
+ if (callback == null) {
+ throw new IllegalArgumentException("callback shouldn't be null");
}
ensureCallingThread();
- if (PlaybackListenerHolder.contains(mListeners, listener)) {
- Log.w(TAG, "listener is already added. Ignoring.");
+ if (mCallbacks.get(callback) != null) {
+ Log.w(TAG, "callback is already added. Ignoring.");
return;
}
- mListeners.add(new PlaybackListenerHolder(executor, listener));
- executor.execute(() -> listener.onPlaybackChanged(getInstance().getPlaybackState()));
+ mCallbacks.put(callback, executor);
+ // TODO(jaewan): Double check if we need this.
+ final PlaybackState2 state = getInstance().getPlaybackState();
+ executor.execute(() -> callback.onPlaybackStateChanged(state));
}
@Override
- public void removePlaybackListener_impl(PlaybackListener listener) {
- if (listener == null) {
- throw new IllegalArgumentException("listener shouldn't be null");
+ public void unregisterPlayerEventCallback_impl(EventCallback callback) {
+ if (callback == null) {
+ throw new IllegalArgumentException("callback shouldn't be null");
}
ensureCallingThread();
- int idx = PlaybackListenerHolder.indexOf(mListeners, listener);
- if (idx >= 0) {
- mListeners.remove(idx);
- }
+ mCallbacks.remove(callback);
}
@Override
@@ -580,7 +579,7 @@
// 1. Allow calls from random threads for all methods.
// 2. Allow calls from random threads for all methods, except for the
// {@link #setPlayer()}.
- private void ensureCallingThread() {
+ void ensureCallingThread() {
// TODO(jaewan): Uncomment or remove
/*
if (mHandler.getLooper() != Looper.myLooper()) {
@@ -588,19 +587,35 @@
}*/
}
- private void notifyPlaybackStateChangedNotLocked(PlaybackState2 state) {
- List<PlaybackListenerHolder> listeners = new ArrayList<>();
+ private void notifyPlaybackStateChangedNotLocked(final PlaybackState2 state) {
+ ArrayMap<EventCallback, Executor> callbacks = new ArrayMap<>();
synchronized (mLock) {
- listeners.addAll(mListeners);
+ callbacks.putAll(mCallbacks);
}
- // Notify to listeners added directly to this session
- for (int i = 0; i < listeners.size(); i++) {
- listeners.get(i).postPlaybackChange(state);
+ // Notify to callbacks added directly to this session
+ for (int i = 0; i < callbacks.size(); i++) {
+ final EventCallback callback = callbacks.keyAt(i);
+ final Executor executor = callbacks.valueAt(i);
+ executor.execute(() -> callback.onPlaybackStateChanged(state));
}
// Notify to controllers as well.
mSessionStub.notifyPlaybackStateChangedNotLocked(state);
}
+ private void notifyErrorNotLocked(String mediaId, int what, int extra) {
+ ArrayMap<EventCallback, Executor> callbacks = new ArrayMap<>();
+ synchronized (mLock) {
+ callbacks.putAll(mCallbacks);
+ }
+ // Notify to callbacks added directly to this session
+ for (int i = 0; i < callbacks.size(); i++) {
+ final EventCallback callback = callbacks.keyAt(i);
+ final Executor executor = callbacks.valueAt(i);
+ executor.execute(() -> callback.onError(mediaId, what, extra));
+ }
+ // TODO(jaewan): Notify to controllers as well.
+ }
+
Context getContext() {
return mContext;
}
@@ -621,6 +636,10 @@
return mCallback;
}
+ MediaSession2Stub getSessionStub() {
+ return mSessionStub;
+ }
+
VolumeProvider2 getVolumeProvider() {
return mVolumeProvider;
}
@@ -631,33 +650,47 @@
}
}
- int getRatingType() {
- return mRatingType;
- }
-
PendingIntent getSessionActivity() {
return mSessionActivity;
}
- private static class MyPlaybackListener implements MediaPlayerInterface.PlaybackListener {
+ private static class MyEventCallback implements EventCallback {
private final WeakReference<MediaSession2Impl> mSession;
private final MediaPlayerInterface mPlayer;
- private MyPlaybackListener(MediaSession2Impl session, MediaPlayerInterface player) {
+ private MyEventCallback(MediaSession2Impl session, MediaPlayerInterface player) {
mSession = new WeakReference<>(session);
mPlayer = player;
}
@Override
- public void onPlaybackChanged(PlaybackState2 state) {
+ public void onPlaybackStateChanged(PlaybackState2 state) {
MediaSession2Impl session = mSession.get();
if (mPlayer != session.mInstance.getPlayer()) {
Log.w(TAG, "Unexpected playback state change notifications. Ignoring.",
new IllegalStateException());
return;
}
+ if (DEBUG) {
+ Log.d(TAG, "onPlaybackStateChanged from player, state=" + state);
+ }
session.notifyPlaybackStateChangedNotLocked(state);
}
+
+ @Override
+ public void onError(String mediaId, int what, int extra) {
+ MediaSession2Impl session = mSession.get();
+ if (mPlayer != session.mInstance.getPlayer()) {
+ Log.w(TAG, "Unexpected playback state change notifications. Ignoring.",
+ new IllegalStateException());
+ return;
+ }
+ if (DEBUG) {
+ Log.d(TAG, "onError from player, mediaId=" + mediaId + ", what=" + what
+ + ", extra=" + extra);
+ }
+ session.notifyErrorNotLocked(mediaId, what, extra);
+ }
}
public static final class CommandImpl implements CommandProvider {
@@ -1167,7 +1200,6 @@
Executor mCallbackExecutor;
C mCallback;
VolumeProvider2 mVolumeProvider;
- int mRatingType;
PendingIntent mSessionActivity;
/**
@@ -1196,10 +1228,6 @@
mVolumeProvider = volumeProvider;
}
- public void setRatingType_impl(int type) {
- mRatingType = type;
- }
-
public void setSessionActivity_impl(PendingIntent pi) {
mSessionActivity = pi;
}
@@ -1239,7 +1267,7 @@
mCallback = new SessionCallback(mContext);
}
- return new MediaSession2Impl(mContext, mPlayer, mId, mVolumeProvider, mRatingType,
+ return new MediaSession2Impl(mContext, mPlayer, mId, mVolumeProvider,
mSessionActivity, mCallbackExecutor, mCallback).getInstance();
}
}
diff --git a/packages/MediaComponents/src/com/android/media/MediaSession2Stub.java b/packages/MediaComponents/src/com/android/media/MediaSession2Stub.java
index f160ef8..7391c3c 100644
--- a/packages/MediaComponents/src/com/android/media/MediaSession2Stub.java
+++ b/packages/MediaComponents/src/com/android/media/MediaSession2Stub.java
@@ -28,6 +28,7 @@
import android.media.MediaSession2.ControllerInfo;
import android.media.MediaSession2.PlaylistParams;
import android.media.PlaybackState2;
+import android.media.Rating2;
import android.media.VolumeProvider2;
import android.net.Uri;
import android.os.Binder;
@@ -36,6 +37,7 @@
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.support.annotation.GuardedBy;
+import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.Log;
@@ -46,6 +48,8 @@
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;
+import java.util.Set;
+import java.util.HashSet;
public class MediaSession2Stub extends IMediaSession2.Stub {
@@ -62,6 +66,8 @@
@GuardedBy("mLock")
private final ArrayMap<IBinder, ControllerInfo> mControllers = new ArrayMap<>();
+ @GuardedBy("mLock")
+ private final ArrayMap<ControllerInfo, Set<String>> mSubscriptions = new ArrayMap<>();
public MediaSession2Stub(MediaSession2Impl session) {
mSession = new WeakReference<>(session);
@@ -75,11 +81,11 @@
mControllers.clear();
}
for (int i = 0; i < list.size(); i++) {
- IMediaSession2Callback callbackBinder =
+ IMediaSession2Callback controllerBinder =
((ControllerInfoImpl) list.get(i).getProvider()).getControllerBinder();
try {
// Should be used without a lock hold to prevent potential deadlock.
- callbackBinder.onDisconnected();
+ controllerBinder.onDisconnected();
} catch (RemoteException e) {
// Controller is gone. Should be fine because we're destroying.
}
@@ -116,30 +122,30 @@
//////////////////////////////////////////////////////////////////////////////////////////////
@Override
- public void connect(String callingPackage, final IMediaSession2Callback callback)
+ public void connect(final IMediaSession2Callback caller, String callingPackage)
throws RuntimeException {
final MediaSession2Impl sessionImpl = getSession();
final Context context = sessionImpl.getContext();
- final ControllerInfo request = new ControllerInfo(context,
- Binder.getCallingUid(), Binder.getCallingPid(), callingPackage, callback);
+ final ControllerInfo controllerInfo = new ControllerInfo(context,
+ Binder.getCallingUid(), Binder.getCallingPid(), callingPackage, caller);
sessionImpl.getCallbackExecutor().execute(() -> {
final MediaSession2Impl session = mSession.get();
if (session == null) {
return;
}
- CommandGroup allowedCommands = session.getCallback().onConnect(request);
- // Don't reject connection for the request from trusted app.
+ CommandGroup allowedCommands = session.getCallback().onConnect(controllerInfo);
+ // Don't reject connection for the controllerInfo from trusted app.
// Otherwise server will fail to retrieve session's information to dispatch
// media keys to.
- boolean accept = allowedCommands != null || request.isTrusted();
- ControllerInfoImpl impl = ControllerInfoImpl.from(request);
- if (accept && allowedCommands != null) {
+ boolean accept = allowedCommands != null || controllerInfo.isTrusted();
+ ControllerInfoImpl impl = ControllerInfoImpl.from(controllerInfo);
+ if (accept) {
if (DEBUG) {
- Log.d(TAG, "Accepting connection, request=" + request
+ Log.d(TAG, "Accepting connection, controllerInfo=" + controllerInfo
+ " allowedCommands=" + allowedCommands);
}
synchronized (mLock) {
- mControllers.put(impl.getId(), request);
+ mControllers.put(impl.getId(), controllerInfo);
}
if (allowedCommands == null) {
// For trusted apps, send non-null allowed commands to keep connection.
@@ -154,11 +160,10 @@
// TODO(jaewan): Should we protect getting playback state?
final PlaybackState2 state = session.getInstance().getPlaybackState();
final Bundle playbackStateBundle = (state != null) ? state.toBundle() : null;
- final Bundle playbackInfoBundle =
- ((MediaController2Impl.PlaybackInfoImpl) session.getPlaybackInfo().getProvider()).toBundle();
+ final Bundle playbackInfoBundle = ((MediaController2Impl.PlaybackInfoImpl)
+ session.getPlaybackInfo().getProvider()).toBundle();
final PlaylistParams params = session.getInstance().getPlaylistParams();
final Bundle paramsBundle = (params != null) ? params.toBundle() : null;
- final int ratingType = session.getRatingType();
final PendingIntent sessionActivity = session.getSessionActivity();
final List<MediaItem2> playlist = session.getInstance().getPlaylist();
final List<Bundle> playlistBundle = new ArrayList<>();
@@ -181,19 +186,19 @@
return;
}
try {
- callback.onConnected(MediaSession2Stub.this,
+ caller.onConnected(MediaSession2Stub.this,
allowedCommands.toBundle(), playbackStateBundle, playbackInfoBundle,
- paramsBundle, playlistBundle, ratingType, sessionActivity);
+ paramsBundle, playlistBundle, sessionActivity);
} catch (RemoteException e) {
// Controller may be died prematurely.
// TODO(jaewan): Handle here.
}
} else {
if (DEBUG) {
- Log.d(TAG, "Rejecting connection, request=" + request);
+ Log.d(TAG, "Rejecting connection, controllerInfo=" + controllerInfo);
}
try {
- callback.onDisconnected();
+ caller.onDisconnected();
} catch (RemoteException e) {
// Controller may be died prematurely.
// Not an issue because we'll ignore it anyway.
@@ -209,6 +214,7 @@
if (DEBUG) {
Log.d(TAG, "releasing " + controllerInfo);
}
+ mSubscriptions.remove(controllerInfo);
}
}
@@ -388,7 +394,7 @@
@Override
public void prepareFromUri(final IMediaSession2Callback caller, final Uri uri,
- final Bundle extra) {
+ final Bundle extras) {
final MediaSession2Impl sessionImpl = getSession();
final ControllerInfo controller = getController(caller);
if (controller == null) {
@@ -402,13 +408,13 @@
if (session == null) {
return;
}
- session.getCallback().onPrepareFromUri(controller, uri, extra);
+ session.getCallback().onPrepareFromUri(controller, uri, extras);
});
}
@Override
public void prepareFromSearch(final IMediaSession2Callback caller, final String query,
- final Bundle extra) {
+ final Bundle extras) {
final MediaSession2Impl sessionImpl = getSession();
final ControllerInfo controller = getController(caller);
if (controller == null) {
@@ -422,13 +428,13 @@
if (session == null) {
return;
}
- session.getCallback().onPrepareFromSearch(controller, query, extra);
+ session.getCallback().onPrepareFromSearch(controller, query, extras);
});
}
@Override
public void prepareFromMediaId(final IMediaSession2Callback caller, final String mediaId,
- final Bundle extra) {
+ final Bundle extras) {
final MediaSession2Impl sessionImpl = getSession();
final ControllerInfo controller = getController(caller);
if (controller == null) {
@@ -442,13 +448,13 @@
if (session == null) {
return;
}
- session.getCallback().onPrepareFromMediaId(controller, mediaId, extra);
+ session.getCallback().onPrepareFromMediaId(controller, mediaId, extras);
});
}
@Override
public void playFromUri(final IMediaSession2Callback caller, final Uri uri,
- final Bundle extra) {
+ final Bundle extras) {
final MediaSession2Impl sessionImpl = getSession();
final ControllerInfo controller = getController(caller);
if (controller == null) {
@@ -462,13 +468,13 @@
if (session == null) {
return;
}
- session.getCallback().onPlayFromUri(controller, uri, extra);
+ session.getCallback().onPlayFromUri(controller, uri, extras);
});
}
@Override
public void playFromSearch(final IMediaSession2Callback caller, final String query,
- final Bundle extra) {
+ final Bundle extras) {
final MediaSession2Impl sessionImpl = getSession();
final ControllerInfo controller = getController(caller);
if (controller == null) {
@@ -482,13 +488,13 @@
if (session == null) {
return;
}
- session.getCallback().onPlayFromSearch(controller, query, extra);
+ session.getCallback().onPlayFromSearch(controller, query, extras);
});
}
@Override
public void playFromMediaId(final IMediaSession2Callback caller, final String mediaId,
- final Bundle extra) {
+ final Bundle extras) {
final MediaSession2Impl sessionImpl = getSession();
final ControllerInfo controller = getController(caller);
if (controller == null) {
@@ -502,7 +508,28 @@
if (session == null) {
return;
}
- session.getCallback().onPlayFromMediaId(controller, mediaId, extra);
+ session.getCallback().onPlayFromMediaId(controller, mediaId, extras);
+ });
+ }
+
+ @Override
+ public void setRating(final IMediaSession2Callback caller, final String mediaId,
+ final Bundle ratingBundle) {
+ final MediaSession2Impl sessionImpl = getSession();
+ final ControllerInfo controller = getController(caller);
+ if (controller == null) {
+ if (DEBUG) {
+ Log.d(TAG, "Command from a controller that hasn't connected. Ignore");
+ }
+ return;
+ }
+ sessionImpl.getCallbackExecutor().execute(() -> {
+ final MediaSession2Impl session = mSession.get();
+ if (session == null) {
+ return;
+ }
+ Rating2 rating = Rating2Impl.fromBundle(session.getContext(), ratingBundle);
+ session.getCallback().onSetRating(controller, mediaId, rating);
});
}
@@ -527,9 +554,9 @@
return;
}
final ControllerInfoImpl controllerImpl = ControllerInfoImpl.from(controller);
- LibraryRoot root = session.getCallback().onGetRoot(controller, rootHints);
+ LibraryRoot root = session.getCallback().onGetLibraryRoot(controller, rootHints);
try {
- controllerImpl.getControllerBinder().onGetRootResult(rootHints,
+ controllerImpl.getControllerBinder().onGetLibraryRootDone(rootHints,
root == null ? null : root.getRootId(),
root == null ? null : root.getExtras());
} catch (RemoteException e) {
@@ -561,9 +588,9 @@
return;
}
final ControllerInfoImpl controllerImpl = ControllerInfoImpl.from(controller);
- MediaItem2 result = session.getCallback().onLoadItem(controller, mediaId);
+ MediaItem2 result = session.getCallback().onGetItem(controller, mediaId);
try {
- controllerImpl.getControllerBinder().onItemLoaded(
+ controllerImpl.getControllerBinder().onGetItemDone(
mediaId, result == null ? null : result.toBundle());
} catch (RemoteException e) {
// Controller may be died prematurely.
@@ -574,7 +601,7 @@
@Override
public void getChildren(IMediaSession2Callback caller, String parentId, int page,
- int pageSize, Bundle options) throws RuntimeException {
+ int pageSize, Bundle extras) throws RuntimeException {
final MediaLibrarySessionImpl sessionImpl = getLibrarySession();
final ControllerInfo controller = getController(caller);
if (controller == null) {
@@ -584,11 +611,15 @@
return;
}
if (parentId == null) {
- Log.d(TAG, "parentId shouldn't be null");
+ if (DEBUG) {
+ Log.d(TAG, "parentId shouldn't be null");
+ }
return;
}
if (page < 1 || pageSize < 1) {
- Log.d(TAG, "Neither page nor pageSize should be less than 1");
+ if (DEBUG) {
+ Log.d(TAG, "Neither page nor pageSize should be less than 1");
+ }
return;
}
@@ -598,10 +629,10 @@
return;
}
final ControllerInfoImpl controllerImpl = ControllerInfoImpl.from(controller);
- List<MediaItem2> result = session.getCallback().onLoadChildren(
- controller, parentId, page, pageSize, options);
+ List<MediaItem2> result = session.getCallback().onGetChildren(
+ controller, parentId, page, pageSize, extras);
if (result != null && result.size() > pageSize) {
- throw new IllegalArgumentException("onLoadChildren() shouldn't return media items "
+ throw new IllegalArgumentException("onGetChildren() shouldn't return media items "
+ "more than pageSize. result.size()=" + result.size() + " pageSize="
+ pageSize);
}
@@ -615,8 +646,8 @@
}
try {
- controllerImpl.getControllerBinder().onChildrenLoaded(
- parentId, page, pageSize, options, bundleList);
+ controllerImpl.getControllerBinder().onGetChildrenDone(
+ parentId, page, pageSize, bundleList, extras);
} catch (RemoteException e) {
// Controller may be died prematurely.
// TODO(jaewan): Handle this.
@@ -624,6 +655,139 @@
});
}
+ @Override
+ public void search(IMediaSession2Callback caller, String query, Bundle extras) {
+ final MediaLibrarySessionImpl sessionImpl = getLibrarySession();
+ final ControllerInfo controller = getController(caller);
+ if (controller == null) {
+ if (DEBUG) {
+ Log.d(TAG, "search() from a controller that hasn't connected. Ignore");
+ }
+ return;
+ }
+ if (TextUtils.isEmpty(query)) {
+ if (DEBUG) {
+ Log.d(TAG, "query shouldn't be empty");
+ }
+ return;
+ }
+
+ sessionImpl.getCallbackExecutor().execute(() -> {
+ final MediaLibrarySessionImpl session = getLibrarySession();
+ if (session == null) {
+ return;
+ }
+ final ControllerInfoImpl controllerImpl = ControllerInfoImpl.from(controller);
+ session.getCallback().onSearch(controller, query, extras);
+ });
+ }
+
+ @Override
+ public void getSearchResult(IMediaSession2Callback caller, String query, int page,
+ int pageSize, Bundle extras) {
+ final MediaLibrarySessionImpl sessionImpl = getLibrarySession();
+ final ControllerInfo controller = getController(caller);
+ if (controller == null) {
+ if (DEBUG) {
+ Log.d(TAG, "getSearchResult() from a controller that hasn't connected. Ignore");
+ }
+ return;
+ }
+ if (TextUtils.isEmpty(query)) {
+ if (DEBUG) {
+ Log.d(TAG, "query shouldn't be empty");
+ }
+ return;
+ }
+ if (page < 1 || pageSize < 1) {
+ if (DEBUG) {
+ Log.d(TAG, "Neither page nor pageSize should be less than 1");
+ }
+ return;
+ }
+
+ sessionImpl.getCallbackExecutor().execute(() -> {
+ final MediaLibrarySessionImpl session = getLibrarySession();
+ if (session == null) {
+ return;
+ }
+ final ControllerInfoImpl controllerImpl = ControllerInfoImpl.from(controller);
+ List<MediaItem2> result = session.getCallback().onGetSearchResult(
+ controller, query, page, pageSize, extras);
+ if (result != null && result.size() > pageSize) {
+ throw new IllegalArgumentException("onGetSearchResult() shouldn't return media "
+ + "items more than pageSize. result.size()=" + result.size() + " pageSize="
+ + pageSize);
+ }
+
+ List<Bundle> bundleList = null;
+ if (result != null) {
+ bundleList = new ArrayList<>();
+ for (MediaItem2 item : result) {
+ bundleList.add(item == null ? null : item.toBundle());
+ }
+ }
+
+ try {
+ controllerImpl.getControllerBinder().onGetSearchResultDone(
+ query, page, pageSize, bundleList, extras);
+ } catch (RemoteException e) {
+ // Controller may be died prematurely.
+ // TODO(jaewan): Handle this.
+ }
+ });
+ }
+
+ @Override
+ public void subscribe(final IMediaSession2Callback caller, final String parentId,
+ final Bundle option) {
+ final MediaLibrarySessionImpl sessionImpl = getLibrarySession();
+ final ControllerInfo controller = getController(caller);
+ if (controller == null) {
+ if (DEBUG) {
+ Log.d(TAG, "subscribe() from a browser that hasn't connected. Ignore");
+ }
+ return;
+ }
+ sessionImpl.getCallbackExecutor().execute(() -> {
+ final MediaLibrarySessionImpl session = getLibrarySession();
+ if (session == null) {
+ return;
+ }
+ session.getCallback().onSubscribe(controller, parentId, option);
+ synchronized (mLock) {
+ Set<String> subscription = mSubscriptions.get(controller);
+ if (subscription == null) {
+ subscription = new HashSet<>();
+ mSubscriptions.put(controller, subscription);
+ }
+ subscription.add(parentId);
+ }
+ });
+ }
+
+ @Override
+ public void unsubscribe(final IMediaSession2Callback caller, final String parentId) {
+ final MediaLibrarySessionImpl sessionImpl = getLibrarySession();
+ final ControllerInfo controller = getController(caller);
+ if (controller == null) {
+ if (DEBUG) {
+ Log.d(TAG, "unsubscribe() from a browser that hasn't connected. Ignore");
+ }
+ return;
+ }
+ sessionImpl.getCallbackExecutor().execute(() -> {
+ final MediaLibrarySessionImpl session = getLibrarySession();
+ if (session == null) {
+ return;
+ }
+ session.getCallback().onUnsubscribe(controller, parentId);
+ synchronized (mLock) {
+ mSubscriptions.remove(controller);
+ }
+ });
+ }
+
//////////////////////////////////////////////////////////////////////////////////////////////
// APIs for MediaSession2Impl
//////////////////////////////////////////////////////////////////////////////////////////////
@@ -643,11 +807,11 @@
public void notifyPlaybackStateChangedNotLocked(PlaybackState2 state) {
final List<ControllerInfo> list = getControllers();
for (int i = 0; i < list.size(); i++) {
- IMediaSession2Callback callbackBinder =
+ IMediaSession2Callback controllerBinder =
ControllerInfoImpl.from(list.get(i)).getControllerBinder();
try {
final Bundle bundle = state != null ? state.toBundle() : null;
- callbackBinder.onPlaybackStateChanged(bundle);
+ controllerBinder.onPlaybackStateChanged(bundle);
} catch (RemoteException e) {
Log.w(TAG, "Controller is gone", e);
// TODO(jaewan): What to do when the controller is gone?
@@ -658,7 +822,7 @@
public void notifyCustomLayoutNotLocked(ControllerInfo controller, List<CommandButton> layout) {
// TODO(jaewan): It's OK to be called while it's connecting, but not OK if the connection
// is rejected. Handle the case.
- IMediaSession2Callback callbackBinder =
+ IMediaSession2Callback controllerBinder =
ControllerInfoImpl.from(controller).getControllerBinder();
try {
List<Bundle> layoutBundles = new ArrayList<>();
@@ -668,7 +832,7 @@
layoutBundles.add(bundle);
}
}
- callbackBinder.onCustomLayoutChanged(layoutBundles);
+ controllerBinder.onCustomLayoutChanged(layoutBundles);
} catch (RemoteException e) {
Log.w(TAG, "Controller is gone", e);
// TODO(jaewan): What to do when the controller is gone?
@@ -690,10 +854,10 @@
}
final List<ControllerInfo> list = getControllers();
for (int i = 0; i < list.size(); i++) {
- IMediaSession2Callback callbackBinder =
+ IMediaSession2Callback controllerBinder =
ControllerInfoImpl.from(list.get(i)).getControllerBinder();
try {
- callbackBinder.onPlaylistChanged(bundleList);
+ controllerBinder.onPlaylistChanged(bundleList);
} catch (RemoteException e) {
Log.w(TAG, "Controller is gone", e);
// TODO(jaewan): What to do when the controller is gone?
@@ -704,10 +868,10 @@
public void notifyPlaylistParamsChanged(MediaSession2.PlaylistParams params) {
final List<ControllerInfo> list = getControllers();
for (int i = 0; i < list.size(); i++) {
- IMediaSession2Callback callbackBinder =
+ IMediaSession2Callback controllerBinder =
ControllerInfoImpl.from(list.get(i)).getControllerBinder();
try {
- callbackBinder.onPlaylistParamsChanged(params.toBundle());
+ controllerBinder.onPlaylistParamsChanged(params.toBundle());
} catch (RemoteException e) {
Log.w(TAG, "Controller is gone", e);
// TODO(jaewan): What to do when the controller is gone?
@@ -718,11 +882,11 @@
public void notifyPlaybackInfoChanged(MediaController2.PlaybackInfo playbackInfo) {
final List<ControllerInfo> list = getControllers();
for (int i = 0; i < list.size(); i++) {
- IMediaSession2Callback callbackBinder =
+ IMediaSession2Callback controllerBinder =
ControllerInfoImpl.from(list.get(i)).getControllerBinder();
try {
- callbackBinder.onPlaybackInfoChanged(
- ((MediaController2Impl.PlaybackInfoImpl) playbackInfo.getProvider()).toBundle());
+ controllerBinder.onPlaybackInfoChanged(((MediaController2Impl.PlaybackInfoImpl)
+ playbackInfo.getProvider()).toBundle());
} catch (RemoteException e) {
Log.w(TAG, "Controller is gone", e);
// TODO(jaewan): What to do when the controller is gone?
@@ -739,9 +903,9 @@
if (command == null) {
throw new IllegalArgumentException("command shouldn't be null");
}
- final IMediaSession2Callback callbackBinder =
+ final IMediaSession2Callback controllerBinder =
ControllerInfoImpl.from(controller).getControllerBinder();
- if (getController(callbackBinder) == null) {
+ if (getController(controllerBinder) == null) {
throw new IllegalArgumentException("Controller is gone");
}
sendCustomCommandInternal(controller, command, args, receiver);
@@ -759,14 +923,64 @@
private void sendCustomCommandInternal(ControllerInfo controller, Command command, Bundle args,
ResultReceiver receiver) {
- final IMediaSession2Callback callbackBinder =
+ final IMediaSession2Callback controllerBinder =
ControllerInfoImpl.from(controller).getControllerBinder();
try {
Bundle commandBundle = command.toBundle();
- callbackBinder.sendCustomCommand(commandBundle, args, receiver);
+ controllerBinder.sendCustomCommand(commandBundle, args, receiver);
} catch (RemoteException e) {
Log.w(TAG, "Controller is gone", e);
// TODO(jaewan): What to do when the controller is gone?
}
}
+
+ //////////////////////////////////////////////////////////////////////////////////////////////
+ // APIs for MediaLibrarySessionImpl
+ //////////////////////////////////////////////////////////////////////////////////////////////
+
+ public void notifySearchResultChanged(ControllerInfo controller, String query, int itemCount,
+ Bundle extras) {
+ final IMediaSession2Callback callbackBinder =
+ ControllerInfoImpl.from(controller).getControllerBinder();
+ try {
+ callbackBinder.onSearchResultChanged(query, itemCount, extras);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Controller is gone", e);
+ // TODO(jaewan): What to do when the controller is gone?
+ }
+ }
+
+ public void notifyChildrenChangedNotLocked(ControllerInfo controller, String parentId,
+ int itemCount, Bundle extras) {
+ // TODO(jaewan): Handle when controller is disconnected and no longer valid.
+ // Note: Commands may be sent while onConnected() is running. Should we also
+ // consider it as error?
+ notifyChildrenChangedInternalNotLocked(controller, parentId, itemCount, extras);
+ }
+
+ public void notifyChildrenChangedNotLocked(String parentId, int itemCount, Bundle extras) {
+ final List<ControllerInfo> controllers = getControllers();
+ for (int i = 0; i < controllers.size(); i++) {
+ notifyChildrenChangedInternalNotLocked(controllers.get(i), parentId, itemCount,
+ extras);
+ }
+ }
+
+ public void notifyChildrenChangedInternalNotLocked(final ControllerInfo controller,
+ String parentId, int itemCount, Bundle extras) {
+ // Ensure subscription
+ synchronized (mLock) {
+ Set<String> subscriptions = mSubscriptions.get(controller);
+ if (subscriptions == null || !subscriptions.contains(parentId)) {
+ return;
+ }
+ }
+ final IMediaSession2Callback callbackBinder =
+ ControllerInfoImpl.from(controller).getControllerBinder();
+ try {
+ callbackBinder.onChildrenChanged(parentId, itemCount, extras);
+ } catch (RemoteException e) {
+ // TODO(jaewan): Handle controller removed?
+ }
+ }
}
diff --git a/packages/MediaComponents/src/com/android/media/MediaSessionService2Impl.java b/packages/MediaComponents/src/com/android/media/MediaSessionService2Impl.java
index 8773df4..7c762cb 100644
--- a/packages/MediaComponents/src/com/android/media/MediaSessionService2Impl.java
+++ b/packages/MediaComponents/src/com/android/media/MediaSessionService2Impl.java
@@ -22,7 +22,7 @@
import android.app.NotificationManager;
import android.content.Context;
import android.content.Intent;
-import android.media.MediaPlayerInterface.PlaybackListener;
+import android.media.MediaPlayerInterface.EventCallback;
import android.media.MediaSession2;
import android.media.MediaSessionService2;
import android.media.MediaSessionService2.MediaNotification;
@@ -42,7 +42,7 @@
private static final boolean DEBUG = true; // TODO(jaewan): Change this.
private final MediaSessionService2 mInstance;
- private final PlaybackListener mListener = new SessionServicePlaybackListener();
+ private final EventCallback mCallback = new SessionServiceEventCallback();
private final Object mLock = new Object();
@GuardedBy("mLock")
@@ -94,7 +94,7 @@
+ ", but got " + mSession);
}
// TODO(jaewan): Uncomment here.
- // mSession.addPlaybackListener(mListener, mSession.getExecutor());
+ // mSession.registerPlayerEventCallback(mCallback, mSession.getExecutor());
}
@TokenType int getSessionType() {
@@ -103,7 +103,7 @@
public IBinder onBind_impl(Intent intent) {
if (MediaSessionService2.SERVICE_INTERFACE.equals(intent.getAction())) {
- return SessionToken2Impl.from(mSession.getToken()).getSessionBinder().asBinder();
+ return ((MediaSession2Impl) mSession.getProvider()).getSessionStub().asBinder();
}
return null;
}
@@ -135,9 +135,9 @@
mediaNotification.getNotification());
}
- private class SessionServicePlaybackListener implements PlaybackListener {
+ private class SessionServiceEventCallback implements EventCallback {
@Override
- public void onPlaybackChanged(PlaybackState2 state) {
+ public void onPlaybackStateChanged(PlaybackState2 state) {
if (state == null) {
Log.w(TAG, "Ignoring null playback state");
return;
diff --git a/packages/MediaComponents/src/com/android/media/PlaybackListenerHolder.java b/packages/MediaComponents/src/com/android/media/PlaybackListenerHolder.java
deleted file mode 100644
index 4241f85..0000000
--- a/packages/MediaComponents/src/com/android/media/PlaybackListenerHolder.java
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Copyright 2018 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.
- */
-
-package com.android.media;
-
-import android.media.MediaPlayerInterface.PlaybackListener;
-import android.media.PlaybackState2;
-import android.os.Handler;
-import android.support.annotation.NonNull;
-
-import java.util.List;
-import java.util.concurrent.Executor;
-
-/**
- * Holds {@link PlaybackListener} with the {@link Handler}.
- */
-public class PlaybackListenerHolder {
- public final Executor executor;
- public final PlaybackListener listener;
-
- public PlaybackListenerHolder(Executor executor, @NonNull PlaybackListener listener) {
- this.executor = executor;
- this.listener = listener;
- }
-
- public void postPlaybackChange(final PlaybackState2 state) {
- executor.execute(() -> listener.onPlaybackChanged(state));
- }
-
- /**
- * Returns {@code true} if the given list contains a {@link PlaybackListenerHolder} that holds
- * the given listener.
- *
- * @param list list to check
- * @param listener listener to check
- * @return {@code true} if the given list contains listener. {@code false} otherwise.
- */
- public static <Holder extends PlaybackListenerHolder> boolean contains(
- @NonNull List<Holder> list, PlaybackListener listener) {
- return indexOf(list, listener) >= 0;
- }
-
- /**
- * Returns the index of the {@link PlaybackListenerHolder} that contains the given listener.
- *
- * @param list list to check
- * @param listener listener to check
- * @return {@code index} of item if the given list contains listener. {@code -1} otherwise.
- */
- public static <Holder extends PlaybackListenerHolder> int indexOf(
- @NonNull List<Holder> list, PlaybackListener listener) {
- for (int i = 0; i < list.size(); i++) {
- if (list.get(i).listener == listener) {
- return i;
- }
- }
- return -1;
- }
-}
diff --git a/packages/MediaComponents/src/com/android/media/PlaybackState2Impl.java b/packages/MediaComponents/src/com/android/media/PlaybackState2Impl.java
index 5eb1129..ee8d6d7 100644
--- a/packages/MediaComponents/src/com/android/media/PlaybackState2Impl.java
+++ b/packages/MediaComponents/src/com/android/media/PlaybackState2Impl.java
@@ -30,7 +30,6 @@
private static final String KEY_BUFFERED_POSITION =
"android.media.playbackstate2.buffered_position";
private static final String KEY_SPEED = "android.media.playbackstate2.speed";
- private static final String KEY_ERROR_MESSAGE = "android.media.playbackstate2.error_message";
private static final String KEY_UPDATE_TIME = "android.media.playbackstate2.update_time";
private static final String KEY_ACTIVE_ITEM_ID = "android.media.playbackstate2.active_item_id";
@@ -42,11 +41,9 @@
private final float mSpeed;
private final long mBufferedPosition;
private final long mActiveItemId;
- private final CharSequence mErrorMessage;
public PlaybackState2Impl(Context context, PlaybackState2 instance, int state, long position,
- long updateTime, float speed, long bufferedPosition, long activeItemId,
- CharSequence error) {
+ long updateTime, float speed, long bufferedPosition, long activeItemId) {
mContext = context;
mInstance = instance;
mState = state;
@@ -55,7 +52,6 @@
mUpdateTime = updateTime;
mBufferedPosition = bufferedPosition;
mActiveItemId = activeItemId;
- mErrorMessage = error;
}
@Override
@@ -67,7 +63,6 @@
bob.append(", speed=").append(mSpeed);
bob.append(", updated=").append(mUpdateTime);
bob.append(", active item id=").append(mActiveItemId);
- bob.append(", error=").append(mErrorMessage);
bob.append("}");
return bob.toString();
}
@@ -93,11 +88,6 @@
}
@Override
- public CharSequence getErrorMessage_impl() {
- return mErrorMessage;
- }
-
- @Override
public long getLastPositionUpdateTime_impl() {
return mUpdateTime;
}
@@ -116,7 +106,6 @@
bundle.putFloat(KEY_SPEED, mSpeed);
bundle.putLong(KEY_BUFFERED_POSITION, mBufferedPosition);
bundle.putLong(KEY_ACTIVE_ITEM_ID, mActiveItemId);
- bundle.putCharSequence(KEY_ERROR_MESSAGE, mErrorMessage);
return bundle;
}
@@ -129,18 +118,15 @@
|| !bundle.containsKey(KEY_UPDATE_TIME)
|| !bundle.containsKey(KEY_SPEED)
|| !bundle.containsKey(KEY_BUFFERED_POSITION)
- || !bundle.containsKey(KEY_ACTIVE_ITEM_ID)
- || !bundle.containsKey(KEY_ERROR_MESSAGE)) {
+ || !bundle.containsKey(KEY_ACTIVE_ITEM_ID)) {
return null;
}
-
return new PlaybackState2(context,
bundle.getInt(KEY_STATE),
bundle.getLong(KEY_POSITION),
bundle.getLong(KEY_UPDATE_TIME),
bundle.getFloat(KEY_SPEED),
bundle.getLong(KEY_BUFFERED_POSITION),
- bundle.getLong(KEY_ACTIVE_ITEM_ID),
- bundle.getCharSequence(KEY_ERROR_MESSAGE));
+ bundle.getLong(KEY_ACTIVE_ITEM_ID));
}
}
\ No newline at end of file
diff --git a/packages/MediaComponents/src/com/android/media/SessionToken2Impl.java b/packages/MediaComponents/src/com/android/media/SessionToken2Impl.java
index b2b7959..d0b0d91 100644
--- a/packages/MediaComponents/src/com/android/media/SessionToken2Impl.java
+++ b/packages/MediaComponents/src/com/android/media/SessionToken2Impl.java
@@ -34,6 +34,8 @@
import android.os.IBinder;
import android.text.TextUtils;
+import java.util.List;
+
public class SessionToken2Impl implements SessionToken2Provider {
private static final String KEY_UID = "android.media.token.uid";
private static final String KEY_TYPE = "android.media.token.type";
@@ -73,25 +75,24 @@
}
}
mUid = uid;
- // calculate id and type
- Intent serviceIntent = new Intent(MediaLibraryService2.SERVICE_INTERFACE);
- serviceIntent.setClassName(packageName, serviceName);
- String id = getSessionId(manager.resolveService(serviceIntent,
- PackageManager.GET_META_DATA));
- int type = TYPE_LIBRARY_SERVICE;
- if (id == null) {
+
+ // Infer id and type from package name and service name
+ // TODO(jaewan): Handle multi-user.
+ String id = getSessionIdFromService(manager, MediaLibraryService2.SERVICE_INTERFACE,
+ packageName, serviceName);
+ if (id != null) {
+ mId = id;
+ mType = TYPE_LIBRARY_SERVICE;
+ } else {
// retry with session service
- serviceIntent.setClassName(packageName, serviceName);
- id = getSessionId(manager.resolveService(serviceIntent,
- PackageManager.GET_META_DATA));
- type = TYPE_SESSION_SERVICE;
+ mId = getSessionIdFromService(manager, MediaSessionService2.SERVICE_INTERFACE,
+ packageName, serviceName);
+ mType = TYPE_SESSION_SERVICE;
}
- if (id == null) {
+ if (mId == null) {
throw new IllegalArgumentException("service " + serviceName + " doesn't implement"
- + " session service nor library service");
+ + " session service nor library service. Use service's full name.");
}
- mId = id;
- mType = type;
mPackageName = packageName;
mServiceName = serviceName;
mSessionBinder = null;
@@ -109,6 +110,29 @@
mInstance = new SessionToken2(this);
}
+ private static String getSessionIdFromService(PackageManager manager, String serviceInterface,
+ String packageName, String serviceName) {
+ Intent serviceIntent = new Intent(serviceInterface);
+ serviceIntent.setPackage(packageName);
+ // Use queryIntentServices to find services with MediaLibraryService2.SERVICE_INTERFACE.
+ // We cannot use resolveService with intent specified class name, because resolveService
+ // ignores actions if Intent.setClassName() is specified.
+ List<ResolveInfo> list = manager.queryIntentServices(
+ serviceIntent, PackageManager.GET_META_DATA);
+ if (list != null) {
+ for (int i = 0; i < list.size(); i++) {
+ ResolveInfo resolveInfo = list.get(i);
+ if (resolveInfo == null || resolveInfo.serviceInfo == null) {
+ continue;
+ }
+ if (TextUtils.equals(resolveInfo.serviceInfo.name, serviceName)) {
+ return getSessionId(resolveInfo);
+ }
+ }
+ }
+ return null;
+ }
+
public static String getSessionId(ResolveInfo resolveInfo) {
if (resolveInfo == null || resolveInfo.serviceInfo == null) {
return null;
@@ -211,8 +235,7 @@
+ prime * (mUid
+ prime * (mPackageName.hashCode()
+ prime * (mId.hashCode()
- + prime * ((mServiceName != null ? mServiceName.hashCode() : 0)
- + prime * (mSessionBinder != null ? mSessionBinder.asBinder().hashCode() : 0)))));
+ + prime * (mServiceName != null ? mServiceName.hashCode() : 0))));
}
@Override
@@ -221,19 +244,11 @@
return false;
}
SessionToken2Impl other = from((SessionToken2) obj);
- if (mUid != other.mUid
- || !TextUtils.equals(mPackageName, other.mPackageName)
- || !TextUtils.equals(mServiceName, other.mServiceName)
- || !TextUtils.equals(mId, other.mId)
- || mType != other.mType) {
- return false;
- }
- if (mSessionBinder == other.mSessionBinder) {
- return true;
- } else if (mSessionBinder == null || other.mSessionBinder == null) {
- return false;
- }
- return mSessionBinder.asBinder().equals(other.mSessionBinder.asBinder());
+ return mUid == other.mUid
+ && TextUtils.equals(mPackageName, other.mPackageName)
+ && TextUtils.equals(mServiceName, other.mServiceName)
+ && TextUtils.equals(mId, other.mId)
+ && mType == other.mType;
}
@Override
diff --git a/packages/MediaComponents/src/com/android/media/update/ApiFactory.java b/packages/MediaComponents/src/com/android/media/update/ApiFactory.java
index 46812e7..994824d 100644
--- a/packages/MediaComponents/src/com/android/media/update/ApiFactory.java
+++ b/packages/MediaComponents/src/com/android/media/update/ApiFactory.java
@@ -44,7 +44,6 @@
import android.media.MediaSessionService2.MediaNotification;
import android.media.PlaybackState2;
import android.media.Rating2;
-import android.media.SessionPlayer2;
import android.media.SessionToken2;
import android.media.VolumeProvider2;
import android.media.update.MediaBrowser2Provider;
@@ -60,7 +59,6 @@
import android.media.update.MediaSessionService2Provider;
import android.media.update.MediaSessionService2Provider.MediaNotificationProvider;
import android.media.update.PlaybackState2Provider;
-import android.media.update.SessionPlayer2Provider;
import android.media.update.SessionToken2Provider;
import android.media.update.StaticProvider;
import android.media.update.VideoView2Provider;
@@ -230,12 +228,6 @@
}
@Override
- public SessionPlayer2Provider createSessionPlayer2(Context context, SessionPlayer2 instance) {
- // TODO(jaewan): Implement this
- return null;
- }
-
- @Override
public MediaItem2Provider createMediaItem2(Context context, MediaItem2 instance,
String mediaId, DataSourceDesc dsd, MediaMetadata2 metadata, int flags) {
return new MediaItem2Impl(context, instance, mediaId, dsd, metadata, flags);
@@ -302,9 +294,9 @@
@Override
public PlaybackState2Provider createPlaybackState2(Context context, PlaybackState2 instance,
int state, long position, long updateTime, float speed, long bufferedPosition,
- long activeItemId, CharSequence error) {
+ long activeItemId) {
return new PlaybackState2Impl(context, instance, state, position, updateTime, speed,
- bufferedPosition, activeItemId, error);
+ bufferedPosition, activeItemId);
}
@Override
diff --git a/packages/MediaComponents/src/com/android/widget/MediaControlView2Impl.java b/packages/MediaComponents/src/com/android/widget/MediaControlView2Impl.java
index 318cbf9..f440ad6 100644
--- a/packages/MediaComponents/src/com/android/widget/MediaControlView2Impl.java
+++ b/packages/MediaComponents/src/com/android/widget/MediaControlView2Impl.java
@@ -26,13 +26,20 @@
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.util.Log;
+import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
+import android.widget.BaseAdapter;
import android.widget.Button;
import android.widget.ImageButton;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.ListView;
import android.widget.MediaControlView2;
import android.widget.ProgressBar;
+import android.widget.PopupWindow;
+import android.widget.RelativeLayout;
import android.widget.SeekBar;
import android.widget.SeekBar.OnSeekBarChangeListener;
import android.widget.TextView;
@@ -43,6 +50,7 @@
import com.android.support.mediarouter.media.MediaRouter;
import com.android.support.mediarouter.media.MediaRouteSelector;
+import java.util.ArrayList;
import java.util.Formatter;
import java.util.List;
import java.util.Locale;
@@ -58,12 +66,18 @@
static final String KEY_STATE_CONTAINS_SUBTITLE = "StateContainsSubtitle";
static final String EVENT_UPDATE_SUBTITLE_STATUS = "UpdateSubtitleStatus";
+ // TODO: Remove this once integrating with MediaSession2 & MediaMetadata2
+ static final String KEY_STATE_IS_ADVERTISEMENT = "MediaTypeAdvertisement";
+ static final String EVENT_UPDATE_MEDIA_TYPE_STATUS = "UpdateMediaTypeStatus";
+
private static final int MAX_PROGRESS = 1000;
private static final int DEFAULT_PROGRESS_UPDATE_TIME_MS = 1000;
-
private static final int REWIND_TIME_MS = 10000;
private static final int FORWARD_TIME_MS = 30000;
+ private static final int AD_SKIP_WAIT_TIME_MS = 5000;
+ private static final int RESOURCE_NON_EXISTENT = -1;
+ private Resources mResources;
private MediaController mController;
private MediaController.TransportControls mControls;
private PlaybackState mPlaybackState;
@@ -71,8 +85,12 @@
private ProgressBar mProgress;
private TextView mEndTime, mCurrentTime;
private TextView mTitleView;
+ private TextView mAdSkipView, mAdRemainingView;
+ private View mAdExternalLink;
+ private View mRoot;
private int mDuration;
private int mPrevState;
+ private int mPrevLeftBarWidth;
private long mPlaybackActions;
private boolean mDragging;
private boolean mIsFullScreen;
@@ -81,6 +99,7 @@
private boolean mSubtitleIsEnabled;
private boolean mContainsSubtitle;
private boolean mSeekAvailable;
+ private boolean mIsAdvertisement;
private ImageButton mPlayPauseButton;
private ImageButton mFfwdButton;
private ImageButton mRewButton;
@@ -99,6 +118,12 @@
private ImageButton mAspectRationButton;
private ImageButton mSettingsButton;
+ private PopupWindow mSettingsWindow;
+ private SettingsAdapter mSettingsAdapter;
+ private List<Integer> mSettingsMainTextIdsList;
+ private List<Integer> mSettingsSubTextIdsList;
+ private List<Integer> mSettingsIconIdsList;
+
private CharSequence mPlayDescription;
private CharSequence mPauseDescription;
private CharSequence mReplayDescription;
@@ -117,9 +142,11 @@
@Override
public void initialize(@Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+ mResources = ApiHelper.getLibResources();
// Inflate MediaControlView2 from XML
- View root = makeControllerView();
- mInstance.addView(root);
+ mRoot = makeControllerView();
+ mRoot.addOnLayoutChangeListener(mTitleBarLayoutChangeListener);
+ mInstance.addView(mRoot);
}
@Override
@@ -140,6 +167,8 @@
@Override
public void setButtonVisibility_impl(int button, int visibility) {
+ // TODO: add member variables for Fast-Forward/Prvious/Rewind buttons to save visibility in
+ // order to prevent being overriden inside updateLayout().
switch (button) {
case MediaControlView2.BUTTON_PLAY_PAUSE:
if (mPlayPauseButton != null && canPause()) {
@@ -343,10 +372,11 @@
}
private void initControllerView(View v) {
- Resources res = ApiHelper.getLibResources();
- mPlayDescription = res.getText(R.string.lockscreen_play_button_content_description);
- mPauseDescription = res.getText(R.string.lockscreen_pause_button_content_description);
- mReplayDescription = res.getText(R.string.lockscreen_replay_button_content_description);
+ mPlayDescription = mResources.getText(R.string.lockscreen_play_button_content_description);
+ mPauseDescription =
+ mResources.getText(R.string.lockscreen_pause_button_content_description);
+ mReplayDescription =
+ mResources.getText(R.string.lockscreen_replay_button_content_description);
mRouteButton = v.findViewById(R.id.cast);
@@ -354,19 +384,19 @@
if (mPlayPauseButton != null) {
mPlayPauseButton.requestFocus();
mPlayPauseButton.setOnClickListener(mPlayPauseListener);
- mPlayPauseButton.setColorFilter(R.integer.gray);
+ mPlayPauseButton.setColorFilter(R.color.gray);
mPlayPauseButton.setEnabled(false);
}
mFfwdButton = v.findViewById(R.id.ffwd);
if (mFfwdButton != null) {
mFfwdButton.setOnClickListener(mFfwdListener);
- mFfwdButton.setColorFilter(R.integer.gray);
+ mFfwdButton.setColorFilter(R.color.gray);
mFfwdButton.setEnabled(false);
}
mRewButton = v.findViewById(R.id.rew);
if (mRewButton != null) {
mRewButton.setOnClickListener(mRewListener);
- mRewButton.setColorFilter(R.integer.gray);
+ mRewButton.setColorFilter(R.color.gray);
mRewButton.setEnabled(false);
}
mNextButton = v.findViewById(R.id.next);
@@ -382,7 +412,7 @@
mSubtitleButton = v.findViewById(R.id.subtitle);
if (mSubtitleButton != null) {
mSubtitleButton.setOnClickListener(mSubtitleListener);
- mSubtitleButton.setColorFilter(R.integer.gray);
+ mSubtitleButton.setColorFilter(R.color.gray);
mSubtitleButton.setEnabled(false);
}
mFullScreenButton = v.findViewById(R.id.fullscreen);
@@ -405,6 +435,9 @@
mMuteButton = v.findViewById(R.id.mute);
mAspectRationButton = v.findViewById(R.id.aspect_ratio);
mSettingsButton = v.findViewById(R.id.settings);
+ if (mSettingsButton != null) {
+ mSettingsButton.setOnClickListener(mSettingsButtonListener);
+ }
mProgress = v.findViewById(R.id.mediacontroller_progress);
if (mProgress != null) {
@@ -421,6 +454,22 @@
mCurrentTime = v.findViewById(R.id.time_current);
mFormatBuilder = new StringBuilder();
mFormatter = new Formatter(mFormatBuilder, Locale.getDefault());
+
+ mAdSkipView = v.findViewById(R.id.ad_skip_time);
+ mAdRemainingView = v.findViewById(R.id.ad_remaining);
+ mAdExternalLink = v.findViewById(R.id.ad_external_link);
+
+ populateResourceIds();
+ ListView settingsListView = (ListView) ApiHelper.inflateLibLayout(mInstance.getContext(),
+ R.layout.settings_list);
+ mSettingsAdapter = new SettingsAdapter(mSettingsMainTextIdsList, mSettingsSubTextIdsList,
+ mSettingsIconIdsList, true);
+ settingsListView.setAdapter(mSettingsAdapter);
+
+ int width = mResources.getDimensionPixelSize(R.dimen.MediaControlView2_settings_width);
+ mSettingsWindow = new PopupWindow(settingsListView, width,
+ ViewGroup.LayoutParams.WRAP_CONTENT, true);
+ // TODO: add listener to list view to allow each item to be selected.
}
/**
@@ -505,6 +554,36 @@
mCurrentTime.setText(stringForTime(currentPosition));
}
+ if (mIsAdvertisement) {
+ // Update the remaining number of seconds until the first 5 seconds of the
+ // advertisement.
+ if (mAdSkipView != null) {
+ if (currentPosition <= AD_SKIP_WAIT_TIME_MS) {
+ if (mAdSkipView.getVisibility() == View.GONE) {
+ mAdSkipView.setVisibility(View.VISIBLE);
+ }
+ String skipTimeText = mResources.getString(
+ R.string.MediaControlView2_ad_skip_wait_time,
+ ((AD_SKIP_WAIT_TIME_MS - currentPosition) / 1000 + 1));
+ mAdSkipView.setText(skipTimeText);
+ } else {
+ if (mAdSkipView.getVisibility() == View.VISIBLE) {
+ mAdSkipView.setVisibility(View.GONE);
+ mNextButton.setEnabled(true);
+ mNextButton.clearColorFilter();
+ }
+ }
+ }
+ // Update the remaining number of seconds of the advertisement.
+ if (mAdRemainingView != null) {
+ int remainingTime =
+ (mDuration - currentPosition < 0) ? 0 : (mDuration - currentPosition);
+ String remainingTimeText = mResources.getString(
+ R.string.MediaControlView2_ad_remaining_time,
+ stringForTime(remainingTime));
+ mAdRemainingView.setText(remainingTimeText);
+ }
+ }
return currentPosition;
}
@@ -512,14 +591,12 @@
if (isPlaying()) {
mControls.pause();
mPlayPauseButton.setImageDrawable(
- ApiHelper.getLibResources().getDrawable(
- R.drawable.ic_play_circle_filled, null));
+ mResources.getDrawable(R.drawable.ic_play_circle_filled, null));
mPlayPauseButton.setContentDescription(mPlayDescription);
} else {
mControls.play();
mPlayPauseButton.setImageDrawable(
- ApiHelper.getLibResources().getDrawable(
- R.drawable.ic_pause_circle_filled, null));
+ mResources.getDrawable(R.drawable.ic_pause_circle_filled, null));
mPlayPauseButton.setContentDescription(mPauseDescription);
}
}
@@ -555,8 +632,7 @@
// show the play image instead of the replay image.
if (mIsStopped) {
mPlayPauseButton.setImageDrawable(
- ApiHelper.getLibResources().getDrawable(
- R.drawable.ic_play_circle_filled, null));
+ mResources.getDrawable(R.drawable.ic_play_circle_filled, null));
mPlayPauseButton.setContentDescription(mPlayDescription);
mIsStopped = false;
}
@@ -642,14 +718,12 @@
public void onClick(View v) {
if (!mSubtitleIsEnabled) {
mSubtitleButton.setImageDrawable(
- ApiHelper.getLibResources().getDrawable(
- R.drawable.ic_media_subtitle_enabled, null));
+ mResources.getDrawable(R.drawable.ic_media_subtitle_enabled, null));
mController.sendCommand(MediaControlView2.COMMAND_SHOW_SUBTITLE, null, null);
mSubtitleIsEnabled = true;
} else {
mSubtitleButton.setImageDrawable(
- ApiHelper.getLibResources().getDrawable(
- R.drawable.ic_media_subtitle_disabled, null));
+ mResources.getDrawable(R.drawable.ic_media_subtitle_disabled, null));
mController.sendCommand(MediaControlView2.COMMAND_HIDE_SUBTITLE, null, null);
mSubtitleIsEnabled = false;
}
@@ -663,11 +737,10 @@
// TODO: Re-arrange the button layouts according to the UX.
if (isEnteringFullScreen) {
mFullScreenButton.setImageDrawable(
- ApiHelper.getLibResources().getDrawable(
- R.drawable.ic_fullscreen_exit, null));
+ mResources.getDrawable(R.drawable.ic_fullscreen_exit, null));
} else {
mFullScreenButton.setImageDrawable(
- ApiHelper.getLibResources().getDrawable(R.drawable.ic_fullscreen, null));
+ mResources.getDrawable(R.drawable.ic_fullscreen, null));
}
Bundle args = new Bundle();
args.putBoolean(ARGUMENT_KEY_FULLSCREEN, isEnteringFullScreen);
@@ -693,6 +766,49 @@
}
};
+ private final View.OnClickListener mSettingsButtonListener = new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ int itemHeight = mResources.getDimensionPixelSize(
+ R.dimen.MediaControlView2_settings_height);
+ int totalHeight = mSettingsAdapter.getCount() * itemHeight;
+ int margin = (-1) * mResources.getDimensionPixelSize(
+ R.dimen.MediaControlView2_settings_offset);
+ mSettingsWindow.showAsDropDown(mInstance, margin, margin - totalHeight,
+ Gravity.BOTTOM | Gravity.RIGHT);
+ }
+ };
+
+ // The title bar is made up of two separate LinearLayouts. If the sum of the two bars are
+ // greater than the length of the title bar, reduce the size of the left bar (which makes the
+ // TextView that contains the title of the media file shrink).
+ private final View.OnLayoutChangeListener mTitleBarLayoutChangeListener
+ = new View.OnLayoutChangeListener() {
+ @Override
+ public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft,
+ int oldTop, int oldRight, int oldBottom) {
+ if (mRoot != null) {
+ int titleBarWidth = mRoot.findViewById(R.id.title_bar).getWidth();
+
+ View leftBar = mRoot.findViewById(R.id.title_bar_left);
+ View rightBar = mRoot.findViewById(R.id.title_bar_right);
+ int leftBarWidth = leftBar.getWidth();
+ int rightBarWidth = rightBar.getWidth();
+
+ RelativeLayout.LayoutParams params =
+ (RelativeLayout.LayoutParams) leftBar.getLayoutParams();
+ if (leftBarWidth + rightBarWidth > titleBarWidth) {
+ params.width = titleBarWidth - rightBarWidth;
+ mPrevLeftBarWidth = leftBarWidth;
+ } else if (leftBarWidth + rightBarWidth < titleBarWidth && mPrevLeftBarWidth != 0) {
+ params.width = mPrevLeftBarWidth;
+ mPrevLeftBarWidth = 0;
+ }
+ leftBar.setLayoutParams(params);
+ }
+ }
+ };
+
private void updateDuration() {
if (mMetadata != null) {
if (mMetadata.containsKey(MediaMetadata.METADATA_KEY_DURATION)) {
@@ -711,6 +827,64 @@
}
}
+ private void updateLayout() {
+ if (mIsAdvertisement) {
+ mRewButton.setVisibility(View.GONE);
+ mFfwdButton.setVisibility(View.GONE);
+ mPrevButton.setVisibility(View.GONE);
+ mCurrentTime.setVisibility(View.GONE);
+ mEndTime.setVisibility(View.GONE);
+
+ mAdSkipView.setVisibility(View.VISIBLE);
+ mAdRemainingView.setVisibility(View.VISIBLE);
+ mAdExternalLink.setVisibility(View.VISIBLE);
+
+ mProgress.setEnabled(false);
+ mNextButton.setEnabled(false);
+ mNextButton.setColorFilter(R.color.gray);
+ } else {
+ mRewButton.setVisibility(View.VISIBLE);
+ mFfwdButton.setVisibility(View.VISIBLE);
+ mPrevButton.setVisibility(View.VISIBLE);
+ mCurrentTime.setVisibility(View.VISIBLE);
+ mEndTime.setVisibility(View.VISIBLE);
+
+ mAdSkipView.setVisibility(View.GONE);
+ mAdRemainingView.setVisibility(View.GONE);
+ mAdExternalLink.setVisibility(View.GONE);
+
+ mProgress.setEnabled(true);
+ mNextButton.setEnabled(true);
+ mNextButton.clearColorFilter();
+ disableUnsupportedButtons();
+ }
+ }
+
+ private void populateResourceIds() {
+ // TODO: create record class for storing this info
+ mSettingsMainTextIdsList = new ArrayList<Integer>();
+ mSettingsMainTextIdsList.add(R.string.MediaControlView2_cc_text);
+ mSettingsMainTextIdsList.add(R.string.MediaControlView2_audio_track_text);
+ mSettingsMainTextIdsList.add(R.string.MediaControlView2_video_quality_text);
+ mSettingsMainTextIdsList.add(R.string.MediaControlView2_playback_speed_text);
+ mSettingsMainTextIdsList.add(R.string.MediaControlView2_help_text);
+
+ // TODO: Update the following code to be dynamic.
+ mSettingsSubTextIdsList = new ArrayList<Integer>();
+ mSettingsSubTextIdsList.add(R.string.MediaControlView2_cc_text);
+ mSettingsSubTextIdsList.add(R.string.MediaControlView2_audio_track_text);
+ mSettingsSubTextIdsList.add(R.string.MediaControlView2_video_quality_text);
+ mSettingsSubTextIdsList.add(R.string.MediaControlView2_playback_speed_text);
+ mSettingsSubTextIdsList.add(RESOURCE_NON_EXISTENT);
+
+ mSettingsIconIdsList = new ArrayList<Integer>();
+ mSettingsIconIdsList.add(R.drawable.ic_closed_caption_off);
+ mSettingsIconIdsList.add(R.drawable.ic_audiotrack);
+ mSettingsIconIdsList.add(R.drawable.ic_high_quality);
+ mSettingsIconIdsList.add(R.drawable.ic_play_circle_filled);
+ mSettingsIconIdsList.add(R.drawable.ic_help);
+ }
+
private class MediaControllerCallback extends MediaController.Callback {
@Override
public void onPlaybackStateChanged(PlaybackState state) {
@@ -724,22 +898,19 @@
switch (mPlaybackState.getState()) {
case PlaybackState.STATE_PLAYING:
mPlayPauseButton.setImageDrawable(
- ApiHelper.getLibResources().getDrawable(
- R.drawable.ic_pause_circle_filled, null));
+ mResources.getDrawable(R.drawable.ic_pause_circle_filled, null));
mPlayPauseButton.setContentDescription(mPauseDescription);
mInstance.removeCallbacks(mUpdateProgress);
mInstance.post(mUpdateProgress);
break;
case PlaybackState.STATE_PAUSED:
mPlayPauseButton.setImageDrawable(
- ApiHelper.getLibResources().getDrawable(
- R.drawable.ic_play_circle_filled, null));
+ mResources.getDrawable(R.drawable.ic_play_circle_filled, null));
mPlayPauseButton.setContentDescription(mPlayDescription);
break;
case PlaybackState.STATE_STOPPED:
mPlayPauseButton.setImageDrawable(
- ApiHelper.getLibResources().getDrawable(
- R.drawable.ic_replay, null));
+ mResources.getDrawable(R.drawable.ic_replay_circle_filled, null));
mPlayPauseButton.setContentDescription(mReplayDescription);
mIsStopped = true;
break;
@@ -813,12 +984,90 @@
mSubtitleButton.clearColorFilter();
mSubtitleButton.setEnabled(true);
} else {
- mSubtitleButton.setColorFilter(R.integer.gray);
+ mSubtitleButton.setColorFilter(R.color.gray);
mSubtitleButton.setEnabled(false);
}
mContainsSubtitle = newSubtitleStatus;
}
+ } else if (event.equals(EVENT_UPDATE_MEDIA_TYPE_STATUS)) {
+ boolean newStatus = extras.getBoolean(KEY_STATE_IS_ADVERTISEMENT);
+ if (newStatus != mIsAdvertisement) {
+ mIsAdvertisement = newStatus;
+ updateLayout();
+ }
}
}
}
+
+ private class SettingsAdapter extends BaseAdapter {
+ List<Integer> mMainTextIds;
+ List<Integer> mSubTextIds;
+ List<Integer> mIconIds;
+ boolean mIsCheckable;
+
+ public SettingsAdapter(List<Integer> mainTextIds, @Nullable List<Integer> subTextIds,
+ @Nullable List<Integer> iconIds, boolean isCheckable) {
+ mMainTextIds = mainTextIds;
+ mSubTextIds = subTextIds;
+ mIconIds = iconIds;
+ mIsCheckable = isCheckable;
+ }
+
+ @Override
+ public int getCount() {
+ return (mMainTextIds == null) ? 0 : mMainTextIds.size();
+ }
+
+ @Override
+ public long getItemId(int position) {
+ // Auto-generated method stub--does not have any purpose here
+ // TODO: implement this.
+ return 0;
+ }
+
+ @Override
+ public Object getItem(int position) {
+ // Auto-generated method stub--does not have any purpose here
+ // TODO: implement this.
+ return null;
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup container) {
+ View row = ApiHelper.inflateLibLayout(mInstance.getContext(),
+ R.layout.settings_list_item);
+ TextView mainTextView = (TextView) row.findViewById(R.id.main_text);
+ TextView subTextView = (TextView) row.findViewById(R.id.sub_text);
+ ImageView iconView = (ImageView) row.findViewById(R.id.icon);
+ ImageView checkView = (ImageView) row.findViewById(R.id.check);
+
+ // Set main text
+ mainTextView.setText(mResources.getString(mMainTextIds.get(position)));
+
+ // Remove sub text and center the main text if sub texts do not exist at all or the sub
+ // text at this particular position is set to RESOURCE_NON_EXISTENT.
+ if (mSubTextIds == null || mSubTextIds.get(position) == RESOURCE_NON_EXISTENT) {
+ subTextView.setVisibility(View.GONE);
+ } else {
+ // Otherwise, set sub text.
+ subTextView.setText(mResources.getString(mSubTextIds.get(position)));
+ }
+
+ // Remove main icon and set visibility to gone if icons are set to null or the icon at
+ // this particular position is set to RESOURCE_NON_EXISTENT.
+ if (mIconIds == null || mIconIds.get(position) == RESOURCE_NON_EXISTENT) {
+ iconView.setVisibility(View.GONE);
+ } else {
+ // Otherwise, set main icon.
+ iconView.setImageDrawable(mResources.getDrawable(mIconIds.get(position), null));
+ }
+
+ // Set check icon
+ // TODO: make the following code dynamic
+ if (!mIsCheckable) {
+ checkView.setVisibility(View.GONE);
+ }
+ return row;
+ }
+ }
}
diff --git a/packages/MediaComponents/src/com/android/widget/VideoView2Impl.java b/packages/MediaComponents/src/com/android/widget/VideoView2Impl.java
index ea7e714..805c262 100644
--- a/packages/MediaComponents/src/com/android/widget/VideoView2Impl.java
+++ b/packages/MediaComponents/src/com/android/widget/VideoView2Impl.java
@@ -17,9 +17,7 @@
package com.android.widget;
import android.annotation.NonNull;
-import android.app.AlertDialog;
import android.content.Context;
-import android.content.DialogInterface;
import android.content.res.Resources;
import android.media.AudioAttributes;
import android.media.AudioFocusRequest;
@@ -29,6 +27,7 @@
import android.media.MediaPlayerInterface;
import android.media.Cea708CaptionRenderer;
import android.media.ClosedCaptionRenderer;
+import android.media.MediaMetadata2;
import android.media.Metadata;
import android.media.PlaybackParams;
import android.media.SRTRenderer;
@@ -48,6 +47,7 @@
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.util.Log;
+import android.util.Pair;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup.LayoutParams;
@@ -92,13 +92,9 @@
private AudioAttributes mAudioAttributes;
private int mAudioFocusType = AudioManager.AUDIOFOCUS_GAIN; // legacy focus gain
- private VideoView2.OnCustomActionListener mOnCustomActionListener;
- private VideoView2.OnPreparedListener mOnPreparedListener;
- private VideoView2.OnCompletionListener mOnCompletionListener;
- private VideoView2.OnErrorListener mOnErrorListener;
- private VideoView2.OnInfoListener mOnInfoListener;
- private VideoView2.OnViewTypeChangedListener mOnViewTypeChangedListener;
- private VideoView2.OnFullScreenRequestListener mOnFullScreenRequestListener;
+ private Pair<Executor, VideoView2.OnCustomActionListener> mCustomActionListenerRecord;
+ private VideoView2.OnViewTypeChangedListener mViewTypeChangedListener;
+ private VideoView2.OnFullScreenRequestListener mFullScreenRequestListener;
private VideoViewInterface mCurrentView;
private VideoTextureView mTextureView;
@@ -112,6 +108,9 @@
private MediaRouter mMediaRouter;
private MediaRouteSelector mRouteSelector;
private Metadata mMetadata;
+ private MediaMetadata2 mMediaMetadata;
+ private boolean mNeedUpdateMediaType;
+ private Bundle mMediaTypeData;
private String mTitle;
private PlaybackState.Builder mStateBuilder;
@@ -240,6 +239,32 @@
}
@Override
+ public MediaMetadata2 getMediaMetadata_impl() {
+ return mMediaMetadata;
+ }
+
+ @Override
+ public void setMediaMetadata_impl(MediaMetadata2 metadata) {
+ // TODO: integrate this with MediaSession2#MediaItem2
+ mMediaMetadata = metadata;
+
+ // TODO: add support for handling website link
+ mMediaTypeData = new Bundle();
+ boolean isAd = metadata == null ?
+ false : metadata.getLong(MediaMetadata2.METADATA_KEY_ADVERTISEMENT) != 0;
+ mMediaTypeData.putBoolean(
+ MediaControlView2Impl.KEY_STATE_IS_ADVERTISEMENT, isAd);
+
+ if (mMediaSession != null) {
+ mMediaSession.sendSessionEvent(
+ MediaControlView2Impl.EVENT_UPDATE_MEDIA_TYPE_STATUS, mMediaTypeData);
+ } else {
+ // Update later inside OnPreparedListener after MediaSession is initialized.
+ mNeedUpdateMediaType = true;
+ }
+ }
+
+ @Override
public void setSubtitleEnabled_impl(boolean enable) {
if (enable != mSubtitleEnabled) {
selectOrDeselectSubtitle(enable);
@@ -351,13 +376,12 @@
return mCurrentView.getViewType();
}
- // TODO: Handle executor properly for all the set listener methods.
@Override
public void setCustomActions_impl(
List<PlaybackState.CustomAction> actionList,
Executor executor, VideoView2.OnCustomActionListener listener) {
mCustomActionList = actionList;
- mOnCustomActionListener = listener;
+ mCustomActionListenerRecord = new Pair<>(executor, listener);
// Create a new playback builder in order to clear existing the custom actions.
mStateBuilder = null;
@@ -365,35 +389,13 @@
}
@Override
- public void setOnPreparedListener_impl(Executor executor, VideoView2.OnPreparedListener l) {
- mOnPreparedListener = l;
+ public void setOnViewTypeChangedListener_impl(VideoView2.OnViewTypeChangedListener l) {
+ mViewTypeChangedListener = l;
}
@Override
- public void setOnCompletionListener_impl(Executor executor, VideoView2.OnCompletionListener l) {
- mOnCompletionListener = l;
- }
-
- @Override
- public void setOnErrorListener_impl(Executor executor, VideoView2.OnErrorListener l) {
- mOnErrorListener = l;
- }
-
- @Override
- public void setOnInfoListener_impl(Executor executor, VideoView2.OnInfoListener l) {
- mOnInfoListener = l;
- }
-
- @Override
- public void setOnViewTypeChangedListener_impl(Executor executor,
- VideoView2.OnViewTypeChangedListener l) {
- mOnViewTypeChangedListener = l;
- }
-
- @Override
- public void setFullScreenRequestListener_impl(Executor executor,
- VideoView2.OnFullScreenRequestListener l) {
- mOnFullScreenRequestListener = l;
+ public void setFullScreenRequestListener_impl(VideoView2.OnFullScreenRequestListener l) {
+ mFullScreenRequestListener = l;
}
@Override
@@ -492,8 +494,8 @@
Log.d(TAG, "onSurfaceTakeOverDone(). Now current view is: " + view);
}
mCurrentView = view;
- if (mOnViewTypeChangedListener != null) {
- mOnViewTypeChangedListener.onViewTypeChanged(mInstance, view.getViewType());
+ if (mViewTypeChangedListener != null) {
+ mViewTypeChangedListener.onViewTypeChanged(mInstance, view.getViewType());
}
if (needToStart()) {
mMediaController.getTransportControls().play();
@@ -845,9 +847,6 @@
updatePlaybackState();
extractSubtitleTracks();
- if (mOnPreparedListener != null) {
- mOnPreparedListener.onPrepared(mInstance);
- }
if (mMediaControlView != null) {
mMediaControlView.setEnabled(true);
}
@@ -897,6 +896,13 @@
if (mMediaSession != null) {
mMediaSession.setMetadata(builder.build());
+
+ // TODO: merge this code with the above code when integrating with MediaSession2.
+ if (mNeedUpdateMediaType) {
+ mMediaSession.sendSessionEvent(
+ MediaControlView2Impl.EVENT_UPDATE_MEDIA_TYPE_STATUS, mMediaTypeData);
+ mNeedUpdateMediaType = false;
+ }
}
}
};
@@ -908,9 +914,6 @@
mTargetState = STATE_PLAYBACK_COMPLETED;
updatePlaybackState();
- if (mOnCompletionListener != null) {
- mOnCompletionListener.onCompletion(mInstance);
- }
if (mAudioFocusType != AudioManager.AUDIOFOCUS_NONE) {
mAudioManager.abandonAudioFocus(null);
}
@@ -920,10 +923,6 @@
private MediaPlayer.OnInfoListener mInfoListener =
new MediaPlayer.OnInfoListener() {
public boolean onInfo(MediaPlayer mp, int what, int extra) {
- if (mOnInfoListener != null) {
- mOnInfoListener.onInfo(mInstance, what, extra);
- }
-
if (what == MediaPlayer.MEDIA_INFO_METADATA_UPDATE) {
extractSubtitleTracks();
}
@@ -944,47 +943,6 @@
if (mMediaControlView != null) {
mMediaControlView.setVisibility(View.GONE);
}
-
- /* If an error handler has been supplied, use it and finish. */
- if (mOnErrorListener != null) {
- if (mOnErrorListener.onError(mInstance, frameworkErr, implErr)) {
- return true;
- }
- }
-
- /* Otherwise, pop up an error dialog so the user knows that
- * something bad has happened. Only try and pop up the dialog
- * if we're attached to a window. When we're going away and no
- * longer have a window, don't bother showing the user an error.
- */
- if (mInstance.getWindowToken() != null) {
- int messageId;
-
- if (frameworkErr
- == MediaPlayer.MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK) {
- messageId = R.string.VideoView2_error_text_invalid_progressive_playback;
- } else {
- messageId = R.string.VideoView2_error_text_unknown;
- }
-
- Resources res = ApiHelper.getLibResources();
- new AlertDialog.Builder(mInstance.getContext())
- .setMessage(res.getString(messageId))
- .setPositiveButton(res.getString(R.string.VideoView2_error_button),
- new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog,
- int whichButton) {
- /* If we get here, there is no onError listener, so
- * at least inform them that the video is over.
- */
- if (mOnCompletionListener != null) {
- mOnCompletionListener.onCompletion(mInstance);
- }
- }
- })
- .setCancelable(false)
- .show();
- }
return true;
}
};
@@ -1020,8 +978,8 @@
mInstance.setSubtitleEnabled(false);
break;
case MediaControlView2.COMMAND_SET_FULLSCREEN:
- if (mOnFullScreenRequestListener != null) {
- mOnFullScreenRequestListener.onFullScreenRequest(
+ if (mFullScreenRequestListener != null) {
+ mFullScreenRequestListener.onFullScreenRequest(
mInstance,
args.getBoolean(MediaControlView2Impl.ARGUMENT_KEY_FULLSCREEN));
}
@@ -1033,7 +991,8 @@
@Override
public void onCustomAction(String action, Bundle extras) {
- mOnCustomActionListener.onCustomAction(action, extras);
+ mCustomActionListenerRecord.first.execute(() ->
+ mCustomActionListenerRecord.second.onCustomAction(action, extras));
showController();
}
diff --git a/packages/MediaComponents/test/AndroidManifest.xml b/packages/MediaComponents/test/AndroidManifest.xml
index 48e4292..5ebe31a 100644
--- a/packages/MediaComponents/test/AndroidManifest.xml
+++ b/packages/MediaComponents/test/AndroidManifest.xml
@@ -34,7 +34,7 @@
<intent-filter>
<action android:name="android.media.MediaLibraryService2" />
</intent-filter>
- <meta-data android:name="android.media.session" android:value="TestBrowser" />
+ <meta-data android:name="android.media.session" android:value="TestLibrary" />
</service>
</application>
diff --git a/packages/MediaComponents/test/src/android/media/MediaBrowser2Test.java b/packages/MediaComponents/test/src/android/media/MediaBrowser2Test.java
index b60fde3..1abb9b4 100644
--- a/packages/MediaComponents/test/src/android/media/MediaBrowser2Test.java
+++ b/packages/MediaComponents/test/src/android/media/MediaBrowser2Test.java
@@ -21,16 +21,21 @@
import static junit.framework.Assert.assertNotNull;
import static junit.framework.Assert.assertTrue;
import static junit.framework.Assert.assertNull;
+import static junit.framework.Assert.fail;
import android.annotation.Nullable;
import android.content.Context;
import android.media.MediaBrowser2.BrowserCallback;
+import android.media.MediaLibraryService2.MediaLibrarySession;
import android.media.MediaSession2.Command;
import android.media.MediaSession2.CommandButton;
import android.media.MediaSession2.CommandGroup;
+import android.media.MediaSession2.ControllerInfo;
import android.media.MediaSession2.PlaylistParams;
+import android.media.TestServiceRegistry.SessionCallbackProxy;
import android.os.Bundle;
import android.os.ResultReceiver;
+import android.os.Process;
import android.support.annotation.CallSuper;
import android.support.annotation.NonNull;
import android.support.test.filters.SmallTest;
@@ -66,10 +71,14 @@
interface TestBrowserCallbackInterface extends TestControllerCallbackInterface {
// Browser specific callbacks
- default void onGetRootResult(Bundle rootHints, String rootMediaId, Bundle rootExtra) {}
- default void onItemLoaded(String mediaId, MediaItem2 result) {}
- default void onChildrenLoaded(String parentId, int page, int pageSize, Bundle options,
- List<MediaItem2> result) {}
+ default void onGetLibraryRootDone(Bundle rootHints, String rootMediaId, Bundle rootExtra) {}
+ default void onGetItemDone(String mediaId, MediaItem2 result) {}
+ default void onChildrenChanged(String parentId, int itemCount, Bundle extras) {}
+ default void onGetChildrenDone(String parentId, int page, int pageSize,
+ List<MediaItem2> result, Bundle extras) {}
+ default void onSearchResultChanged(String query, int itemCount, Bundle extras) {}
+ default void onGetSearchResultDone(String query, int page, int pageSize,
+ List<MediaItem2> result, Bundle extras) {}
}
@Test
@@ -80,10 +89,11 @@
final CountDownLatch latch = new CountDownLatch(1);
final TestControllerCallbackInterface callback = new TestBrowserCallbackInterface() {
@Override
- public void onGetRootResult(Bundle rootHints, String rootMediaId, Bundle rootExtra) {
+ public void onGetLibraryRootDone(Bundle rootHints, String rootMediaId,
+ Bundle rootExtra) {
assertTrue(TestUtils.equals(param, rootHints));
assertEquals(MockMediaLibraryService2.ROOT_ID, rootMediaId);
- assertTrue(TestUtils.equals(MockMediaLibraryService2.EXTRA, rootExtra));
+ assertTrue(TestUtils.equals(MockMediaLibraryService2.EXTRAS, rootExtra));
latch.countDown();
}
};
@@ -102,7 +112,7 @@
final CountDownLatch latch = new CountDownLatch(1);
final TestControllerCallbackInterface callback = new TestBrowserCallbackInterface() {
@Override
- public void onItemLoaded(String mediaIdOut, MediaItem2 result) {
+ public void onGetItemDone(String mediaIdOut, MediaItem2 result) {
assertEquals(mediaId, mediaIdOut);
assertNotNull(result);
assertEquals(mediaId, result.getMediaId());
@@ -123,7 +133,7 @@
final CountDownLatch latch = new CountDownLatch(1);
final TestControllerCallbackInterface callback = new TestBrowserCallbackInterface() {
@Override
- public void onItemLoaded(String mediaIdOut, MediaItem2 result) {
+ public void onGetItemDone(String mediaIdOut, MediaItem2 result) {
assertEquals(mediaId, mediaIdOut);
assertNull(result);
latch.countDown();
@@ -141,23 +151,22 @@
final String parentId = MockMediaLibraryService2.PARENT_ID;
final int page = 4;
final int pageSize = 10;
- final Bundle options = new Bundle();
- options.putString(TAG, TAG);
+ final Bundle extras = new Bundle();
+ extras.putString(TAG, TAG);
final CountDownLatch latch = new CountDownLatch(1);
final TestControllerCallbackInterface callback = new TestBrowserCallbackInterface() {
@Override
- public void onChildrenLoaded(String parentIdOut, int pageOut, int pageSizeOut,
- Bundle optionsOut, List<MediaItem2> result) {
+ public void onGetChildrenDone(String parentIdOut, int pageOut, int pageSizeOut,
+ List<MediaItem2> result, Bundle extrasOut) {
assertEquals(parentId, parentIdOut);
assertEquals(page, pageOut);
assertEquals(pageSize, pageSizeOut);
- assertTrue(TestUtils.equals(options, optionsOut));
+ assertTrue(TestUtils.equals(extras, extrasOut));
assertNotNull(result);
int fromIndex = (page - 1) * pageSize;
- int toIndex = Math.min(page * pageSize,
- MockMediaLibraryService2.GET_CHILDREN_RESULT.size());
+ int toIndex = Math.min(page * pageSize, MockMediaLibraryService2.CHILDREN_COUNT);
// Compare the given results with originals.
for (int originalIndex = fromIndex; originalIndex < toIndex; originalIndex++) {
@@ -173,7 +182,7 @@
final SessionToken2 token = MockMediaLibraryService2.getToken(mContext);
MediaBrowser2 browser = (MediaBrowser2) createController(token, true, callback);
- browser.getChildren(parentId, page, pageSize, options);
+ browser.getChildren(parentId, page, pageSize, extras);
assertTrue(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
}
@@ -184,8 +193,8 @@
final CountDownLatch latch = new CountDownLatch(1);
final TestControllerCallbackInterface callback = new TestBrowserCallbackInterface() {
@Override
- public void onChildrenLoaded(String parentIdOut, int pageOut, int pageSizeOut,
- Bundle optionsOut, List<MediaItem2> result) {
+ public void onGetChildrenDone(String parentIdOut, int pageOut, int pageSizeOut,
+ List<MediaItem2> result, Bundle extrasOut) {
assertNotNull(result);
assertEquals(0, result.size());
latch.countDown();
@@ -205,8 +214,8 @@
final CountDownLatch latch = new CountDownLatch(1);
final TestControllerCallbackInterface callback = new TestBrowserCallbackInterface() {
@Override
- public void onChildrenLoaded(String parentIdOut, int pageOut, int pageSizeOut,
- Bundle optionsOut, List<MediaItem2> result) {
+ public void onGetChildrenDone(String parentIdOut, int pageOut, int pageSizeOut,
+ List<MediaItem2> result, Bundle extrasOut) {
assertNull(result);
latch.countDown();
}
@@ -218,6 +227,223 @@
assertTrue(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
}
+ @Test
+ public void testSearch() throws InterruptedException {
+ final String query = MockMediaLibraryService2.SEARCH_QUERY;
+ final int page = 4;
+ final int pageSize = 10;
+ final Bundle extras = new Bundle();
+ extras.putString(TAG, TAG);
+
+ final CountDownLatch latchForSearch = new CountDownLatch(1);
+ final CountDownLatch latchForGetSearchResult = new CountDownLatch(1);
+ final TestControllerCallbackInterface callback = new TestBrowserCallbackInterface() {
+ @Override
+ public void onSearchResultChanged(String queryOut, int itemCount, Bundle extrasOut) {
+ assertEquals(query, queryOut);
+ assertTrue(TestUtils.equals(extras, extrasOut));
+ assertEquals(MockMediaLibraryService2.SEARCH_RESULT_COUNT, itemCount);
+ latchForSearch.countDown();
+ }
+
+ @Override
+ public void onGetSearchResultDone(String queryOut, int pageOut, int pageSizeOut,
+ List<MediaItem2> result, Bundle extrasOut) {
+ assertEquals(query, queryOut);
+ assertEquals(page, pageOut);
+ assertEquals(pageSize, pageSizeOut);
+ assertTrue(TestUtils.equals(extras, extrasOut));
+ assertNotNull(result);
+
+ int fromIndex = (page - 1) * pageSize;
+ int toIndex = Math.min(
+ page * pageSize, MockMediaLibraryService2.SEARCH_RESULT_COUNT);
+
+ // Compare the given results with originals.
+ for (int originalIndex = fromIndex; originalIndex < toIndex; originalIndex++) {
+ int relativeIndex = originalIndex - fromIndex;
+ assertEquals(
+ MockMediaLibraryService2.SEARCH_RESULT.get(originalIndex).getMediaId(),
+ result.get(relativeIndex).getMediaId());
+ }
+ latchForGetSearchResult.countDown();
+ }
+ };
+
+ // Request the search.
+ final SessionToken2 token = MockMediaLibraryService2.getToken(mContext);
+ MediaBrowser2 browser = (MediaBrowser2) createController(token, true, callback);
+ browser.search(query, extras);
+ assertTrue(latchForSearch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+
+ // Get the search result.
+ browser.getSearchResult(query, page, pageSize, extras);
+ assertTrue(latchForGetSearchResult.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+ }
+
+ @Test
+ public void testSearchTakesTime() throws InterruptedException {
+ final String query = MockMediaLibraryService2.SEARCH_QUERY_TAKES_TIME;
+ final Bundle extras = new Bundle();
+ extras.putString(TAG, TAG);
+
+ final CountDownLatch latch = new CountDownLatch(1);
+ final TestControllerCallbackInterface callback = new TestBrowserCallbackInterface() {
+ @Override
+ public void onSearchResultChanged(String queryOut, int itemCount, Bundle extrasOut) {
+ assertEquals(query, queryOut);
+ assertTrue(TestUtils.equals(extras, extrasOut));
+ assertEquals(MockMediaLibraryService2.SEARCH_RESULT_COUNT, itemCount);
+ latch.countDown();
+ }
+ };
+
+ final SessionToken2 token = MockMediaLibraryService2.getToken(mContext);
+ MediaBrowser2 browser = (MediaBrowser2) createController(token, true, callback);
+ browser.search(query, extras);
+ assertTrue(latch.await(
+ MockMediaLibraryService2.SEARCH_TIME_IN_MS + WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+ }
+
+ @Test
+ public void testSearchEmptyResult() throws InterruptedException {
+ final String query = MockMediaLibraryService2.SEARCH_QUERY_EMPTY_RESULT;
+ final Bundle extras = new Bundle();
+ extras.putString(TAG, TAG);
+
+ final CountDownLatch latch = new CountDownLatch(1);
+ final TestControllerCallbackInterface callback = new TestBrowserCallbackInterface() {
+ @Override
+ public void onSearchResultChanged(String queryOut, int itemCount, Bundle extrasOut) {
+ assertEquals(query, queryOut);
+ assertTrue(TestUtils.equals(extras, extrasOut));
+ assertEquals(0, itemCount);
+ latch.countDown();
+ }
+ };
+
+ final SessionToken2 token = MockMediaLibraryService2.getToken(mContext);
+ MediaBrowser2 browser = (MediaBrowser2) createController(token, true, callback);
+ browser.search(query, extras);
+ assertTrue(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+ }
+
+ @Test
+ public void testSubscribe() throws InterruptedException {
+ final String testParentId = "testSubscribeId";
+ final Bundle testExtras = new Bundle();
+ testExtras.putString(testParentId, testParentId);
+
+ final CountDownLatch latch = new CountDownLatch(1);
+ final SessionCallbackProxy callbackProxy = new SessionCallbackProxy(mContext) {
+ @Override
+ public void onSubscribe(ControllerInfo info, String parentId, Bundle extras) {
+ if (Process.myUid() == info.getUid()) {
+ assertEquals(testParentId, parentId);
+ assertTrue(TestUtils.equals(testExtras, extras));
+ latch.countDown();
+ }
+ }
+ };
+ TestServiceRegistry.getInstance().setSessionCallbackProxy(callbackProxy);
+ final SessionToken2 token = MockMediaLibraryService2.getToken(mContext);
+ MediaBrowser2 browser = (MediaBrowser2) createController(token);
+ browser.subscribe(testParentId, testExtras);
+ assertTrue(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+ }
+
+ @Test
+ public void testUnsubscribe() throws InterruptedException {
+ final String testParentId = "testUnsubscribeId";
+ final CountDownLatch latch = new CountDownLatch(1);
+ final SessionCallbackProxy callbackProxy = new SessionCallbackProxy(mContext) {
+ @Override
+ public void onUnsubscribe(ControllerInfo info, String parentId) {
+ if (Process.myUid() == info.getUid()) {
+ assertEquals(testParentId, parentId);
+ latch.countDown();
+ }
+ }
+ };
+ TestServiceRegistry.getInstance().setSessionCallbackProxy(callbackProxy);
+ final SessionToken2 token = MockMediaLibraryService2.getToken(mContext);
+ MediaBrowser2 browser = (MediaBrowser2) createController(token);
+ browser.unsubscribe(testParentId);
+ assertTrue(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+ }
+
+ @Test
+ public void testBrowserCallback_notifyChildrenChanged() throws InterruptedException {
+ // TODO(jaewan): Add test for the notifyChildrenChanged itself.
+ final String testParentId1 = "testBrowserCallback_notifyChildrenChanged_unexpectedParent";
+ final String testParentId2 = "testBrowserCallback_notifyChildrenChanged";
+ final int testChildrenCount = 101;
+ final Bundle testExtras = new Bundle();
+ testExtras.putString(testParentId1, testParentId1);
+
+ final CountDownLatch latch = new CountDownLatch(3);
+ final SessionCallbackProxy sessionCallbackProxy = new SessionCallbackProxy(mContext) {
+ @Override
+ public CommandGroup onConnect(ControllerInfo controller) {
+ final MockMediaLibraryService2 service = (MockMediaLibraryService2)
+ TestServiceRegistry.getInstance().getServiceInstance();
+ final MediaLibrarySession session = (MediaLibrarySession) service.getSession();
+ // Shouldn't trigger onChildrenChanged() for the browser, because it hasn't
+ // subscribed.
+ session.notifyChildrenChanged(testParentId1, testChildrenCount, null);
+ session.notifyChildrenChanged(controller, testParentId1, testChildrenCount, null);
+ return super.onConnect(controller);
+ }
+
+ @Override
+ public void onSubscribe(ControllerInfo info, String parentId, Bundle extras) {
+ if (Process.myUid() == info.getUid()) {
+ final MediaLibrarySession session = (MediaLibrarySession) mSession;
+ session.notifyChildrenChanged(testParentId2, testChildrenCount, null);
+ session.notifyChildrenChanged(info, testParentId2, testChildrenCount,
+ testExtras);
+ }
+ }
+ };
+ final TestBrowserCallbackInterface controllerCallbackProxy =
+ new TestBrowserCallbackInterface() {
+ @Override
+ public void onChildrenChanged(String parentId, int itemCount, Bundle extras) {
+ switch ((int) latch.getCount()) {
+ case 3:
+ assertEquals(testParentId2, parentId);
+ assertEquals(testChildrenCount, itemCount);
+ assertNull(extras);
+ latch.countDown();
+ break;
+ case 2:
+ assertEquals(testParentId2, parentId);
+ assertEquals(testChildrenCount, itemCount);
+ assertTrue(TestUtils.equals(testExtras, extras));
+ latch.countDown();
+ break;
+ default:
+ // Unexpected call.
+ fail();
+ }
+ }
+ };
+ TestServiceRegistry.getInstance().setSessionCallbackProxy(sessionCallbackProxy);
+ final SessionToken2 token = MockMediaLibraryService2.getToken(mContext);
+ final MediaBrowser2 browser = (MediaBrowser2) createController(
+ token, true, controllerCallbackProxy);
+ final MockMediaLibraryService2 service =
+ (MockMediaLibraryService2) TestServiceRegistry.getInstance().getServiceInstance();
+ if (mSession != null) {
+ mSession.close();
+ }
+ mSession = service.getSession();
+ assertTrue(mSession instanceof MediaLibrarySession);
+ browser.subscribe(testParentId2, null);
+ // This ensures that onChildrenChanged() is only called for the expected reasons.
+ assertFalse(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+ }
+
public static class TestBrowserCallback extends BrowserCallback
implements WaitForConnectionInterface {
private final TestControllerCallbackInterface mCallbackProxy;
@@ -270,29 +496,57 @@
}
@Override
- public void onGetRootResult(Bundle rootHints, String rootMediaId, Bundle rootExtra) {
- super.onGetRootResult(rootHints, rootMediaId, rootExtra);
+ public void onGetLibraryRootDone(Bundle rootHints, String rootMediaId, Bundle rootExtra) {
+ super.onGetLibraryRootDone(rootHints, rootMediaId, rootExtra);
if (mCallbackProxy instanceof TestBrowserCallbackInterface) {
((TestBrowserCallbackInterface) mCallbackProxy)
- .onGetRootResult(rootHints, rootMediaId, rootExtra);
+ .onGetLibraryRootDone(rootHints, rootMediaId, rootExtra);
}
}
@Override
- public void onItemLoaded(String mediaId, MediaItem2 result) {
- super.onItemLoaded(mediaId, result);
+ public void onGetItemDone(String mediaId, MediaItem2 result) {
+ super.onGetItemDone(mediaId, result);
if (mCallbackProxy instanceof TestBrowserCallbackInterface) {
- ((TestBrowserCallbackInterface) mCallbackProxy).onItemLoaded(mediaId, result);
+ ((TestBrowserCallbackInterface) mCallbackProxy).onGetItemDone(mediaId, result);
}
}
@Override
- public void onChildrenLoaded(String parentId, int page, int pageSize, Bundle options,
- List<MediaItem2> result) {
- super.onChildrenLoaded(parentId, page, pageSize, options, result);
+ public void onGetChildrenDone(String parentId, int page, int pageSize,
+ List<MediaItem2> result, Bundle extras) {
+ super.onGetChildrenDone(parentId, page, pageSize, result, extras);
if (mCallbackProxy instanceof TestBrowserCallbackInterface) {
((TestBrowserCallbackInterface) mCallbackProxy)
- .onChildrenLoaded(parentId, page, pageSize, options, result);
+ .onGetChildrenDone(parentId, page, pageSize, result, extras);
+ }
+ }
+
+ @Override
+ public void onSearchResultChanged(String query, int itemCount, Bundle extras) {
+ super.onSearchResultChanged(query, itemCount, extras);
+ if (mCallbackProxy instanceof TestBrowserCallbackInterface) {
+ ((TestBrowserCallbackInterface) mCallbackProxy)
+ .onSearchResultChanged(query, itemCount, extras);
+ }
+ }
+
+ @Override
+ public void onGetSearchResultDone(String query, int page, int pageSize,
+ List<MediaItem2> result, Bundle extras) {
+ super.onGetSearchResultDone(query, page, pageSize, result, extras);
+ if (mCallbackProxy instanceof TestBrowserCallbackInterface) {
+ ((TestBrowserCallbackInterface) mCallbackProxy)
+ .onGetSearchResultDone(query, page, pageSize, result, extras);
+ }
+ }
+
+ @Override
+ public void onChildrenChanged(String parentId, int itemCount, Bundle extras) {
+ super.onChildrenChanged(parentId, itemCount, extras);
+ if (mCallbackProxy instanceof TestBrowserCallbackInterface) {
+ ((TestBrowserCallbackInterface) mCallbackProxy)
+ .onChildrenChanged(parentId, itemCount, extras);
}
}
@@ -329,4 +583,4 @@
return mCallback;
}
}
-}
\ No newline at end of file
+}
diff --git a/packages/MediaComponents/test/src/android/media/MediaController2Test.java b/packages/MediaComponents/test/src/android/media/MediaController2Test.java
index 7bf0fd2..e2bdc25 100644
--- a/packages/MediaComponents/test/src/android/media/MediaController2Test.java
+++ b/packages/MediaComponents/test/src/android/media/MediaController2Test.java
@@ -19,7 +19,7 @@
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
-import android.media.MediaPlayerInterface.PlaybackListener;
+import android.media.MediaPlayerInterface.EventCallback;
import android.media.MediaSession2.Command;
import android.media.MediaSession2.CommandGroup;
import android.media.MediaSession2.ControllerInfo;
@@ -36,7 +36,6 @@
import android.support.test.filters.FlakyTest;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
-import android.text.TextUtils;
import org.junit.After;
import org.junit.Before;
@@ -62,7 +61,6 @@
@FlakyTest
public class MediaController2Test extends MediaSession2TestBase {
private static final String TAG = "MediaController2Test";
- private static final int DEFAULT_RATING_TYPE = Rating2.RATING_5_STARS;
PendingIntent mIntent;
MediaSession2 mSession;
@@ -80,7 +78,6 @@
mPlayer = new MockPlayer(1);
mSession = new MediaSession2.Builder(mContext, mPlayer)
.setSessionCallback(sHandlerExecutor, new SessionCallback(mContext))
- .setRatingType(DEFAULT_RATING_TYPE)
.setSessionActivity(mIntent)
.setId(TAG).build();
mController = createController(mSession.getToken());
@@ -212,11 +209,6 @@
}
@Test
- public void testGetRatingType() throws InterruptedException {
- assertEquals(DEFAULT_RATING_TYPE, mController.getRatingType());
- }
-
- @Test
public void testGetSessionActivity() throws InterruptedException {
PendingIntent sessionActivity = mController.getSessionActivity();
assertEquals(mContext.getPackageName(), sessionActivity.getCreatorPackage());
@@ -391,7 +383,7 @@
assertEquals(mContext.getPackageName(), controller.getPackageName());
assertEquals(request, query);
assertTrue(TestUtils.equals(bundle, extras));
- latch.countDown();;
+ latch.countDown();
}
};
try (MediaSession2 session = new MediaSession2.Builder(mContext, mPlayer)
@@ -415,7 +407,7 @@
assertEquals(mContext.getPackageName(), controller.getPackageName());
assertEquals(request, uri);
assertTrue(TestUtils.equals(bundle, extras));
- latch.countDown();;
+ latch.countDown();
}
};
try (MediaSession2 session = new MediaSession2.Builder(mContext, mPlayer)
@@ -439,7 +431,7 @@
assertEquals(mContext.getPackageName(), controller.getPackageName());
assertEquals(request, id);
assertTrue(TestUtils.equals(bundle, extras));
- latch.countDown();;
+ latch.countDown();
}
};
try (MediaSession2 session = new MediaSession2.Builder(mContext, mPlayer)
@@ -460,11 +452,12 @@
final CountDownLatch latch = new CountDownLatch(1);
final SessionCallback callback = new SessionCallback(mContext) {
@Override
- public void onPrepareFromSearch(ControllerInfo controller, String query, Bundle extras) {
+ public void onPrepareFromSearch(ControllerInfo controller, String query,
+ Bundle extras) {
assertEquals(mContext.getPackageName(), controller.getPackageName());
assertEquals(request, query);
assertTrue(TestUtils.equals(bundle, extras));
- latch.countDown();;
+ latch.countDown();
}
};
try (MediaSession2 session = new MediaSession2.Builder(mContext, mPlayer)
@@ -488,7 +481,7 @@
assertEquals(mContext.getPackageName(), controller.getPackageName());
assertEquals(request, uri);
assertTrue(TestUtils.equals(bundle, extras));
- latch.countDown();;
+ latch.countDown();
}
};
try (MediaSession2 session = new MediaSession2.Builder(mContext, mPlayer)
@@ -512,7 +505,7 @@
assertEquals(mContext.getPackageName(), controller.getPackageName());
assertEquals(request, id);
assertTrue(TestUtils.equals(bundle, extras));
- latch.countDown();;
+ latch.countDown();
}
};
try (MediaSession2 session = new MediaSession2.Builder(mContext, mPlayer)
@@ -525,6 +518,34 @@
}
@Test
+ public void testSetRating() throws InterruptedException {
+ final int ratingType = Rating2.RATING_5_STARS;
+ final float ratingValue = 3.5f;
+ final Rating2 rating = Rating2.newStarRating(mContext, ratingType, ratingValue);
+ final String mediaId = "media_id";
+
+ final CountDownLatch latch = new CountDownLatch(1);
+ final SessionCallback callback = new SessionCallback(mContext) {
+ @Override
+ public void onSetRating(ControllerInfo controller, String mediaIdOut,
+ Rating2 ratingOut) {
+ assertEquals(mContext.getPackageName(), controller.getPackageName());
+ assertEquals(mediaId, mediaIdOut);
+ assertEquals(rating, ratingOut);
+ latch.countDown();
+ }
+ };
+
+ try (MediaSession2 session = new MediaSession2.Builder(mContext, mPlayer)
+ .setSessionCallback(sHandlerExecutor, callback)
+ .setId("testSetRating").build()) {
+ MediaController2 controller = createController(session.getToken());
+ controller.setRating(mediaId, rating);
+ assertTrue(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+ }
+ }
+
+ @Test
public void testIsConnected() throws InterruptedException {
assertTrue(mController.isConnected());
sHandler.postAndSync(()->{
@@ -601,7 +622,6 @@
}
}
- @Ignore
@Test
public void testGetServiceToken() {
SessionToken2 token = TestUtils.getServiceToken(mContext, MockMediaSessionService2.ID);
@@ -612,20 +632,19 @@
}
private void connectToService(SessionToken2 token) throws InterruptedException {
+ if (mSession != null) {
+ mSession.close();
+ }
mController = createController(token);
mSession = TestServiceRegistry.getInstance().getServiceInstance().getSession();
mPlayer = (MockPlayer) mSession.getPlayer();
}
- // TODO(jaewan): Reenable when session manager detects app installs
- @Ignore
@Test
public void testConnectToService_sessionService() throws InterruptedException {
testConnectToService(MockMediaSessionService2.ID);
}
- // TODO(jaewan): Reenable when session manager detects app installs
- @Ignore
@Test
public void testConnectToService_libraryService() throws InterruptedException {
testConnectToService(MockMediaLibraryService2.ID);
@@ -639,13 +658,13 @@
if (Process.myUid() == controller.getUid()) {
assertEquals(mContext.getPackageName(), controller.getPackageName());
assertFalse(controller.isTrusted());
- latch.countDown();;
+ latch.countDown();
}
return super.onConnect(controller);
}
};
TestServiceRegistry.getInstance().setSessionCallbackProxy(proxy);
- mController = createController(TestUtils.getServiceToken(mContext, id));
+ connectToService(TestUtils.getServiceToken(mContext, id));
assertTrue(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
// Test command from controller to session service
@@ -657,7 +676,7 @@
// TODO(jaewan): Add equivalent tests again
/*
final CountDownLatch latch = new CountDownLatch(1);
- mController.addPlaybackListener((state) -> {
+ mController.registerPlayerEventCallback((state) -> {
assertNotNull(state);
assertEquals(PlaybackState.STATE_REWINDING, state.getState());
latch.countDown();
@@ -668,15 +687,11 @@
*/
}
- // TODO(jaewan): Re-enable after b/72792686 is fixed
- @Ignore
@Test
public void testControllerAfterSessionIsGone_session() throws InterruptedException {
testControllerAfterSessionIsGone(mSession.getToken().getId());
}
- // TODO(jaewan): Re-enable after b/72792686 is fixed
- @Ignore
@Test
public void testControllerAfterSessionIsGone_sessionService() throws InterruptedException {
connectToService(TestUtils.getServiceToken(mContext, MockMediaSessionService2.ID));
@@ -708,14 +723,12 @@
testControllerAfterSessionIsGone(id);
}
- // TODO(jaewan): Reenable when session manager detects app installs
@Ignore
@Test
public void testClose_sessionService() throws InterruptedException {
testCloseFromService(MockMediaSessionService2.ID);
}
- // TODO(jaewan): Reenable when session manager detects app installs
@Ignore
@Test
public void testClose_libraryService() throws InterruptedException {
@@ -751,7 +764,7 @@
waitForDisconnect(mController, true);
testNoInteraction();
- // Test with the newly created session.
+ // Ensure that the controller cannot use newly create session with the same ID.
sHandler.postAndSync(() -> {
// Recreated session has different session stub, so previously created controller
// shouldn't be available.
@@ -764,16 +777,19 @@
private void testNoInteraction() throws InterruptedException {
final CountDownLatch latch = new CountDownLatch(1);
- final PlaybackListener playbackListener = (state) -> {
- fail("Controller shouldn't be notified about change in session after the close.");
- latch.countDown();
+ final EventCallback callback = new EventCallback() {
+ @Override
+ public void onPlaybackStateChanged(PlaybackState2 state) {
+ fail("Controller shouldn't be notified about change in session after the close.");
+ latch.countDown();
+ }
};
// TODO(jaewan): Add equivalent tests again
/*
- mController.addPlaybackListener(playbackListener, sHandler);
+ mController.registerPlayerEventCallback(playbackListener, sHandler);
mPlayer.notifyPlaybackState(TestUtils.createPlaybackState(PlaybackState.STATE_BUFFERING));
assertFalse(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
- mController.removePlaybackListener(playbackListener);
+ mController.unregisterPlayerEventCallback(playbackListener);
*/
}
diff --git a/packages/MediaComponents/test/src/android/media/MediaMetadata2Test.java b/packages/MediaComponents/test/src/android/media/MediaMetadata2Test.java
index ea42651..06b51bd 100644
--- a/packages/MediaComponents/test/src/android/media/MediaMetadata2Test.java
+++ b/packages/MediaComponents/test/src/android/media/MediaMetadata2Test.java
@@ -42,20 +42,20 @@
@Test
public void testBuilder() {
- final Bundle extra = new Bundle();
- extra.putString("MediaMetadata2Test", "testBuilder");
+ final Bundle extras = new Bundle();
+ extras.putString("MediaMetadata2Test", "testBuilder");
final String title = "title";
final long discNumber = 10;
final Rating2 rating = Rating2.newThumbRating(mContext, true);
MediaMetadata2.Builder builder = new Builder(mContext);
- builder.setExtra(extra);
+ builder.setExtras(extras);
builder.putString(MediaMetadata2.METADATA_KEY_DISPLAY_TITLE, title);
builder.putLong(MediaMetadata2.METADATA_KEY_DISC_NUMBER, discNumber);
builder.putRating(MediaMetadata2.METADATA_KEY_USER_RATING, rating);
MediaMetadata2 metadata = builder.build();
- assertTrue(TestUtils.equals(extra, metadata.getExtra()));
+ assertTrue(TestUtils.equals(extras, metadata.getExtras()));
assertEquals(title, metadata.getString(MediaMetadata2.METADATA_KEY_DISPLAY_TITLE));
assertEquals(discNumber, metadata.getLong(MediaMetadata2.METADATA_KEY_DISC_NUMBER));
assertEquals(rating, metadata.getRating(MediaMetadata2.METADATA_KEY_USER_RATING));
diff --git a/packages/MediaComponents/test/src/android/media/MediaSession2Test.java b/packages/MediaComponents/test/src/android/media/MediaSession2Test.java
index f5ac6aa..9de4ce7 100644
--- a/packages/MediaComponents/test/src/android/media/MediaSession2Test.java
+++ b/packages/MediaComponents/test/src/android/media/MediaSession2Test.java
@@ -28,9 +28,8 @@
import static org.junit.Assert.fail;
import android.content.Context;
-import android.media.AudioManager;
import android.media.MediaController2.PlaybackInfo;
-import android.media.MediaPlayerInterface.PlaybackListener;
+import android.media.MediaPlayerInterface.EventCallback;
import android.media.MediaSession2.Builder;
import android.media.MediaSession2.Command;
import android.media.MediaSession2.CommandButton;
@@ -49,7 +48,6 @@
import org.junit.After;
import org.junit.Before;
-import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -253,51 +251,67 @@
assertTrue(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
}
- // TODO(jaewan): Re-enable test..
- @Ignore
@Test
- public void testPlaybackStateChangedListener() throws InterruptedException {
- final CountDownLatch latch = new CountDownLatch(2);
+ public void testRegisterEventCallback() throws InterruptedException {
+ final int testWhat = 1001;
final MockPlayer player = new MockPlayer(0);
- final PlaybackListener listener = (state) -> {
- assertEquals(sHandler.getLooper(), Looper.myLooper());
- assertNotNull(state);
- switch ((int) latch.getCount()) {
- case 2:
- assertEquals(PlaybackState2.STATE_PLAYING, state.getState());
- break;
- case 1:
- assertEquals(PlaybackState2.STATE_PAUSED, state.getState());
- break;
- case 0:
- fail();
+ final CountDownLatch playbackLatch = new CountDownLatch(3);
+ final CountDownLatch errorLatch = new CountDownLatch(1);
+ final EventCallback callback = new EventCallback() {
+ @Override
+ public void onPlaybackStateChanged(PlaybackState2 state) {
+ assertEquals(sHandler.getLooper(), Looper.myLooper());
+ switch ((int) playbackLatch.getCount()) {
+ case 3:
+ assertNull(state);
+ break;
+ case 2:
+ assertNotNull(state);
+ assertEquals(PlaybackState2.STATE_PLAYING, state.getState());
+ break;
+ case 1:
+ assertNotNull(state);
+ assertEquals(PlaybackState2.STATE_PAUSED, state.getState());
+ break;
+ case 0:
+ fail();
+ }
+ playbackLatch.countDown();
}
- latch.countDown();
+
+ @Override
+ public void onError(String mediaId, int what, int extra) {
+ assertEquals(testWhat, what);
+ errorLatch.countDown();
+ }
};
player.notifyPlaybackState(createPlaybackState(PlaybackState2.STATE_PLAYING));
- sHandler.postAndSync(() -> {
- mSession.addPlaybackListener(sHandlerExecutor, listener);
- // When the player is set, listeners will be notified about the player's current state.
- mSession.setPlayer(player);
- });
+ // EventCallback will be notified with the mPlayer's playback state (null)
+ mSession.registerPlayerEventCallback(sHandlerExecutor, callback);
+ // When the player is set, EventCallback will be notified about the new player's state.
+ mSession.setPlayer(player);
+ // When the player is set, EventCallback will be notified about the new player's state.
player.notifyPlaybackState(createPlaybackState(PlaybackState2.STATE_PAUSED));
- assertTrue(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+ assertTrue(playbackLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+ player.notifyError(testWhat);
+ assertTrue(errorLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
}
@Test
public void testBadPlayer() throws InterruptedException {
// TODO(jaewan): Add equivalent tests again
- final CountDownLatch latch = new CountDownLatch(3); // expected call + 1
+ final CountDownLatch latch = new CountDownLatch(4); // expected call + 1
final BadPlayer player = new BadPlayer(0);
- sHandler.postAndSync(() -> {
- mSession.addPlaybackListener(sHandlerExecutor, (state) -> {
+ mSession.registerPlayerEventCallback(sHandlerExecutor, new EventCallback() {
+ @Override
+ public void onPlaybackStateChanged(PlaybackState2 state) {
// This will be called for every setPlayer() calls, but no more.
assertNull(state);
latch.countDown();
- });
- mSession.setPlayer(player);
- mSession.setPlayer(mPlayer);
+ }
});
+ mSession.setPlayer(player);
+ mSession.setPlayer(mPlayer);
player.notifyPlaybackState(createPlaybackState(PlaybackState2.STATE_PAUSED));
assertFalse(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
}
@@ -308,7 +322,7 @@
}
@Override
- public void removePlaybackListener(@NonNull PlaybackListener listener) {
+ public void unregisterEventCallback(@NonNull EventCallback listener) {
// No-op. This bad player will keep push notification to the listener that is previously
// registered by session.setPlayer().
}
@@ -461,7 +475,8 @@
}
@Override
- public boolean onCommandRequest(ControllerInfo controllerInfo, MediaSession2.Command command) {
+ public boolean onCommandRequest(ControllerInfo controllerInfo,
+ MediaSession2.Command command) {
assertEquals(mContext.getPackageName(), controllerInfo.getPackageName());
assertEquals(Process.myUid(), controllerInfo.getUid());
assertFalse(controllerInfo.isTrusted());
diff --git a/packages/MediaComponents/test/src/android/media/MediaSession2TestBase.java b/packages/MediaComponents/test/src/android/media/MediaSession2TestBase.java
index f5abfff..7106561 100644
--- a/packages/MediaComponents/test/src/android/media/MediaSession2TestBase.java
+++ b/packages/MediaComponents/test/src/android/media/MediaSession2TestBase.java
@@ -118,7 +118,7 @@
*/
public PlaybackState2 createPlaybackState(int state) {
return new PlaybackState2(mContext, state, 0, 0, 1.0f,
- 0, 0, null);
+ 0, 0);
}
final MediaController2 createController(SessionToken2 token) throws InterruptedException {
diff --git a/packages/MediaComponents/test/src/android/media/MediaSessionManager_MediaSession2.java b/packages/MediaComponents/test/src/android/media/MediaSessionManager_MediaSession2.java
index 852f2fa..d61baab 100644
--- a/packages/MediaComponents/test/src/android/media/MediaSessionManager_MediaSession2.java
+++ b/packages/MediaComponents/test/src/android/media/MediaSessionManager_MediaSession2.java
@@ -26,7 +26,6 @@
import org.junit.After;
import org.junit.Before;
-import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -40,9 +39,6 @@
*/
@RunWith(AndroidJUnit4.class)
@SmallTest
-@Ignore
-// TODO(jaewan): Reenable test when the media session service detects newly installed sesison
-// service app.
public class MediaSessionManager_MediaSession2 extends MediaSession2TestBase {
private static final String TAG = "MediaSessionManager_MediaSession2";
@@ -101,7 +97,7 @@
}
/**
- * Test if server recognizes session even if session refuses the connection from server.
+ * Test if server recognizes a session even if the session refuses the connection from server.
*
* @throws InterruptedException
*/
diff --git a/packages/MediaComponents/test/src/android/media/MockMediaLibraryService2.java b/packages/MediaComponents/test/src/android/media/MockMediaLibraryService2.java
index 5fabebc..af14507 100644
--- a/packages/MediaComponents/test/src/android/media/MockMediaLibraryService2.java
+++ b/packages/MediaComponents/test/src/android/media/MockMediaLibraryService2.java
@@ -26,7 +26,6 @@
import android.media.TestServiceRegistry.SessionCallbackProxy;
import android.media.TestUtils.SyncHandler;
import android.os.Bundle;
-import android.os.Process;
import android.util.Log;
import java.io.FileDescriptor;
@@ -34,6 +33,8 @@
import java.util.List;
import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
import javax.annotation.concurrent.GuardedBy;
@@ -45,23 +46,32 @@
public static final String ID = "TestLibrary";
public static final String ROOT_ID = "rootId";
- public static final Bundle EXTRA = new Bundle();
+ public static final Bundle EXTRAS = new Bundle();
public static final String MEDIA_ID_GET_ITEM = "media_id_get_item";
public static final String PARENT_ID = "parent_id";
public static final String PARENT_ID_NO_CHILDREN = "parent_id_no_children";
public static final String PARENT_ID_ERROR = "parent_id_error";
- public static final List<MediaItem2> GET_CHILDREN_RESULT = new ArrayList<>();
- private static final int CHILDREN_COUNT = 100;
+ public static final List<MediaItem2> GET_CHILDREN_RESULT = new ArrayList<>();
+ public static final int CHILDREN_COUNT = 100;
+
+ public static final String SEARCH_QUERY = "search_query";
+ public static final String SEARCH_QUERY_TAKES_TIME = "search_query_takes_time";
+ public static final int SEARCH_TIME_IN_MS = 5000;
+ public static final String SEARCH_QUERY_EMPTY_RESULT = "search_query_empty_result";
+
+ public static final List<MediaItem2> SEARCH_RESULT = new ArrayList<>();
+ public static final int SEARCH_RESULT_COUNT = 50;
+
private static final DataSourceDesc DATA_SOURCE_DESC =
new DataSourceDesc.Builder().setDataSource(new FileDescriptor()).build();
private static final String TAG = "MockMediaLibrarySvc2";
static {
- EXTRA.putString(ROOT_ID, ROOT_ID);
+ EXTRAS.putString(ROOT_ID, ROOT_ID);
}
@GuardedBy("MockMediaLibraryService2.class")
private static SessionToken2 sToken;
@@ -71,9 +81,15 @@
public MockMediaLibraryService2() {
super();
GET_CHILDREN_RESULT.clear();
- String mediaIdPrefix = "media_id_";
+ String getChildrenMediaIdPrefix = "get_children_media_id_";
for (int i = 0; i < CHILDREN_COUNT; i++) {
- GET_CHILDREN_RESULT.add(createMediaItem(mediaIdPrefix + i));
+ GET_CHILDREN_RESULT.add(createMediaItem(getChildrenMediaIdPrefix + i));
+ }
+
+ SEARCH_RESULT.clear();
+ String getSearchResultMediaIdPrefix = "get_search_result_media_id_";
+ for (int i = 0; i < SEARCH_RESULT_COUNT; i++) {
+ SEARCH_RESULT.add(createMediaItem(getSearchResultMediaIdPrefix + i));
}
}
@@ -132,12 +148,12 @@
}
@Override
- public LibraryRoot onGetRoot(ControllerInfo controller, Bundle rootHints) {
- return new LibraryRoot(MockMediaLibraryService2.this, ROOT_ID, EXTRA);
+ public LibraryRoot onGetLibraryRoot(ControllerInfo controller, Bundle rootHints) {
+ return new LibraryRoot(MockMediaLibraryService2.this, ROOT_ID, EXTRAS);
}
@Override
- public MediaItem2 onLoadItem(ControllerInfo controller, String mediaId) {
+ public MediaItem2 onGetItem(ControllerInfo controller, String mediaId) {
if (MEDIA_ID_GET_ITEM.equals(mediaId)) {
return createMediaItem(mediaId);
} else {
@@ -146,8 +162,8 @@
}
@Override
- public List<MediaItem2> onLoadChildren(ControllerInfo controller, String parentId, int page,
- int pageSize, Bundle options) {
+ public List<MediaItem2> onGetChildren(ControllerInfo controller, String parentId, int page,
+ int pageSize, Bundle extras) {
if (PARENT_ID.equals(parentId)) {
return getPaginatedResult(GET_CHILDREN_RESULT, page, pageSize);
} else if (PARENT_ID_ERROR.equals(parentId)) {
@@ -156,6 +172,47 @@
// Includes the case of PARENT_ID_NO_CHILDREN.
return new ArrayList<>();
}
+
+ @Override
+ public void onSearch(ControllerInfo controllerInfo, String query, Bundle extras) {
+ if (SEARCH_QUERY.equals(query)) {
+ mSession.notifySearchResultChanged(controllerInfo, query, SEARCH_RESULT_COUNT,
+ extras);
+ } else if (SEARCH_QUERY_TAKES_TIME.equals(query)) {
+ // Searching takes some time. Notify after 5 seconds.
+ Executors.newSingleThreadScheduledExecutor().schedule(new Runnable() {
+ @Override
+ public void run() {
+ mSession.notifySearchResultChanged(
+ controllerInfo, query, SEARCH_RESULT_COUNT, extras);
+ }
+ }, SEARCH_TIME_IN_MS, TimeUnit.MILLISECONDS);
+ } else if (SEARCH_QUERY_EMPTY_RESULT.equals(query)) {
+ mSession.notifySearchResultChanged(controllerInfo, query, 0, extras);
+ } else {
+ // TODO: For the error case, how should we notify the browser?
+ }
+ }
+
+ @Override
+ public List<MediaItem2> onGetSearchResult(ControllerInfo controllerInfo,
+ String query, int page, int pageSize, Bundle extras) {
+ if (SEARCH_QUERY.equals(query)) {
+ return getPaginatedResult(SEARCH_RESULT, page, pageSize);
+ } else {
+ return null;
+ }
+ }
+
+ @Override
+ public void onSubscribe(ControllerInfo controller, String parentId, Bundle extras) {
+ mCallbackProxy.onSubscribe(controller, parentId, extras);
+ }
+
+ @Override
+ public void onUnsubscribe(ControllerInfo controller, String parentId) {
+ mCallbackProxy.onUnsubscribe(controller, parentId);
+ }
}
private List<MediaItem2> getPaginatedResult(List<MediaItem2> items, int page, int pageSize) {
@@ -191,4 +248,4 @@
.build(),
0 /* Flags */);
}
-}
\ No newline at end of file
+}
diff --git a/packages/MediaComponents/test/src/android/media/MockPlayer.java b/packages/MediaComponents/test/src/android/media/MockPlayer.java
index 1faf0f4..ae31ce6 100644
--- a/packages/MediaComponents/test/src/android/media/MockPlayer.java
+++ b/packages/MediaComponents/test/src/android/media/MockPlayer.java
@@ -19,6 +19,7 @@
import android.media.MediaSession2.PlaylistParams;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
+import android.util.ArrayMap;
import java.util.ArrayList;
import java.util.List;
@@ -46,7 +47,7 @@
public boolean mSetPlaylistCalled;
public boolean mSetPlaylistParamsCalled;
- public List<PlaybackListenerHolder> mListeners = new ArrayList<>();
+ public ArrayMap<EventCallback, Executor> mCallbacks = new ArrayMap<>();
public List<MediaItem2> mPlaylist;
public PlaylistParams mPlaylistParams;
@@ -146,23 +147,30 @@
}
@Override
- public void addPlaybackListener(@NonNull Executor executor,
- @NonNull PlaybackListener listener) {
- mListeners.add(new PlaybackListenerHolder(executor, listener));
+ public void registerEventCallback(@NonNull Executor executor,
+ @NonNull EventCallback callback) {
+ mCallbacks.put(callback, executor);
}
@Override
- public void removePlaybackListener(@NonNull PlaybackListener listener) {
- int index = PlaybackListenerHolder.indexOf(mListeners, listener);
- if (index >= 0) {
- mListeners.remove(index);
- }
+ public void unregisterEventCallback(@NonNull EventCallback callback) {
+ mCallbacks.remove(callback);
}
public void notifyPlaybackState(final PlaybackState2 state) {
mLastPlaybackState = state;
- for (int i = 0; i < mListeners.size(); i++) {
- mListeners.get(i).postPlaybackChange(state);
+ for (int i = 0; i < mCallbacks.size(); i++) {
+ final EventCallback callback = mCallbacks.keyAt(i);
+ final Executor executor = mCallbacks.valueAt(i);
+ executor.execute(() -> callback.onPlaybackStateChanged(state));
+ }
+ }
+
+ public void notifyError(int what) {
+ for (int i = 0; i < mCallbacks.size(); i++) {
+ final EventCallback callback = mCallbacks.keyAt(i);
+ final Executor executor = mCallbacks.valueAt(i);
+ executor.execute(() -> callback.onError(null, what, 0));
}
}
diff --git a/packages/MediaComponents/test/src/android/media/PlaybackListenerHolder.java b/packages/MediaComponents/test/src/android/media/PlaybackListenerHolder.java
deleted file mode 100644
index 0f1644c..0000000
--- a/packages/MediaComponents/test/src/android/media/PlaybackListenerHolder.java
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * Copyright 2018 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.
- */
-
-package android.media;
-
-import android.media.MediaPlayerInterface.PlaybackListener;
-import android.os.Handler;
-import android.support.annotation.NonNull;
-
-import java.util.List;
-import java.util.concurrent.Executor;
-
-/**
- * Holds {@link PlaybackListener} with the {@link Handler}.
- */
-public class PlaybackListenerHolder {
- public final Executor executor;
- public final PlaybackListener listener;
-
- public PlaybackListenerHolder(Executor executor, @NonNull PlaybackListener listener) {
- this.executor = executor;
- this.listener = listener;
- }
-
- public void postPlaybackChange(final PlaybackState2 state) {
- executor.execute(() -> listener.onPlaybackChanged(state));
- }
-
- /**
- * Returns {@code true} if the given list contains a {@link PlaybackListenerHolder} that holds
- * the given listener.
- *
- * @param list list to check
- * @param listener listener to check
- * @return {@code true} if the given list contains listener. {@code false} otherwise.
- */
- public static <Holder extends PlaybackListenerHolder> boolean contains(
- @NonNull List<Holder> list, PlaybackListener listener) {
- return indexOf(list, listener) >= 0;
- }
-
- /**
- * Returns the index of the {@link PlaybackListenerHolder} that contains the given listener.
- *
- * @param list list to check
- * @param listener listener to check
- * @return {@code index} of item if the given list contains listener. {@code -1} otherwise.
- */
- public static <Holder extends PlaybackListenerHolder> int indexOf(
- @NonNull List<Holder> list, PlaybackListener listener) {
- for (int i = 0; i < list.size(); i++) {
- if (list.get(i).listener == listener) {
- return i;
- }
- }
- return -1;
- }
-}
diff --git a/packages/MediaComponents/test/src/android/media/SessionToken2Test.java b/packages/MediaComponents/test/src/android/media/SessionToken2Test.java
new file mode 100644
index 0000000..efde78a
--- /dev/null
+++ b/packages/MediaComponents/test/src/android/media/SessionToken2Test.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2018 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.
+ */
+
+package android.media;
+
+import static junit.framework.Assert.assertEquals;
+
+import android.content.Context;
+import android.os.Process;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.support.test.InstrumentationRegistry;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Tests {@link SessionToken2}.
+ */
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class SessionToken2Test {
+ private Context mContext;
+
+ @Before
+ public void setUp() throws Exception {
+ mContext = InstrumentationRegistry.getTargetContext();
+ }
+
+ @Test
+ public void testConstructor_sessionService() {
+ SessionToken2 token = new SessionToken2(mContext, mContext.getPackageName(),
+ MockMediaSessionService2.class.getCanonicalName());
+ assertEquals(MockMediaSessionService2.ID, token.getId());
+ assertEquals(mContext.getPackageName(), token.getPackageName());
+ assertEquals(Process.myUid(), token.getUid());
+ assertEquals(SessionToken2.TYPE_SESSION_SERVICE, token.getType());
+ }
+
+ @Test
+ public void testConstructor_libraryService() {
+ SessionToken2 token = new SessionToken2(mContext, mContext.getPackageName(),
+ MockMediaLibraryService2.class.getCanonicalName());
+ assertEquals(MockMediaLibraryService2.ID, token.getId());
+ assertEquals(mContext.getPackageName(), token.getPackageName());
+ assertEquals(Process.myUid(), token.getUid());
+ assertEquals(SessionToken2.TYPE_LIBRARY_SERVICE, token.getType());
+ }
+}
\ No newline at end of file
diff --git a/packages/MediaComponents/test/src/android/media/TestServiceRegistry.java b/packages/MediaComponents/test/src/android/media/TestServiceRegistry.java
index 3800c28..08e0cf0 100644
--- a/packages/MediaComponents/test/src/android/media/TestServiceRegistry.java
+++ b/packages/MediaComponents/test/src/android/media/TestServiceRegistry.java
@@ -22,6 +22,7 @@
import android.media.MediaSession2.CommandGroup;
import android.media.MediaSession2.ControllerInfo;
import android.media.TestUtils.SyncHandler;
+import android.os.Bundle;
import android.os.Handler;
import android.os.Process;
import android.support.annotation.GuardedBy;
@@ -73,6 +74,9 @@
* Called when enclosing service is destroyed.
*/
public void onServiceDestroyed() { }
+
+ public void onSubscribe(ControllerInfo info, String parentId, Bundle extra) { }
+ public void onUnsubscribe(ControllerInfo info, String parentId) { }
}
@GuardedBy("TestServiceRegistry.class")
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index a3ce1f6..229e08e 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -39,8 +39,8 @@
#include <memunreachable/memunreachable.h>
#include <utils/String16.h>
#include <utils/threads.h>
-#include <utils/Atomic.h>
+#include <cutils/atomic.h>
#include <cutils/properties.h>
#include <system/audio.h>
@@ -1099,26 +1099,17 @@
if (status != NO_ERROR) {
return status;
}
+ if (output == AUDIO_IO_HANDLE_NONE) {
+ return BAD_VALUE;
+ }
ALOG_ASSERT(stream != AUDIO_STREAM_PATCH, "attempt to change AUDIO_STREAM_PATCH volume");
AutoMutex lock(mLock);
- Vector<VolumeInterface *> volumeInterfaces;
- if (output != AUDIO_IO_HANDLE_NONE) {
- VolumeInterface *volumeInterface = getVolumeInterface_l(output);
- if (volumeInterface == NULL) {
- return BAD_VALUE;
- }
- volumeInterfaces.add(volumeInterface);
+ VolumeInterface *volumeInterface = getVolumeInterface_l(output);
+ if (volumeInterface == NULL) {
+ return BAD_VALUE;
}
-
- mStreamTypes[stream].volume = value;
-
- if (volumeInterfaces.size() == 0) {
- volumeInterfaces = getAllVolumeInterfaces_l();
- }
- for (size_t i = 0; i < volumeInterfaces.size(); i++) {
- volumeInterfaces[i]->setStreamVolume(stream, value);
- }
+ volumeInterface->setStreamVolume(stream, value);
return NO_ERROR;
}
@@ -1157,21 +1148,17 @@
if (status != NO_ERROR) {
return 0.0f;
}
-
- AutoMutex lock(mLock);
- float volume;
- if (output != AUDIO_IO_HANDLE_NONE) {
- VolumeInterface *volumeInterface = getVolumeInterface_l(output);
- if (volumeInterface != NULL) {
- volume = volumeInterface->streamVolume(stream);
- } else {
- volume = 0.0f;
- }
- } else {
- volume = streamVolume_l(stream);
+ if (output == AUDIO_IO_HANDLE_NONE) {
+ return 0.0f;
}
- return volume;
+ AutoMutex lock(mLock);
+ VolumeInterface *volumeInterface = getVolumeInterface_l(output);
+ if (volumeInterface == NULL) {
+ return 0.0f;
+ }
+
+ return volumeInterface->streamVolume(stream);
}
bool AudioFlinger::streamMute(audio_stream_type_t stream) const
diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h
index 7c38bcc..0e2da4e 100644
--- a/services/audioflinger/AudioFlinger.h
+++ b/services/audioflinger/AudioFlinger.h
@@ -27,6 +27,7 @@
#include <android-base/macros.h>
+#include <cutils/atomic.h>
#include <cutils/compiler.h>
#include <cutils/properties.h>
@@ -38,7 +39,6 @@
#include <media/MmapStreamInterface.h>
#include <media/MmapStreamCallback.h>
-#include <utils/Atomic.h>
#include <utils/Errors.h>
#include <utils/threads.h>
#include <utils/SortedVector.h>
@@ -616,9 +616,6 @@
// no range check, AudioFlinger::mLock held
bool streamMute_l(audio_stream_type_t stream) const
{ return mStreamTypes[stream].mute; }
- // no range check, doesn't check per-thread stream volume, AudioFlinger::mLock held
- float streamVolume_l(audio_stream_type_t stream) const
- { return mStreamTypes[stream].volume; }
void ioConfigChanged(audio_io_config_event event,
const sp<AudioIoDescriptor>& ioDesc,
pid_t pid = 0);
diff --git a/services/audioflinger/FastMixer.cpp b/services/audioflinger/FastMixer.cpp
index ace586c..ef466a2 100644
--- a/services/audioflinger/FastMixer.cpp
+++ b/services/audioflinger/FastMixer.cpp
@@ -79,7 +79,6 @@
unsigned i;
for (i = 0; i < FastMixerState::sMaxFastTracks; ++i) {
- mFastTrackNames[i] = -1;
mGenerations[i] = 0;
}
#ifdef FAST_THREAD_STATISTICS
@@ -190,7 +189,7 @@
// FIXME new may block for unbounded time at internal mutex of the heap
// implementation; it would be better to have normal mixer allocate for us
// to avoid blocking here and to prevent possible priority inversion
- mMixer = new AudioMixer(frameCount, mSampleRate, FastMixerState::sMaxFastTracks);
+ mMixer = new AudioMixer(frameCount, mSampleRate);
// FIXME See the other FIXME at FastMixer::setNBLogWriter()
const size_t mixerFrameSize = mSinkChannelCount
* audio_bytes_per_sample(mMixerBufferFormat);
@@ -235,7 +234,6 @@
dumpState->mTrackMask = currentTrackMask;
if (current->mFastTracksGen != mFastTracksGen) {
ALOG_ASSERT(mMixerBuffer != NULL);
- int name;
// process removed tracks first to avoid running out of track names
unsigned removedTracks = previousTrackMask & ~currentTrackMask;
@@ -245,9 +243,7 @@
const FastTrack* fastTrack = ¤t->mFastTracks[i];
ALOG_ASSERT(fastTrack->mBufferProvider == NULL);
if (mMixer != NULL) {
- name = mFastTrackNames[i];
- ALOG_ASSERT(name >= 0);
- mMixer->deleteTrackName(name);
+ mMixer->destroy(i);
}
#if !LOG_NDEBUG
mFastTrackNames[i] = -1;
@@ -265,10 +261,16 @@
AudioBufferProvider *bufferProvider = fastTrack->mBufferProvider;
ALOG_ASSERT(bufferProvider != NULL && mFastTrackNames[i] == -1);
if (mMixer != NULL) {
- name = mMixer->getTrackName(fastTrack->mChannelMask,
+ const int name = i; // for clarity, choose name as fast track index.
+ status_t status = mMixer->create(
+ name,
+ fastTrack->mChannelMask,
fastTrack->mFormat, AUDIO_SESSION_OUTPUT_MIX);
- ALOG_ASSERT(name >= 0);
- mFastTrackNames[i] = name;
+ LOG_ALWAYS_FATAL_IF(status != NO_ERROR,
+ "%s: cannot create track name"
+ " %d, mask %#x, format %#x, sessionId %d in AudioMixer",
+ __func__, name,
+ fastTrack->mChannelMask, fastTrack->mFormat, AUDIO_SESSION_OUTPUT_MIX);
mMixer->setBufferProvider(name, bufferProvider);
mMixer->setParameter(name, AudioMixer::TRACK, AudioMixer::MAIN_BUFFER,
(void *)mMixerBuffer);
@@ -300,8 +302,7 @@
AudioBufferProvider *bufferProvider = fastTrack->mBufferProvider;
ALOG_ASSERT(bufferProvider != NULL);
if (mMixer != NULL) {
- name = mFastTrackNames[i];
- ALOG_ASSERT(name >= 0);
+ const int name = i;
mMixer->setBufferProvider(name, bufferProvider);
if (fastTrack->mVolumeProvider == NULL) {
float f = AudioMixer::UNITY_GAIN_FLOAT;
@@ -378,8 +379,7 @@
perTrackTimestamp.mPosition[ExtendedTimestamp::LOCATION_SERVER] = trackFramesWritten;
fastTrack->mBufferProvider->onTimestamp(perTrackTimestamp);
- int name = mFastTrackNames[i];
- ALOG_ASSERT(name >= 0);
+ const int name = i;
if (fastTrack->mVolumeProvider != NULL) {
gain_minifloat_packed_t vlr = fastTrack->mVolumeProvider->getVolumeLR();
float vlf = float_from_gain(gain_minifloat_unpack_left(vlr));
diff --git a/services/audioflinger/FastMixer.h b/services/audioflinger/FastMixer.h
index 930fa8d..235d23f 100644
--- a/services/audioflinger/FastMixer.h
+++ b/services/audioflinger/FastMixer.h
@@ -57,8 +57,6 @@
static const FastMixerState sInitial;
FastMixerState mPreIdle; // copy of state before we went into idle
- int mFastTrackNames[FastMixerState::kMaxFastTracks];
- // handles used by mixer to identify tracks
int mGenerations[FastMixerState::kMaxFastTracks];
// last observed mFastTracks[i].mGeneration
NBAIO_Sink* mOutputSink;
diff --git a/services/audioflinger/PatchPanel.cpp b/services/audioflinger/PatchPanel.cpp
index 27c6d35..e5cb8a2 100644
--- a/services/audioflinger/PatchPanel.cpp
+++ b/services/audioflinger/PatchPanel.cpp
@@ -497,9 +497,6 @@
patch->mPatchRecord->buffer(),
patch->mPatchRecord->bufferSize(),
AUDIO_OUTPUT_FLAG_NONE);
- if (patch->mPatchTrack == 0) {
- return NO_MEMORY;
- }
status = patch->mPatchTrack->initCheck();
if (status != NO_ERROR) {
return status;
diff --git a/services/audioflinger/PlaybackTracks.h b/services/audioflinger/PlaybackTracks.h
index e97bb06..6454be5 100644
--- a/services/audioflinger/PlaybackTracks.h
+++ b/services/audioflinger/PlaybackTracks.h
@@ -51,6 +51,11 @@
void flush();
void destroy();
int name() const { return mName; }
+ void setName(int name) {
+ LOG_ALWAYS_FATAL_IF(mName >= 0 && name >= 0,
+ "%s both old name %d and new name %d are valid", __func__, mName, name);
+ mName = name;
+ }
virtual uint32_t sampleRate() const;
@@ -146,10 +151,7 @@
bool mResetDone;
const audio_stream_type_t mStreamType;
- int mName; // track name on the normal mixer,
- // allocated statically at track creation time,
- // and is even allocated (though unused) for fast tracks
- // FIXME don't allocate track name for fast tracks
+ int mName;
effect_buffer_t *mMainBuffer;
int32_t *mAuxBuffer;
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index d6021b3..3134323 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -1661,6 +1661,7 @@
mSuspendedFrames(0),
mActiveTracks(&this->mLocalLog),
// mStreamTypes[] initialized in constructor body
+ mTracks(type == MIXER),
mOutput(output),
mLastWriteTime(-1), mNumWrites(0), mNumDelayedWrites(0), mInWrite(false),
mMixerStatus(MIXER_IDLE),
@@ -1702,11 +1703,14 @@
readOutputParameters_l();
// ++ operator does not compile
- for (audio_stream_type_t stream = AUDIO_STREAM_MIN; stream < AUDIO_STREAM_CNT;
+ for (audio_stream_type_t stream = AUDIO_STREAM_MIN; stream < AUDIO_STREAM_FOR_POLICY_CNT;
stream = (audio_stream_type_t) (stream + 1)) {
- mStreamTypes[stream].volume = mAudioFlinger->streamVolume_l(stream);
+ mStreamTypes[stream].volume = 0.0f;
mStreamTypes[stream].mute = mAudioFlinger->streamMute_l(stream);
}
+ // Audio patch volume is always max
+ mStreamTypes[AUDIO_STREAM_PATCH].volume = 1.0f;
+ mStreamTypes[AUDIO_STREAM_PATCH].mute = false;
}
AudioFlinger::PlaybackThread::~PlaybackThread()
@@ -2157,6 +2161,53 @@
return track;
}
+template<typename T>
+ssize_t AudioFlinger::PlaybackThread::Tracks<T>::add(const sp<T> &track)
+{
+ const ssize_t index = mTracks.add(track);
+ if (index >= 0) {
+ // set name for track when adding.
+ int name;
+ if (mUnusedTrackNames.empty()) {
+ name = mTracks.size() - 1; // new name {0 ... size-1}.
+ } else {
+ // reuse smallest name for deleted track.
+ auto it = mUnusedTrackNames.begin();
+ name = *it;
+ (void)mUnusedTrackNames.erase(it);
+ }
+ track->setName(name);
+ } else {
+ LOG_ALWAYS_FATAL("cannot add track");
+ }
+ return index;
+}
+
+template<typename T>
+ssize_t AudioFlinger::PlaybackThread::Tracks<T>::remove(const sp<T> &track)
+{
+ const int name = track->name();
+ const ssize_t index = mTracks.remove(track);
+ if (index >= 0) {
+ // invalidate name when removing from mTracks.
+ LOG_ALWAYS_FATAL_IF(name < 0, "invalid name %d for track on mTracks", name);
+
+ if (mSaveDeletedTrackNames) {
+ // We can't directly access mAudioMixer since the caller may be outside of threadLoop.
+ // Instead, we add to mDeletedTrackNames which is solely used for mAudioMixer update,
+ // to be handled when MixerThread::prepareTracks_l() next changes mAudioMixer.
+ mDeletedTrackNames.emplace(name);
+ }
+
+ mUnusedTrackNames.emplace(name);
+ track->setName(T::TRACK_NAME_PENDING);
+ } else {
+ LOG_ALWAYS_FATAL_IF(name >= 0,
+ "valid name %d for track not in mTracks (returned %zd)", name, index);
+ }
+ return index;
+}
+
uint32_t AudioFlinger::PlaybackThread::correctLatency_l(uint32_t latency) const
{
return latency;
@@ -2313,9 +2364,6 @@
mLocalLog.log("removeTrack_l (%p) %s", track.get(), result.string());
mTracks.remove(track);
- deleteTrackName_l(track->name());
- // redundant as track is about to be destroyed, for dumpsys only
- track->mName = -1;
if (track->isFastTrack()) {
int index = track->mFastIndex;
ALOG_ASSERT(0 < index && index < (int)FastMixerState::sMaxFastTracks);
@@ -4111,6 +4159,14 @@
AudioFlinger::PlaybackThread::mixer_state AudioFlinger::MixerThread::prepareTracks_l(
Vector< sp<Track> > *tracksToRemove)
{
+ // clean up deleted track names in AudioMixer before allocating new tracks
+ (void)mTracks.processDeletedTrackNames([this](int name) {
+ // for each name, destroy it in the AudioMixer
+ if (mAudioMixer->exists(name)) {
+ mAudioMixer->destroy(name);
+ }
+ });
+ mTracks.clearDeletedTrackNames();
mixer_state mixerStatus = MIXER_IDLE;
// find out which tracks need to be processed
@@ -4332,6 +4388,24 @@
// The first time a track is added we wait
// for all its buffers to be filled before processing it
int name = track->name();
+
+ // if an active track doesn't exist in the AudioMixer, create it.
+ if (!mAudioMixer->exists(name)) {
+ status_t status = mAudioMixer->create(
+ name,
+ track->mChannelMask,
+ track->mFormat,
+ track->mSessionId);
+ if (status != OK) {
+ ALOGW("%s: cannot create track name"
+ " %d, mask %#x, format %#x, sessionId %d in AudioMixer",
+ __func__, name, track->mChannelMask, track->mFormat, track->mSessionId);
+ tracksToRemove->add(track);
+ track->invalidate(); // consider it dead.
+ continue;
+ }
+ }
+
// make sure that we have enough frames to mix one full buffer.
// enforce this condition only once to enable draining the buffer in case the client
// app does not call stop() and relies on underrun to stop:
@@ -4357,20 +4431,9 @@
size_t framesReady = track->framesReady();
if (ATRACE_ENABLED()) {
// I wish we had formatted trace names
- char traceName[16];
- strcpy(traceName, "nRdy");
- int name = track->name();
- if (AudioMixer::TRACK0 <= name &&
- name < (int) (AudioMixer::TRACK0 + AudioMixer::MAX_NUM_TRACKS)) {
- name -= AudioMixer::TRACK0;
- traceName[4] = (name / 10) + '0';
- traceName[5] = (name % 10) + '0';
- } else {
- traceName[4] = '?';
- traceName[5] = '?';
- }
- traceName[6] = '\0';
- ATRACE_INT(traceName, framesReady);
+ std::string traceName("nRdy");
+ traceName += std::to_string(track->name());
+ ATRACE_INT(traceName.c_str(), framesReady);
}
if ((framesReady >= minFrames) && track->isReady() &&
!track->isPaused() && !track->isTerminated())
@@ -4736,7 +4799,7 @@
}
// trackCountForUid_l() must be called with ThreadBase::mLock held
-uint32_t AudioFlinger::PlaybackThread::trackCountForUid_l(uid_t uid)
+uint32_t AudioFlinger::PlaybackThread::trackCountForUid_l(uid_t uid) const
{
uint32_t trackCount = 0;
for (size_t i = 0; i < mTracks.size() ; i++) {
@@ -4747,21 +4810,24 @@
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, uid_t uid)
+// isTrackAllowed_l() must be called with ThreadBase::mLock held
+bool AudioFlinger::MixerThread::isTrackAllowed_l(
+ audio_channel_mask_t channelMask, audio_format_t format,
+ audio_session_t sessionId, uid_t uid) const
{
- if (trackCountForUid_l(uid) > (PlaybackThread::kMaxTracksPerUid - 1)) {
- return -1;
+ if (!PlaybackThread::isTrackAllowed_l(channelMask, format, sessionId, uid)) {
+ return false;
}
- return mAudioMixer->getTrackName(channelMask, format, sessionId);
-}
-
-// deleteTrackName_l() must be called with ThreadBase::mLock held
-void AudioFlinger::MixerThread::deleteTrackName_l(int name)
-{
- ALOGV("remove track (%d) and delete from mixer", name);
- mAudioMixer->deleteTrackName(name);
+ // Check validity as we don't call AudioMixer::create() here.
+ if (!AudioMixer::isValidFormat(format)) {
+ ALOGW("%s: invalid format: %#x", __func__, format);
+ return false;
+ }
+ if (!AudioMixer::isValidChannelMask(channelMask)) {
+ ALOGW("%s: invalid channelMask: %#x", __func__, channelMask);
+ return false;
+ }
+ return true;
}
// checkForNewParameter_l() must be called with ThreadBase::mLock held
@@ -4854,13 +4920,18 @@
readOutputParameters_l();
delete mAudioMixer;
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]->uid());
- if (name < 0) {
- break;
- }
- mTracks[i]->mName = name;
+ for (const auto &track : mTracks) {
+ const int name = track->name();
+ status_t status = mAudioMixer->create(
+ name,
+ track->mChannelMask,
+ track->mFormat,
+ track->mSessionId);
+ ALOGW_IF(status != NO_ERROR,
+ "%s: cannot create track name"
+ " %d, mask %#x, format %#x, sessionId %d in AudioMixer",
+ __func__,
+ name, track->mChannelMask, track->mFormat, track->mSessionId);
}
sendIoConfigEvent_l(AUDIO_OUTPUT_CONFIG_CHANGED);
}
@@ -5309,21 +5380,6 @@
return !mStandby && !(trackPaused || (mHwPaused && !trackStopped));
}
-// 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, uid_t uid)
-{
- if (trackCountForUid_l(uid) > (PlaybackThread::kMaxTracksPerUid - 1)) {
- return -1;
- }
- return 0;
-}
-
-// deleteTrackName_l() must be called with ThreadBase::mLock held
-void AudioFlinger::DirectOutputThread::deleteTrackName_l(int name __unused)
-{
-}
-
// checkForNewParameter_l() must be called with ThreadBase::mLock held
bool AudioFlinger::DirectOutputThread::checkForNewParameter_l(const String8& keyValuePair,
status_t& status)
@@ -5941,6 +5997,31 @@
}
}
+void AudioFlinger::DuplicatingThread::dumpInternals(int fd, const Vector<String16>& args __unused)
+{
+ MixerThread::dumpInternals(fd, args);
+
+ std::stringstream ss;
+ const size_t numTracks = mOutputTracks.size();
+ ss << " " << numTracks << " OutputTracks";
+ if (numTracks > 0) {
+ ss << ":";
+ for (const auto &track : mOutputTracks) {
+ const sp<ThreadBase> thread = track->thread().promote();
+ ss << " (" << track->name() << " : ";
+ if (thread.get() != nullptr) {
+ ss << thread.get() << ", " << thread->id();
+ } else {
+ ss << "null";
+ }
+ ss << ")";
+ }
+ }
+ ss << "\n";
+ std::string result = ss.str();
+ write(fd, result.c_str(), result.size());
+}
+
void AudioFlinger::DuplicatingThread::saveOutputTracks()
{
outputTracks = mOutputTracks;
diff --git a/services/audioflinger/Threads.h b/services/audioflinger/Threads.h
index 53cb8ad..ae14ac1 100644
--- a/services/audioflinger/Threads.h
+++ b/services/audioflinger/Threads.h
@@ -624,6 +624,7 @@
static const int8_t kMaxTrackStartupRetriesOffload = 100;
static const int8_t kMaxTrackStopRetriesOffload = 2;
static constexpr uint32_t kMaxTracksPerUid = 40;
+ static constexpr size_t kMaxTracks = 256;
// Maximum delay (in nanoseconds) for upcoming buffers in suspend mode, otherwise
// if delay is greater, the estimated time for timeLoopNextNs is reset.
@@ -778,6 +779,16 @@
virtual bool isOutput() const override { return true; }
+ // returns true if the track is allowed to be added to the thread.
+ virtual bool isTrackAllowed_l(
+ audio_channel_mask_t channelMask __unused,
+ audio_format_t format __unused,
+ audio_session_t sessionId __unused,
+ uid_t uid) const {
+ return trackCountForUid_l(uid) < PlaybackThread::kMaxTracksPerUid
+ && mTracks.size() < PlaybackThread::kMaxTracks;
+ }
+
protected:
// updated by readOutputParameters_l()
size_t mNormalFrameCount; // normal mixer and effects
@@ -866,12 +877,6 @@
protected:
ActiveTracks<Track> mActiveTracks;
- // 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, uid_t uid) = 0;
- virtual void deleteTrackName_l(int name) = 0;
-
// Time to sleep between cycles when:
virtual uint32_t activeSleepTimeUs() const; // mixer state MIXER_TRACKS_ENABLED
virtual uint32_t idleSleepTimeUs() const = 0; // mixer state MIXER_IDLE
@@ -899,7 +904,7 @@
&& mHwSupportsPause
&& (mOutput->flags & AUDIO_OUTPUT_FLAG_HW_AV_SYNC); }
- uint32_t trackCountForUid_l(uid_t uid);
+ uint32_t trackCountForUid_l(uid_t uid) const;
private:
@@ -916,7 +921,64 @@
virtual void dumpInternals(int fd, const Vector<String16>& args);
void dumpTracks(int fd, const Vector<String16>& args);
- SortedVector< sp<Track> > mTracks;
+ // The Tracks class manages names for all tracks
+ // added and removed from the Thread.
+ template <typename T>
+ class Tracks {
+ public:
+ Tracks(bool saveDeletedTrackNames) :
+ mSaveDeletedTrackNames(saveDeletedTrackNames) { }
+
+ // SortedVector methods
+ ssize_t add(const sp<T> &track);
+ ssize_t remove(const sp<T> &track);
+ size_t size() const {
+ return mTracks.size();
+ }
+ bool isEmpty() const {
+ return mTracks.isEmpty();
+ }
+ ssize_t indexOf(const sp<T> &item) {
+ return mTracks.indexOf(item);
+ }
+ sp<T> operator[](size_t index) const {
+ return mTracks[index];
+ }
+ typename SortedVector<sp<T>>::iterator begin() {
+ return mTracks.begin();
+ }
+ typename SortedVector<sp<T>>::iterator end() {
+ return mTracks.end();
+ }
+
+ size_t processDeletedTrackNames(std::function<void(int)> f) {
+ const size_t size = mDeletedTrackNames.size();
+ if (size > 0) {
+ for (const int name : mDeletedTrackNames) {
+ f(name);
+ }
+ }
+ return size;
+ }
+
+ void clearDeletedTrackNames() { mDeletedTrackNames.clear(); }
+
+ private:
+ // Track names pending deletion for MIXER type threads
+ const bool mSaveDeletedTrackNames; // true to enable tracking
+ std::set<int> mDeletedTrackNames;
+
+ // Fast lookup of previously deleted track names for reuse.
+ // This is an arbitrary decision (actually any non-negative
+ // integer that isn't in mTracks[*]->names() could be used) - we attempt
+ // to use the smallest possible available name.
+ std::set<int> mUnusedTrackNames;
+
+ SortedVector<sp<T>> mTracks; // wrapped SortedVector.
+ };
+
+ Tracks<Track> mTracks;
+
stream_type_t mStreamTypes[AUDIO_STREAM_CNT];
AudioStreamOut *mOutput;
@@ -1023,11 +1085,11 @@
status_t& status);
virtual void dumpInternals(int fd, const Vector<String16>& args);
+ virtual bool isTrackAllowed_l(
+ audio_channel_mask_t channelMask, audio_format_t format,
+ audio_session_t sessionId, uid_t uid) const override;
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, uid_t uid);
- virtual void deleteTrackName_l(int name);
virtual uint32_t idleSleepTimeUs() const;
virtual uint32_t suspendSleepTimeUs() const;
virtual void cacheParameters_l();
@@ -1105,9 +1167,6 @@
virtual void flushHw_l();
protected:
- 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;
virtual uint32_t suspendSleepTimeUs() const;
@@ -1211,6 +1270,8 @@
virtual ~DuplicatingThread();
// Thread virtuals
+ virtual void dumpInternals(int fd, const Vector<String16>& args) override;
+
void addOutputTrack(MixerThread* thread);
void removeOutputTrack(MixerThread* thread);
uint32_t waitTimeMs() const { return mWaitTimeMs; }
diff --git a/services/audioflinger/TrackBase.h b/services/audioflinger/TrackBase.h
index a3ea756..a7e966f 100644
--- a/services/audioflinger/TrackBase.h
+++ b/services/audioflinger/TrackBase.h
@@ -54,6 +54,11 @@
TYPE_PATCH,
};
+ enum {
+ TRACK_NAME_PENDING = -1,
+ TRACK_NAME_FAILURE = -2,
+ };
+
TrackBase(ThreadBase *thread,
const sp<Client>& client,
uint32_t sampleRate,
diff --git a/services/audioflinger/Tracks.cpp b/services/audioflinger/Tracks.cpp
index 67f27d0..9b93939 100644
--- a/services/audioflinger/Tracks.cpp
+++ b/services/audioflinger/Tracks.cpp
@@ -394,7 +394,7 @@
// mRetryCount initialized later when needed
mSharedBuffer(sharedBuffer),
mStreamType(streamType),
- mName(-1), // see note below
+ mName(TRACK_NAME_FAILURE), // set to TRACK_NAME_PENDING on constructor success.
mMainBuffer(thread->sinkBuffer()),
mAuxBuffer(NULL),
mAuxEffectId(0), mHasVolumeController(false),
@@ -427,9 +427,8 @@
}
mServerProxy = mAudioTrackServerProxy;
- mName = thread->getTrackName_l(channelMask, format, sessionId, uid);
- if (mName < 0) {
- ALOGE("no more track names available");
+ if (!thread->isTrackAllowed_l(channelMask, format, sessionId, uid)) {
+ ALOGE("no more tracks available");
return;
}
// only allocate a fast track index if we were able to allocate a normal track name
@@ -448,6 +447,7 @@
mFastIndex = i;
thread->mFastTrackAvailMask &= ~(1 << i);
}
+ mName = TRACK_NAME_PENDING;
}
AudioFlinger::PlaybackThread::Track::~Track()
@@ -466,7 +466,7 @@
status_t AudioFlinger::PlaybackThread::Track::initCheck() const
{
status_t status = TrackBase::initCheck();
- if (status == NO_ERROR && mName < 0) {
+ if (status == NO_ERROR && mName == TRACK_NAME_FAILURE) {
status = NO_MEMORY;
}
return status;
@@ -527,10 +527,12 @@
if (isFastTrack()) {
result.appendFormat("F%c %3d", trackType, mFastIndex);
- } else if (mName >= AudioMixer::TRACK0) {
- result.appendFormat("%c %4d", trackType, mName - AudioMixer::TRACK0);
+ } else if (mName == TRACK_NAME_PENDING) {
+ result.appendFormat("%c pend", trackType);
+ } else if (mName == TRACK_NAME_FAILURE) {
+ result.appendFormat("%c fail", trackType);
} else {
- result.appendFormat("%c none", trackType);
+ result.appendFormat("%c %4d", trackType, mName);
}
char nowInUnderrun;
diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioOutputDescriptor.h b/services/audiopolicy/common/managerdefinitions/include/AudioOutputDescriptor.h
index cd2174d..78a184b 100644
--- a/services/audiopolicy/common/managerdefinitions/include/AudioOutputDescriptor.h
+++ b/services/audiopolicy/common/managerdefinitions/include/AudioOutputDescriptor.h
@@ -133,6 +133,9 @@
// Note: called after changeRefCount(-1);
void stop();
void close();
+ status_t openDuplicating(const sp<SwAudioOutputDescriptor>& output1,
+ const sp<SwAudioOutputDescriptor>& output2,
+ audio_io_handle_t *ioHandle);
const sp<IOProfile> mProfile; // I/O profile this output derives from
audio_io_handle_t mIoHandle; // output handle
diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp
index 17fc272..caaa0f7 100644
--- a/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp
@@ -222,7 +222,7 @@
SwAudioOutputDescriptor::SwAudioOutputDescriptor(const sp<IOProfile>& profile,
AudioPolicyClientInterface *clientInterface)
: AudioOutputDescriptor(profile, clientInterface),
- mProfile(profile), mIoHandle(0), mLatency(0),
+ mProfile(profile), mIoHandle(AUDIO_IO_HANDLE_NONE), mLatency(0),
mFlags((audio_output_flags_t)0), mPolicyMix(NULL),
mOutput1(0), mOutput2(0), mDirectOpenCount(0),
mDirectClientSession(AUDIO_SESSION_NONE), mGlobalRefCount(0)
@@ -509,6 +509,30 @@
}
}
+status_t SwAudioOutputDescriptor::openDuplicating(const sp<SwAudioOutputDescriptor>& output1,
+ const sp<SwAudioOutputDescriptor>& output2,
+ audio_io_handle_t *ioHandle)
+{
+ // open a duplicating output thread for the new output and the primary output
+ // Note: openDuplicateOutput() API expects the output handles in the reverse order from the
+ // numbering in SwAudioOutputDescriptor mOutput1 and mOutput2
+ *ioHandle = mClientInterface->openDuplicateOutput(output2->mIoHandle, output1->mIoHandle);
+ if (*ioHandle == AUDIO_IO_HANDLE_NONE) {
+ return INVALID_OPERATION;
+ }
+
+ mId = AudioPort::getNextUniqueId();
+ mIoHandle = *ioHandle;
+ mOutput1 = output1;
+ mOutput2 = output2;
+ mSamplingRate = output2->mSamplingRate;
+ mFormat = output2->mFormat;
+ mChannelMask = output2->mChannelMask;
+ mLatency = output2->mLatency;
+
+ return NO_ERROR;
+}
+
// HwAudioOutputDescriptor implementation
HwAudioOutputDescriptor::HwAudioOutputDescriptor(const sp<AudioSourceDescriptor>& source,
AudioPolicyClientInterface *clientInterface)
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
index 54bfcbc..57d9371 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
@@ -67,7 +67,9 @@
const char *device_address,
const char *device_name)
{
- return setDeviceConnectionStateInt(device, state, device_address, device_name);
+ status_t status = setDeviceConnectionStateInt(device, state, device_address, device_name);
+ nextAudioPortGeneration();
+ return status;
}
void AudioPolicyManager::broadcastDeviceConnectionState(audio_devices_t device,
@@ -1158,6 +1160,12 @@
bool force = !outputDesc->isActive() &&
(outputDesc->getPatchHandle() == AUDIO_PATCH_HANDLE_NONE);
+ // requiresMuteCheck is false when we can bypass mute strategy.
+ // It covers a common case when there is no materially active audio
+ // and muting would result in unnecessary delay and dropped audio.
+ const uint32_t outputLatencyMs = outputDesc->latency();
+ bool requiresMuteCheck = outputDesc->isActive(outputLatencyMs * 2); // account for drain
+
// 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
// necessary for a correct control of hardware output routing by startOutput() and stopOutput()
@@ -1181,29 +1189,44 @@
for (size_t i = 0; i < mOutputs.size(); i++) {
sp<AudioOutputDescriptor> desc = mOutputs.valueAt(i);
if (desc != outputDesc) {
+ // An output has a shared device if
+ // - managed by the same hw module
+ // - supports the currently selected device
+ const bool sharedDevice = outputDesc->sharesHwModuleWith(desc)
+ && (desc->supportedDevices() & device) != AUDIO_DEVICE_NONE;
+
// 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 a current device selection that differs from 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) &&
+ // change the device currently selected by the other output.
+ if (sharedDevice &&
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
// a notification so that audio focus effect can propagate, or that a mute/unmute
// event occurred for beacon
- uint32_t latency = desc->latency();
- if (shouldWait && desc->isActive(latency * 2) && (waitMs < latency)) {
- waitMs = latency;
+ const uint32_t latencyMs = desc->latency();
+ const bool isActive = desc->isActive(latencyMs * 2); // account for drain
+
+ if (shouldWait && isActive && (waitMs < latencyMs)) {
+ waitMs = latencyMs;
}
+
+ // Require mute check if another output is on a shared device
+ // and currently active to have proper drain and avoid pops.
+ // Note restoring AudioTracks onto this output needs to invoke
+ // a volume ramp if there is no mute.
+ requiresMuteCheck |= sharedDevice && isActive;
}
}
- uint32_t muteWaitMs = setOutputDevice(outputDesc, device, force, 0, NULL, address);
+
+ const uint32_t muteWaitMs =
+ setOutputDevice(outputDesc, device, force, 0, NULL, address, requiresMuteCheck);
// handle special case for sonification while in call
if (isInCall()) {
@@ -1228,6 +1251,14 @@
if (waitMs > muteWaitMs) {
*delayMs = waitMs - muteWaitMs;
}
+
+ // FIXME: A device change (muteWaitMs > 0) likely introduces a volume change.
+ // A volume change enacted by APM with 0 delay is not synchronous, as it goes
+ // via AudioCommandThread to AudioFlinger. Hence it is possible that the volume
+ // change occurs after the MixerThread starts and causes a stream volume
+ // glitch.
+ //
+ // We do not introduce additional delay here.
}
return NO_ERROR;
@@ -3760,9 +3791,11 @@
// ---
-void AudioPolicyManager::addOutput(audio_io_handle_t output, const sp<SwAudioOutputDescriptor>& outputDesc)
+void AudioPolicyManager::addOutput(audio_io_handle_t output,
+ const sp<SwAudioOutputDescriptor>& outputDesc)
{
mOutputs.add(output, outputDesc);
+ applyStreamVolumes(outputDesc, AUDIO_DEVICE_NONE, 0 /* delayMs */, true /* force */);
updateMono(output); // update mono status when adding to output list
selectOutputForMusicEffects();
nextAudioPortGeneration();
@@ -3774,7 +3807,8 @@
selectOutputForMusicEffects();
}
-void AudioPolicyManager::addInput(audio_io_handle_t input, const sp<AudioInputDescriptor>& inputDesc)
+void AudioPolicyManager::addInput(audio_io_handle_t input,
+ const sp<AudioInputDescriptor>& inputDesc)
{
mInputs.add(input, inputDesc);
nextAudioPortGeneration();
@@ -3924,27 +3958,16 @@
// outputs used by dynamic policy mixes
audio_io_handle_t duplicatedOutput = AUDIO_IO_HANDLE_NONE;
- // set initial stream volume for device
- applyStreamVolumes(desc, device, 0, true);
-
//TODO: configure audio effect output stage here
// open a duplicating output thread for the new output and the primary output
- duplicatedOutput =
- mpClientInterface->openDuplicateOutput(output,
- mPrimaryOutput->mIoHandle);
- if (duplicatedOutput != AUDIO_IO_HANDLE_NONE) {
+ sp<SwAudioOutputDescriptor> dupOutputDesc =
+ new SwAudioOutputDescriptor(NULL, mpClientInterface);
+ status_t status = dupOutputDesc->openDuplicating(mPrimaryOutput, desc,
+ &duplicatedOutput);
+ if (status == NO_ERROR) {
// add duplicated output descriptor
- sp<SwAudioOutputDescriptor> dupOutputDesc =
- new SwAudioOutputDescriptor(NULL, mpClientInterface);
- dupOutputDesc->mOutput1 = mPrimaryOutput;
- dupOutputDesc->mOutput2 = desc;
- dupOutputDesc->mSamplingRate = desc->mSamplingRate;
- dupOutputDesc->mFormat = desc->mFormat;
- dupOutputDesc->mChannelMask = desc->mChannelMask;
- dupOutputDesc->mLatency = desc->mLatency;
addOutput(duplicatedOutput, dupOutputDesc);
- applyStreamVolumes(dupOutputDesc, device, 0, true);
} else {
ALOGW("checkOutputsForDevice() could not open dup output for %d and %d",
mPrimaryOutput->mIoHandle, output);
@@ -4742,21 +4765,24 @@
bool force,
int delayMs,
audio_patch_handle_t *patchHandle,
- const char* address)
+ const char *address,
+ bool requiresMuteCheck)
{
ALOGV("setOutputDevice() device %04x delayMs %d", device, delayMs);
AudioParameter param;
uint32_t muteWaitMs;
if (outputDesc->isDuplicated()) {
- muteWaitMs = setOutputDevice(outputDesc->subOutput1(), device, force, delayMs);
- muteWaitMs += setOutputDevice(outputDesc->subOutput2(), device, force, delayMs);
+ muteWaitMs = setOutputDevice(outputDesc->subOutput1(), device, force, delayMs,
+ nullptr /* patchHandle */, nullptr /* address */, requiresMuteCheck);
+ muteWaitMs += setOutputDevice(outputDesc->subOutput2(), device, force, delayMs,
+ nullptr /* patchHandle */, nullptr /* address */, requiresMuteCheck);
return muteWaitMs;
}
// no need to proceed if new device is not AUDIO_DEVICE_NONE and not supported by current
// output profile
if ((device != AUDIO_DEVICE_NONE) &&
- ((device & outputDesc->supportedDevices()) == 0)) {
+ ((device & outputDesc->supportedDevices()) == AUDIO_DEVICE_NONE)) {
return 0;
}
@@ -4770,7 +4796,14 @@
if (device != AUDIO_DEVICE_NONE) {
outputDesc->mDevice = device;
}
- muteWaitMs = checkDeviceMuteStrategies(outputDesc, prevDevice, delayMs);
+
+ // if the outputs are not materially active, there is no need to mute.
+ if (requiresMuteCheck) {
+ muteWaitMs = checkDeviceMuteStrategies(outputDesc, prevDevice, delayMs);
+ } else {
+ ALOGV("%s: suppressing checkDeviceMuteStrategies", __func__);
+ muteWaitMs = 0;
+ }
// Do not change the routing if:
// the requested device is AUDIO_DEVICE_NONE
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.h b/services/audiopolicy/managerdefault/AudioPolicyManager.h
index ee339e7..2b68882 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.h
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.h
@@ -317,7 +317,8 @@
bool force = false,
int delayMs = 0,
audio_patch_handle_t *patchHandle = NULL,
- const char* address = NULL);
+ const char *address = nullptr,
+ bool requiresMuteCheck = true);
status_t resetOutputDevice(const sp<AudioOutputDescriptor>& outputDesc,
int delayMs = 0,
audio_patch_handle_t *patchHandle = NULL);
diff --git a/services/audiopolicy/service/AudioPolicyEffects.cpp b/services/audiopolicy/service/AudioPolicyEffects.cpp
index 7fe363d..c7dfe0f 100644
--- a/services/audiopolicy/service/AudioPolicyEffects.cpp
+++ b/services/audiopolicy/service/AudioPolicyEffects.cpp
@@ -399,11 +399,12 @@
while (pos + size > *totSize) {
*totSize += ((*totSize + 7) / 8) * 4;
}
- *param = (char *)realloc(*param, *totSize);
- if (*param == NULL) {
+ char *newParam = (char *)realloc(*param, *totSize);
+ if (newParam == NULL) {
ALOGE("%s realloc error for size %zu", __func__, *totSize);
return 0;
}
+ *param = newParam;
}
*curSize = pos + size;
return pos;
diff --git a/services/camera/libcameraservice/CameraFlashlight.cpp b/services/camera/libcameraservice/CameraFlashlight.cpp
index e06a81f..394701a 100644
--- a/services/camera/libcameraservice/CameraFlashlight.cpp
+++ b/services/camera/libcameraservice/CameraFlashlight.cpp
@@ -32,13 +32,15 @@
namespace android {
+using hardware::camera::common::V1_0::TorchModeStatus;
+
/////////////////////////////////////////////////////////////////////
// CameraFlashlight implementation begins
// used by camera service to control flashflight.
/////////////////////////////////////////////////////////////////////
CameraFlashlight::CameraFlashlight(sp<CameraProviderManager> providerManager,
- camera_module_callbacks_t* callbacks) :
+ CameraProviderManager::StatusListener* callbacks) :
mProviderManager(providerManager),
mCallbacks(callbacks),
mFlashlightMapInitialized(false) {
@@ -59,7 +61,7 @@
} else {
// Only HAL1 devices do not support setTorchMode
mFlashControl =
- new CameraHardwareInterfaceFlashControl(mProviderManager, *mCallbacks);
+ new CameraHardwareInterfaceFlashControl(mProviderManager, mCallbacks);
}
return OK;
@@ -119,7 +121,8 @@
}
int CameraFlashlight::getNumberOfCameras() {
- return mProviderManager->getAPI1CompatibleCameraCount();
+ size_t len = mProviderManager->getAPI1CompatibleCameraDeviceIds().size();
+ return static_cast<int>(len);
}
status_t CameraFlashlight::findFlashUnits() {
@@ -221,9 +224,8 @@
int numCameras = getNumberOfCameras();
for (int i = 0; i < numCameras; i++) {
if (hasFlashUnitLocked(String8::format("%d", i))) {
- mCallbacks->torch_mode_status_change(mCallbacks,
- String8::format("%d", i).string(),
- TORCH_MODE_STATUS_NOT_AVAILABLE);
+ mCallbacks->onTorchStatusChanged(
+ String8::format("%d", i), TorchModeStatus::NOT_AVAILABLE);
}
}
}
@@ -266,9 +268,8 @@
int numCameras = getNumberOfCameras();
for (int i = 0; i < numCameras; i++) {
if (hasFlashUnitLocked(String8::format("%d", i))) {
- mCallbacks->torch_mode_status_change(mCallbacks,
- String8::format("%d", i).string(),
- TORCH_MODE_STATUS_AVAILABLE_OFF);
+ mCallbacks->onTorchStatusChanged(
+ String8::format("%d", i), TorchModeStatus::AVAILABLE_OFF);
}
}
}
@@ -315,9 +316,9 @@
CameraHardwareInterfaceFlashControl::CameraHardwareInterfaceFlashControl(
sp<CameraProviderManager> manager,
- const camera_module_callbacks_t& callbacks) :
+ CameraProviderManager::StatusListener* callbacks) :
mProviderManager(manager),
- mCallbacks(&callbacks),
+ mCallbacks(callbacks),
mTorchEnabled(false) {
}
@@ -333,8 +334,7 @@
if (mCallbacks) {
ALOGV("%s: notify the framework that torch was turned off",
__FUNCTION__);
- mCallbacks->torch_mode_status_change(mCallbacks,
- mCameraId.string(), TORCH_MODE_STATUS_AVAILABLE_OFF);
+ mCallbacks->onTorchStatusChanged(mCameraId, TorchModeStatus::AVAILABLE_OFF);
}
}
}
@@ -368,8 +368,7 @@
// disabling the torch mode of currently opened device
disconnectCameraDevice();
mTorchEnabled = false;
- mCallbacks->torch_mode_status_change(mCallbacks,
- cameraId.string(), TORCH_MODE_STATUS_AVAILABLE_OFF);
+ mCallbacks->onTorchStatusChanged(cameraId, TorchModeStatus::AVAILABLE_OFF);
return OK;
}
@@ -379,8 +378,7 @@
}
mTorchEnabled = true;
- mCallbacks->torch_mode_status_change(mCallbacks,
- cameraId.string(), TORCH_MODE_STATUS_AVAILABLE_ON);
+ mCallbacks->onTorchStatusChanged(cameraId, TorchModeStatus::AVAILABLE_ON);
return OK;
}
diff --git a/services/camera/libcameraservice/CameraFlashlight.h b/services/camera/libcameraservice/CameraFlashlight.h
index c86ee85..07ce829 100644
--- a/services/camera/libcameraservice/CameraFlashlight.h
+++ b/services/camera/libcameraservice/CameraFlashlight.h
@@ -19,7 +19,6 @@
#include <gui/GLConsumer.h>
#include <gui/Surface.h>
-#include <hardware/camera_common.h>
#include <utils/KeyedVector.h>
#include <utils/SortedVector.h>
#include "common/CameraProviderManager.h"
@@ -55,7 +54,7 @@
class CameraFlashlight : public virtual VirtualLightRefBase {
public:
CameraFlashlight(sp<CameraProviderManager> providerManager,
- camera_module_callbacks_t* callbacks);
+ CameraProviderManager::StatusListener* callbacks);
virtual ~CameraFlashlight();
// Find all flash units. This must be called before other methods. All
@@ -99,7 +98,7 @@
sp<CameraProviderManager> mProviderManager;
- const camera_module_callbacks_t *mCallbacks;
+ CameraProviderManager::StatusListener* mCallbacks;
SortedVector<String8> mOpenedCameraIds;
// camera id -> if it has a flash unit
@@ -134,7 +133,7 @@
public:
CameraHardwareInterfaceFlashControl(
sp<CameraProviderManager> manager,
- const camera_module_callbacks_t& callbacks);
+ CameraProviderManager::StatusListener* callbacks);
virtual ~CameraHardwareInterfaceFlashControl();
// FlashControlBase
@@ -166,7 +165,7 @@
status_t hasFlashUnitLocked(const String8& cameraId, bool *hasFlash, bool keepDeviceOpen);
sp<CameraProviderManager> mProviderManager;
- const camera_module_callbacks_t *mCallbacks;
+ CameraProviderManager::StatusListener* mCallbacks;
sp<CameraHardwareInterface> mDevice;
String8 mCameraId;
CameraParameters mParameters;
diff --git a/services/camera/libcameraservice/CameraService.cpp b/services/camera/libcameraservice/CameraService.cpp
index e7609ed..1df6b6a 100644
--- a/services/camera/libcameraservice/CameraService.cpp
+++ b/services/camera/libcameraservice/CameraService.cpp
@@ -67,6 +67,7 @@
#include "api1/Camera2Client.h"
#include "api2/CameraDeviceClient.h"
#include "utils/CameraTraces.h"
+#include "utils/TagMonitor.h"
namespace {
const char* kPermissionServiceName = "permission";
@@ -107,79 +108,13 @@
// ----------------------------------------------------------------------------
-extern "C" {
-static void camera_device_status_change(
- const struct camera_module_callbacks* callbacks,
- int camera_id,
- int new_status) {
- sp<CameraService> cs = const_cast<CameraService*>(
- static_cast<const CameraService*>(callbacks));
- String8 id = String8::format("%d", camera_id);
-
- CameraDeviceStatus newStatus{CameraDeviceStatus::NOT_PRESENT};
- switch (new_status) {
- case CAMERA_DEVICE_STATUS_NOT_PRESENT:
- newStatus = CameraDeviceStatus::NOT_PRESENT;
- break;
- case CAMERA_DEVICE_STATUS_PRESENT:
- newStatus = CameraDeviceStatus::PRESENT;
- break;
- case CAMERA_DEVICE_STATUS_ENUMERATING:
- newStatus = CameraDeviceStatus::ENUMERATING;
- break;
- default:
- ALOGW("Unknown device status change to %d", new_status);
- break;
- }
- cs->onDeviceStatusChanged(id, newStatus);
-}
-
-static void torch_mode_status_change(
- const struct camera_module_callbacks* callbacks,
- const char* camera_id,
- int new_status) {
- if (!callbacks || !camera_id) {
- ALOGE("%s invalid parameters. callbacks %p, camera_id %p", __FUNCTION__,
- callbacks, camera_id);
- }
- sp<CameraService> cs = const_cast<CameraService*>(
- static_cast<const CameraService*>(callbacks));
-
- TorchModeStatus status;
- switch (new_status) {
- case TORCH_MODE_STATUS_NOT_AVAILABLE:
- status = TorchModeStatus::NOT_AVAILABLE;
- break;
- case TORCH_MODE_STATUS_AVAILABLE_OFF:
- status = TorchModeStatus::AVAILABLE_OFF;
- break;
- case TORCH_MODE_STATUS_AVAILABLE_ON:
- status = TorchModeStatus::AVAILABLE_ON;
- break;
- default:
- ALOGE("Unknown torch status %d", new_status);
- return;
- }
-
- cs->onTorchStatusChanged(
- String8(camera_id),
- status);
-}
-} // extern "C"
-
-// ----------------------------------------------------------------------------
-
static const String16 sManageCameraPermission("android.permission.MANAGE_CAMERA");
CameraService::CameraService() :
mEventLog(DEFAULT_EVENT_LOG_LENGTH),
- mNumberOfCameras(0), mNumberOfNormalCameras(0),
+ mNumberOfCameras(0),
mSoundRef(0), mInitialized(false) {
ALOGI("CameraService started (pid=%d)", getpid());
-
- this->camera_device_status_change = android::camera_device_status_change;
- this->torch_mode_status_change = android::torch_mode_status_change;
-
mServiceLockWrapper = std::make_shared<WaitableMutexWrapper>(&mServiceLock);
}
@@ -209,52 +144,42 @@
status_t CameraService::enumerateProviders() {
status_t res;
- Mutex::Autolock l(mServiceLock);
- if (nullptr == mCameraProviderManager.get()) {
- mCameraProviderManager = new CameraProviderManager();
- res = mCameraProviderManager->initialize(this);
- if (res != OK) {
- ALOGE("%s: Unable to initialize camera provider manager: %s (%d)",
- __FUNCTION__, strerror(-res), res);
- return res;
- }
- }
+ std::vector<std::string> deviceIds;
+ {
+ Mutex::Autolock l(mServiceLock);
- mNumberOfCameras = mCameraProviderManager->getCameraCount();
- mNumberOfNormalCameras =
- mCameraProviderManager->getAPI1CompatibleCameraCount();
-
- // Setup vendor tags before we call get_camera_info the first time
- // because HAL might need to setup static vendor keys in get_camera_info
- // TODO: maybe put this into CameraProviderManager::initialize()?
- mCameraProviderManager->setUpVendorTags();
-
- if (nullptr == mFlashlight.get()) {
- mFlashlight = new CameraFlashlight(mCameraProviderManager, this);
- }
-
- res = mFlashlight->findFlashUnits();
- if (res != OK) {
- ALOGE("Failed to enumerate flash units: %s (%d)", strerror(-res), res);
- }
-
- for (auto& cameraId : mCameraProviderManager->getCameraDeviceIds()) {
- String8 id8 = String8(cameraId.c_str());
- bool cameraFound = false;
- {
-
- Mutex::Autolock lock(mCameraStatesLock);
- auto iter = mCameraStates.find(id8);
- if (iter != mCameraStates.end()) {
- cameraFound = true;
+ if (nullptr == mCameraProviderManager.get()) {
+ mCameraProviderManager = new CameraProviderManager();
+ res = mCameraProviderManager->initialize(this);
+ if (res != OK) {
+ ALOGE("%s: Unable to initialize camera provider manager: %s (%d)",
+ __FUNCTION__, strerror(-res), res);
+ return res;
}
}
- if (!cameraFound) {
- addStates(id8);
+
+ // Setup vendor tags before we call get_camera_info the first time
+ // because HAL might need to setup static vendor keys in get_camera_info
+ // TODO: maybe put this into CameraProviderManager::initialize()?
+ mCameraProviderManager->setUpVendorTags();
+
+ if (nullptr == mFlashlight.get()) {
+ mFlashlight = new CameraFlashlight(mCameraProviderManager, this);
}
+ res = mFlashlight->findFlashUnits();
+ if (res != OK) {
+ ALOGE("Failed to enumerate flash units: %s (%d)", strerror(-res), res);
+ }
+
+ deviceIds = mCameraProviderManager->getCameraDeviceIds();
+ }
+
+
+ for (auto& cameraId : deviceIds) {
+ String8 id8 = String8(cameraId.c_str());
onDeviceStatusChanged(id8, CameraDeviceStatus::PRESENT);
}
@@ -291,6 +216,13 @@
enumerateProviders();
}
+void CameraService::updateCameraNumAndIds() {
+ Mutex::Autolock l(mServiceLock);
+ mNumberOfCameras = mCameraProviderManager->getCameraCount();
+ mNormalDeviceIds =
+ mCameraProviderManager->getAPI1CompatibleCameraDeviceIds();
+}
+
void CameraService::addStates(const String8 id) {
std::string cameraId(id.c_str());
hardware::camera::common::V1_0::CameraResourceCost cost;
@@ -313,10 +245,13 @@
if (mFlashlight->hasFlashUnit(id)) {
mTorchStatusMap.add(id, TorchModeStatus::AVAILABLE_OFF);
}
+
+ updateCameraNumAndIds();
logDeviceAdded(id, "Device added");
}
void CameraService::removeStates(const String8 id) {
+ updateCameraNumAndIds();
if (mFlashlight->hasFlashUnit(id)) {
mTorchStatusMap.removeItem(id);
}
@@ -361,15 +296,16 @@
if (newStatus == StatusInternal::NOT_PRESENT) {
logDeviceRemoved(id, String8::format("Device status changed from %d to %d", oldStatus,
newStatus));
+
+ // Set the device status to NOT_PRESENT, clients will no longer be able to connect
+ // to this device until the status changes
+ updateStatus(StatusInternal::NOT_PRESENT, id);
+
sp<BasicClient> clientToDisconnect;
{
// Don't do this in updateStatus to avoid deadlock over mServiceLock
Mutex::Autolock lock(mServiceLock);
- // Set the device status to NOT_PRESENT, clients will no longer be able to connect
- // to this device until the status changes
- updateStatus(StatusInternal::NOT_PRESENT, id);
-
// Remove cached shim parameters
state->setShimParams(CameraParameters());
@@ -472,7 +408,7 @@
Mutex::Autolock l(mServiceLock);
switch (type) {
case CAMERA_TYPE_BACKWARD_COMPATIBLE:
- *numCameras = mNumberOfNormalCameras;
+ *numCameras = static_cast<int>(mNormalDeviceIds.size());
break;
case CAMERA_TYPE_ALL:
*numCameras = mNumberOfCameras;
@@ -502,7 +438,8 @@
}
Status ret = Status::ok();
- status_t err = mCameraProviderManager->getCameraInfo(std::to_string(cameraId), cameraInfo);
+ status_t err = mCameraProviderManager->getCameraInfo(
+ cameraIdIntToStrLocked(cameraId), cameraInfo);
if (err != OK) {
ret = STATUS_ERROR_FMT(ERROR_INVALID_OPERATION,
"Error retrieving camera info from device %d: %s (%d)", cameraId,
@@ -512,13 +449,19 @@
return ret;
}
-int CameraService::cameraIdToInt(const String8& cameraId) {
- int id;
- bool success = base::ParseInt(cameraId.string(), &id, 0);
- if (!success) {
- return -1;
+std::string CameraService::cameraIdIntToStrLocked(int cameraIdInt) {
+ if (cameraIdInt < 0 || cameraIdInt >= static_cast<int>(mNormalDeviceIds.size())) {
+ ALOGE("%s: input id %d invalid: valid range (0, %zu)",
+ __FUNCTION__, cameraIdInt, mNormalDeviceIds.size());
+ return std::string{};
}
- return id;
+
+ return mNormalDeviceIds[cameraIdInt];
+}
+
+String8 CameraService::cameraIdIntToStr(int cameraIdInt) {
+ Mutex::Autolock lock(mServiceLock);
+ return String8(cameraIdIntToStrLocked(cameraIdInt).c_str());
}
Status CameraService::getCameraCharacteristics(const String16& cameraId,
@@ -635,8 +578,8 @@
Status CameraService::makeClient(const sp<CameraService>& cameraService,
const sp<IInterface>& cameraCb, const String16& packageName, const String8& cameraId,
- int facing, int clientPid, uid_t clientUid, int servicePid, bool legacyMode,
- int halVersion, int deviceVersion, apiLevel effectiveApiLevel,
+ int api1CameraId, int facing, int clientPid, uid_t clientUid, int servicePid,
+ bool legacyMode, int halVersion, int deviceVersion, apiLevel effectiveApiLevel,
/*out*/sp<BasicClient>* client) {
if (halVersion < 0 || halVersion == deviceVersion) {
@@ -646,8 +589,9 @@
case CAMERA_DEVICE_API_VERSION_1_0:
if (effectiveApiLevel == API_1) { // Camera1 API route
sp<ICameraClient> tmp = static_cast<ICameraClient*>(cameraCb.get());
- *client = new CameraClient(cameraService, tmp, packageName, cameraIdToInt(cameraId),
- facing, clientPid, clientUid, getpid(), legacyMode);
+ *client = new CameraClient(cameraService, tmp, packageName,
+ api1CameraId, facing, clientPid, clientUid,
+ getpid(), legacyMode);
} else { // Camera2 API route
ALOGW("Camera using old HAL version: %d", deviceVersion);
return STATUS_ERROR_FMT(ERROR_DEPRECATED_HAL,
@@ -662,8 +606,10 @@
case CAMERA_DEVICE_API_VERSION_3_4:
if (effectiveApiLevel == API_1) { // Camera1 API route
sp<ICameraClient> tmp = static_cast<ICameraClient*>(cameraCb.get());
- *client = new Camera2Client(cameraService, tmp, packageName, cameraIdToInt(cameraId),
- facing, clientPid, clientUid, servicePid, legacyMode);
+ *client = new Camera2Client(cameraService, tmp, packageName,
+ cameraId, api1CameraId,
+ facing, clientPid, clientUid,
+ servicePid, legacyMode);
} else { // Camera2 API route
sp<hardware::camera2::ICameraDeviceCallbacks> tmp =
static_cast<hardware::camera2::ICameraDeviceCallbacks*>(cameraCb.get());
@@ -685,8 +631,9 @@
halVersion == CAMERA_DEVICE_API_VERSION_1_0) {
// Only support higher HAL version device opened as HAL1.0 device.
sp<ICameraClient> tmp = static_cast<ICameraClient*>(cameraCb.get());
- *client = new CameraClient(cameraService, tmp, packageName, cameraIdToInt(cameraId),
- facing, clientPid, clientUid, servicePid, legacyMode);
+ *client = new CameraClient(cameraService, tmp, packageName,
+ api1CameraId, facing, clientPid, clientUid,
+ servicePid, legacyMode);
} else {
// Other combinations (e.g. HAL3.x open as HAL2.x) are not supported yet.
ALOGE("Invalid camera HAL version %x: HAL %x device can only be"
@@ -782,7 +729,8 @@
Status ret = Status::ok();
sp<Client> tmp = nullptr;
if (!(ret = connectHelper<ICameraClient,Client>(
- sp<ICameraClient>{nullptr}, id, static_cast<int>(CAMERA_HAL_API_VERSION_UNSPECIFIED),
+ sp<ICameraClient>{nullptr}, id, cameraId,
+ static_cast<int>(CAMERA_HAL_API_VERSION_UNSPECIFIED),
internalPackageName, uid, USE_CALLING_PID,
API_1, /*legacyMode*/ false, /*shimUpdateOnly*/ true,
/*out*/ tmp)
@@ -1235,7 +1183,7 @@
Status CameraService::connect(
const sp<ICameraClient>& cameraClient,
- int cameraId,
+ int api1CameraId,
const String16& clientPackageName,
int clientUid,
int clientPid,
@@ -1244,9 +1192,10 @@
ATRACE_CALL();
Status ret = Status::ok();
- String8 id = String8::format("%d", cameraId);
+
+ String8 id = cameraIdIntToStr(api1CameraId);
sp<Client> client = nullptr;
- ret = connectHelper<ICameraClient,Client>(cameraClient, id,
+ ret = connectHelper<ICameraClient,Client>(cameraClient, id, api1CameraId,
CAMERA_HAL_API_VERSION_UNSPECIFIED, clientPackageName, clientUid, clientPid, API_1,
/*legacyMode*/ false, /*shimUpdateOnly*/ false,
/*out*/client);
@@ -1263,18 +1212,18 @@
Status CameraService::connectLegacy(
const sp<ICameraClient>& cameraClient,
- int cameraId, int halVersion,
+ int api1CameraId, int halVersion,
const String16& clientPackageName,
int clientUid,
/*out*/
sp<ICamera>* device) {
ATRACE_CALL();
- String8 id = String8::format("%d", cameraId);
+ String8 id = cameraIdIntToStr(api1CameraId);
Status ret = Status::ok();
sp<Client> client = nullptr;
- ret = connectHelper<ICameraClient,Client>(cameraClient, id, halVersion,
+ ret = connectHelper<ICameraClient,Client>(cameraClient, id, api1CameraId, halVersion,
clientPackageName, clientUid, USE_CALLING_PID, API_1,
/*legacyMode*/ true, /*shimUpdateOnly*/ false,
/*out*/client);
@@ -1302,6 +1251,7 @@
String8 id = String8(cameraId);
sp<CameraDeviceClient> client = nullptr;
ret = connectHelper<hardware::camera2::ICameraDeviceCallbacks,CameraDeviceClient>(cameraCb, id,
+ /*api1CameraId*/-1,
CAMERA_HAL_API_VERSION_UNSPECIFIED, clientPackageName,
clientUid, USE_CALLING_PID, API_2,
/*legacyMode*/ false, /*shimUpdateOnly*/ false,
@@ -1319,8 +1269,8 @@
template<class CALLBACK, class CLIENT>
Status CameraService::connectHelper(const sp<CALLBACK>& cameraCb, const String8& cameraId,
- int halVersion, const String16& clientPackageName, int clientUid, int clientPid,
- apiLevel effectiveApiLevel, bool legacyMode, bool shimUpdateOnly,
+ int api1CameraId, int halVersion, const String16& clientPackageName, int clientUid,
+ int clientPid, apiLevel effectiveApiLevel, bool legacyMode, bool shimUpdateOnly,
/*out*/sp<CLIENT>& device) {
binder::Status ret = binder::Status::ok();
@@ -1403,8 +1353,10 @@
}
sp<BasicClient> tmp = nullptr;
- if(!(ret = makeClient(this, cameraCb, clientPackageName, cameraId, facing, clientPid,
- clientUid, getpid(), legacyMode, halVersion, deviceVersion, effectiveApiLevel,
+ if(!(ret = makeClient(this, cameraCb, clientPackageName,
+ cameraId, api1CameraId, facing,
+ clientPid, clientUid, getpid(), legacyMode,
+ halVersion, deviceVersion, effectiveApiLevel,
/*out*/&tmp)).isOk()) {
return ret;
}
@@ -1413,7 +1365,7 @@
LOG_ALWAYS_FATAL_IF(client.get() == nullptr, "%s: CameraService in invalid state",
__FUNCTION__);
- err = client->initialize(mCameraProviderManager);
+ err = client->initialize(mCameraProviderManager, mMonitorTags);
if (err != OK) {
ALOGE("%s: Could not initialize client from HAL.", __FUNCTION__);
// Errors could be from the HAL module open call or from AppOpsManager
@@ -1782,8 +1734,6 @@
}
bool CameraService::evictClientIdByRemote(const wp<IBinder>& remote) {
- const int callingPid = getCallingPid();
- const int servicePid = getpid();
bool ret = false;
{
// Acquire mServiceLock and prevent other clients from connecting
@@ -1799,8 +1749,7 @@
mActiveClientManager.remove(i);
continue;
}
- if (remote == clientSp->getRemote() && (callingPid == servicePid ||
- callingPid == clientSp->getClientPid())) {
+ if (remote == clientSp->getRemote()) {
mActiveClientManager.remove(i);
evicted.push_back(clientSp);
@@ -2112,7 +2061,8 @@
CameraService::Client::Client(const sp<CameraService>& cameraService,
const sp<ICameraClient>& cameraClient,
const String16& clientPackageName,
- const String8& cameraIdStr, int cameraFacing,
+ const String8& cameraIdStr,
+ int api1CameraId, int cameraFacing,
int clientPid, uid_t clientUid,
int servicePid) :
CameraService::BasicClient(cameraService,
@@ -2121,7 +2071,7 @@
cameraIdStr, cameraFacing,
clientPid, clientUid,
servicePid),
- mCameraId(CameraService::cameraIdToInt(cameraIdStr))
+ mCameraId(api1CameraId)
{
int callingPid = getCallingPid();
LOG1("Client::Client E (pid %d, id %d)", callingPid, mCameraId);
@@ -2676,7 +2626,10 @@
}
dprintf(fd, "\n== Service global info: ==\n\n");
dprintf(fd, "Number of camera devices: %d\n", mNumberOfCameras);
- dprintf(fd, "Number of normal camera devices: %d\n", mNumberOfNormalCameras);
+ dprintf(fd, "Number of normal camera devices: %zu\n", mNormalDeviceIds.size());
+ for (size_t i = 0; i < mNormalDeviceIds.size(); i++) {
+ dprintf(fd, " Device %zu maps to \"%s\"\n", i, mNormalDeviceIds[i].c_str());
+ }
String8 activeClientString = mActiveClientManager.toString();
dprintf(fd, "Active Camera Clients:\n%s", activeClientString.string());
dprintf(fd, "Allowed user IDs: %s\n", toString(mAllowedUsers).string());
@@ -2688,6 +2641,16 @@
dprintf(fd, "CameraStates in use, may be deadlocked\n");
}
+ int argSize = args.size();
+ for (int i = 0; i < argSize; i++) {
+ if (args[i] == TagMonitor::kMonitorOption) {
+ if (i + 1 < argSize) {
+ mMonitorTags = String8(args[i + 1]);
+ }
+ break;
+ }
+ }
+
for (auto& state : mCameraStates) {
String8 cameraId = state.first;
@@ -2815,7 +2778,7 @@
* While tempting to promote the wp<IBinder> into a sp, it's actually not supported by the
* binder driver
*/
-
+ // PID here is approximate and can be wrong.
logClientDied(getCallingPid(), String8("Binder died unexpectedly"));
// check torch client
diff --git a/services/camera/libcameraservice/CameraService.h b/services/camera/libcameraservice/CameraService.h
index 67db7ec..cbfc50b 100644
--- a/services/camera/libcameraservice/CameraService.h
+++ b/services/camera/libcameraservice/CameraService.h
@@ -62,7 +62,6 @@
public BinderService<CameraService>,
public virtual ::android::hardware::BnCameraService,
public virtual IBinder::DeathRecipient,
- public camera_module_callbacks_t,
public virtual CameraProviderManager::StatusListener
{
friend class BinderService<CameraService>;
@@ -205,7 +204,8 @@
class BasicClient : public virtual RefBase {
public:
- virtual status_t initialize(sp<CameraProviderManager> manager) = 0;
+ virtual status_t initialize(sp<CameraProviderManager> manager,
+ const String8& monitorTags) = 0;
virtual binder::Status disconnect();
// because we can't virtually inherit IInterface, which breaks
@@ -333,6 +333,7 @@
const sp<hardware::ICameraClient>& cameraClient,
const String16& clientPackageName,
const String8& cameraIdStr,
+ int api1CameraId,
int cameraFacing,
int clientPid,
uid_t clientUid,
@@ -551,7 +552,8 @@
// Eumerate all camera providers in the system
status_t enumerateProviders();
- // Add a new camera to camera and torch state lists or remove an unplugged one
+ // Add/remove a new camera to camera and torch state lists or remove an unplugged one
+ // Caller must not hold mServiceLock
void addStates(const String8 id);
void removeStates(const String8 id);
@@ -578,7 +580,7 @@
// Single implementation shared between the various connect calls
template<class CALLBACK, class CLIENT>
binder::Status connectHelper(const sp<CALLBACK>& cameraCb, const String8& cameraId,
- int halVersion, const String16& clientPackageName,
+ int api1CameraId, int halVersion, const String16& clientPackageName,
int clientUid, int clientPid,
apiLevel effectiveApiLevel, bool legacyMode, bool shimUpdateOnly,
/*out*/sp<CLIENT>& device);
@@ -605,6 +607,9 @@
RingBuffer<String8> mEventLog;
Mutex mLogLock;
+ // The last monitored tags set by client
+ String8 mMonitorTags;
+
// Currently allowed user IDs
std::set<userid_t> mAllowedUsers;
@@ -640,9 +645,16 @@
void finishConnectLocked(const sp<BasicClient>& client, const DescriptorPtr& desc);
/**
- * Returns the integer corresponding to the given camera ID string, or -1 on failure.
+ * Returns the underlying camera Id string mapped to a camera id int
+ * Empty string is returned when the cameraIdInt is invalid.
*/
- static int cameraIdToInt(const String8& cameraId);
+ String8 cameraIdIntToStr(int cameraIdInt);
+
+ /**
+ * Returns the underlying camera Id string mapped to a camera id int
+ * Empty string is returned when the cameraIdInt is invalid.
+ */
+ std::string cameraIdIntToStrLocked(int cameraIdInt);
/**
* Remove a single client corresponding to the given camera id from the list of active clients.
@@ -710,8 +722,14 @@
*/
void dumpEventLog(int fd);
+ /**
+ * This method will acquire mServiceLock
+ */
+ void updateCameraNumAndIds();
+
int mNumberOfCameras;
- int mNumberOfNormalCameras;
+
+ std::vector<std::string> mNormalDeviceIds;
// sounds
MediaPlayer* newMediaPlayer(const char *file);
@@ -821,8 +839,8 @@
static binder::Status makeClient(const sp<CameraService>& cameraService,
const sp<IInterface>& cameraCb, const String16& packageName, const String8& cameraId,
- int facing, int clientPid, uid_t clientUid, int servicePid, bool legacyMode,
- int halVersion, int deviceVersion, apiLevel effectiveApiLevel,
+ int api1CameraId, int facing, int clientPid, uid_t clientUid, int servicePid,
+ bool legacyMode, int halVersion, int deviceVersion, apiLevel effectiveApiLevel,
/*out*/sp<BasicClient>* client);
status_t checkCameraAccess(const String16& opPackageName);
diff --git a/services/camera/libcameraservice/api1/Camera2Client.cpp b/services/camera/libcameraservice/api1/Camera2Client.cpp
index 585d2eb..3578bba 100644
--- a/services/camera/libcameraservice/api1/Camera2Client.cpp
+++ b/services/camera/libcameraservice/api1/Camera2Client.cpp
@@ -49,16 +49,17 @@
Camera2Client::Camera2Client(const sp<CameraService>& cameraService,
const sp<hardware::ICameraClient>& cameraClient,
const String16& clientPackageName,
- int cameraId,
+ const String8& cameraDeviceId,
+ int api1CameraId,
int cameraFacing,
int clientPid,
uid_t clientUid,
int servicePid,
bool legacyMode):
Camera2ClientBase(cameraService, cameraClient, clientPackageName,
- String8::format("%d", cameraId), cameraFacing,
+ cameraDeviceId, api1CameraId, cameraFacing,
clientPid, clientUid, servicePid),
- mParameters(cameraId, cameraFacing)
+ mParameters(api1CameraId, cameraFacing)
{
ATRACE_CALL();
@@ -68,8 +69,8 @@
mLegacyMode = legacyMode;
}
-status_t Camera2Client::initialize(sp<CameraProviderManager> manager) {
- return initializeImpl(manager);
+status_t Camera2Client::initialize(sp<CameraProviderManager> manager, const String8& monitorTags) {
+ return initializeImpl(manager, monitorTags);
}
bool Camera2Client::isZslEnabledInStillTemplate() {
@@ -87,13 +88,13 @@
}
template<typename TProviderPtr>
-status_t Camera2Client::initializeImpl(TProviderPtr providerPtr)
+status_t Camera2Client::initializeImpl(TProviderPtr providerPtr, const String8& monitorTags)
{
ATRACE_CALL();
ALOGV("%s: Initializing client for camera %d", __FUNCTION__, mCameraId);
status_t res;
- res = Camera2ClientBase::initialize(providerPtr);
+ res = Camera2ClientBase::initialize(providerPtr, monitorTags);
if (res != OK) {
return res;
}
diff --git a/services/camera/libcameraservice/api1/Camera2Client.h b/services/camera/libcameraservice/api1/Camera2Client.h
index 5af74eb..44929c3 100644
--- a/services/camera/libcameraservice/api1/Camera2Client.h
+++ b/services/camera/libcameraservice/api1/Camera2Client.h
@@ -91,7 +91,8 @@
Camera2Client(const sp<CameraService>& cameraService,
const sp<hardware::ICameraClient>& cameraClient,
const String16& clientPackageName,
- int cameraId,
+ const String8& cameraDeviceId,
+ int api1CameraId,
int cameraFacing,
int clientPid,
uid_t clientUid,
@@ -100,7 +101,8 @@
virtual ~Camera2Client();
- virtual status_t initialize(sp<CameraProviderManager> manager) override;
+ virtual status_t initialize(sp<CameraProviderManager> manager,
+ const String8& monitorTags) override;
virtual status_t dump(int fd, const Vector<String16>& args);
@@ -223,7 +225,7 @@
status_t overrideVideoSnapshotSize(Parameters ¶ms);
template<typename TProviderPtr>
- status_t initializeImpl(TProviderPtr providerPtr);
+ status_t initializeImpl(TProviderPtr providerPtr, const String8& monitorTags);
bool isZslEnabledInStillTemplate();
};
diff --git a/services/camera/libcameraservice/api1/CameraClient.cpp b/services/camera/libcameraservice/api1/CameraClient.cpp
index e848a3f..f6d27ab 100644
--- a/services/camera/libcameraservice/api1/CameraClient.cpp
+++ b/services/camera/libcameraservice/api1/CameraClient.cpp
@@ -42,7 +42,7 @@
int clientPid, int clientUid,
int servicePid, bool legacyMode):
Client(cameraService, cameraClient, clientPackageName,
- String8::format("%d", cameraId), cameraFacing, clientPid,
+ String8::format("%d", cameraId), cameraId, cameraFacing, clientPid,
clientUid, servicePid)
{
int callingPid = getCallingPid();
@@ -62,7 +62,8 @@
LOG1("CameraClient::CameraClient X (pid %d, id %d)", callingPid, cameraId);
}
-status_t CameraClient::initialize(sp<CameraProviderManager> manager) {
+status_t CameraClient::initialize(sp<CameraProviderManager> manager,
+ const String8& /*monitorTags*/) {
int callingPid = getCallingPid();
status_t res;
diff --git a/services/camera/libcameraservice/api1/CameraClient.h b/services/camera/libcameraservice/api1/CameraClient.h
index 7f93fef..1910536 100644
--- a/services/camera/libcameraservice/api1/CameraClient.h
+++ b/services/camera/libcameraservice/api1/CameraClient.h
@@ -72,7 +72,8 @@
bool legacyMode = false);
~CameraClient();
- virtual status_t initialize(sp<CameraProviderManager> manager) override;
+ virtual status_t initialize(sp<CameraProviderManager> manager,
+ const String8& monitorTags) override;
virtual status_t dump(int fd, const Vector<String16>& args);
diff --git a/services/camera/libcameraservice/api1/client2/Parameters.cpp b/services/camera/libcameraservice/api1/client2/Parameters.cpp
index 050c3f7..b4c7e9d 100644
--- a/services/camera/libcameraservice/api1/client2/Parameters.cpp
+++ b/services/camera/libcameraservice/api1/client2/Parameters.cpp
@@ -759,12 +759,17 @@
focusingAreas.clear();
focusingAreas.add(Parameters::Area(0,0,0,0,0));
- camera_metadata_ro_entry_t availableFocalLengths =
- staticInfo(ANDROID_LENS_INFO_AVAILABLE_FOCAL_LENGTHS, 0, 0, false);
- if (!availableFocalLengths.count) return NO_INIT;
+ if (fastInfo.isExternalCamera) {
+ params.setFloat(CameraParameters::KEY_FOCAL_LENGTH, -1.0);
+ } else {
+ camera_metadata_ro_entry_t availableFocalLengths =
+ staticInfo(ANDROID_LENS_INFO_AVAILABLE_FOCAL_LENGTHS, 0, 0, false);
+ if (!availableFocalLengths.count) return NO_INIT;
- float minFocalLength = availableFocalLengths.data.f[0];
- params.setFloat(CameraParameters::KEY_FOCAL_LENGTH, minFocalLength);
+ float minFocalLength = availableFocalLengths.data.f[0];
+ params.setFloat(CameraParameters::KEY_FOCAL_LENGTH, minFocalLength);
+ }
+
float horizFov, vertFov;
res = calculatePictureFovs(&horizFov, &vertFov);
@@ -1091,9 +1096,15 @@
focusDistanceCalibration.data.u8[0] !=
ANDROID_LENS_INFO_FOCUS_DISTANCE_CALIBRATION_UNCALIBRATED);
+
+ camera_metadata_ro_entry_t hwLevel = staticInfo(ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL);
+ if (!hwLevel.count) return NO_INIT;
+ fastInfo.isExternalCamera =
+ hwLevel.data.u8[0] == ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL_EXTERNAL;
+
camera_metadata_ro_entry_t availableFocalLengths =
- staticInfo(ANDROID_LENS_INFO_AVAILABLE_FOCAL_LENGTHS);
- if (!availableFocalLengths.count) return NO_INIT;
+ staticInfo(ANDROID_LENS_INFO_AVAILABLE_FOCAL_LENGTHS, 0, 0, /*required*/false);
+ if (!availableFocalLengths.count && !fastInfo.isExternalCamera) return NO_INIT;
SortedVector<int32_t> availableFormats = getAvailableOutputFormats();
if (!availableFormats.size()) return NO_INIT;
@@ -1178,10 +1189,14 @@
// Find smallest (widest-angle) focal length to use as basis of still
// picture FOV reporting.
- fastInfo.minFocalLength = availableFocalLengths.data.f[0];
- for (size_t i = 1; i < availableFocalLengths.count; i++) {
- if (fastInfo.minFocalLength > availableFocalLengths.data.f[i]) {
- fastInfo.minFocalLength = availableFocalLengths.data.f[i];
+ if (fastInfo.isExternalCamera) {
+ fastInfo.minFocalLength = -1.0;
+ } else {
+ fastInfo.minFocalLength = availableFocalLengths.data.f[0];
+ for (size_t i = 1; i < availableFocalLengths.count; i++) {
+ if (fastInfo.minFocalLength > availableFocalLengths.data.f[i]) {
+ fastInfo.minFocalLength = availableFocalLengths.data.f[i];
+ }
}
}
@@ -2870,8 +2885,13 @@
if (sc.isInput == ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT &&
sc.format == HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED &&
sc.width <= limit.width && sc.height <= limit.height) {
- Size sz = {sc.width, sc.height};
- sizes->push(sz);
+ int64_t minFrameDuration = getMinFrameDurationNs(
+ {sc.width, sc.height}, HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED);
+ if (minFrameDuration > MAX_PREVIEW_RECORD_DURATION_NS) {
+ // Filter slow sizes from preview/record
+ continue;
+ }
+ sizes->push({sc.width, sc.height});
}
}
@@ -3081,6 +3101,16 @@
status_t Parameters::calculatePictureFovs(float *horizFov, float *vertFov)
const {
+ if (fastInfo.isExternalCamera) {
+ if (horizFov != NULL) {
+ *horizFov = -1.0;
+ }
+ if (vertFov != NULL) {
+ *vertFov = -1.0;
+ }
+ return OK;
+ }
+
camera_metadata_ro_entry_t sensorSize =
staticInfo(ANDROID_SENSOR_INFO_PHYSICAL_SIZE, 2, 2);
if (!sensorSize.count) return NO_INIT;
diff --git a/services/camera/libcameraservice/api1/client2/Parameters.h b/services/camera/libcameraservice/api1/client2/Parameters.h
index 17e3d75..fe725fd 100644
--- a/services/camera/libcameraservice/api1/client2/Parameters.h
+++ b/services/camera/libcameraservice/api1/client2/Parameters.h
@@ -207,6 +207,11 @@
static const int32_t FPS_MARGIN = 1;
// Max FPS for default parameters
static const int32_t MAX_DEFAULT_FPS = 30;
+ // Minimum FPS for a size to be listed in supported preview/video sizes
+ // Set to slightly less than 30.0 to have some tolerance margin
+ static constexpr double MIN_PREVIEW_RECORD_FPS = 29.97;
+ // Maximum frame duration for a size to be listed in supported preview/video sizes
+ static constexpr int64_t MAX_PREVIEW_RECORD_DURATION_NS = 1e9 / MIN_PREVIEW_RECORD_FPS;
// Full static camera info, object owned by someone else, such as
// Camera2Device.
@@ -233,6 +238,7 @@
}
};
DefaultKeyedVector<uint8_t, OverrideModes> sceneModeOverrides;
+ bool isExternalCamera;
float minFocalLength;
bool useFlexibleYuv;
Size maxJpegSize;
@@ -380,6 +386,7 @@
Vector<Size> availablePreviewSizes;
Vector<Size> availableVideoSizes;
// Get size list (that are no larger than limit) from static metadata.
+ // This method filtered size with minFrameDuration < MAX_PREVIEW_RECORD_DURATION_NS
status_t getFilteredSizes(Size limit, Vector<Size> *sizes);
// Get max size (from the size array) that matches the given aspect ratio.
Size getMaxSizeForRatio(float ratio, const int32_t* sizeArray, size_t count);
diff --git a/services/camera/libcameraservice/api2/CameraDeviceClient.cpp b/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
index 8e112a1..efe3ca1 100644
--- a/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
+++ b/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
@@ -28,6 +28,8 @@
#include "common/CameraDeviceBase.h"
#include "api2/CameraDeviceClient.h"
+#include <camera_metadata_hidden.h>
+
// Convenience methods for constructing binder::Status objects for error returns
#define STATUS_ERROR(errorCode, errorString) \
@@ -47,6 +49,7 @@
const sp<hardware::camera2::ICameraDeviceCallbacks>& remoteCallback,
const String16& clientPackageName,
const String8& cameraId,
+ int api1CameraId,
int cameraFacing,
int clientPid,
uid_t clientUid,
@@ -60,6 +63,8 @@
clientUid,
servicePid),
mRemoteCallback(remoteCallback) {
+ // We don't need it for API2 clients, but Camera2ClientBase requires it.
+ (void) api1CameraId;
}
// Interface used by CameraService
@@ -73,7 +78,8 @@
uid_t clientUid,
int servicePid) :
Camera2ClientBase(cameraService, remoteCallback, clientPackageName,
- cameraId, cameraFacing, clientPid, clientUid, servicePid),
+ cameraId, /*API1 camera ID*/ -1,
+ cameraFacing, clientPid, clientUid, servicePid),
mInputStream(),
mStreamingRequestId(REQUEST_ID_NONE),
mRequestIdCounter(0) {
@@ -82,16 +88,17 @@
ALOGI("CameraDeviceClient %s: Opened", cameraId.string());
}
-status_t CameraDeviceClient::initialize(sp<CameraProviderManager> manager) {
- return initializeImpl(manager);
+status_t CameraDeviceClient::initialize(sp<CameraProviderManager> manager,
+ const String8& monitorTags) {
+ return initializeImpl(manager, monitorTags);
}
template<typename TProviderPtr>
-status_t CameraDeviceClient::initializeImpl(TProviderPtr providerPtr) {
+status_t CameraDeviceClient::initializeImpl(TProviderPtr providerPtr, const String8& monitorTags) {
ATRACE_CALL();
status_t res;
- res = Camera2ClientBase::initialize(providerPtr);
+ res = Camera2ClientBase::initialize(providerPtr, monitorTags);
if (res != OK) {
return res;
}
@@ -106,6 +113,15 @@
/*listener*/this,
/*sendPartials*/true);
+ auto deviceInfo = mDevice->info();
+ camera_metadata_entry_t physicalKeysEntry = deviceInfo.find(
+ ANDROID_REQUEST_AVAILABLE_PHYSICAL_CAMERA_REQUEST_KEYS);
+ if (physicalKeysEntry.count > 0) {
+ mSupportedPhysicalRequestKeys.insert(mSupportedPhysicalRequestKeys.begin(),
+ physicalKeysEntry.data.i32,
+ physicalKeysEntry.data.i32 + physicalKeysEntry.count);
+ }
+
return OK;
}
@@ -291,6 +307,13 @@
CameraDeviceBase::PhysicalCameraSettingsList physicalSettingsList;
for (const auto& it : request.mPhysicalCameraSettings) {
+ if (it.settings.isEmpty()) {
+ ALOGE("%s: Camera %s: Sent empty metadata packet. Rejecting request.",
+ __FUNCTION__, mCameraIdStr.string());
+ return STATUS_ERROR(CameraService::ERROR_ILLEGAL_ARGUMENT,
+ "Request settings are empty");
+ }
+
String8 physicalId(it.id.c_str());
if (physicalId != mDevice->getId()) {
auto found = std::find(requestedPhysicalIds.begin(), requestedPhysicalIds.end(),
@@ -301,24 +324,27 @@
return STATUS_ERROR(CameraService::ERROR_ILLEGAL_ARGUMENT,
"Invalid physical camera id");
}
+
+ if (!mSupportedPhysicalRequestKeys.empty()) {
+ // Filter out any unsupported physical request keys.
+ CameraMetadata filteredParams(mSupportedPhysicalRequestKeys.size());
+ camera_metadata_t *meta = const_cast<camera_metadata_t *>(
+ filteredParams.getAndLock());
+ set_camera_metadata_vendor_id(meta, mDevice->getVendorTagId());
+ filteredParams.unlock(meta);
+
+ for (const auto& keyIt : mSupportedPhysicalRequestKeys) {
+ camera_metadata_ro_entry entry = it.settings.find(keyIt);
+ if (entry.count > 0) {
+ filteredParams.update(entry);
+ }
+ }
+
+ physicalSettingsList.push_back({it.id, filteredParams});
+ }
+ } else {
+ physicalSettingsList.push_back({it.id, it.settings});
}
-
- CameraMetadata metadata(it.settings);
- if (metadata.isEmpty()) {
- ALOGE("%s: Camera %s: Sent empty metadata packet. Rejecting request.",
- __FUNCTION__, mCameraIdStr.string());
- return STATUS_ERROR(CameraService::ERROR_ILLEGAL_ARGUMENT,
- "Request settings are empty");
- }
-
- physicalSettingsList.push_back({it.id, metadata});
- }
-
- if (streaming && (physicalSettingsList.size() > 1)) {
- ALOGE("%s: Camera %s: Individual physical camera settings are not supported in "
- "streaming requests. Rejecting request.", __FUNCTION__, mCameraIdStr.string());
- return STATUS_ERROR(CameraService::ERROR_ILLEGAL_ARGUMENT,
- "Streaming request contains individual physical requests");
}
if (!enforceRequestPermissions(physicalSettingsList.begin()->metadata)) {
@@ -633,14 +659,6 @@
return res;
if (!isStreamInfoValid) {
- // Streaming sharing is only supported for IMPLEMENTATION_DEFINED
- // formats.
- if (isShared && streamInfo.format != HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED) {
- String8 msg = String8::format("Camera %s: Stream sharing is only supported for "
- "IMPLEMENTATION_DEFINED format", mCameraIdStr.string());
- ALOGW("%s: %s", __FUNCTION__, msg.string());
- return STATUS_ERROR(CameraService::ERROR_ILLEGAL_ARGUMENT, msg.string());
- }
isStreamInfoValid = true;
}
@@ -920,14 +938,6 @@
if (!res.isOk())
return res;
- // Stream sharing is only supported for IMPLEMENTATION_DEFINED
- // formats.
- if (outInfo.format != HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED) {
- String8 msg = String8::format("Camera %s: Stream sharing is only supported for "
- "IMPLEMENTATION_DEFINED format", mCameraIdStr.string());
- ALOGW("%s: %s", __FUNCTION__, msg.string());
- return STATUS_ERROR(CameraService::ERROR_ILLEGAL_ARGUMENT, msg.string());
- }
streamInfos.push_back(outInfo);
newOutputs.push_back(surface);
}
diff --git a/services/camera/libcameraservice/api2/CameraDeviceClient.h b/services/camera/libcameraservice/api2/CameraDeviceClient.h
index 435c99d..5aaf5aa 100644
--- a/services/camera/libcameraservice/api2/CameraDeviceClient.h
+++ b/services/camera/libcameraservice/api2/CameraDeviceClient.h
@@ -45,6 +45,7 @@
const sp<hardware::camera2::ICameraDeviceCallbacks>& remoteCallback,
const String16& clientPackageName,
const String8& cameraId,
+ int api1CameraId,
int cameraFacing,
int clientPid,
uid_t clientUid,
@@ -156,7 +157,8 @@
int servicePid);
virtual ~CameraDeviceClient();
- virtual status_t initialize(sp<CameraProviderManager> manager) override;
+ virtual status_t initialize(sp<CameraProviderManager> manager,
+ const String8& monitorTags) override;
virtual status_t dump(int fd, const Vector<String16>& args);
@@ -221,8 +223,10 @@
static const int32_t FRAME_PROCESSOR_LISTENER_MIN_ID = 0;
static const int32_t FRAME_PROCESSOR_LISTENER_MAX_ID = 0x7fffffffL;
+ std::vector<int32_t> mSupportedPhysicalRequestKeys;
+
template<typename TProviderPtr>
- status_t initializeImpl(TProviderPtr providerPtr);
+ status_t initializeImpl(TProviderPtr providerPtr, const String8& monitorTags);
/** Utility members */
binder::Status checkPidStatus(const char* checkLocation);
diff --git a/services/camera/libcameraservice/common/Camera2ClientBase.cpp b/services/camera/libcameraservice/common/Camera2ClientBase.cpp
index 4ce82dc..459e45d 100644
--- a/services/camera/libcameraservice/common/Camera2ClientBase.cpp
+++ b/services/camera/libcameraservice/common/Camera2ClientBase.cpp
@@ -48,15 +48,16 @@
const sp<TCamCallbacks>& remoteCallback,
const String16& clientPackageName,
const String8& cameraId,
+ int api1CameraId,
int cameraFacing,
int clientPid,
uid_t clientUid,
int servicePid):
TClientBase(cameraService, remoteCallback, clientPackageName,
- cameraId, cameraFacing, clientPid, clientUid, servicePid),
+ cameraId, api1CameraId, cameraFacing, clientPid, clientUid, servicePid),
mSharedCameraCallbacks(remoteCallback),
mDeviceVersion(cameraService->getDeviceVersion(TClientBase::mCameraIdStr)),
- mDeviceActive(false)
+ mDeviceActive(false), mApi1CameraId(api1CameraId)
{
ALOGI("Camera %s: Opened. Client: %s (PID %d, UID %d)", cameraId.string(),
String8(clientPackageName).string(), clientPid, clientUid);
@@ -79,13 +80,15 @@
}
template <typename TClientBase>
-status_t Camera2ClientBase<TClientBase>::initialize(sp<CameraProviderManager> manager) {
- return initializeImpl(manager);
+status_t Camera2ClientBase<TClientBase>::initialize(sp<CameraProviderManager> manager,
+ const String8& monitorTags) {
+ return initializeImpl(manager, monitorTags);
}
template <typename TClientBase>
template <typename TProviderPtr>
-status_t Camera2ClientBase<TClientBase>::initializeImpl(TProviderPtr providerPtr) {
+status_t Camera2ClientBase<TClientBase>::initializeImpl(TProviderPtr providerPtr,
+ const String8& monitorTags) {
ATRACE_CALL();
ALOGV("%s: Initializing client for camera %s", __FUNCTION__,
TClientBase::mCameraIdStr.string());
@@ -103,7 +106,7 @@
return NO_INIT;
}
- res = mDevice->initialize(providerPtr);
+ res = mDevice->initialize(providerPtr, monitorTags);
if (res != OK) {
ALOGE("%s: Camera %s: unable to initialize device: %s (%d)",
__FUNCTION__, TClientBase::mCameraIdStr.string(), strerror(-res), res);
@@ -329,7 +332,7 @@
template <typename TClientBase>
int Camera2ClientBase<TClientBase>::getCameraId() const {
- return std::stoi(TClientBase::mCameraIdStr.string());
+ return mApi1CameraId;
}
template <typename TClientBase>
diff --git a/services/camera/libcameraservice/common/Camera2ClientBase.h b/services/camera/libcameraservice/common/Camera2ClientBase.h
index e898d5d..e74fbdf 100644
--- a/services/camera/libcameraservice/common/Camera2ClientBase.h
+++ b/services/camera/libcameraservice/common/Camera2ClientBase.h
@@ -49,13 +49,14 @@
const sp<TCamCallbacks>& remoteCallback,
const String16& clientPackageName,
const String8& cameraId,
+ int api1CameraId,
int cameraFacing,
int clientPid,
uid_t clientUid,
int servicePid);
virtual ~Camera2ClientBase();
- virtual status_t initialize(sp<CameraProviderManager> manager);
+ virtual status_t initialize(sp<CameraProviderManager> manager, const String8& monitorTags);
virtual status_t dumpClient(int fd, const Vector<String16>& args);
/**
@@ -140,9 +141,11 @@
bool mDeviceActive;
+ const int mApi1CameraId; // -1 if client is API2
+
private:
template<typename TProviderPtr>
- status_t initializeImpl(TProviderPtr providerPtr);
+ status_t initializeImpl(TProviderPtr providerPtr, const String8& monitorTags);
};
}; // namespace android
diff --git a/services/camera/libcameraservice/common/CameraDeviceBase.h b/services/camera/libcameraservice/common/CameraDeviceBase.h
index 7956be5..0ba7403 100644
--- a/services/camera/libcameraservice/common/CameraDeviceBase.h
+++ b/services/camera/libcameraservice/common/CameraDeviceBase.h
@@ -55,7 +55,12 @@
*/
virtual const String8& getId() const = 0;
- virtual status_t initialize(sp<CameraProviderManager> manager) = 0;
+ /**
+ * The device vendor tag ID
+ */
+ virtual metadata_vendor_id_t getVendorTagId() const = 0;
+
+ virtual status_t initialize(sp<CameraProviderManager> manager, const String8& monitorTags) = 0;
virtual status_t disconnect() = 0;
virtual status_t dump(int fd, const Vector<String16> &args) = 0;
diff --git a/services/camera/libcameraservice/common/CameraProviderManager.cpp b/services/camera/libcameraservice/common/CameraProviderManager.cpp
index 70e7761..b37f004 100644
--- a/services/camera/libcameraservice/common/CameraProviderManager.cpp
+++ b/services/camera/libcameraservice/common/CameraProviderManager.cpp
@@ -20,11 +20,13 @@
#include "CameraProviderManager.h"
+#include <algorithm>
#include <chrono>
#include <inttypes.h>
#include <hidl/ServiceManagement.h>
#include <functional>
#include <camera_metadata_hidden.h>
+#include <android-base/parseint.h>
namespace android {
@@ -38,7 +40,7 @@
const std::string kExternalProviderName("external/0");
// Slash-separated list of provider types to consider for use via the old camera API
-const std::string kStandardProviderTypes("internal/legacy");
+const std::string kStandardProviderTypes("internal/legacy/external");
} // anonymous namespace
@@ -79,18 +81,7 @@
std::lock_guard<std::mutex> lock(mInterfaceMutex);
int count = 0;
for (auto& provider : mProviders) {
- count += provider->mUniqueDeviceCount;
- }
- return count;
-}
-
-int CameraProviderManager::getAPI1CompatibleCameraCount() const {
- std::lock_guard<std::mutex> lock(mInterfaceMutex);
- int count = 0;
- for (auto& provider : mProviders) {
- if (kStandardProviderTypes.find(provider->getType()) != std::string::npos) {
- count += provider->mUniqueAPI1CompatibleCameraIds.size();
- }
+ count += provider->mUniqueCameraIds.size();
}
return count;
}
@@ -116,6 +107,24 @@
}
}
}
+
+ std::sort(deviceIds.begin(), deviceIds.end(),
+ [](const std::string& a, const std::string& b) -> bool {
+ uint32_t aUint = 0, bUint = 0;
+ bool aIsUint = base::ParseUint(a, &aUint);
+ bool bIsUint = base::ParseUint(b, &bUint);
+
+ // Uint device IDs first
+ if (aIsUint && bIsUint) {
+ return aUint < bUint;
+ } else if (aIsUint) {
+ return true;
+ } else if (bIsUint) {
+ return false;
+ }
+ // Simple string compare if both id are not uint
+ return a < b;
+ });
return deviceIds;
}
@@ -480,6 +489,8 @@
}
ALOGI("Connecting to new camera provider: %s, isRemote? %d",
mProviderName.c_str(), mInterface->isRemote());
+ // cameraDeviceStatusChange callbacks may be called (and causing new devices added)
+ // before setCallback returns
hardware::Return<Status> status = mInterface->setCallback(this);
if (!status.isOk()) {
ALOGE("%s: Transaction error setting up callbacks with camera provider '%s': %s",
@@ -536,17 +547,10 @@
}
}
- for (auto& device : mDevices) {
- mUniqueCameraIds.insert(device->mId);
- if (device->isAPI1Compatible()) {
- mUniqueAPI1CompatibleCameraIds.insert(device->mId);
- }
- }
- mUniqueDeviceCount = mUniqueCameraIds.size();
-
ALOGI("Camera provider %s ready with %zu camera devices",
mProviderName.c_str(), mDevices.size());
+ mInitialized = true;
return OK;
}
@@ -594,9 +598,15 @@
}
if (deviceInfo == nullptr) return BAD_VALUE;
deviceInfo->mStatus = initialStatus;
+ bool isAPI1Compatible = deviceInfo->isAPI1Compatible();
mDevices.push_back(std::move(deviceInfo));
+ mUniqueCameraIds.insert(id);
+ if (isAPI1Compatible) {
+ mUniqueAPI1CompatibleCameraIds.insert(id);
+ }
+
if (parsedId != nullptr) {
*parsedId = id;
}
@@ -606,6 +616,10 @@
void CameraProviderManager::ProviderInfo::removeDevice(std::string id) {
for (auto it = mDevices.begin(); it != mDevices.end(); it++) {
if ((*it)->mId == id) {
+ mUniqueCameraIds.erase(id);
+ if ((*it)->isAPI1Compatible()) {
+ mUniqueAPI1CompatibleCameraIds.erase(id);
+ }
mDevices.erase(it);
break;
}
@@ -671,6 +685,7 @@
CameraDeviceStatus newStatus) {
sp<StatusListener> listener;
std::string id;
+ bool initialized = false;
{
std::lock_guard<std::mutex> lock(mLock);
bool known = false;
@@ -697,9 +712,13 @@
removeDevice(id);
}
listener = mManager->getStatusListener();
+ initialized = mInitialized;
}
// Call without lock held to allow reentrancy into provider manager
- if (listener != nullptr) {
+ // Don't send the callback if providerInfo hasn't been initialized.
+ // CameraService will initialize device status after provider is
+ // initialized
+ if (listener != nullptr && initialized) {
listener->onDeviceStatusChanged(String8(id.c_str()), newStatus);
}
return hardware::Void();
diff --git a/services/camera/libcameraservice/common/CameraProviderManager.h b/services/camera/libcameraservice/common/CameraProviderManager.h
index d02abb0..bbe6789 100644
--- a/services/camera/libcameraservice/common/CameraProviderManager.h
+++ b/services/camera/libcameraservice/common/CameraProviderManager.h
@@ -18,7 +18,7 @@
#define ANDROID_SERVERS_CAMERA_CAMERAPROVIDER_H
#include <vector>
-#include <set>
+#include <unordered_set>
#include <string>
#include <mutex>
@@ -125,16 +125,14 @@
*/
int getCameraCount() const;
+ std::vector<std::string> getCameraDeviceIds() const;
+
/**
* Retrieve the number of API1 compatible cameras; these are internal and
* backwards-compatible. This is the set of cameras that will be
- * accessible via the old camera API, with IDs in range of
- * [0, getAPI1CompatibleCameraCount()-1]. This value is not expected to change dynamically.
+ * accessible via the old camera API.
+ * The return value may change dynamically due to external camera hotplug.
*/
- int getAPI1CompatibleCameraCount() const;
-
- std::vector<std::string> getCameraDeviceIds() const;
-
std::vector<std::string> getAPI1CompatibleCameraDeviceIds() const;
/**
@@ -314,9 +312,9 @@
static status_t setTorchMode(InterfaceT& interface, bool enabled);
};
std::vector<std::unique_ptr<DeviceInfo>> mDevices;
- std::set<std::string> mUniqueCameraIds;
+ std::unordered_set<std::string> mUniqueCameraIds;
int mUniqueDeviceCount;
- std::set<std::string> mUniqueAPI1CompatibleCameraIds;
+ std::unordered_set<std::string> mUniqueAPI1CompatibleCameraIds;
// HALv1-specific camera fields, including the actual device interface
struct DeviceInfo1 : public DeviceInfo {
@@ -366,6 +364,8 @@
CameraProviderManager *mManager;
+ bool mInitialized = false;
+
// Templated method to instantiate the right kind of DeviceInfo and call the
// right CameraProvider getCameraDeviceInterface_* method.
template<class DeviceInfoT>
diff --git a/services/camera/libcameraservice/device3/Camera3Device.cpp b/services/camera/libcameraservice/device3/Camera3Device.cpp
index 59bb2e2..42bcb2d 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.cpp
+++ b/services/camera/libcameraservice/device3/Camera3Device.cpp
@@ -96,7 +96,7 @@
return mId;
}
-status_t Camera3Device::initialize(sp<CameraProviderManager> manager) {
+status_t Camera3Device::initialize(sp<CameraProviderManager> manager, const String8& monitorTags) {
ATRACE_CALL();
Mutex::Autolock il(mInterfaceLock);
Mutex::Autolock l(mLock);
@@ -169,6 +169,10 @@
mInterface = new HalInterface(session, queue);
std::string providerType;
mVendorTagId = manager->getProviderTagIdLocked(mId.string());
+ mTagMonitor.initialize(mVendorTagId);
+ if (!monitorTags.isEmpty()) {
+ mTagMonitor.parseTagsToMonitor(String8(monitorTags));
+ }
return initializeCommonLocked();
}
@@ -192,8 +196,6 @@
/** Create buffer manager */
mBufferManager = new Camera3BufferManager();
- mTagMonitor.initialize(mVendorTagId);
-
Vector<int32_t> sessionParamKeys;
camera_metadata_entry_t sessionKeysEntry = mDeviceInfo.find(
ANDROID_REQUEST_AVAILABLE_SESSION_KEYS);
@@ -582,13 +584,12 @@
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 (args[i] == TagMonitor::kMonitorOption) {
if (i + 1 < n) {
String8 monitorTags = String8(args[i + 1]);
if (monitorTags == "off") {
@@ -3828,21 +3829,27 @@
if (hidlSession_3_4 != nullptr) {
captureRequests_3_4[i].physicalCameraSettings.resize(request->num_physcam_settings);
for (size_t j = 0; j < request->num_physcam_settings; j++) {
- size_t settingsSize = get_camera_metadata_size(request->physcam_settings[j]);
- if (mRequestMetadataQueue != nullptr && mRequestMetadataQueue->write(
- reinterpret_cast<const uint8_t*>(request->physcam_settings[j]),
- settingsSize)) {
- captureRequests_3_4[i].physicalCameraSettings[j].settings.resize(0);
- captureRequests_3_4[i].physicalCameraSettings[j].fmqSettingsSize = settingsSize;
- } else {
- if (mRequestMetadataQueue != nullptr) {
- ALOGW("%s: couldn't utilize fmq, fallback to hwbinder", __FUNCTION__);
+ if (request->physcam_settings != nullptr) {
+ size_t settingsSize = get_camera_metadata_size(request->physcam_settings[j]);
+ if (mRequestMetadataQueue != nullptr && mRequestMetadataQueue->write(
+ reinterpret_cast<const uint8_t*>(request->physcam_settings[j]),
+ settingsSize)) {
+ captureRequests_3_4[i].physicalCameraSettings[j].settings.resize(0);
+ captureRequests_3_4[i].physicalCameraSettings[j].fmqSettingsSize =
+ settingsSize;
+ } else {
+ if (mRequestMetadataQueue != nullptr) {
+ ALOGW("%s: couldn't utilize fmq, fallback to hwbinder", __FUNCTION__);
+ }
+ captureRequests_3_4[i].physicalCameraSettings[j].settings.setToExternal(
+ reinterpret_cast<uint8_t*>(const_cast<camera_metadata_t*>(
+ request->physcam_settings[j])),
+ get_camera_metadata_size(request->physcam_settings[j]));
+ captureRequests_3_4[i].physicalCameraSettings[j].fmqSettingsSize = 0u;
}
- captureRequests_3_4[i].physicalCameraSettings[j].settings.setToExternal(
- reinterpret_cast<uint8_t*>(const_cast<camera_metadata_t*>(
- request->physcam_settings[j])),
- get_camera_metadata_size(request->physcam_settings[j]));
+ } else {
captureRequests_3_4[i].physicalCameraSettings[j].fmqSettingsSize = 0u;
+ captureRequests_3_4[i].physicalCameraSettings[j].settings.resize(0);
}
captureRequests_3_4[i].physicalCameraSettings[j].physicalCameraId =
request->physcam_id[j];
@@ -4680,7 +4687,8 @@
mPrevTriggers = triggerCount;
// If the request is the same as last, or we had triggers last time
- if (mPrevRequest != captureRequest || triggersMixedIn) {
+ bool newRequest = mPrevRequest != captureRequest || triggersMixedIn;
+ if (newRequest) {
/**
* HAL workaround:
* Insert a dummy trigger ID if a trigger is set but no trigger ID is
@@ -4725,14 +4733,20 @@
if (captureRequest->mSettingsList.size() > 1) {
halRequest->num_physcam_settings = captureRequest->mSettingsList.size() - 1;
halRequest->physcam_id = new const char* [halRequest->num_physcam_settings];
- halRequest->physcam_settings =
- new const camera_metadata* [halRequest->num_physcam_settings];
+ if (newRequest) {
+ halRequest->physcam_settings =
+ new const camera_metadata* [halRequest->num_physcam_settings];
+ } else {
+ halRequest->physcam_settings = nullptr;
+ }
auto it = ++captureRequest->mSettingsList.begin();
size_t i = 0;
for (; it != captureRequest->mSettingsList.end(); it++, i++) {
halRequest->physcam_id[i] = it->cameraId.c_str();
- it->metadata.sort();
- halRequest->physcam_settings[i] = it->metadata.getAndLock();
+ if (newRequest) {
+ it->metadata.sort();
+ halRequest->physcam_settings[i] = it->metadata.getAndLock();
+ }
}
}
diff --git a/services/camera/libcameraservice/device3/Camera3Device.h b/services/camera/libcameraservice/device3/Camera3Device.h
index 12cb6b4..13b83ba 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.h
+++ b/services/camera/libcameraservice/device3/Camera3Device.h
@@ -93,8 +93,10 @@
const String8& getId() const override;
+ metadata_vendor_id_t getVendorTagId() const override { return mVendorTagId; }
+
// Transitions to idle state on success.
- status_t initialize(sp<CameraProviderManager> manager) override;
+ status_t initialize(sp<CameraProviderManager> manager, const String8& monitorTags) override;
status_t disconnect() override;
status_t dump(int fd, const Vector<String16> &args) override;
const CameraMetadata& info() const override;
diff --git a/services/camera/libcameraservice/utils/TagMonitor.cpp b/services/camera/libcameraservice/utils/TagMonitor.cpp
index dec97d7..c0a353f 100644
--- a/services/camera/libcameraservice/utils/TagMonitor.cpp
+++ b/services/camera/libcameraservice/utils/TagMonitor.cpp
@@ -33,6 +33,8 @@
mVendorTagId(CAMERA_METADATA_INVALID_VENDOR_ID)
{}
+const String16 TagMonitor::kMonitorOption = String16("-m");
+
const char* TagMonitor::k3aTags =
"android.control.aeMode, android.control.afMode, android.control.awbMode,"
"android.control.aeState, android.control.afState, android.control.awbState,"
diff --git a/services/camera/libcameraservice/utils/TagMonitor.h b/services/camera/libcameraservice/utils/TagMonitor.h
index 7155314..2dece62 100644
--- a/services/camera/libcameraservice/utils/TagMonitor.h
+++ b/services/camera/libcameraservice/utils/TagMonitor.h
@@ -38,6 +38,10 @@
* buffer log that can be dumped at will. */
class TagMonitor {
public:
+
+ // Monitor argument
+ static const String16 kMonitorOption;
+
enum eventSource {
REQUEST,
RESULT
diff --git a/services/minijail/Android.bp b/services/minijail/Android.bp
new file mode 100644
index 0000000..07a94cc
--- /dev/null
+++ b/services/minijail/Android.bp
@@ -0,0 +1,38 @@
+minijail_common_cflags = [
+ "-Wall",
+ "-Werror",
+]
+
+cc_defaults {
+ name: "libavservices_minijail_defaults",
+ srcs: ["minijail.cpp"],
+ cflags: minijail_common_cflags,
+ shared_libs: [
+ "libbase",
+ "libminijail",
+ ],
+}
+
+// Small library for media.extractor and media.codec sandboxing.
+cc_library_shared {
+ name: "libavservices_minijail",
+ defaults: ["libavservices_minijail_defaults"],
+ export_include_dirs: ["."],
+}
+
+// Small library for media.extractor and media.codec sandboxing.
+cc_library_shared {
+ name: "libavservices_minijail_vendor",
+ vendor: true,
+ defaults: ["libavservices_minijail_defaults"],
+ export_include_dirs: ["."],
+}
+
+// Unit tests.
+cc_test {
+ name: "libavservices_minijail_unittest",
+ defaults: ["libavservices_minijail_defaults"],
+ srcs: [
+ "av_services_minijail_unittest.cpp",
+ ],
+}
diff --git a/services/minijail/Android.mk b/services/minijail/Android.mk
deleted file mode 100644
index 67055a8..0000000
--- a/services/minijail/Android.mk
+++ /dev/null
@@ -1,30 +0,0 @@
-LOCAL_PATH := $(call my-dir)
-
-minijail_common_cflags := -Wall -Werror
-
-# Small library for media.extractor and media.codec sandboxing.
-include $(CLEAR_VARS)
-LOCAL_MODULE := libavservices_minijail
-LOCAL_SRC_FILES := minijail.cpp
-LOCAL_CFLAGS := $(minijail_common_cflags)
-LOCAL_SHARED_LIBRARIES := libbase libminijail
-LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)
-include $(BUILD_SHARED_LIBRARY)
-
-# Small library for media.extractor and media.codec sandboxing.
-include $(CLEAR_VARS)
-LOCAL_MODULE := libavservices_minijail_vendor
-LOCAL_VENDOR_MODULE := true
-LOCAL_SRC_FILES := minijail.cpp
-LOCAL_CFLAGS := $(minijail_common_cflags)
-LOCAL_SHARED_LIBRARIES := libbase libminijail
-LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)
-include $(BUILD_SHARED_LIBRARY)
-
-# Unit tests.
-include $(CLEAR_VARS)
-LOCAL_MODULE := libavservices_minijail_unittest
-LOCAL_SRC_FILES := minijail.cpp av_services_minijail_unittest.cpp
-LOCAL_CFLAGS := $(minijail_common_cflags)
-LOCAL_SHARED_LIBRARIES := libbase libminijail
-include $(BUILD_NATIVE_TEST)