Merge "Split nblog off from nbaio"
diff --git a/camera/ICameraClient.cpp b/camera/ICameraClient.cpp
index 7e6297c..8620f36 100644
--- a/camera/ICameraClient.cpp
+++ b/camera/ICameraClient.cpp
@@ -192,12 +192,14 @@
timestamps.reserve(n);
handles.reserve(n);
for (uint32_t i = 0; i < n; i++) {
- res = data.readInt64(×tamps[i]);
+ nsecs_t t;
+ res = data.readInt64(&t);
if (res != OK) {
ALOGE("%s: Failed to read timestamp[%d]: %s (%d)",
__FUNCTION__, i, strerror(-res), res);
return BAD_VALUE;
}
+ timestamps.push_back(t);
}
for (uint32_t i = 0; i < n; i++) {
native_handle_t* handle = data.readNativeHandle();
diff --git a/camera/ICameraRecordingProxyListener.cpp b/camera/ICameraRecordingProxyListener.cpp
index c954241..66faf8f 100644
--- a/camera/ICameraRecordingProxyListener.cpp
+++ b/camera/ICameraRecordingProxyListener.cpp
@@ -146,12 +146,14 @@
timestamps.reserve(n);
handles.reserve(n);
for (uint32_t i = 0; i < n; i++) {
- res = data.readInt64(×tamps[i]);
+ nsecs_t t;
+ res = data.readInt64(&t);
if (res != OK) {
ALOGE("%s: Failed to read timestamp[%d]: %s (%d)",
__FUNCTION__, i, strerror(-res), res);
return BAD_VALUE;
}
+ timestamps.push_back(t);
}
for (uint32_t i = 0; i < n; i++) {
native_handle_t* handle = data.readNativeHandle();
diff --git a/camera/aidl/android/hardware/camera2/ICameraDeviceCallbacks.aidl b/camera/aidl/android/hardware/camera2/ICameraDeviceCallbacks.aidl
index 8308095..28252c0 100644
--- a/camera/aidl/android/hardware/camera2/ICameraDeviceCallbacks.aidl
+++ b/camera/aidl/android/hardware/camera2/ICameraDeviceCallbacks.aidl
@@ -42,7 +42,9 @@
* Repeating request encountered an error and was stopped.
*
* @param lastFrameNumber Frame number of the last frame of the streaming request.
+ * @param repeatingRequestId the ID of the repeating request being stopped
*/
- oneway void onRepeatingRequestError(in long lastFrameNumber);
+ oneway void onRepeatingRequestError(in long lastFrameNumber,
+ in int repeatingRequestId);
oneway void onRequestQueueEmpty();
}
diff --git a/camera/ndk/impl/ACameraDevice.cpp b/camera/ndk/impl/ACameraDevice.cpp
index 229b159..3ae208a 100644
--- a/camera/ndk/impl/ACameraDevice.cpp
+++ b/camera/ndk/impl/ACameraDevice.cpp
@@ -1353,7 +1353,8 @@
}
binder::Status
-CameraDevice::ServiceCallback::onRepeatingRequestError(int64_t lastFrameNumber) {
+CameraDevice::ServiceCallback::onRepeatingRequestError(
+ int64_t lastFrameNumber, int32_t stoppedSequenceId) {
binder::Status ret = binder::Status::ok();
sp<CameraDevice> dev = mDevice.promote();
@@ -1364,7 +1365,9 @@
Mutex::Autolock _l(dev->mDeviceLock);
int repeatingSequenceId = dev->mRepeatingSequenceId;
- dev->mRepeatingSequenceId = REQUEST_ID_NONE;
+ if (stoppedSequenceId == repeatingSequenceId) {
+ dev->mRepeatingSequenceId = REQUEST_ID_NONE;
+ }
dev->checkRepeatingSequenceCompleteLocked(repeatingSequenceId, lastFrameNumber);
diff --git a/camera/ndk/impl/ACameraDevice.h b/camera/ndk/impl/ACameraDevice.h
index c566cd2..855efe1 100644
--- a/camera/ndk/impl/ACameraDevice.h
+++ b/camera/ndk/impl/ACameraDevice.h
@@ -75,7 +75,8 @@
const CaptureResultExtras& resultExtras) override;
binder::Status onPrepared(int streamId) override;
binder::Status onRequestQueueEmpty() override;
- binder::Status onRepeatingRequestError(int64_t lastFrameNumber) override;
+ binder::Status onRepeatingRequestError(int64_t lastFrameNumber,
+ int32_t stoppedSequenceId) override;
private:
const wp<CameraDevice> mDevice;
};
diff --git a/camera/tests/CameraBinderTests.cpp b/camera/tests/CameraBinderTests.cpp
index 946e3b8..51d9214 100644
--- a/camera/tests/CameraBinderTests.cpp
+++ b/camera/tests/CameraBinderTests.cpp
@@ -217,8 +217,10 @@
return binder::Status::ok();
}
- virtual binder::Status onRepeatingRequestError(int64_t lastFrameNumber) {
+ virtual binder::Status onRepeatingRequestError(
+ int64_t lastFrameNumber, int32_t stoppedSequenceId) {
(void) lastFrameNumber;
+ (void) stoppedSequenceId;
Mutex::Autolock l(mLock);
mLastStatus = REPEATING_REQUEST_ERROR;
mStatusesHit.push_back(mLastStatus);
diff --git a/cmds/stagefright/audioloop.cpp b/cmds/stagefright/audioloop.cpp
index ed44b4d..67017eb 100644
--- a/cmds/stagefright/audioloop.cpp
+++ b/cmds/stagefright/audioloop.cpp
@@ -112,7 +112,7 @@
looper->setName("audioloop");
looper->start();
- sp<IMediaSource> encoder = MediaCodecSource::Create(looper, meta, source);
+ sp<MediaSource> encoder = MediaCodecSource::Create(looper, meta, source);
if (fileOut != NULL) {
// target file specified, write encoded AMR output
@@ -128,7 +128,7 @@
writer->stop();
} else {
// otherwise decode to speaker
- sp<IMediaSource> decoder = SimpleDecodingSource::Create(encoder);
+ sp<MediaSource> decoder = SimpleDecodingSource::Create(encoder);
if (playToSpeaker) {
AudioPlayer *player = new AudioPlayer(NULL);
diff --git a/cmds/stagefright/record.cpp b/cmds/stagefright/record.cpp
index 94c2e96..69b00c9 100644
--- a/cmds/stagefright/record.cpp
+++ b/cmds/stagefright/record.cpp
@@ -320,7 +320,7 @@
looper->setName("record");
looper->start();
- sp<IMediaSource> encoder =
+ sp<MediaSource> encoder =
MediaCodecSource::Create(looper, encMeta, audioSource);
encoder->start();
diff --git a/cmds/stagefright/recordvideo.cpp b/cmds/stagefright/recordvideo.cpp
index 7a3c842..af39d46 100644
--- a/cmds/stagefright/recordvideo.cpp
+++ b/cmds/stagefright/recordvideo.cpp
@@ -303,7 +303,7 @@
looper->setName("recordvideo");
looper->start();
- sp<IMediaSource> encoder =
+ sp<MediaSource> encoder =
MediaCodecSource::Create(
looper, enc_meta, source, NULL /* consumer */,
preferSoftwareCodec ? MediaCodecSource::FLAG_PREFER_SOFTWARE_CODEC : 0);
diff --git a/cmds/stagefright/stagefright.cpp b/cmds/stagefright/stagefright.cpp
index d70282b..7a5d129 100644
--- a/cmds/stagefright/stagefright.cpp
+++ b/cmds/stagefright/stagefright.cpp
@@ -141,7 +141,7 @@
}
}
-static void dumpSource(const sp<IMediaSource> &source, const String8 &filename) {
+static void dumpSource(const sp<MediaSource> &source, const String8 &filename) {
FILE *out = fopen(filename.string(), "wb");
CHECK_EQ((status_t)OK, source->start());
@@ -174,13 +174,13 @@
out = NULL;
}
-static void playSource(sp<IMediaSource> &source) {
+static void playSource(sp<MediaSource> &source) {
sp<MetaData> meta = source->getFormat();
const char *mime;
CHECK(meta->findCString(kKeyMIMEType, &mime));
- sp<IMediaSource> rawSource;
+ sp<MediaSource> rawSource;
if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_RAW, mime)) {
rawSource = source;
} else {
@@ -404,7 +404,7 @@
////////////////////////////////////////////////////////////////////////////////
struct DetectSyncSource : public MediaSource {
- explicit DetectSyncSource(const sp<IMediaSource> &source);
+ explicit DetectSyncSource(const sp<MediaSource> &source);
virtual status_t start(MetaData *params = NULL);
virtual status_t stop();
@@ -421,14 +421,14 @@
OTHER,
};
- sp<IMediaSource> mSource;
+ sp<MediaSource> mSource;
StreamType mStreamType;
bool mSawFirstIDRFrame;
DISALLOW_EVIL_CONSTRUCTORS(DetectSyncSource);
};
-DetectSyncSource::DetectSyncSource(const sp<IMediaSource> &source)
+DetectSyncSource::DetectSyncSource(const sp<MediaSource> &source)
: mSource(source),
mStreamType(OTHER),
mSawFirstIDRFrame(false) {
@@ -510,7 +510,7 @@
////////////////////////////////////////////////////////////////////////////////
static void writeSourcesToMP4(
- Vector<sp<IMediaSource> > &sources, bool syncInfoPresent) {
+ Vector<sp<MediaSource> > &sources, bool syncInfoPresent) {
#if 0
sp<MPEG4Writer> writer =
new MPEG4Writer(gWriteMP4Filename.string());
@@ -528,7 +528,7 @@
writer->setMaxFileDuration(60000000ll);
for (size_t i = 0; i < sources.size(); ++i) {
- sp<IMediaSource> source = sources.editItemAt(i);
+ sp<MediaSource> source = sources.editItemAt(i);
CHECK_EQ(writer->addSource(
syncInfoPresent ? source : new DetectSyncSource(source)),
@@ -545,7 +545,7 @@
writer->stop();
}
-static void performSeekTest(const sp<IMediaSource> &source) {
+static void performSeekTest(const sp<MediaSource> &source) {
CHECK_EQ((status_t)OK, source->start());
int64_t durationUs;
@@ -1002,8 +1002,8 @@
isJPEG = true;
}
- Vector<sp<IMediaSource> > mediaSources;
- sp<IMediaSource> mediaSource;
+ Vector<sp<MediaSource> > mediaSources;
+ sp<MediaSource> mediaSource;
if (isJPEG) {
mediaSource = new JPEGSource(dataSource);
@@ -1049,7 +1049,8 @@
bool haveAudio = false;
bool haveVideo = false;
for (size_t i = 0; i < numTracks; ++i) {
- sp<IMediaSource> source = extractor->getTrack(i);
+ sp<MediaSource> source = MediaSource::CreateFromIMediaSource(
+ extractor->getTrack(i));
if (source == nullptr) {
fprintf(stderr, "skip NULL track %zu, track count %zu.\n", i, numTracks);
continue;
@@ -1115,7 +1116,7 @@
thumbTimeUs, thumbTimeUs / 1E6);
}
- mediaSource = extractor->getTrack(i);
+ mediaSource = MediaSource::CreateFromIMediaSource(extractor->getTrack(i));
if (mediaSource == nullptr) {
fprintf(stderr, "skip NULL track %zu, total tracks %zu.\n", i, numTracks);
return -1;
@@ -1128,7 +1129,7 @@
} else if (dumpStream) {
dumpSource(mediaSource, dumpStreamFilename);
} else if (dumpPCMStream) {
- sp<IMediaSource> decSource = SimpleDecodingSource::Create(mediaSource);
+ sp<MediaSource> decSource = SimpleDecodingSource::Create(mediaSource);
dumpSource(decSource, dumpStreamFilename);
} else if (seekTest) {
performSeekTest(mediaSource);
diff --git a/cmds/stagefright/stream.cpp b/cmds/stagefright/stream.cpp
index 2e1d240..3b7a0e0 100644
--- a/cmds/stagefright/stream.cpp
+++ b/cmds/stagefright/stream.cpp
@@ -182,7 +182,7 @@
continue;
}
- sp<IMediaSource> track = extractor->getTrack(i);
+ sp<MediaSource> track = MediaSource::CreateFromIMediaSource(extractor->getTrack(i));
if (track == nullptr) {
fprintf(stderr, "skip NULL track %zu, total tracks %zu\n", i, numTracks);
continue;
diff --git a/drm/libmediadrm/PluginMetricsReporting.cpp b/drm/libmediadrm/PluginMetricsReporting.cpp
index a9302ea..57ff5b8 100644
--- a/drm/libmediadrm/PluginMetricsReporting.cpp
+++ b/drm/libmediadrm/PluginMetricsReporting.cpp
@@ -44,6 +44,13 @@
analyticsItem.setInt64(kParentAttribute, *parentId);
}
+ // Report the package name.
+ if (metricsGroup.has_app_package_name()) {
+ AString app_package_name(metricsGroup.app_package_name().c_str(),
+ metricsGroup.app_package_name().size());
+ analyticsItem.setPkgName(app_package_name);
+ }
+
for (int i = 0; i < metricsGroup.metric_size(); ++i) {
const MetricsGroup_Metric& metric = metricsGroup.metric(i);
if (!metric.has_name()) {
@@ -73,7 +80,12 @@
}
analyticsItem.setFinalized(true);
- analyticsItem.selfrecord();
+ if (!analyticsItem.selfrecord()) {
+ // Note the cast to int is because we build on 32 and 64 bit.
+ // The cast prevents a peculiar printf problem where one format cannot
+ // satisfy both.
+ ALOGE("selfrecord() returned false. sessioId %d", (int) sessionId);
+ }
for (int i = 0; i < metricsGroup.metric_sub_group_size(); ++i) {
const MetricsGroup& subGroup = metricsGroup.metric_sub_group(i);
diff --git a/drm/libmediadrm/protos/plugin_metrics.proto b/drm/libmediadrm/protos/plugin_metrics.proto
index 2d26f14..7e3bcf5 100644
--- a/drm/libmediadrm/protos/plugin_metrics.proto
+++ b/drm/libmediadrm/protos/plugin_metrics.proto
@@ -44,4 +44,7 @@
// 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/mediadrm/plugins/clearkey/DrmPlugin.cpp b/drm/mediadrm/plugins/clearkey/DrmPlugin.cpp
index 5fdac5c..ec07d87 100644
--- a/drm/mediadrm/plugins/clearkey/DrmPlugin.cpp
+++ b/drm/mediadrm/plugins/clearkey/DrmPlugin.cpp
@@ -25,10 +25,28 @@
#include "Session.h"
+namespace {
+const android::String8 kStreaming("Streaming");
+const android::String8 kOffline("Offline");
+const android::String8 kTrue("True");
+
+const android::String8 kQueryKeyLicenseType("LicenseType");
+ // Value: "Streaming" or "Offline"
+const android::String8 kQueryKeyPlayAllowed("PlayAllowed");
+ // Value: "True" or "False"
+const android::String8 kQueryKeyRenewAllowed("RenewAllowed");
+ // Value: "True" or "False"
+};
+
namespace clearkeydrm {
using android::sp;
+DrmPlugin::DrmPlugin(SessionLibrary* sessionLibrary)
+ : mSessionLibrary(sessionLibrary) {
+ mPlayPolicy.clear();
+}
+
status_t DrmPlugin::openSession(Vector<uint8_t>& sessionId) {
sp<Session> session = mSessionLibrary->createSession();
sessionId = session->sessionId();
@@ -60,18 +78,28 @@
if (scope.size() == 0) {
return android::BAD_VALUE;
}
+
if (keyType != kKeyType_Streaming) {
return android::ERROR_DRM_CANNOT_HANDLE;
}
+
*keyRequestType = DrmPlugin::kKeyRequestType_Initial;
defaultUrl.clear();
sp<Session> session = mSessionLibrary->findSession(scope);
if (!session.get()) {
return android::ERROR_DRM_SESSION_NOT_OPENED;
}
+
return session->getKeyRequest(initData, mimeType, &request);
}
+void DrmPlugin::setPlayPolicy() {
+ mPlayPolicy.clear();
+ mPlayPolicy.add(kQueryKeyLicenseType, kStreaming);
+ mPlayPolicy.add(kQueryKeyPlayAllowed, kTrue);
+ mPlayPolicy.add(kQueryKeyRenewAllowed, kTrue);
+}
+
status_t DrmPlugin::provideKeyResponse(
const Vector<uint8_t>& scope,
const Vector<uint8_t>& response,
@@ -83,6 +111,8 @@
if (!session.get()) {
return android::ERROR_DRM_SESSION_NOT_OPENED;
}
+
+ setPlayPolicy();
status_t res = session->provideKeyResponse(response);
if (res == android::OK) {
// This is for testing AMediaDrm_setOnEventListener only.
@@ -111,4 +141,18 @@
return android::OK;
}
+status_t DrmPlugin::queryKeyStatus(
+ const Vector<uint8_t>& sessionId,
+ KeyedVector<String8, String8>& infoMap) const {
+
+ if (sessionId.size() == 0) {
+ return android::BAD_VALUE;
+ }
+
+ infoMap.clear();
+ for (size_t i = 0; i < mPlayPolicy.size(); ++i) {
+ infoMap.add(mPlayPolicy.keyAt(i), mPlayPolicy.valueAt(i));
+ }
+ return android::OK;
+}
} // namespace clearkeydrm
diff --git a/drm/mediadrm/plugins/clearkey/DrmPlugin.h b/drm/mediadrm/plugins/clearkey/DrmPlugin.h
index 58421b9..f37a706 100644
--- a/drm/mediadrm/plugins/clearkey/DrmPlugin.h
+++ b/drm/mediadrm/plugins/clearkey/DrmPlugin.h
@@ -39,8 +39,8 @@
class DrmPlugin : public android::DrmPlugin {
public:
- explicit DrmPlugin(SessionLibrary* sessionLibrary)
- : mSessionLibrary(sessionLibrary) {}
+ explicit DrmPlugin(SessionLibrary* sessionLibrary);
+
virtual ~DrmPlugin() {}
virtual status_t openSession(Vector<uint8_t>& sessionId);
@@ -81,13 +81,7 @@
virtual status_t queryKeyStatus(
const Vector<uint8_t>& sessionId,
- KeyedVector<String8, String8>& infoMap) const {
- if (sessionId.size() == 0) {
- return android::BAD_VALUE;
- }
- UNUSED(infoMap);
- return android::ERROR_DRM_CANNOT_HANDLE;
- }
+ KeyedVector<String8, String8>& infoMap) const;
virtual status_t getProvisionRequest(
const String8& cert_type,
@@ -248,9 +242,12 @@
}
private:
- DISALLOW_EVIL_CONSTRUCTORS(DrmPlugin);
+ void setPlayPolicy();
+ android::KeyedVector<android::String8, android::String8> mPlayPolicy;
SessionLibrary* mSessionLibrary;
+
+ DISALLOW_EVIL_CONSTRUCTORS(DrmPlugin);
};
} // namespace clearkeydrm
diff --git a/include/media/IHDCP.h b/include/media/IHDCP.h
deleted file mode 120000
index 9d4568e..0000000
--- a/include/media/IHDCP.h
+++ /dev/null
@@ -1 +0,0 @@
-../../media/libmedia/include/media/IHDCP.h
\ No newline at end of file
diff --git a/media/audioserver/audioserver.rc b/media/audioserver/audioserver.rc
index 9d42bce..7874f90 100644
--- a/media/audioserver/audioserver.rc
+++ b/media/audioserver/audioserver.rc
@@ -5,6 +5,9 @@
group audio camera drmrpc inet media mediadrm net_bt net_bt_admin net_bw_acct
ioprio rt 4
writepid /dev/cpuset/foreground/tasks /dev/stune/foreground/tasks
+ onrestart restart vendor.audio-hal-2-0
+ # Keep the original service name for backward compatibility when upgrading
+ # O-MR1 devices with framework-only.
onrestart restart audio-hal-2-0
on property:vts.native_server.on=1
diff --git a/media/img_utils/src/TiffWriter.cpp b/media/img_utils/src/TiffWriter.cpp
index 564474f..1711242 100644
--- a/media/img_utils/src/TiffWriter.cpp
+++ b/media/img_utils/src/TiffWriter.cpp
@@ -350,7 +350,7 @@
if (nextIfd == NULL) {
break;
}
- ifd = nextIfd;
+ ifd = std::move(nextIfd);
}
return ifd;
}
diff --git a/media/libaaudio/Doxyfile b/media/libaaudio/Doxyfile
index e2c4960..7298d11 100644
--- a/media/libaaudio/Doxyfile
+++ b/media/libaaudio/Doxyfile
@@ -744,12 +744,12 @@
# Note: If this tag is empty the current directory is searched.
INPUT = include/aaudio/AAudio.h \
+ src/binding/AAudioCommon.h \
src/legacy/AudioStreamTrack.h \
src/legacy/AudioStreamRecord.h \
src/legacy/AAudioLegacy.h \
src/core/AudioStreamBuilder.h \
src/core/AudioStream.h \
- src/utility/HandleTracker.h \
src/utility/MonotonicCounter.h \
src/utility/AudioClock.h \
src/utility/AAudioUtilities.h
diff --git a/media/libaaudio/examples/utils/AAudioExampleUtils.h b/media/libaaudio/examples/utils/AAudioExampleUtils.h
index 9ef62c9..c179ce6 100644
--- a/media/libaaudio/examples/utils/AAudioExampleUtils.h
+++ b/media/libaaudio/examples/utils/AAudioExampleUtils.h
@@ -31,18 +31,51 @@
#define NANOS_PER_SECOND (NANOS_PER_MILLISECOND * 1000)
const char *getSharingModeText(aaudio_sharing_mode_t mode) {
- const char *modeText = "unknown";
+ const char *text = "unknown";
switch (mode) {
- case AAUDIO_SHARING_MODE_EXCLUSIVE:
- modeText = "EXCLUSIVE";
- break;
- case AAUDIO_SHARING_MODE_SHARED:
- modeText = "SHARED";
- break;
- default:
- break;
+ case AAUDIO_SHARING_MODE_EXCLUSIVE:
+ text = "EXCLUSIVE";
+ break;
+ case AAUDIO_SHARING_MODE_SHARED:
+ text = "SHARED";
+ break;
+ default:
+ break;
}
- return modeText;
+ return text;
+}
+
+const char *getPerformanceModeText(aaudio_performance_mode_t mode) {
+ const char *text = "unknown";
+ switch (mode) {
+ case AAUDIO_PERFORMANCE_MODE_NONE:
+ text = "NONE";
+ break;
+ case AAUDIO_PERFORMANCE_MODE_LOW_LATENCY:
+ text = "LOW_LATENCY";
+ break;
+ case AAUDIO_PERFORMANCE_MODE_POWER_SAVING:
+ text = "POWER_SAVING";
+ break;
+ default:
+ break;
+ }
+ return text;
+}
+
+const char *getDirectionText(aaudio_direction_t direction) {
+ const char *text = "unknown";
+ switch (direction) {
+ case AAUDIO_DIRECTION_INPUT:
+ text = "INPUT";
+ break;
+ case AAUDIO_DIRECTION_OUTPUT:
+ text = "OUTPUT";
+ break;
+ default:
+ break;
+ }
+ return text;
}
static void convertNanosecondsToTimespec(int64_t nanoseconds, struct timespec *time) {
diff --git a/media/libaaudio/examples/utils/AAudioSimplePlayer.h b/media/libaaudio/examples/utils/AAudioSimplePlayer.h
index d2e7f23..606c4ba 100644
--- a/media/libaaudio/examples/utils/AAudioSimplePlayer.h
+++ b/media/libaaudio/examples/utils/AAudioSimplePlayer.h
@@ -36,6 +36,13 @@
// How long to sleep in a callback to cause an intentional glitch. For testing.
#define FORCED_UNDERRUN_SLEEP_MICROS (10 * 1000)
+#define MAX_TIMESTAMPS 16
+
+typedef struct Timestamp {
+ int64_t position;
+ int64_t nanoseconds;
+} Timestamp;
+
/**
* Simple wrapper for AAudio that opens an output stream either in callback or blocking write mode.
*/
@@ -227,10 +234,12 @@
SineGenerator sineOsc1;
SineGenerator sineOsc2;
+ Timestamp timestamps[MAX_TIMESTAMPS];
int64_t framesTotal = 0;
int64_t nextFrameToGlitch = FORCED_UNDERRUN_PERIOD_FRAMES;
int32_t minNumFrames = INT32_MAX;
int32_t maxNumFrames = 0;
+ int32_t timestampCount = 0; // in timestamps
int scheduler = 0;
bool schedulerChecked = false;
@@ -273,6 +282,17 @@
sineData->schedulerChecked = true;
}
+ if (sineData->timestampCount < MAX_TIMESTAMPS) {
+ Timestamp *timestamp = &sineData->timestamps[sineData->timestampCount];
+ aaudio_result_t result = AAudioStream_getTimestamp(stream,
+ CLOCK_MONOTONIC, ×tamp->position, ×tamp->nanoseconds);
+ if (result == AAUDIO_OK && // valid?
+ (sineData->timestampCount == 0 || // first one?
+ (timestamp->position != (timestamp - 1)->position))) { // advanced position?
+ sineData->timestampCount++; // keep this one
+ }
+ }
+
if (numFrames > sineData->maxNumFrames) {
sineData->maxNumFrames = numFrames;
}
diff --git a/media/libaaudio/examples/write_sine/src/write_sine_callback.cpp b/media/libaaudio/examples/write_sine/src/write_sine_callback.cpp
index 2280b72..4f9cde6 100644
--- a/media/libaaudio/examples/write_sine/src/write_sine_callback.cpp
+++ b/media/libaaudio/examples/write_sine/src/write_sine_callback.cpp
@@ -120,6 +120,18 @@
goto error;
}
+ for (int i = 0; i < myData.timestampCount; i++) {
+ Timestamp *timestamp = &myData.timestamps[i];
+ bool retro = (i > 0 &&
+ ((timestamp->position < (timestamp - 1)->position)
+ || ((timestamp->nanoseconds < (timestamp - 1)->nanoseconds))));
+ const char *message = retro ? " <= RETROGRADE!" : "";
+ printf("Timestamp %3d : %8lld, %8lld %s\n", i,
+ (long long) timestamp->position,
+ (long long) timestamp->nanoseconds,
+ message);
+ }
+
if (myData.schedulerChecked) {
printf("scheduler = 0x%08x, SCHED_FIFO = 0x%08X\n",
myData.scheduler,
diff --git a/media/libaaudio/src/Android.mk b/media/libaaudio/src/Android.mk
index 112a192..f7a5f9b 100644
--- a/media/libaaudio/src/Android.mk
+++ b/media/libaaudio/src/Android.mk
@@ -38,7 +38,6 @@
legacy/AudioStreamLegacy.cpp \
legacy/AudioStreamRecord.cpp \
legacy/AudioStreamTrack.cpp \
- utility/HandleTracker.cpp \
utility/AAudioUtilities.cpp \
utility/FixedBlockAdapter.cpp \
utility/FixedBlockReader.cpp \
@@ -99,7 +98,6 @@
legacy/AudioStreamLegacy.cpp \
legacy/AudioStreamRecord.cpp \
legacy/AudioStreamTrack.cpp \
- utility/HandleTracker.cpp \
utility/AAudioUtilities.cpp \
utility/FixedBlockAdapter.cpp \
utility/FixedBlockReader.cpp \
diff --git a/media/libaaudio/src/binding/AAudioCommon.h b/media/libaaudio/src/binding/AAudioCommon.h
new file mode 100644
index 0000000..e3e9e82
--- /dev/null
+++ b/media/libaaudio/src/binding/AAudioCommon.h
@@ -0,0 +1,32 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_AAUDIO_COMMON_H
+#define ANDROID_AAUDIO_COMMON_H
+
+#include <stdint.h>
+
+/*
+ * Internal header that is common to both client and server.
+ *
+ */
+namespace aaudio {
+
+typedef int32_t aaudio_handle_t;
+
+} /* namespace aaudio */
+
+#endif // ANDROID_AAUDIO_COMMON_H
diff --git a/media/libaaudio/src/binding/IAAudioClient.h b/media/libaaudio/src/binding/IAAudioClient.h
index 21cc33b..f21fd93 100644
--- a/media/libaaudio/src/binding/IAAudioClient.h
+++ b/media/libaaudio/src/binding/IAAudioClient.h
@@ -22,7 +22,7 @@
#include <aaudio/AAudio.h>
-#include "utility/HandleTracker.h"
+#include "binding/AAudioCommon.h"
namespace android {
@@ -33,7 +33,7 @@
DECLARE_META_INTERFACE(AAudioClient);
- virtual void onStreamChange(aaudio_handle_t handle, int32_t opcode, int32_t value) = 0;
+ virtual void onStreamChange(aaudio::aaudio_handle_t handle, int32_t opcode, int32_t value) = 0;
};
diff --git a/media/libaaudio/src/binding/IAAudioService.h b/media/libaaudio/src/binding/IAAudioService.h
index 30b3ead..6bdb826 100644
--- a/media/libaaudio/src/binding/IAAudioService.h
+++ b/media/libaaudio/src/binding/IAAudioService.h
@@ -24,12 +24,12 @@
#include <aaudio/AAudio.h>
+#include "binding/AAudioCommon.h"
#include "binding/AAudioServiceDefinitions.h"
-#include "binding/AudioEndpointParcelable.h"
-#include "binding/AAudioStreamRequest.h"
#include "binding/AAudioStreamConfiguration.h"
+#include "binding/AAudioStreamRequest.h"
+#include "binding/AudioEndpointParcelable.h"
#include "binding/IAAudioClient.h"
-#include "utility/HandleTracker.h"
namespace android {
@@ -51,7 +51,7 @@
* @param configuration contains information about the created stream
* @return handle to the stream or a negative error
*/
- virtual aaudio_handle_t openStream(const aaudio::AAudioStreamRequest &request,
+ virtual aaudio::aaudio_handle_t openStream(const aaudio::AAudioStreamRequest &request,
aaudio::AAudioStreamConfiguration &configurationOutput) = 0;
virtual aaudio_result_t closeStream(aaudio::aaudio_handle_t streamHandle) = 0;
@@ -89,11 +89,11 @@
/**
* Manage the specified thread as a low latency audio thread.
*/
- virtual aaudio_result_t registerAudioThread(aaudio_handle_t streamHandle,
+ virtual aaudio_result_t registerAudioThread(aaudio::aaudio_handle_t streamHandle,
pid_t clientThreadId,
int64_t periodNanoseconds) = 0;
- virtual aaudio_result_t unregisterAudioThread(aaudio_handle_t streamHandle,
+ virtual aaudio_result_t unregisterAudioThread(aaudio::aaudio_handle_t streamHandle,
pid_t clientThreadId) = 0;
};
diff --git a/media/libaaudio/src/client/AudioStreamInternal.cpp b/media/libaaudio/src/client/AudioStreamInternal.cpp
index 7f2e495..2fdbfaf 100644
--- a/media/libaaudio/src/client/AudioStreamInternal.cpp
+++ b/media/libaaudio/src/client/AudioStreamInternal.cpp
@@ -23,7 +23,6 @@
#define ATRACE_TAG ATRACE_TAG_AUDIO
#include <stdint.h>
-#include <assert.h>
#include <binder/IServiceManager.h>
@@ -55,7 +54,7 @@
// Wait at least this many times longer than the operation should take.
#define MIN_TIMEOUT_OPERATIONS 4
-#define LOG_TIMESTAMPS 0
+#define LOG_TIMESTAMPS 0
AudioStreamInternal::AudioStreamInternal(AAudioServiceInterface &serviceInterface, bool inService)
: AudioStream()
@@ -63,12 +62,11 @@
, mAudioEndpoint()
, mServiceStreamHandle(AAUDIO_HANDLE_INVALID)
, mFramesPerBurst(16)
- , mStreamVolume(1.0f)
, mInService(inService)
, mServiceInterface(serviceInterface)
+ , mAtomicTimestamp()
, mWakeupDelayNanos(AAudioProperty_getWakeupDelayMicros() * AAUDIO_NANOS_PER_MICROSECOND)
, mMinimumSleepNanos(AAudioProperty_getMinimumSleepMicros() * AAUDIO_NANOS_PER_MICROSECOND)
- , mAtomicTimestamp()
{
ALOGD("AudioStreamInternal(): mWakeupDelayNanos = %d, mMinimumSleepNanos = %d",
mWakeupDelayNanos, mMinimumSleepNanos);
@@ -196,10 +194,6 @@
}
setState(AAUDIO_STREAM_STATE_OPEN);
- // Only connect to AudioManager if this is a playback stream running in client process.
- if (!mInService && getDirection() == AAUDIO_DIRECTION_OUTPUT) {
- init(android::PLAYER_TYPE_AAUDIO, AUDIO_USAGE_MEDIA);
- }
return result;
@@ -209,7 +203,8 @@
}
aaudio_result_t AudioStreamInternal::close() {
- ALOGD("AudioStreamInternal::close(): mServiceStreamHandle = 0x%08X",
+ aaudio_result_t result = AAUDIO_OK;
+ ALOGD("close(): mServiceStreamHandle = 0x%08X",
mServiceStreamHandle);
if (mServiceStreamHandle != AAUDIO_HANDLE_INVALID) {
// Don't close a stream while it is running.
@@ -218,10 +213,10 @@
requestStop();
aaudio_stream_state_t nextState;
int64_t timeoutNanoseconds = MIN_TIMEOUT_NANOS;
- aaudio_result_t result = waitForStateChange(currentState, &nextState,
+ result = waitForStateChange(currentState, &nextState,
timeoutNanoseconds);
if (result != AAUDIO_OK) {
- ALOGE("AudioStreamInternal::close() waitForStateChange() returned %d %s",
+ ALOGE("close() waitForStateChange() returned %d %s",
result, AAudio_convertResultToText(result));
}
}
@@ -232,8 +227,11 @@
mServiceInterface.closeStream(serviceStreamHandle);
delete[] mCallbackBuffer;
mCallbackBuffer = nullptr;
+
setState(AAUDIO_STREAM_STATE_CLOSED);
- return mEndPointParcelable.close();
+ result = mEndPointParcelable.close();
+ aaudio_result_t result2 = AudioStream::close();
+ return (result != AAUDIO_OK) ? result : result2;
} else {
return AAUDIO_ERROR_INVALID_HANDLE;
}
@@ -250,26 +248,46 @@
}
}
+/*
+ * It normally takes about 20-30 msec to start a stream on the server.
+ * But the first time can take as much as 200-300 msec. The HW
+ * starts right away so by the time the client gets a chance to write into
+ * the buffer, it is already in a deep underflow state. That can cause the
+ * XRunCount to be non-zero, which could lead an app to tune its latency higher.
+ * To avoid this problem, we set a request for the processing code to start the
+ * client stream at the same position as the server stream.
+ * The processing code will then save the current offset
+ * between client and server and apply that to any position given to the app.
+ */
aaudio_result_t AudioStreamInternal::requestStart()
{
int64_t startTime;
- ALOGD("AudioStreamInternal()::requestStart()");
if (mServiceStreamHandle == AAUDIO_HANDLE_INVALID) {
- ALOGE("AudioStreamInternal::requestStart() mServiceStreamHandle invalid");
+ ALOGE("requestStart() mServiceStreamHandle invalid");
return AAUDIO_ERROR_INVALID_STATE;
}
if (isActive()) {
- ALOGE("AudioStreamInternal::requestStart() already active");
+ ALOGE("requestStart() already active");
return AAUDIO_ERROR_INVALID_STATE;
}
- aaudio_stream_state_t originalState = getState();
+ aaudio_stream_state_t originalState = getState();
+ if (originalState == AAUDIO_STREAM_STATE_DISCONNECTED) {
+ ALOGE("requestStart() but DISCONNECTED");
+ return AAUDIO_ERROR_DISCONNECTED;
+ }
setState(AAUDIO_STREAM_STATE_STARTING);
- aaudio_result_t result = AAudioConvert_androidToAAudioResult(startWithStatus());
+
+ // Clear any stale timestamps from the previous run.
+ drainTimestampsFromService();
+
+ aaudio_result_t result = mServiceInterface.startStream(mServiceStreamHandle);
startTime = AudioClock::getNanoseconds();
mClockModel.start(startTime);
+ mNeedCatchUp.request(); // Ask data processing code to catch up when first timestamp received.
+ // Start data callback thread.
if (result == AAUDIO_OK && getDataCallbackProc() != nullptr) {
// Launch the callback loop thread.
int64_t periodNanos = mCallbackFrames
@@ -314,14 +332,16 @@
aaudio_result_t AudioStreamInternal::requestStopInternal()
{
if (mServiceStreamHandle == AAUDIO_HANDLE_INVALID) {
- ALOGE("AudioStreamInternal::requestStopInternal() mServiceStreamHandle invalid = 0x%08X",
+ ALOGE("requestStopInternal() mServiceStreamHandle invalid = 0x%08X",
mServiceStreamHandle);
return AAUDIO_ERROR_INVALID_STATE;
}
mClockModel.stop(AudioClock::getNanoseconds());
setState(AAUDIO_STREAM_STATE_STOPPING);
- return AAudioConvert_androidToAAudioResult(stopWithStatus());
+ mAtomicTimestamp.clear();
+
+ return mServiceInterface.stopStream(mServiceStreamHandle);
}
aaudio_result_t AudioStreamInternal::requestStop()
@@ -336,7 +356,7 @@
aaudio_result_t AudioStreamInternal::registerThread() {
if (mServiceStreamHandle == AAUDIO_HANDLE_INVALID) {
- ALOGE("AudioStreamInternal::registerThread() mServiceStreamHandle invalid");
+ ALOGE("registerThread() mServiceStreamHandle invalid");
return AAUDIO_ERROR_INVALID_STATE;
}
return mServiceInterface.registerAudioThread(mServiceStreamHandle,
@@ -346,7 +366,7 @@
aaudio_result_t AudioStreamInternal::unregisterThread() {
if (mServiceStreamHandle == AAUDIO_HANDLE_INVALID) {
- ALOGE("AudioStreamInternal::unregisterThread() mServiceStreamHandle invalid");
+ ALOGE("unregisterThread() mServiceStreamHandle invalid");
return AAUDIO_ERROR_INVALID_STATE;
}
return mServiceInterface.unregisterAudioThread(mServiceStreamHandle, gettid());
@@ -374,12 +394,14 @@
// Generated in server and passed to client. Return latest.
if (mAtomicTimestamp.isValid()) {
Timestamp timestamp = mAtomicTimestamp.read();
- *framePosition = timestamp.getPosition();
- *timeNanoseconds = timestamp.getNanoseconds();
- return AAUDIO_OK;
- } else {
- return AAUDIO_ERROR_UNAVAILABLE;
+ int64_t position = timestamp.getPosition() + mFramesOffsetFromService;
+ if (position >= 0) {
+ *framePosition = position;
+ *timeNanoseconds = timestamp.getNanoseconds();
+ return AAUDIO_OK;
+ }
}
+ return AAUDIO_ERROR_INVALID_STATE;
}
aaudio_result_t AudioStreamInternal::updateStateMachine() {
@@ -394,14 +416,14 @@
static int64_t oldTime = 0;
int64_t framePosition = command.timestamp.position;
int64_t nanoTime = command.timestamp.timestamp;
- ALOGD("AudioStreamInternal: timestamp says framePosition = %08lld at nanoTime %lld",
+ ALOGD("logTimestamp: timestamp says framePosition = %8lld at nanoTime %lld",
(long long) framePosition,
(long long) nanoTime);
int64_t nanosDelta = nanoTime - oldTime;
if (nanosDelta > 0 && oldTime > 0) {
int64_t framesDelta = framePosition - oldPosition;
int64_t rate = (framesDelta * AAUDIO_NANOS_PER_SECOND) / nanosDelta;
- ALOGD("AudioStreamInternal: framesDelta = %08lld, nanosDelta = %08lld, rate = %lld",
+ ALOGD("logTimestamp: framesDelta = %8lld, nanosDelta = %8lld, rate = %lld",
(long long) framesDelta, (long long) nanosDelta, (long long) rate);
}
oldPosition = framePosition;
@@ -478,6 +500,34 @@
return result;
}
+aaudio_result_t AudioStreamInternal::drainTimestampsFromService() {
+ aaudio_result_t result = AAUDIO_OK;
+
+ while (result == AAUDIO_OK) {
+ AAudioServiceMessage message;
+ if (mAudioEndpoint.readUpCommand(&message) != 1) {
+ break; // no command this time, no problem
+ }
+ switch (message.what) {
+ // ignore most messages
+ case AAudioServiceMessage::code::TIMESTAMP_SERVICE:
+ case AAudioServiceMessage::code::TIMESTAMP_HARDWARE:
+ break;
+
+ case AAudioServiceMessage::code::EVENT:
+ result = onEventFromServer(&message);
+ break;
+
+ default:
+ ALOGE("WARNING - drainTimestampsFromService() Unrecognized what = %d",
+ (int) message.what);
+ result = AAUDIO_ERROR_INTERNAL;
+ break;
+ }
+ }
+ return result;
+}
+
// Process all the commands coming from the server.
aaudio_result_t AudioStreamInternal::processCommands() {
aaudio_result_t result = AAUDIO_OK;
@@ -502,7 +552,7 @@
break;
default:
- ALOGE("WARNING - AudioStreamInternal::processCommands() Unrecognized what = %d",
+ ALOGE("WARNING - processCommands() Unrecognized what = %d",
(int) message.what);
result = AAUDIO_ERROR_INTERNAL;
break;
@@ -613,7 +663,7 @@
}
aaudio_result_t result = mAudioEndpoint.setBufferSizeInFrames(requestedFrames, &actualFrames);
- ALOGD("AudioStreamInternal::setBufferSize() req = %d => %d", requestedFrames, actualFrames);
+ ALOGD("setBufferSize() req = %d => %d", requestedFrames, actualFrames);
if (result < 0) {
return result;
} else {
@@ -636,32 +686,3 @@
aaudio_result_t AudioStreamInternal::joinThread(void** returnArg) {
return AudioStream::joinThread(returnArg, calculateReasonableTimeout(getFramesPerBurst()));
}
-
-void AudioStreamInternal::doSetVolume() {
- // No pan and only left volume is taken into account from IPLayer interface
- mVolumeRamp.setTarget(mStreamVolume * mVolumeMultiplierL /* * mPanMultiplierL */);
-}
-
-
-//------------------------------------------------------------------------------
-// Implementation of PlayerBase
-status_t AudioStreamInternal::playerStart() {
- return AAudioConvert_aaudioToAndroidStatus(mServiceInterface.startStream(mServiceStreamHandle));
-}
-
-status_t AudioStreamInternal::playerPause() {
- return AAudioConvert_aaudioToAndroidStatus(mServiceInterface.pauseStream(mServiceStreamHandle));
-}
-
-status_t AudioStreamInternal::playerStop() {
- return AAudioConvert_aaudioToAndroidStatus(mServiceInterface.stopStream(mServiceStreamHandle));
-}
-
-status_t AudioStreamInternal::playerSetVolume() {
- doSetVolume();
- return NO_ERROR;
-}
-
-void AudioStreamInternal::destroy() {
- baseDestroy();
-}
diff --git a/media/libaaudio/src/client/AudioStreamInternal.h b/media/libaaudio/src/client/AudioStreamInternal.h
index 3523294..47024c0 100644
--- a/media/libaaudio/src/client/AudioStreamInternal.h
+++ b/media/libaaudio/src/client/AudioStreamInternal.h
@@ -18,7 +18,6 @@
#define ANDROID_AAUDIO_AUDIO_STREAM_INTERNAL_H
#include <stdint.h>
-#include <media/PlayerBase.h>
#include <aaudio/AAudio.h>
#include "binding/IAAudioService.h"
@@ -36,7 +35,7 @@
namespace aaudio {
// A stream that talks to the AAudioService or directly to a HAL.
-class AudioStreamInternal : public AudioStream, public android::PlayerBase {
+class AudioStreamInternal : public AudioStream {
public:
AudioStreamInternal(AAudioServiceInterface &serviceInterface, bool inService);
@@ -85,9 +84,6 @@
// Calculate timeout based on framesPerBurst
int64_t calculateReasonableTimeout();
- //PlayerBase virtuals
- virtual void destroy();
-
aaudio_result_t startClient(const android::AudioClient& client,
audio_port_handle_t *clientHandle);
@@ -115,12 +111,15 @@
int64_t currentTimeNanos,
int64_t *wakeTimePtr) = 0;
+ aaudio_result_t drainTimestampsFromService();
+
aaudio_result_t processCommands();
aaudio_result_t requestStopInternal();
aaudio_result_t stopCallback();
+ virtual void advanceClientToMatchServerPosition() = 0;
virtual void onFlushFromServer() {}
@@ -135,14 +134,6 @@
// Calculate timeout for an operation involving framesPerOperation.
int64_t calculateReasonableTimeout(int32_t framesPerOperation);
- void doSetVolume();
-
- //PlayerBase virtuals
- virtual status_t playerStart();
- virtual status_t playerPause();
- virtual status_t playerStop();
- virtual status_t playerSetVolume();
-
aaudio_format_t mDeviceFormat = AAUDIO_FORMAT_UNSPECIFIED;
IsochronousClockModel mClockModel; // timing model for chasing the HAL
@@ -153,9 +144,6 @@
int32_t mFramesPerBurst; // frames per HAL transfer
int32_t mXRunCount = 0; // how many underrun events?
- LinearRamp mVolumeRamp;
- float mStreamVolume;
-
// Offset from underlying frame position.
int64_t mFramesOffsetFromService = 0; // offset for timestamps
@@ -167,6 +155,12 @@
AAudioServiceInterface &mServiceInterface; // abstract interface to the service
+ SimpleDoubleBuffer<Timestamp> mAtomicTimestamp;
+
+ AtomicRequestor mNeedCatchUp; // Ask read() or write() to sync on first timestamp.
+
+ float mStreamVolume = 1.0f;
+
private:
/*
* Asynchronous write with data conversion.
@@ -188,10 +182,7 @@
AudioEndpointParcelable mEndPointParcelable; // description of the buffers filled by service
EndpointDescriptor mEndpointDescriptor; // buffer description with resolved addresses
- SimpleDoubleBuffer<Timestamp> mAtomicTimestamp;
-
int64_t mServiceLatencyNanos = 0;
-
};
} /* namespace aaudio */
diff --git a/media/libaaudio/src/client/AudioStreamInternalCapture.cpp b/media/libaaudio/src/client/AudioStreamInternalCapture.cpp
index 7b1e53e..b792ecd 100644
--- a/media/libaaudio/src/client/AudioStreamInternalCapture.cpp
+++ b/media/libaaudio/src/client/AudioStreamInternalCapture.cpp
@@ -39,6 +39,21 @@
AudioStreamInternalCapture::~AudioStreamInternalCapture() {}
+void AudioStreamInternalCapture::advanceClientToMatchServerPosition() {
+ int64_t readCounter = mAudioEndpoint.getDataReadCounter();
+ int64_t writeCounter = mAudioEndpoint.getDataWriteCounter();
+
+ // Bump offset so caller does not see the retrograde motion in getFramesRead().
+ int64_t offset = readCounter - writeCounter;
+ mFramesOffsetFromService += offset;
+ ALOGD("advanceClientToMatchServerPosition() readN = %lld, writeN = %lld, offset = %lld",
+ (long long)readCounter, (long long)writeCounter, (long long)mFramesOffsetFromService);
+
+ // Force readCounter to match writeCounter.
+ // This is because we cannot change the write counter in the hardware.
+ mAudioEndpoint.setDataReadCounter(writeCounter);
+}
+
// Write the data, block if needed and timeoutMillis > 0
aaudio_result_t AudioStreamInternalCapture::read(void *buffer, int32_t numFrames,
int64_t timeoutNanoseconds)
@@ -57,6 +72,18 @@
const char *traceName = "aaRdNow";
ATRACE_BEGIN(traceName);
+ if (mClockModel.isStarting()) {
+ // Still haven't got any timestamps from server.
+ // Keep waiting until we get some valid timestamps then start writing to the
+ // current buffer position.
+ ALOGD("processDataNow() wait for valid timestamps");
+ // Sleep very briefly and hope we get a timestamp soon.
+ *wakeTimePtr = currentNanoTime + (2000 * AAUDIO_NANOS_PER_MICROSECOND);
+ ATRACE_END();
+ return 0;
+ }
+ // If we have gotten this far then we have at least one timestamp from server.
+
if (mAudioEndpoint.isFreeRunning()) {
//ALOGD("AudioStreamInternalCapture::processDataNow() - update remote counter");
// Update data queue based on the timing model.
@@ -65,6 +92,14 @@
mAudioEndpoint.setDataWriteCounter(estimatedRemoteCounter);
}
+ // This code assumes that we have already received valid timestamps.
+ if (mNeedCatchUp.isRequested()) {
+ // Catch an MMAP pointer that is already advancing.
+ // This will avoid initial underruns caused by a slow cold start.
+ advanceClientToMatchServerPosition();
+ mNeedCatchUp.acknowledge();
+ }
+
// If the write index passed the read index then consider it an overrun.
if (mAudioEndpoint.getEmptyFramesAvailable() < 0) {
mXRunCount++;
@@ -100,8 +135,8 @@
// Calculate frame position based off of the readCounter because
// the writeCounter might have just advanced in the background,
// causing us to sleep until a later burst.
- int64_t nextReadPosition = mAudioEndpoint.getDataReadCounter() + mFramesPerBurst;
- wakeTime = mClockModel.convertPositionToTime(nextReadPosition);
+ int64_t nextPosition = mAudioEndpoint.getDataReadCounter() + mFramesPerBurst;
+ wakeTime = mClockModel.convertPositionToTime(nextPosition);
}
break;
default:
@@ -186,8 +221,7 @@
}
int64_t AudioStreamInternalCapture::getFramesRead() {
- int64_t frames = mAudioEndpoint.getDataWriteCounter()
- + mFramesOffsetFromService;
+ int64_t frames = mAudioEndpoint.getDataReadCounter() + mFramesOffsetFromService;
//ALOGD("AudioStreamInternalCapture::getFramesRead() returns %lld", (long long)frames);
return frames;
}
diff --git a/media/libaaudio/src/client/AudioStreamInternalCapture.h b/media/libaaudio/src/client/AudioStreamInternalCapture.h
index 17f37e8..294dbaf 100644
--- a/media/libaaudio/src/client/AudioStreamInternalCapture.h
+++ b/media/libaaudio/src/client/AudioStreamInternalCapture.h
@@ -46,6 +46,8 @@
}
protected:
+ void advanceClientToMatchServerPosition() override;
+
/**
* Low level data processing that will not block. It will just read or write as much as it can.
*
diff --git a/media/libaaudio/src/client/AudioStreamInternalPlay.cpp b/media/libaaudio/src/client/AudioStreamInternalPlay.cpp
index 31e0a40..1e02eee 100644
--- a/media/libaaudio/src/client/AudioStreamInternalPlay.cpp
+++ b/media/libaaudio/src/client/AudioStreamInternalPlay.cpp
@@ -48,7 +48,8 @@
mClockModel.stop(AudioClock::getNanoseconds());
setState(AAUDIO_STREAM_STATE_PAUSING);
- return AAudioConvert_androidToAAudioResult(pauseWithStatus());
+ mAtomicTimestamp.clear();
+ return mServiceInterface.pauseStream(mServiceStreamHandle);
}
aaudio_result_t AudioStreamInternalPlay::requestPause()
@@ -72,21 +73,25 @@
return mServiceInterface.flushStream(mServiceStreamHandle);
}
-void AudioStreamInternalPlay::onFlushFromServer() {
+void AudioStreamInternalPlay::advanceClientToMatchServerPosition() {
int64_t readCounter = mAudioEndpoint.getDataReadCounter();
int64_t writeCounter = mAudioEndpoint.getDataWriteCounter();
// Bump offset so caller does not see the retrograde motion in getFramesRead().
- int64_t framesFlushed = writeCounter - readCounter;
- mFramesOffsetFromService += framesFlushed;
- ALOGD("AudioStreamInternal::onFlushFromServer() readN = %lld, writeN = %lld, offset = %lld",
+ int64_t offset = writeCounter - readCounter;
+ mFramesOffsetFromService += offset;
+ ALOGD("advanceClientToMatchServerPosition() readN = %lld, writeN = %lld, offset = %lld",
(long long)readCounter, (long long)writeCounter, (long long)mFramesOffsetFromService);
- // Flush written frames by forcing writeCounter to readCounter.
- // This is because we cannot move the read counter in the hardware.
+ // Force writeCounter to match readCounter.
+ // This is because we cannot change the read counter in the hardware.
mAudioEndpoint.setDataWriteCounter(readCounter);
}
+void AudioStreamInternalPlay::onFlushFromServer() {
+ advanceClientToMatchServerPosition();
+}
+
// Write the data, block if needed and timeoutMillis > 0
aaudio_result_t AudioStreamInternalPlay::write(const void *buffer, int32_t numFrames,
int64_t timeoutNanoseconds)
@@ -106,6 +111,18 @@
const char *traceName = "aaWrNow";
ATRACE_BEGIN(traceName);
+ if (mClockModel.isStarting()) {
+ // Still haven't got any timestamps from server.
+ // Keep waiting until we get some valid timestamps then start writing to the
+ // current buffer position.
+ ALOGD("processDataNow() wait for valid timestamps");
+ // Sleep very briefly and hope we get a timestamp soon.
+ *wakeTimePtr = currentNanoTime + (2000 * AAUDIO_NANOS_PER_MICROSECOND);
+ ATRACE_END();
+ return 0;
+ }
+ // If we have gotten this far then we have at least one timestamp from server.
+
// If a DMA channel or DSP is reading the other end then we have to update the readCounter.
if (mAudioEndpoint.isFreeRunning()) {
// Update data queue based on the timing model.
@@ -114,6 +131,13 @@
mAudioEndpoint.setDataReadCounter(estimatedReadCounter);
}
+ if (mNeedCatchUp.isRequested()) {
+ // Catch an MMAP pointer that is already advancing.
+ // This will avoid initial underruns caused by a slow cold start.
+ advanceClientToMatchServerPosition();
+ mNeedCatchUp.acknowledge();
+ }
+
// If the read index passed the write index then consider it an underrun.
if (mAudioEndpoint.getFullFramesAvailable() < 0) {
mXRunCount++;
@@ -153,9 +177,9 @@
// Calculate frame position based off of the writeCounter because
// the readCounter might have just advanced in the background,
// causing us to sleep until a later burst.
- int64_t nextReadPosition = mAudioEndpoint.getDataWriteCounter() + mFramesPerBurst
+ int64_t nextPosition = mAudioEndpoint.getDataWriteCounter() + mFramesPerBurst
- mAudioEndpoint.getBufferSizeInFrames();
- wakeTime = mClockModel.convertPositionToTime(nextReadPosition);
+ wakeTime = mClockModel.convertPositionToTime(nextPosition);
}
break;
default:
@@ -266,7 +290,6 @@
return framesWritten;
}
-
int64_t AudioStreamInternalPlay::getFramesRead()
{
int64_t framesReadHardware;
@@ -340,3 +363,10 @@
result, (int) isActive());
return NULL;
}
+
+//------------------------------------------------------------------------------
+// Implementation of PlayerBase
+status_t AudioStreamInternalPlay::doSetVolume() {
+ mVolumeRamp.setTarget(mStreamVolume * getDuckAndMuteVolume());
+ return android::NO_ERROR;
+}
diff --git a/media/libaaudio/src/client/AudioStreamInternalPlay.h b/media/libaaudio/src/client/AudioStreamInternalPlay.h
index e59d02c..d5c1b1e 100644
--- a/media/libaaudio/src/client/AudioStreamInternalPlay.h
+++ b/media/libaaudio/src/client/AudioStreamInternalPlay.h
@@ -54,8 +54,12 @@
aaudio_result_t requestPauseInternal();
+ void advanceClientToMatchServerPosition() override;
+
void onFlushFromServer() override;
+ android::status_t doSetVolume() override;
+
/**
* Low level write that will not block. It will just write as much as it can.
*
@@ -78,6 +82,9 @@
int32_t numFrames);
int64_t mLastFramesRead = 0; // used to prevent retrograde motion
+
+ LinearRamp mVolumeRamp;
+
};
} /* namespace aaudio */
diff --git a/media/libaaudio/src/client/IsochronousClockModel.cpp b/media/libaaudio/src/client/IsochronousClockModel.cpp
index c06c8a9..bac69f1 100644
--- a/media/libaaudio/src/client/IsochronousClockModel.cpp
+++ b/media/libaaudio/src/client/IsochronousClockModel.cpp
@@ -48,19 +48,26 @@
}
void IsochronousClockModel::start(int64_t nanoTime) {
- ALOGD("IsochronousClockModel::start(nanos = %lld)\n", (long long) nanoTime);
+ ALOGV("IsochronousClockModel::start(nanos = %lld)\n", (long long) nanoTime);
mMarkerNanoTime = nanoTime;
mState = STATE_STARTING;
}
void IsochronousClockModel::stop(int64_t nanoTime) {
- ALOGD("IsochronousClockModel::stop(nanos = %lld)\n", (long long) nanoTime);
+ ALOGV("IsochronousClockModel::stop(nanos = %lld)\n", (long long) nanoTime);
setPositionAndTime(convertTimeToPosition(nanoTime), nanoTime);
// TODO should we set position?
mState = STATE_STOPPED;
}
+bool IsochronousClockModel::isStarting() {
+ return mState == STATE_STARTING;
+}
+
void IsochronousClockModel::processTimestamp(int64_t framePosition, int64_t nanoTime) {
+// ALOGD("processTimestamp() - framePosition = %lld at nanoTime %llu",
+// (long long)framePosition,
+// (long long)nanoTime);
int64_t framesDelta = framePosition - mMarkerFramePosition;
int64_t nanosDelta = nanoTime - mMarkerNanoTime;
if (nanosDelta < 1000) {
@@ -70,9 +77,6 @@
// ALOGD("processTimestamp() - mMarkerFramePosition = %lld at mMarkerNanoTime %llu",
// (long long)mMarkerFramePosition,
// (long long)mMarkerNanoTime);
-// ALOGD("processTimestamp() - framePosition = %lld at nanoTime %llu",
-// (long long)framePosition,
-// (long long)nanoTime);
int64_t expectedNanosDelta = convertDeltaPositionToTime(framesDelta);
// ALOGD("processTimestamp() - expectedNanosDelta = %lld, nanosDelta = %llu",
@@ -116,6 +120,8 @@
default:
break;
}
+
+// ALOGD("processTimestamp() - mState = %d", mState);
}
void IsochronousClockModel::setSampleRate(int32_t sampleRate) {
diff --git a/media/libaaudio/src/client/IsochronousClockModel.h b/media/libaaudio/src/client/IsochronousClockModel.h
index 585f53a..7182376 100644
--- a/media/libaaudio/src/client/IsochronousClockModel.h
+++ b/media/libaaudio/src/client/IsochronousClockModel.h
@@ -36,6 +36,8 @@
void start(int64_t nanoTime);
void stop(int64_t nanoTime);
+ bool isStarting();
+
void processTimestamp(int64_t framePosition, int64_t nanoTime);
/**
diff --git a/media/libaaudio/src/core/AAudioAudio.cpp b/media/libaaudio/src/core/AAudioAudio.cpp
index 5089b00..1eaee81 100644
--- a/media/libaaudio/src/core/AAudioAudio.cpp
+++ b/media/libaaudio/src/core/AAudioAudio.cpp
@@ -24,15 +24,14 @@
#include <aaudio/AAudio.h>
#include <aaudio/AAudioTesting.h>
+#include "AudioClock.h"
#include "AudioStreamBuilder.h"
#include "AudioStream.h"
-#include "AudioClock.h"
+#include "binding/AAudioCommon.h"
#include "client/AudioStreamInternal.h"
-#include "HandleTracker.h"
using namespace aaudio;
-
// Macros for common code that includes a return.
// TODO Consider using do{}while(0) construct. I tried but it hung AndroidStudio
#define CONVERT_BUILDER_HANDLE_OR_RETURN() \
@@ -219,6 +218,7 @@
ALOGD("AAudioStreamBuilder_openStream() returns %d = %s for (%p) ----------------",
result, AAudio_convertResultToText(result), audioStream);
if (result == AAUDIO_OK) {
+ audioStream->registerPlayerBase();
*streamPtr = (AAudioStream*) audioStream;
} else {
*streamPtr = nullptr;
@@ -242,6 +242,7 @@
ALOGD("AAudioStream_close(%p)", stream);
if (audioStream != nullptr) {
audioStream->close();
+ audioStream->unregisterPlayerBase();
delete audioStream;
return AAUDIO_OK;
}
@@ -252,7 +253,7 @@
{
AudioStream *audioStream = convertAAudioStreamToAudioStream(stream);
ALOGD("AAudioStream_requestStart(%p) called --------------", stream);
- aaudio_result_t result = audioStream->requestStart();
+ aaudio_result_t result = audioStream->systemStart();
ALOGD("AAudioStream_requestStart(%p) returned %d ---------", stream, result);
return result;
}
@@ -261,7 +262,7 @@
{
AudioStream *audioStream = convertAAudioStreamToAudioStream(stream);
ALOGD("AAudioStream_requestPause(%p)", stream);
- return audioStream->requestPause();
+ return audioStream->systemPause();
}
AAUDIO_API aaudio_result_t AAudioStream_requestFlush(AAudioStream* stream)
@@ -275,7 +276,7 @@
{
AudioStream *audioStream = convertAAudioStreamToAudioStream(stream);
ALOGD("AAudioStream_requestStop(%p)", stream);
- return audioStream->requestStop();
+ return audioStream->systemStop();
}
AAUDIO_API aaudio_result_t AAudioStream_waitForStateChange(AAudioStream* stream,
diff --git a/media/libaaudio/src/core/AudioStream.cpp b/media/libaaudio/src/core/AudioStream.cpp
index 4f1cc37..8dcc37a 100644
--- a/media/libaaudio/src/core/AudioStream.cpp
+++ b/media/libaaudio/src/core/AudioStream.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#define LOG_TAG "AAudio"
+#define LOG_TAG "AAudioStream"
//#define LOG_NDEBUG 0
#include <utils/Log.h>
@@ -29,13 +29,26 @@
using namespace aaudio;
AudioStream::AudioStream()
- : mCallbackEnabled(false)
+ : mPlayerBase(new MyPlayerBase(this))
{
// mThread is a pthread_t of unknown size so we need memset.
memset(&mThread, 0, sizeof(mThread));
setPeriodNanoseconds(0);
}
+AudioStream::~AudioStream() {
+ ALOGD("destroying %p, state = %s", this, AAudio_convertStreamStateToText(getState()));
+ // If the stream is deleted when OPEN or in use then audio resources will leak.
+ // This would indicate an internal error. So we want to find this ASAP.
+ LOG_ALWAYS_FATAL_IF(!(getState() == AAUDIO_STREAM_STATE_CLOSED
+ || getState() == AAUDIO_STREAM_STATE_UNINITIALIZED
+ || getState() == AAUDIO_STREAM_STATE_DISCONNECTED),
+ "aaudio stream still in use, state = %s",
+ AAudio_convertStreamStateToText(getState()));
+
+ mPlayerBase->clearParentReference(); // remove reference to this AudioStream
+}
+
static const char *AudioStream_convertSharingModeToShortText(aaudio_sharing_mode_t sharingMode) {
const char *result;
switch (sharingMode) {
@@ -90,9 +103,6 @@
return AAUDIO_OK;
}
-AudioStream::~AudioStream() {
- close();
-}
aaudio_result_t AudioStream::waitForStateChange(aaudio_stream_state_t currentState,
aaudio_stream_state_t *nextState,
@@ -189,3 +199,38 @@
return err ? AAudioConvert_androidToAAudioResult(-errno) : mThreadRegistrationResult;
}
+
+#if AAUDIO_USE_VOLUME_SHAPER
+android::media::VolumeShaper::Status AudioStream::applyVolumeShaper(
+ const android::media::VolumeShaper::Configuration& configuration __unused,
+ const android::media::VolumeShaper::Operation& operation __unused) {
+ ALOGW("applyVolumeShaper() is not supported");
+ return android::media::VolumeShaper::Status::ok();
+}
+#endif
+
+AudioStream::MyPlayerBase::MyPlayerBase(AudioStream *parent) : mParent(parent) {
+}
+
+AudioStream::MyPlayerBase::~MyPlayerBase() {
+ ALOGV("MyPlayerBase::~MyPlayerBase(%p) deleted", this);
+}
+
+void AudioStream::MyPlayerBase::registerWithAudioManager() {
+ if (!mRegistered) {
+ init(android::PLAYER_TYPE_AAUDIO, AUDIO_USAGE_MEDIA);
+ mRegistered = true;
+ }
+}
+
+void AudioStream::MyPlayerBase::unregisterWithAudioManager() {
+ if (mRegistered) {
+ baseDestroy();
+ mRegistered = false;
+ }
+}
+
+
+void AudioStream::MyPlayerBase::destroy() {
+ unregisterWithAudioManager();
+}
diff --git a/media/libaaudio/src/core/AudioStream.h b/media/libaaudio/src/core/AudioStream.h
index ad18751..34202d2 100644
--- a/media/libaaudio/src/core/AudioStream.h
+++ b/media/libaaudio/src/core/AudioStream.h
@@ -21,10 +21,18 @@
#include <mutex>
#include <stdint.h>
#include <aaudio/AAudio.h>
+#include <binder/IServiceManager.h>
+#include <binder/Status.h>
+#include <utils/StrongPointer.h>
+#include "media/VolumeShaper.h"
+#include "media/PlayerBase.h"
#include "utility/AAudioUtilities.h"
#include "utility/MonotonicCounter.h"
+// Cannot get android::media::VolumeShaper to compile!
+#define AAUDIO_USE_VOLUME_SHAPER 0
+
namespace aaudio {
typedef void *(*aaudio_audio_thread_proc_t)(void *);
@@ -234,8 +242,132 @@
return AAUDIO_ERROR_UNIMPLEMENTED;
}
+ // This is used by the AudioManager to duck and mute the stream when changing audio focus.
+ void setDuckAndMuteVolume(float duckAndMuteVolume) {
+ mDuckAndMuteVolume = duckAndMuteVolume;
+ doSetVolume(); // apply this change
+ }
+
+ float getDuckAndMuteVolume() {
+ return mDuckAndMuteVolume;
+ }
+
+ // Implement this in the output subclasses.
+ virtual android::status_t doSetVolume() { return android::NO_ERROR; }
+
+#if AAUDIO_USE_VOLUME_SHAPER
+ virtual ::android::binder::Status applyVolumeShaper(
+ const ::android::media::VolumeShaper::Configuration& configuration __unused,
+ const ::android::media::VolumeShaper::Operation& operation __unused);
+#endif
+
+ /**
+ * Register this stream's PlayerBase with the AudioManager if needed.
+ * Only register output streams.
+ * This should only be called for client streams and not for streams
+ * that run in the service.
+ */
+ void registerPlayerBase() {
+ if (getDirection() == AAUDIO_DIRECTION_OUTPUT) {
+ mPlayerBase->registerWithAudioManager();
+ }
+ }
+
+ /**
+ * Unregister this stream's PlayerBase with the AudioManager.
+ * This will only unregister if already registered.
+ */
+ void unregisterPlayerBase() {
+ mPlayerBase->unregisterWithAudioManager();
+ }
+
+ // Pass start request through PlayerBase for tracking.
+ aaudio_result_t systemStart() {
+ mPlayerBase->start();
+ // Pass aaudio_result_t around the PlayerBase interface, which uses status__t.
+ return mPlayerBase->getResult();
+ }
+
+ aaudio_result_t systemPause() {
+ mPlayerBase->pause();
+ return mPlayerBase->getResult();
+ }
+
+ aaudio_result_t systemStop() {
+ mPlayerBase->stop();
+ return mPlayerBase->getResult();
+ }
+
protected:
+ // PlayerBase allows the system to control the stream.
+ // Calling through PlayerBase->start() notifies the AudioManager of the player state.
+ // The AudioManager also can start/stop a stream by calling mPlayerBase->playerStart().
+ // systemStart() ==> mPlayerBase->start() mPlayerBase->playerStart() ==> requestStart()
+ // \ /
+ // ------ AudioManager -------
+ class MyPlayerBase : public android::PlayerBase {
+ public:
+ explicit MyPlayerBase(AudioStream *parent);
+
+ virtual ~MyPlayerBase();
+
+ /**
+ * Register for volume changes and remote control.
+ */
+ void registerWithAudioManager();
+
+ /**
+ * UnRegister.
+ */
+ void unregisterWithAudioManager();
+
+ /**
+ * Just calls unregisterWithAudioManager().
+ */
+ void destroy() override;
+
+ void clearParentReference() { mParent = nullptr; }
+
+ android::status_t playerStart() override {
+ // mParent should NOT be null. So go ahead and crash if it is.
+ mResult = mParent->requestStart();
+ return AAudioConvert_aaudioToAndroidStatus(mResult);
+ }
+
+ android::status_t playerPause() override {
+ mResult = mParent->requestPause();
+ return AAudioConvert_aaudioToAndroidStatus(mResult);
+ }
+
+ android::status_t playerStop() override {
+ mResult = mParent->requestStop();
+ return AAudioConvert_aaudioToAndroidStatus(mResult);
+ }
+
+ android::status_t playerSetVolume() override {
+ // No pan and only left volume is taken into account from IPLayer interface
+ mParent->setDuckAndMuteVolume(mVolumeMultiplierL /* * mPanMultiplierL */);
+ return android::NO_ERROR;
+ }
+
+#if AAUDIO_USE_VOLUME_SHAPER
+ ::android::binder::Status applyVolumeShaper(
+ const ::android::media::VolumeShaper::Configuration& configuration,
+ const ::android::media::VolumeShaper::Operation& operation) {
+ return mParent->applyVolumeShaper(configuration, operation);
+ }
+#endif
+
+ aaudio_result_t getResult() {
+ return mResult;
+ }
+
+ private:
+ AudioStream *mParent;
+ aaudio_result_t mResult = AAUDIO_OK;
+ bool mRegistered = false;
+ };
/**
* This should not be called after the open() call.
@@ -275,7 +407,9 @@
std::mutex mStreamMutex;
- std::atomic<bool> mCallbackEnabled;
+ std::atomic<bool> mCallbackEnabled{false};
+
+ float mDuckAndMuteVolume = 1.0f;
protected:
@@ -288,6 +422,8 @@
}
private:
+ const android::sp<MyPlayerBase> mPlayerBase;
+
// These do not change after open().
int32_t mSamplesPerFrame = AAUDIO_UNSPECIFIED;
int32_t mSampleRate = AAUDIO_UNSPECIFIED;
diff --git a/media/libaaudio/src/legacy/AudioStreamLegacy.cpp b/media/libaaudio/src/legacy/AudioStreamLegacy.cpp
index 2816bac..ee2504d 100644
--- a/media/libaaudio/src/legacy/AudioStreamLegacy.cpp
+++ b/media/libaaudio/src/legacy/AudioStreamLegacy.cpp
@@ -21,6 +21,7 @@
#include <stdint.h>
#include <utils/String16.h>
#include <media/AudioTrack.h>
+#include <media/AudioTimestamp.h>
#include <aaudio/AAudio.h>
#include "core/AudioStream.h"
@@ -30,7 +31,8 @@
using namespace aaudio;
AudioStreamLegacy::AudioStreamLegacy()
- : AudioStream(), mDeviceCallback(new StreamDeviceCallback(this)) {
+ : AudioStream()
+ , mDeviceCallback(new StreamDeviceCallback(this)) {
}
AudioStreamLegacy::~AudioStreamLegacy() {
@@ -46,33 +48,51 @@
return AudioStreamLegacy_callback;
}
-// Implement FixedBlockProcessor
-int32_t AudioStreamLegacy::onProcessFixedBlock(uint8_t *buffer, int32_t numBytes) {
- int32_t frameCount = numBytes / getBytesPerFrame();
+int32_t AudioStreamLegacy::callDataCallbackFrames(uint8_t *buffer, int32_t numFrames) {
+ if (getDirection() == AAUDIO_DIRECTION_INPUT) {
+ // Increment before because we already got the data from the device.
+ incrementFramesRead(numFrames);
+ }
+
// Call using the AAudio callback interface.
AAudioStream_dataCallback appCallback = getDataCallbackProc();
- return (*appCallback)(
+ aaudio_data_callback_result_t callbackResult = (*appCallback)(
(AAudioStream *) this,
getDataCallbackUserData(),
buffer,
- frameCount);
+ numFrames);
+
+ if (callbackResult == AAUDIO_CALLBACK_RESULT_CONTINUE
+ && getDirection() == AAUDIO_DIRECTION_OUTPUT) {
+ // Increment after because we are going to write the data to the device.
+ incrementFramesWritten(numFrames);
+ }
+ return callbackResult;
+}
+
+// Implement FixedBlockProcessor
+int32_t AudioStreamLegacy::onProcessFixedBlock(uint8_t *buffer, int32_t numBytes) {
+ int32_t numFrames = numBytes / getBytesPerFrame();
+ return callDataCallbackFrames(buffer, numFrames);
}
void AudioStreamLegacy::processCallbackCommon(aaudio_callback_operation_t opcode, void *info) {
aaudio_data_callback_result_t callbackResult;
- if (!mCallbackEnabled.load()) {
- return;
- }
-
switch (opcode) {
case AAUDIO_CALLBACK_OPERATION_PROCESS_DATA: {
- if (getState() != AAUDIO_STREAM_STATE_DISCONNECTED) {
- // Note that this code assumes an AudioTrack::Buffer is the same as
- // AudioRecord::Buffer
- // TODO define our own AudioBuffer and pass it from the subclasses.
- AudioTrack::Buffer *audioBuffer = static_cast<AudioTrack::Buffer *>(info);
- if (audioBuffer->frameCount == 0) return;
+ checkForDisconnectRequest();
+
+ // Note that this code assumes an AudioTrack::Buffer is the same as
+ // AudioRecord::Buffer
+ // TODO define our own AudioBuffer and pass it from the subclasses.
+ AudioTrack::Buffer *audioBuffer = static_cast<AudioTrack::Buffer *>(info);
+ if (getState() == AAUDIO_STREAM_STATE_DISCONNECTED || !mCallbackEnabled.load()) {
+ audioBuffer->size = 0; // silence the buffer
+ } else {
+ if (audioBuffer->frameCount == 0) {
+ return;
+ }
// If the caller specified an exact size then use a block size adapter.
if (mBlockAdapter != nullptr) {
@@ -81,40 +101,28 @@
(uint8_t *) audioBuffer->raw, byteCount);
} else {
// Call using the AAudio callback interface.
- callbackResult = (*getDataCallbackProc())(
- (AAudioStream *) this,
- getDataCallbackUserData(),
- audioBuffer->raw,
- audioBuffer->frameCount
- );
+ callbackResult = callDataCallbackFrames((uint8_t *)audioBuffer->raw,
+ audioBuffer->frameCount);
}
if (callbackResult == AAUDIO_CALLBACK_RESULT_CONTINUE) {
audioBuffer->size = audioBuffer->frameCount * getBytesPerFrame();
- incrementClientFrameCounter(audioBuffer->frameCount);
} else {
audioBuffer->size = 0;
}
- if (updateStateMachine() == AAUDIO_OK) {
- break; // don't fall through
+ if (updateStateMachine() != AAUDIO_OK) {
+ forceDisconnect();
+ mCallbackEnabled.store(false);
}
}
}
- /// FALL THROUGH
+ break;
// Stream got rerouted so we disconnect.
- case AAUDIO_CALLBACK_OPERATION_DISCONNECTED: {
- setState(AAUDIO_STREAM_STATE_DISCONNECTED);
+ case AAUDIO_CALLBACK_OPERATION_DISCONNECTED:
ALOGD("processCallbackCommon() stream disconnected");
- if (getErrorCallbackProc() != nullptr) {
- (*getErrorCallbackProc())(
- (AAudioStream *) this,
- getErrorCallbackUserData(),
- AAUDIO_ERROR_DISCONNECTED
- );
- }
+ forceDisconnect();
mCallbackEnabled.store(false);
- }
break;
default:
@@ -122,6 +130,30 @@
}
}
+
+
+void AudioStreamLegacy::checkForDisconnectRequest() {
+ if (mRequestDisconnect.isRequested()) {
+ ALOGD("checkForDisconnectRequest() mRequestDisconnect acknowledged");
+ forceDisconnect();
+ mRequestDisconnect.acknowledge();
+ mCallbackEnabled.store(false);
+ }
+}
+
+void AudioStreamLegacy::forceDisconnect() {
+ if (getState() != AAUDIO_STREAM_STATE_DISCONNECTED) {
+ setState(AAUDIO_STREAM_STATE_DISCONNECTED);
+ if (getErrorCallbackProc() != nullptr) {
+ (*getErrorCallbackProc())(
+ (AAudioStream *) this,
+ getErrorCallbackUserData(),
+ AAUDIO_ERROR_DISCONNECTED
+ );
+ }
+ }
+}
+
aaudio_result_t AudioStreamLegacy::getBestTimestamp(clockid_t clockId,
int64_t *framePosition,
int64_t *timeNanoseconds,
@@ -139,8 +171,23 @@
return AAUDIO_ERROR_ILLEGAL_ARGUMENT;
break;
}
- status_t status = extendedTimestamp->getBestTimestamp(framePosition, timeNanoseconds, timebase);
- return AAudioConvert_androidToAAudioResult(status);
+ ExtendedTimestamp::Location location = ExtendedTimestamp::Location::LOCATION_INVALID;
+ int64_t localPosition;
+ status_t status = extendedTimestamp->getBestTimestamp(&localPosition, timeNanoseconds,
+ timebase, &location);
+ // use MonotonicCounter to prevent retrograde motion.
+ mTimestampPosition.update32((int32_t)localPosition);
+ *framePosition = mTimestampPosition.get();
+
+// ALOGD("getBestTimestamp() fposition: server = %6lld, kernel = %6lld, location = %d",
+// (long long) extendedTimestamp->mPosition[ExtendedTimestamp::Location::LOCATION_SERVER],
+// (long long) extendedTimestamp->mPosition[ExtendedTimestamp::Location::LOCATION_KERNEL],
+// (int)location);
+ if (status == WOULD_BLOCK) {
+ return AAUDIO_ERROR_INVALID_STATE;
+ } else {
+ return AAudioConvert_androidToAAudioResult(status);
+ }
}
void AudioStreamLegacy::onAudioDeviceUpdate(audio_port_handle_t deviceId)
@@ -148,15 +195,18 @@
ALOGD("onAudioDeviceUpdate() deviceId %d", (int)deviceId);
if (getDeviceId() != AAUDIO_UNSPECIFIED && getDeviceId() != deviceId &&
getState() != AAUDIO_STREAM_STATE_DISCONNECTED) {
- setState(AAUDIO_STREAM_STATE_DISCONNECTED);
- // if we have a data callback and the stream is active, send the error callback from
- // data callback thread when it sees the DISCONNECTED state
- if (!isDataCallbackActive() && getErrorCallbackProc() != nullptr) {
- (*getErrorCallbackProc())(
- (AAudioStream *) this,
- getErrorCallbackUserData(),
- AAUDIO_ERROR_DISCONNECTED
- );
+ // Note that isDataCallbackActive() is affected by state so call it before DISCONNECTING.
+ // If we have a data callback and the stream is active, then ask the data callback
+ // to DISCONNECT and call the error callback.
+ if (isDataCallbackActive()) {
+ ALOGD("onAudioDeviceUpdate() request DISCONNECT in data callback due to device change");
+ // If the stream is stopped before the data callback has a chance to handle the
+ // request then the requestStop() and requestPause() methods will handle it after
+ // the callback has stopped.
+ mRequestDisconnect.request();
+ } else {
+ ALOGD("onAudioDeviceUpdate() DISCONNECT the stream now");
+ forceDisconnect();
}
}
setDeviceId(deviceId);
diff --git a/media/libaaudio/src/legacy/AudioStreamLegacy.h b/media/libaaudio/src/legacy/AudioStreamLegacy.h
index d2ef3c7..7e28579 100644
--- a/media/libaaudio/src/legacy/AudioStreamLegacy.h
+++ b/media/libaaudio/src/legacy/AudioStreamLegacy.h
@@ -24,6 +24,7 @@
#include "AudioStream.h"
#include "AAudioLegacy.h"
+#include "utility/AAudioUtilities.h"
#include "utility/FixedBlockAdapter.h"
namespace aaudio {
@@ -63,6 +64,8 @@
aaudio_legacy_callback_t getLegacyCallback();
+ int32_t callDataCallbackFrames(uint8_t *buffer, int32_t numFrames);
+
// This is public so it can be called from the C callback function.
// This is called from the AudioTrack/AudioRecord client.
virtual void processCallback(int event, void *info) = 0;
@@ -109,6 +112,10 @@
void onAudioDeviceUpdate(audio_port_handle_t deviceId);
+ void checkForDisconnectRequest();
+
+ void forceDisconnect();
+
void onStart() { mCallbackEnabled.store(true); }
void onStop() { mCallbackEnabled.store(false); }
@@ -122,11 +129,14 @@
MonotonicCounter mFramesWritten;
MonotonicCounter mFramesRead;
+ MonotonicCounter mTimestampPosition;
FixedBlockAdapter *mBlockAdapter = nullptr;
aaudio_wrapping_frames_t mPositionWhenStarting = 0;
int32_t mCallbackBufferSize = 0;
const android::sp<StreamDeviceCallback> mDeviceCallback;
+
+ AtomicRequestor mRequestDisconnect;
};
} /* namespace aaudio */
diff --git a/media/libaaudio/src/legacy/AudioStreamRecord.cpp b/media/libaaudio/src/legacy/AudioStreamRecord.cpp
index 041280d..bc6e60c 100644
--- a/media/libaaudio/src/legacy/AudioStreamRecord.cpp
+++ b/media/libaaudio/src/legacy/AudioStreamRecord.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#define LOG_TAG "AAudio"
+#define LOG_TAG "AudioStreamRecord"
//#define LOG_NDEBUG 0
#include <utils/Log.h>
@@ -181,11 +181,12 @@
{
// TODO add close() or release() to AudioRecord API then call it from here
if (getState() != AAUDIO_STREAM_STATE_CLOSED) {
+ mAudioRecord->removeAudioDeviceCallback(mDeviceCallback);
mAudioRecord.clear();
setState(AAUDIO_STREAM_STATE_CLOSED);
}
mFixedBlockWriter.close();
- return AAUDIO_OK;
+ return AudioStream::close();
}
void AudioStreamRecord::processCallback(int event, void *info) {
@@ -233,8 +234,11 @@
onStop();
setState(AAUDIO_STREAM_STATE_STOPPING);
incrementFramesWritten(getFramesRead() - getFramesWritten()); // TODO review
+ mTimestampPosition.set(getFramesRead());
mAudioRecord->stop();
mFramesRead.reset32();
+ mTimestampPosition.reset32();
+ checkForDisconnectRequest();
return AAUDIO_OK;
}
@@ -334,7 +338,9 @@
int64_t *timeNanoseconds) {
ExtendedTimestamp extendedTimestamp;
status_t status = mAudioRecord->getTimestamp(&extendedTimestamp);
- if (status != NO_ERROR) {
+ if (status == WOULD_BLOCK) {
+ return AAUDIO_ERROR_INVALID_STATE;
+ } else if (status != NO_ERROR) {
return AAudioConvert_androidToAAudioResult(status);
}
return getBestTimestamp(clockId, framePosition, timeNanoseconds, &extendedTimestamp);
diff --git a/media/libaaudio/src/legacy/AudioStreamTrack.cpp b/media/libaaudio/src/legacy/AudioStreamTrack.cpp
index 155362c..0e9aaef 100644
--- a/media/libaaudio/src/legacy/AudioStreamTrack.cpp
+++ b/media/libaaudio/src/legacy/AudioStreamTrack.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#define LOG_TAG "AAudio"
+#define LOG_TAG "AudioStreamTrack"
//#define LOG_NDEBUG 0
#include <utils/Log.h>
@@ -115,7 +115,7 @@
ALOGD("AudioStreamTrack::open(), request notificationFrames = %d, frameCount = %u",
notificationFrames, (uint)frameCount);
- mAudioTrack = new AudioTrack();
+ mAudioTrack = new AudioTrack(); // TODO review
if (getDeviceId() != AAUDIO_UNSPECIFIED) {
mAudioTrack->setOutputDevice(getDeviceId());
}
@@ -143,8 +143,7 @@
return AAudioConvert_androidToAAudioResult(status);
}
- //TrackPlayerBase init
- init(mAudioTrack.get(), PLAYER_TYPE_AAUDIO, AUDIO_USAGE_MEDIA);
+ doSetVolume();
// Get the actual values from the AudioTrack.
setSamplesPerFrame(mAudioTrack->channelCount());
@@ -199,7 +198,7 @@
aaudio_result_t AudioStreamTrack::close()
{
if (getState() != AAUDIO_STREAM_STATE_CLOSED) {
- destroy();
+ mAudioTrack->removeAudioDeviceCallback(mDeviceCallback);
setState(AAUDIO_STREAM_STATE_CLOSED);
}
mFixedBlockReader.close();
@@ -224,8 +223,7 @@
return;
}
-aaudio_result_t AudioStreamTrack::requestStart()
-{
+aaudio_result_t AudioStreamTrack::requestStart() {
std::lock_guard<std::mutex> lock(mStreamMutex);
if (mAudioTrack.get() == nullptr) {
@@ -238,7 +236,7 @@
return AAudioConvert_androidToAAudioResult(err);
}
- err = startWithStatus();
+ err = mAudioTrack->start();
if (err != OK) {
return AAudioConvert_androidToAAudioResult(err);
} else {
@@ -248,12 +246,11 @@
return AAUDIO_OK;
}
-aaudio_result_t AudioStreamTrack::requestPause()
-{
+aaudio_result_t AudioStreamTrack::requestPause() {
std::lock_guard<std::mutex> lock(mStreamMutex);
if (mAudioTrack.get() == nullptr) {
- ALOGE("AudioStreamTrack::requestPause() no AudioTrack");
+ ALOGE("requestPause() no AudioTrack");
return AAUDIO_ERROR_INVALID_STATE;
} else if (getState() != AAUDIO_STREAM_STATE_STARTING
&& getState() != AAUDIO_STREAM_STATE_STARTED) {
@@ -263,7 +260,8 @@
}
onStop();
setState(AAUDIO_STREAM_STATE_PAUSING);
- pause();
+ mAudioTrack->pause();
+ checkForDisconnectRequest();
status_t err = mAudioTrack->getPosition(&mPositionWhenPausing);
if (err != OK) {
return AAudioConvert_androidToAAudioResult(err);
@@ -285,6 +283,7 @@
incrementFramesRead(getFramesWritten() - getFramesRead());
mAudioTrack->flush();
mFramesWritten.reset32();
+ mTimestampPosition.reset32();
return AAUDIO_OK;
}
@@ -298,8 +297,11 @@
onStop();
setState(AAUDIO_STREAM_STATE_STOPPING);
incrementFramesRead(getFramesWritten() - getFramesRead()); // TODO review
- stop();
+ mTimestampPosition.set(getFramesWritten());
mFramesWritten.reset32();
+ mTimestampPosition.reset32();
+ mAudioTrack->stop();
+ checkForDisconnectRequest();
return AAUDIO_OK;
}
@@ -444,8 +446,59 @@
int64_t *timeNanoseconds) {
ExtendedTimestamp extendedTimestamp;
status_t status = mAudioTrack->getTimestamp(&extendedTimestamp);
- if (status != NO_ERROR) {
+ if (status == WOULD_BLOCK) {
+ return AAUDIO_ERROR_INVALID_STATE;
+ } if (status != NO_ERROR) {
return AAudioConvert_androidToAAudioResult(status);
}
- return getBestTimestamp(clockId, framePosition, timeNanoseconds, &extendedTimestamp);
+ int64_t position = 0;
+ int64_t nanoseconds = 0;
+ aaudio_result_t result = getBestTimestamp(clockId, &position,
+ &nanoseconds, &extendedTimestamp);
+ if (result == AAUDIO_OK) {
+ if (position < getFramesWritten()) {
+ *framePosition = position;
+ *timeNanoseconds = nanoseconds;
+ return result;
+ } else {
+ return AAUDIO_ERROR_INVALID_STATE; // TODO review, documented but not consistent
+ }
+ }
+ return result;
}
+
+status_t AudioStreamTrack::doSetVolume() {
+ status_t status = NO_INIT;
+ if (mAudioTrack.get() != nullptr) {
+ float volume = getDuckAndMuteVolume();
+ mAudioTrack->setVolume(volume, volume);
+ status = NO_ERROR;
+ }
+ return status;
+}
+
+#if AAUDIO_USE_VOLUME_SHAPER
+
+using namespace android::media::VolumeShaper;
+
+binder::Status AudioStreamTrack::applyVolumeShaper(
+ const VolumeShaper::Configuration& configuration,
+ const VolumeShaper::Operation& operation) {
+
+ sp<VolumeShaper::Configuration> spConfiguration = new VolumeShaper::Configuration(configuration);
+ sp<VolumeShaper::Operation> spOperation = new VolumeShaper::Operation(operation);
+
+ if (mAudioTrack.get() != nullptr) {
+ ALOGD("applyVolumeShaper() from IPlayer");
+ binder::Status status = mAudioTrack->applyVolumeShaper(spConfiguration, spOperation);
+ if (status < 0) { // a non-negative value is the volume shaper id.
+ ALOGE("applyVolumeShaper() failed with status %d", status);
+ }
+ return binder::Status::fromStatusT(status);
+ } else {
+ ALOGD("applyVolumeShaper()"
+ " no AudioTrack for volume control from IPlayer");
+ return binder::Status::ok();
+ }
+}
+#endif
diff --git a/media/libaaudio/src/legacy/AudioStreamTrack.h b/media/libaaudio/src/legacy/AudioStreamTrack.h
index 3230ac8..a871db4 100644
--- a/media/libaaudio/src/legacy/AudioStreamTrack.h
+++ b/media/libaaudio/src/legacy/AudioStreamTrack.h
@@ -19,6 +19,7 @@
#include <math.h>
#include <media/TrackPlayerBase.h>
+#include <media/AudioTrack.h>
#include <aaudio/AAudio.h>
#include "AudioStreamBuilder.h"
@@ -32,7 +33,7 @@
/**
* Internal stream that uses the legacy AudioTrack path.
*/
-class AudioStreamTrack : public AudioStreamLegacy, public android::TrackPlayerBase {
+class AudioStreamTrack : public AudioStreamLegacy {
public:
AudioStreamTrack();
@@ -76,8 +77,18 @@
return incrementFramesWritten(frames);
}
+ android::status_t doSetVolume() override;
+
+#if AAUDIO_USE_VOLUME_SHAPER
+ virtual android::binder::Status applyVolumeShaper(
+ const android::media::VolumeShaper::Configuration& configuration,
+ const android::media::VolumeShaper::Operation& operation) override;
+#endif
+
private:
+ android::sp<android::AudioTrack> mAudioTrack;
+
// adapts between variable sized blocks and fixed size blocks
FixedBlockReader mFixedBlockReader;
diff --git a/media/libaaudio/src/utility/AAudioUtilities.h b/media/libaaudio/src/utility/AAudioUtilities.h
index b0c6c94..3afa976 100644
--- a/media/libaaudio/src/utility/AAudioUtilities.h
+++ b/media/libaaudio/src/utility/AAudioUtilities.h
@@ -272,8 +272,7 @@
class SimpleDoubleBuffer {
public:
SimpleDoubleBuffer()
- : mValues()
- , mCounter(0) {}
+ : mValues() {}
__attribute__((no_sanitize("integer")))
void write(T value) {
@@ -282,6 +281,14 @@
mCounter++; // Increment AFTER updating storage, OK if it wraps.
}
+ /**
+ * This should only be called by the same thread that calls write() or when
+ * no other thread is calling write.
+ */
+ void clear() {
+ mCounter.store(0);
+ }
+
T read() const {
T result;
int before;
@@ -293,7 +300,7 @@
int index = (before & 1) ^ 1;
result = mValues[index];
after = mCounter.load();
- } while ((after != before) && --timeout > 0);
+ } while ((after != before) && (after > 0) && (--timeout > 0));
return result;
}
@@ -306,7 +313,7 @@
private:
T mValues[2];
- std::atomic<int> mCounter;
+ std::atomic<int> mCounter{0};
};
class Timestamp {
@@ -328,4 +335,35 @@
int64_t mNanoseconds;
};
+
+/**
+ * Pass a request to another thread.
+ * This is used when one thread, A, wants another thread, B, to do something.
+ * A naive approach would be for A to set a flag and for B to clear it when done.
+ * But that creates a race condition. This technique avoids the race condition.
+ *
+ * Assumes only one requester and one acknowledger.
+ */
+class AtomicRequestor {
+public:
+
+ __attribute__((no_sanitize("integer")))
+ void request() {
+ mRequested++;
+ }
+
+ __attribute__((no_sanitize("integer")))
+ bool isRequested() {
+ return (mRequested.load() - mAcknowledged.load()) > 0;
+ }
+
+ __attribute__((no_sanitize("integer")))
+ void acknowledge() {
+ mAcknowledged++;
+ }
+
+private:
+ std::atomic<int> mRequested{0};
+ std::atomic<int> mAcknowledged{0};
+};
#endif //UTILITY_AAUDIO_UTILITIES_H
diff --git a/media/libaaudio/src/utility/HandleTracker.cpp b/media/libaaudio/src/utility/HandleTracker.cpp
deleted file mode 100644
index 35ce95a..0000000
--- a/media/libaaudio/src/utility/HandleTracker.cpp
+++ /dev/null
@@ -1,276 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-#define LOG_TAG "AAudio"
-//#define LOG_NDEBUG 0
-#include <utils/Log.h>
-
-#include <assert.h>
-#include <functional>
-#include <iomanip>
-#include <new>
-#include <sstream>
-#include <stdint.h>
-#include <utils/Mutex.h>
-
-#include <aaudio/AAudio.h>
-#include "AAudioUtilities.h"
-#include "HandleTracker.h"
-
-using android::Mutex;
-
-// Handle format is: tgggiiii
-// where each letter is 4 bits, t=type, g=generation, i=index
-
-#define TYPE_SIZE 4
-#define GENERATION_SIZE 12
-#define INDEX_SIZE 16
-
-#define GENERATION_INVALID 0
-#define GENERATION_SHIFT INDEX_SIZE
-
-#define TYPE_MASK ((1 << TYPE_SIZE) - 1)
-#define GENERATION_MASK ((1 << GENERATION_SIZE) - 1)
-#define INDEX_MASK ((1 << INDEX_SIZE) - 1)
-
-#define SLOT_UNAVAILABLE (-1)
-
-// Error if handle is negative so type is limited to bottom half.
-#define HANDLE_INVALID_TYPE TYPE_MASK
-
-static_assert(HANDLE_TRACKER_MAX_TYPES == (1 << (TYPE_SIZE - 1)),
- "Mismatch between header and cpp.");
-static_assert(HANDLE_TRACKER_MAX_HANDLES == (1 << (INDEX_SIZE)),
- "Mismatch between header and cpp.");
-
-HandleTracker::HandleTracker(uint32_t maxHandles)
- : mMaxHandleCount(maxHandles)
- , mHandleHeaders(nullptr)
-{
- assert(maxHandles <= HANDLE_TRACKER_MAX_HANDLES);
- // Allocate arrays to hold addresses and validation info.
- mHandleAddresses = (handle_tracker_address_t *)
- new(std::nothrow) handle_tracker_address_t[maxHandles];
- if (mHandleAddresses != nullptr) {
- mHandleHeaders = new(std::nothrow) handle_tracker_header_t[maxHandles];
-
- if (mHandleHeaders != nullptr) {
- handle_tracker_header_t initialHeader = buildHeader(0, 1);
- // Initialize linked list of free nodes. nullptr terminated.
- for (uint32_t i = 0; i < (maxHandles - 1); i++) {
- mHandleAddresses[i] = &mHandleAddresses[i + 1]; // point to next node
- mHandleHeaders[i] = initialHeader;
- }
- mNextFreeAddress = &mHandleAddresses[0];
- mHandleAddresses[maxHandles - 1] = nullptr;
- mHandleHeaders[maxHandles - 1] = 0;
- } else {
- delete[] mHandleAddresses; // so the class appears uninitialized
- mHandleAddresses = nullptr;
- }
- }
-}
-
-HandleTracker::~HandleTracker()
-{
- Mutex::Autolock _l(mLock);
- delete[] mHandleAddresses;
- delete[] mHandleHeaders;
- mHandleAddresses = nullptr;
-}
-
-bool HandleTracker::isInitialized() const {
- return mHandleAddresses != nullptr;
-}
-
-
-
-std::string HandleTracker::dump() const {
- if (!isInitialized()) {
- return "HandleTracker is not initialized\n";
- }
-
- std::stringstream result;
- const bool isLocked = AAudio_tryUntilTrue(
- [this]()->bool { return mLock.tryLock(); } /* f */,
- 50 /* times */,
- 20 /* sleepMs */);
- if (!isLocked) {
- result << "HandleTracker may be deadlocked\n";
- }
-
- result << "HandleTracker:\n";
- result << " HandleHeaders:\n";
- // atLineStart() can be changed to support an arbitrary line breaking algorithm;
- // it should return true when a new line starts.
- // For simplicity, we will use a constant 16 items per line.
- const auto atLineStart = [](int index) -> bool {
- // Magic constant of 0xf used for mask to detect start every 16 items.
- return (index & 0xf) == 0; };
- const auto atLineEnd = [this, &atLineStart](int index) -> bool {
- return atLineStart(index + 1) || index == mMaxHandleCount - 1; };
-
- for (int i = 0; i < mMaxHandleCount; ++i) {
- if (atLineStart(i)) {
- result << " ";
- }
- result << std::hex << std::setw(4) << std::setfill('0') << mHandleHeaders[i]
- << (atLineEnd(i) ? "\n" : " ");
- }
-
- if (isLocked) {
- mLock.unlock();
- }
- return result.str();
-}
-
-handle_tracker_slot_t HandleTracker::allocateSlot_l() {
- void **allocated = mNextFreeAddress;
- if (allocated == nullptr) {
- return SLOT_UNAVAILABLE;
- }
- // Remove this slot from the head of the linked list.
- mNextFreeAddress = (void **) *allocated;
- return (allocated - mHandleAddresses);
-}
-
-handle_tracker_generation_t HandleTracker::nextGeneration_l(handle_tracker_slot_t index) {
- handle_tracker_generation_t generation = (mHandleHeaders[index] + 1) & GENERATION_MASK;
- // Avoid generation zero so that 0x0 is not a valid handle.
- if (generation == GENERATION_INVALID) {
- generation++;
- }
- return generation;
-}
-
-aaudio_handle_t HandleTracker::put(handle_tracker_type_t type, void *address)
-{
- if (type < 0 || type >= HANDLE_TRACKER_MAX_TYPES) {
- return static_cast<aaudio_handle_t>(AAUDIO_ERROR_OUT_OF_RANGE);
- }
- if (!isInitialized()) {
- return static_cast<aaudio_handle_t>(AAUDIO_ERROR_NO_MEMORY);
- }
-
- Mutex::Autolock _l(mLock);
-
- // Find an empty slot.
- handle_tracker_slot_t index = allocateSlot_l();
- if (index == SLOT_UNAVAILABLE) {
- ALOGE("HandleTracker::put() no room for more handles");
- return static_cast<aaudio_handle_t>(AAUDIO_ERROR_NO_FREE_HANDLES);
- }
-
- // Cycle the generation counter so stale handles can be detected.
- handle_tracker_generation_t generation = nextGeneration_l(index); // reads header table
- handle_tracker_header_t inputHeader = buildHeader(type, generation);
-
- // These two writes may need to be observed by other threads or cores during get().
- mHandleHeaders[index] = inputHeader;
- mHandleAddresses[index] = address;
- // TODO use store release to enforce memory order with get()
-
- // Generate a handle.
- aaudio_handle_t handle = buildHandle(inputHeader, index);
-
- ALOGV("HandleTracker::put(%p) returns 0x%08x", address, handle);
- return handle;
-}
-
-handle_tracker_slot_t HandleTracker::handleToIndex(handle_tracker_type_t type,
- aaudio_handle_t handle) const
-{
- // Validate the handle.
- handle_tracker_slot_t index = extractIndex(handle);
- if (index >= mMaxHandleCount) {
- ALOGE("HandleTracker::handleToIndex() invalid handle = 0x%08X", handle);
- return static_cast<aaudio_handle_t>(AAUDIO_ERROR_INVALID_HANDLE);
- }
- handle_tracker_generation_t handleGeneration = extractGeneration(handle);
- handle_tracker_header_t inputHeader = buildHeader(type, handleGeneration);
- // We do not need to synchronize this access to mHandleHeaders because it is constant for
- // the lifetime of the handle.
- if (inputHeader != mHandleHeaders[index]) {
- ALOGE("HandleTracker::handleToIndex() inputHeader = 0x%08x != mHandleHeaders[%d] = 0x%08x",
- inputHeader, index, mHandleHeaders[index]);
- return static_cast<aaudio_handle_t>(AAUDIO_ERROR_INVALID_HANDLE);
- }
- return index;
-}
-
-handle_tracker_address_t HandleTracker::get(handle_tracker_type_t type, aaudio_handle_t handle) const
-{
- if (!isInitialized()) {
- return nullptr;
- }
- handle_tracker_slot_t index = handleToIndex(type, handle);
- if (index >= 0) {
- // We do not need to synchronize this access to mHandleHeaders because this slot
- // is allocated and, therefore, not part of the linked list of free slots.
- return mHandleAddresses[index];
- } else {
- return nullptr;
- }
-}
-
-handle_tracker_address_t HandleTracker::remove(handle_tracker_type_t type, aaudio_handle_t handle) {
- if (!isInitialized()) {
- return nullptr;
- }
-
- Mutex::Autolock _l(mLock);
-
- handle_tracker_slot_t index = handleToIndex(type,handle);
- if (index >= 0) {
- handle_tracker_address_t address = mHandleAddresses[index];
-
- // Invalidate the header type but preserve the generation count.
- handle_tracker_generation_t generation = mHandleHeaders[index] & GENERATION_MASK;
- handle_tracker_header_t inputHeader = buildHeader(
- (handle_tracker_type_t) HANDLE_INVALID_TYPE, generation);
- mHandleHeaders[index] = inputHeader;
-
- // Add this slot to the head of the linked list.
- mHandleAddresses[index] = mNextFreeAddress;
- mNextFreeAddress = (handle_tracker_address_t *) &mHandleAddresses[index];
- return address;
- } else {
- return nullptr;
- }
-}
-
-aaudio_handle_t HandleTracker::buildHandle(handle_tracker_header_t typeGeneration,
- handle_tracker_slot_t index) {
- return (aaudio_handle_t)((typeGeneration << GENERATION_SHIFT) | (index & INDEX_MASK));
-}
-
-handle_tracker_header_t HandleTracker::buildHeader(handle_tracker_type_t type,
- handle_tracker_generation_t generation)
-{
- return (handle_tracker_header_t) (((type & TYPE_MASK) << GENERATION_SIZE)
- | (generation & GENERATION_MASK));
-}
-
-handle_tracker_slot_t HandleTracker::extractIndex(aaudio_handle_t handle)
-{
- return handle & INDEX_MASK;
-}
-
-handle_tracker_generation_t HandleTracker::extractGeneration(aaudio_handle_t handle)
-{
- return (handle >> GENERATION_SHIFT) & GENERATION_MASK;
-}
diff --git a/media/libaaudio/src/utility/HandleTracker.h b/media/libaaudio/src/utility/HandleTracker.h
deleted file mode 100644
index a4c51c0..0000000
--- a/media/libaaudio/src/utility/HandleTracker.h
+++ /dev/null
@@ -1,164 +0,0 @@
-/*
- * Copyright 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 UTILITY_HANDLE_TRACKER_H
-#define UTILITY_HANDLE_TRACKER_H
-
-#include <stdint.h>
-#include <string>
-#include <utils/Mutex.h>
-
-typedef int32_t aaudio_handle_t;
-typedef int32_t handle_tracker_type_t; // what kind of handle
-typedef int32_t handle_tracker_slot_t; // index in allocation table
-typedef int32_t handle_tracker_generation_t; // incremented when slot used
-typedef uint16_t handle_tracker_header_t; // combines type and generation
-typedef void *handle_tracker_address_t; // address of something that is stored here
-
-#define HANDLE_TRACKER_MAX_TYPES (1 << 3)
-#define HANDLE_TRACKER_MAX_HANDLES (1 << 16)
-
-/**
- * Represent Objects using an integer handle that can be used with Java.
- * This also makes the 'C' ABI more robust.
- *
- * Note that this should only be called from a single thread.
- * If you call it from more than one thread then you need to use your own mutex.
- */
-class HandleTracker {
-
-public:
- /**
- * @param maxHandles cannot exceed HANDLE_TRACKER_MAX_HANDLES
- */
- HandleTracker(uint32_t maxHandles = 256);
- virtual ~HandleTracker();
-
- /**
- * Don't use if this returns false;
- * @return true if the internal allocation succeeded
- */
- bool isInitialized() const;
-
- /**
- * Returns HandleTracker information.
- *
- * Will attempt to get the object lock, but will proceed
- * even if it cannot.
- *
- * Each line of information ends with a newline.
- *
- * @return a string representing the HandleTracker info.
- */
- std::string dump() const;
-
- /**
- * Store a pointer and return a handle that can be used to retrieve the pointer.
- *
- * It is safe to call put() or remove() from multiple threads.
- *
- * @param expectedType the type of the object to be tracked
- * @param address pointer to be converted to a handle
- * @return a valid handle or a negative error
- */
- aaudio_handle_t put(handle_tracker_type_t expectedType, handle_tracker_address_t address);
-
- /**
- * Get the original pointer associated with the handle.
- * The handle will be validated to prevent stale handles from being reused.
- * Note that the validation is designed to prevent common coding errors and not
- * to prevent deliberate hacking.
- *
- * @param expectedType shouldmatch the type we passed to put()
- * @param handle to be converted to a pointer
- * @return address associated with handle or nullptr
- */
- handle_tracker_address_t get(handle_tracker_type_t expectedType, aaudio_handle_t handle) const;
-
- /**
- * Free up the storage associated with the handle.
- * Subsequent attempts to use the handle will fail.
- *
- * Do NOT remove() a handle while get() is being called for the same handle from another thread.
- *
- * @param expectedType shouldmatch the type we passed to put()
- * @param handle to be removed from tracking
- * @return address associated with handle or nullptr if not found
- */
- handle_tracker_address_t remove(handle_tracker_type_t expectedType, aaudio_handle_t handle);
-
-private:
- const int32_t mMaxHandleCount; // size of array
- // This address is const after initialization.
- handle_tracker_address_t * mHandleAddresses; // address of objects or a free linked list node
- // This address is const after initialization.
- handle_tracker_header_t * mHandleHeaders; // combination of type and generation
- // head of the linked list of free nodes in mHandleAddresses
- handle_tracker_address_t * mNextFreeAddress;
-
- // This Mutex protects the linked list of free nodes.
- // The list is managed using mHandleAddresses and mNextFreeAddress.
- // The data in mHandleHeaders is only changed by put() and remove().
- mutable android::Mutex mLock;
-
- /**
- * Pull slot off of a list of empty slots.
- * @return index or a negative error
- */
- handle_tracker_slot_t allocateSlot_l();
-
- /**
- * Increment the generation for the slot, avoiding zero.
- */
- handle_tracker_generation_t nextGeneration_l(handle_tracker_slot_t index);
-
- /**
- * Validate the handle and return the corresponding index.
- * @return slot index or a negative error
- */
- handle_tracker_slot_t handleToIndex(aaudio_handle_t handle, handle_tracker_type_t type) const;
-
- /**
- * Construct a handle from a header and an index.
- * @param header combination of a type and a generation
- * @param index slot index returned from allocateSlot
- * @return handle or a negative error
- */
- static aaudio_handle_t buildHandle(handle_tracker_header_t header, handle_tracker_slot_t index);
-
- /**
- * Combine a type and a generation field into a header.
- */
- static handle_tracker_header_t buildHeader(handle_tracker_type_t type,
- handle_tracker_generation_t generation);
-
- /**
- * Extract the index from a handle.
- * Does not validate the handle.
- * @return index associated with a handle
- */
- static handle_tracker_slot_t extractIndex(aaudio_handle_t handle);
-
- /**
- * Extract the generation from a handle.
- * Does not validate the handle.
- * @return generation associated with a handle
- */
- static handle_tracker_generation_t extractGeneration(aaudio_handle_t handle);
-
-};
-
-#endif //UTILITY_HANDLE_TRACKER_H
diff --git a/media/libaaudio/src/utility/MonotonicCounter.h b/media/libaaudio/src/utility/MonotonicCounter.h
index 81d7f89..13c92a2 100644
--- a/media/libaaudio/src/utility/MonotonicCounter.h
+++ b/media/libaaudio/src/utility/MonotonicCounter.h
@@ -41,6 +41,13 @@
}
/**
+ * set the current value of the counter
+ */
+ void set(int64_t counter) {
+ mCounter64 = counter;
+ }
+
+ /**
* Advance the counter if delta is positive.
* @return current value of the counter
*/
diff --git a/media/libaaudio/tests/Android.mk b/media/libaaudio/tests/Android.mk
index 4402919..37b010d 100644
--- a/media/libaaudio/tests/Android.mk
+++ b/media/libaaudio/tests/Android.mk
@@ -5,16 +5,6 @@
$(call include-path-for, audio-utils) \
frameworks/av/media/libaaudio/include \
frameworks/av/media/libaaudio/src
-LOCAL_SRC_FILES:= test_handle_tracker.cpp
-LOCAL_SHARED_LIBRARIES := libaaudio
-LOCAL_MODULE := test_handle_tracker
-include $(BUILD_NATIVE_TEST)
-
-include $(CLEAR_VARS)
-LOCAL_C_INCLUDES := \
- $(call include-path-for, audio-utils) \
- frameworks/av/media/libaaudio/include \
- frameworks/av/media/libaaudio/src
LOCAL_SRC_FILES:= test_marshalling.cpp
LOCAL_SHARED_LIBRARIES := libaaudio libbinder libcutils libutils
LOCAL_MODULE := test_aaudio_marshalling
diff --git a/media/libaaudio/tests/test_handle_tracker.cpp b/media/libaaudio/tests/test_handle_tracker.cpp
deleted file mode 100644
index c4db47a..0000000
--- a/media/libaaudio/tests/test_handle_tracker.cpp
+++ /dev/null
@@ -1,117 +0,0 @@
-/*
- * 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.
- */
-
-// Unit tests for AAudio Handle Tracker
-
-#include <stdlib.h>
-#include <math.h>
-
-#include <gtest/gtest.h>
-
-#include <aaudio/AAudio.h>
-#include "utility/HandleTracker.h"
-
-// Test adding one address.
-TEST(test_handle_tracker, aaudio_handle_tracker) {
- const int MAX_HANDLES = 4;
- HandleTracker tracker(MAX_HANDLES);
- handle_tracker_type_t type = 3; // arbitrary generic type
- int data; // something that has an address we can use
- handle_tracker_address_t found;
-
- // repeat the test several times to see if it breaks
- const int SEVERAL = 5; // arbitrary
- for (int i = 0; i < SEVERAL; i++) {
- // should fail to find a bogus handle
- found = tracker.get(type, 0); // bad handle
- EXPECT_EQ(nullptr, found);
-
- // create a valid handle and use it to lookup the object again
- aaudio_handle_t dataHandle = tracker.put(type, &data);
- ASSERT_TRUE(dataHandle > 0);
- found = tracker.get(type, dataHandle);
- EXPECT_EQ(&data, found);
- found = tracker.get(type, 0); // bad handle
- EXPECT_EQ(nullptr, found);
-
- // wrong type
- found = tracker.get(type+1, dataHandle);
- EXPECT_EQ(nullptr, found);
-
- // remove from storage
- found = tracker.remove(type, dataHandle);
- EXPECT_EQ(&data, found);
- // should fail the second time
- found = tracker.remove(type, dataHandle);
- EXPECT_EQ(nullptr, found);
- }
-}
-
-// Test filling the tracker.
-TEST(test_handle_tracker, aaudio_full_up) {
- const int MAX_HANDLES = 5;
- HandleTracker tracker(MAX_HANDLES);
- handle_tracker_type_t type = 4; // arbitrary generic type
- int data[MAX_HANDLES];
- aaudio_handle_t handles[MAX_HANDLES];
- handle_tracker_address_t found;
-
- // repeat the test several times to see if it breaks
- const int SEVERAL = 5; // arbitrary
- for (int i = 0; i < SEVERAL; i++) {
- for (int i = 0; i < MAX_HANDLES; i++) {
- // add a handle
- handles[i] = tracker.put(type, &data[i]);
- ASSERT_TRUE(handles[i] > 0);
- found = tracker.get(type, handles[i]);
- EXPECT_EQ(&data[i], found);
- }
-
- // Now that it is full, try to add one more.
- aaudio_handle_t handle = tracker.put(type, &data[0]);
- EXPECT_TRUE(handle < 0);
-
- for (int i = 0; i < MAX_HANDLES; i++) {
- // look up each handle
- found = tracker.get(type, handles[i]);
- EXPECT_EQ(&data[i], found);
- }
-
- // remove one from storage
- found = tracker.remove(type, handles[2]);
- EXPECT_EQ(&data[2], found);
- // now try to look up the same handle and fail
- found = tracker.get(type, handles[2]);
- EXPECT_EQ(nullptr, found);
-
- // add that same one back
- handle = tracker.put(type, &data[2]);
- ASSERT_TRUE(handle > 0);
- found = tracker.get(type, handle);
- EXPECT_EQ(&data[2], found);
- // now use a stale handle again with a valid index and fail
- found = tracker.get(type, handles[2]);
- EXPECT_EQ(nullptr, found);
-
- // remove them all
- handles[2] = handle;
- for (int i = 0; i < MAX_HANDLES; i++) {
- // look up each handle
- found = tracker.remove(type, handles[i]);
- EXPECT_EQ(&data[i], found);
- }
- }
-}
diff --git a/media/libaaudio/tests/test_timestamps.cpp b/media/libaaudio/tests/test_timestamps.cpp
index d9ca391..fb363e7 100644
--- a/media/libaaudio/tests/test_timestamps.cpp
+++ b/media/libaaudio/tests/test_timestamps.cpp
@@ -17,23 +17,99 @@
// Play silence and recover from dead servers or disconnected devices.
#include <stdio.h>
+#include <stdlib.h>
#include <unistd.h>
#include <aaudio/AAudio.h>
#include <aaudio/AAudioTesting.h>
-
#include "utils/AAudioExampleUtils.h"
+#include "../examples/utils/AAudioExampleUtils.h"
-#define DEFAULT_TIMEOUT_NANOS ((int64_t)1000000000)
+// Arbitrary period for glitches, once per second at 48000 Hz.
+#define FORCED_UNDERRUN_PERIOD_FRAMES 48000
+// How long to sleep in a callback to cause an intentional glitch. For testing.
+#define FORCED_UNDERRUN_SLEEP_MICROS (10 * 1000)
-int main(int argc, char **argv) {
- (void) argc;
- (void *)argv;
+#define MAX_TIMESTAMPS 1000
+#define DEFAULT_TIMEOUT_NANOS ((int64_t)1000000000)
+
+#define NUM_SECONDS 1
+#define NUM_LOOPS 4
+
+typedef struct TimestampInfo {
+ int64_t framesTotal;
+ int64_t appPosition; // frames
+ int64_t appNanoseconds;
+ int64_t timestampPosition; // frames
+ int64_t timestampNanos;
+ aaudio_result_t result;
+} TimestampInfo;
+
+typedef struct TimestampCallbackData_s {
+ TimestampInfo timestamps[MAX_TIMESTAMPS];
+ int64_t framesTotal = 0;
+ int64_t nextFrameToGlitch = FORCED_UNDERRUN_PERIOD_FRAMES;
+ int32_t timestampCount = 0; // in timestamps
+ bool forceUnderruns = false;
+} TimestampCallbackData_t;
+
+// Callback function that fills the audio output buffer.
+aaudio_data_callback_result_t timestampDataCallbackProc(
+ AAudioStream *stream,
+ void *userData,
+ void *audioData __unused,
+ int32_t numFrames
+) {
+
+ // should not happen but just in case...
+ if (userData == nullptr) {
+ printf("ERROR - SimplePlayerDataCallbackProc needs userData\n");
+ return AAUDIO_CALLBACK_RESULT_STOP;
+ }
+ TimestampCallbackData_t *timestampData = (TimestampCallbackData_t *) userData;
+
+ aaudio_direction_t direction = AAudioStream_getDirection(stream);
+ if (direction == AAUDIO_DIRECTION_INPUT) {
+ timestampData->framesTotal += numFrames;
+ }
+
+ if (timestampData->forceUnderruns) {
+ if (timestampData->framesTotal > timestampData->nextFrameToGlitch) {
+ usleep(FORCED_UNDERRUN_SLEEP_MICROS);
+ printf("Simulate glitch at %lld\n", (long long) timestampData->framesTotal);
+ timestampData->nextFrameToGlitch += FORCED_UNDERRUN_PERIOD_FRAMES;
+ }
+ }
+
+ if (timestampData->timestampCount < MAX_TIMESTAMPS) {
+ TimestampInfo *timestamp = ×tampData->timestamps[timestampData->timestampCount];
+ timestamp->result = AAudioStream_getTimestamp(stream,
+ CLOCK_MONOTONIC,
+ ×tamp->timestampPosition,
+ ×tamp->timestampNanos);
+ timestamp->framesTotal = timestampData->framesTotal;
+ timestamp->appPosition = (direction == AAUDIO_DIRECTION_OUTPUT)
+ ? AAudioStream_getFramesWritten(stream)
+ : AAudioStream_getFramesRead(stream);
+ timestamp->appNanoseconds = getNanoseconds();
+ timestampData->timestampCount++;
+ }
+
+ if (direction == AAUDIO_DIRECTION_OUTPUT) {
+ timestampData->framesTotal += numFrames;
+ }
+ return AAUDIO_CALLBACK_RESULT_CONTINUE;
+}
+
+static TimestampCallbackData_t sTimestampData;
+
+static aaudio_result_t testTimeStamps(aaudio_policy_t mmapPolicy,
+ aaudio_sharing_mode_t sharingMode,
+ aaudio_performance_mode_t performanceMode,
+ aaudio_direction_t direction) {
aaudio_result_t result = AAUDIO_OK;
- int32_t triesLeft = 3;
- int32_t bufferCapacity;
int32_t framesPerBurst = 0;
float *buffer = nullptr;
@@ -44,22 +120,20 @@
int32_t finalBufferSize = 0;
aaudio_format_t actualDataFormat = AAUDIO_FORMAT_PCM_FLOAT;
aaudio_sharing_mode_t actualSharingMode = AAUDIO_SHARING_MODE_SHARED;
- int32_t framesMax;
- int64_t framesTotal;
- int64_t printAt;
- int samplesPerBurst;
- int64_t previousFramePosition = -1;
+ aaudio_sharing_mode_t actualPerformanceMode = AAUDIO_PERFORMANCE_MODE_NONE;
AAudioStreamBuilder *aaudioBuilder = nullptr;
AAudioStream *aaudioStream = nullptr;
- // Make printf print immediately so that debug info is not stuck
- // in a buffer if we hang or crash.
- setvbuf(stdout, nullptr, _IONBF, (size_t) 0);
+ memset(&sTimestampData, 0, sizeof(sTimestampData));
- printf("Test Timestamps V0.1.1\n");
+ printf("------------ testTimeStamps(policy = %d, sharing = %s, perf = %s, dir = %s) -----------\n",
+ mmapPolicy,
+ getSharingModeText(sharingMode),
+ getPerformanceModeText(performanceMode),
+ getDirectionText(direction));
- AAudio_setMMapPolicy(AAUDIO_POLICY_AUTO);
+ AAudio_setMMapPolicy(mmapPolicy);
// Use an AAudioStreamBuilder to contain requested parameters.
result = AAudio_createStreamBuilder(&aaudioBuilder);
@@ -70,9 +144,11 @@
}
// Request stream properties.
- AAudioStreamBuilder_setFormat(aaudioBuilder, AAUDIO_FORMAT_PCM_FLOAT);
- //AAudioStreamBuilder_setPerformanceMode(aaudioBuilder, AAUDIO_PERFORMANCE_MODE_NONE);
- AAudioStreamBuilder_setPerformanceMode(aaudioBuilder, AAUDIO_PERFORMANCE_MODE_LOW_LATENCY);
+ AAudioStreamBuilder_setFormat(aaudioBuilder, AAUDIO_FORMAT_PCM_I16);
+ AAudioStreamBuilder_setSharingMode(aaudioBuilder, sharingMode);
+ AAudioStreamBuilder_setPerformanceMode(aaudioBuilder, performanceMode);
+ AAudioStreamBuilder_setDirection(aaudioBuilder, direction);
+ AAudioStreamBuilder_setDataCallback(aaudioBuilder, timestampDataCallbackProc, &sTimestampData);
// Create an AAudioStream using the Builder.
result = AAudioStreamBuilder_openStream(aaudioBuilder, &aaudioStream);
@@ -87,10 +163,25 @@
actualChannelCount = AAudioStream_getChannelCount(aaudioStream);
actualDataFormat = AAudioStream_getFormat(aaudioStream);
- printf("-------- chans = %3d, rate = %6d format = %d\n",
- actualChannelCount, actualSampleRate, actualDataFormat);
+ actualSharingMode = AAudioStream_getSharingMode(aaudioStream);
+ if (actualSharingMode != sharingMode) {
+ printf("did not get expected sharingMode, got %3d, skipping test\n",
+ actualSharingMode);
+ result = AAUDIO_OK;
+ goto finish;
+ }
+ actualPerformanceMode = AAudioStream_getPerformanceMode(aaudioStream);
+ if (actualPerformanceMode != performanceMode) {
+ printf("did not get expected performanceMode, got %3d, skipping test\n",
+ actualPerformanceMode);
+ result = AAUDIO_OK;
+ goto finish;
+ }
+
+ printf(" chans = %3d, rate = %6d format = %d\n",
+ actualChannelCount, actualSampleRate, actualDataFormat);
printf(" Is MMAP used? %s\n", AAudioStream_isMMapUsed(aaudioStream)
- ? "yes" : "no");
+ ? "yes" : "no");
// This is the number of frames that are read in one chunk by a DMA controller
// or a DSP or a mixer.
@@ -98,91 +189,151 @@
printf(" framesPerBurst = %3d\n", framesPerBurst);
originalBufferSize = AAudioStream_getBufferSizeInFrames(aaudioStream);
- requestedBufferSize = 2 * framesPerBurst;
+ requestedBufferSize = 4 * framesPerBurst;
finalBufferSize = AAudioStream_setBufferSizeInFrames(aaudioStream, requestedBufferSize);
printf(" BufferSize: original = %4d, requested = %4d, final = %4d\n",
originalBufferSize, requestedBufferSize, finalBufferSize);
- samplesPerBurst = framesPerBurst * actualChannelCount;
- buffer = new float[samplesPerBurst];
-
- result = AAudioStream_requestStart(aaudioStream);
- if (result != AAUDIO_OK) {
- printf("AAudioStream_requestStart returned %s",
+ {
+ int64_t position;
+ int64_t nanoseconds;
+ result = AAudioStream_getTimestamp(aaudioStream, CLOCK_MONOTONIC, &position, &nanoseconds);
+ printf("before start, AAudioStream_getTimestamp() returns %s\n",
AAudio_convertResultToText(result));
- goto finish;
}
- // Play silence very briefly.
- framesMax = actualSampleRate * 4;
- framesTotal = 0;
- printAt = actualSampleRate;
- while (result == AAUDIO_OK && framesTotal < framesMax) {
- int32_t framesWritten = AAudioStream_write(aaudioStream,
- buffer, framesPerBurst,
- DEFAULT_TIMEOUT_NANOS);
- if (framesWritten < 0) {
- result = framesWritten;
- printf("write() returned %s, frames = %d\n",
- AAudio_convertResultToText(result), (int)framesTotal);
- printf(" frames = %d\n", (int)framesTotal);
- } else if (framesWritten != framesPerBurst) {
- printf("write() returned %d, frames = %d\n", framesWritten, (int)framesTotal);
- result = AAUDIO_ERROR_TIMEOUT;
- } else {
- framesTotal += framesWritten;
- if (framesTotal >= printAt) {
- printf("frames = %d\n", (int)framesTotal);
- printAt += actualSampleRate;
+ for (int runs = 0; runs < NUM_LOOPS; runs++) {
+ printf("------------------ loop #%d\n", runs);
+
+ int64_t temp = sTimestampData.framesTotal;
+ memset(&sTimestampData, 0, sizeof(sTimestampData));
+ sTimestampData.framesTotal = temp;
+
+ sTimestampData.forceUnderruns = false;
+
+ result = AAudioStream_requestStart(aaudioStream);
+ if (result != AAUDIO_OK) {
+ printf("AAudioStream_requestStart returned %s",
+ AAudio_convertResultToText(result));
+ goto finish;
+ }
+
+ for (int second = 0; second < NUM_SECONDS; second++) {
+ // Give AAudio callback time to run in the background.
+ sleep(1);
+
+ // Periodically print the progress so we know it hasn't died.
+ printf("framesWritten = %d, XRuns = %d\n",
+ (int) AAudioStream_getFramesWritten(aaudioStream),
+ (int) AAudioStream_getXRunCount(aaudioStream)
+ );
+ }
+
+ result = AAudioStream_requestStop(aaudioStream);
+ if (result != AAUDIO_OK) {
+ printf("AAudioStream_requestStop returned %s\n",
+ AAudio_convertResultToText(result));
+ }
+
+ printf("timestampCount = %d\n", sTimestampData.timestampCount);
+ int printed = 0;
+ for (int i = 0; i < sTimestampData.timestampCount; i++) {
+ TimestampInfo *timestamp = &sTimestampData.timestamps[i];
+ bool posChanged = (timestamp->timestampPosition != (timestamp - 1)->timestampPosition);
+ bool timeChanged = (timestamp->timestampNanos != (timestamp - 1)->timestampNanos);
+ if ((printed < 20) && ((i < 10) || posChanged || timeChanged)) {
+ printf(" %3d : frames %8lld, xferd %8lld", i,
+ (long long) timestamp->framesTotal,
+ (long long) timestamp->appPosition);
+ if (timestamp->result != AAUDIO_OK) {
+ printf(", result = %s\n", AAudio_convertResultToText(timestamp->result));
+ } else {
+ bool negative = timestamp->timestampPosition < 0;
+ bool retro = (i > 0 && (timestamp->timestampPosition <
+ (timestamp - 1)->timestampPosition));
+ const char *message = negative ? " <=NEGATIVE!"
+ : (retro ? " <= RETROGRADE!" : "");
+
+ double latency = calculateLatencyMillis(timestamp->timestampPosition,
+ timestamp->timestampNanos,
+ timestamp->appPosition,
+ timestamp->appNanoseconds,
+ actualSampleRate);
+ printf(", STAMP: pos = %8lld, nanos = %8lld, lat = %7.1f msec %s\n",
+ (long long) timestamp->timestampPosition,
+ (long long) timestamp->timestampNanos,
+ latency,
+ message);
+ }
+ printed++;
}
}
- // Print timestamps.
- int64_t framePosition = 0;
- int64_t frameTime = 0;
- aaudio_result_t timeResult;
- timeResult = AAudioStream_getTimestamp(aaudioStream, CLOCK_MONOTONIC,
- &framePosition, &frameTime);
-
- if (timeResult == AAUDIO_OK) {
- if (framePosition > (previousFramePosition + 5000)) {
- int64_t realTime = getNanoseconds();
- int64_t framesWritten = AAudioStream_getFramesWritten(aaudioStream);
-
- double latencyMillis = calculateLatencyMillis(framePosition, frameTime,
- framesWritten, realTime,
- actualSampleRate);
-
- printf("--- timestamp: result = %4d, position = %lld, at %lld nanos"
- ", latency = %7.2f msec\n",
- timeResult,
- (long long) framePosition,
- (long long) frameTime,
- latencyMillis);
- previousFramePosition = framePosition;
- }
- }
+ // Avoid race conditions in AudioFlinger.
+ // There is normally a delay between a real user stopping and restarting a stream.
+ sleep(1);
}
- result = AAudioStream_requestStop(aaudioStream);
- if (result != AAUDIO_OK) {
- printf("AAudioStream_requestStop returned %s\n",
- AAudio_convertResultToText(result));
- }
- result = AAudioStream_close(aaudioStream);
- if (result != AAUDIO_OK) {
- printf("AAudioStream_close returned %s\n",
- AAudio_convertResultToText(result));
- }
- aaudioStream = nullptr;
-
-
finish:
if (aaudioStream != nullptr) {
AAudioStream_close(aaudioStream);
}
AAudioStreamBuilder_delete(aaudioBuilder);
- delete[] buffer;
printf("result = %d = %s\n", result, AAudio_convertResultToText(result));
+
+ return result;
+}
+
+int main(int argc, char **argv) {
+ (void) argc;
+ (void *) argv;
+
+ aaudio_result_t result = AAUDIO_OK;
+
+ // Make printf print immediately so that debug info is not stuck
+ // in a buffer if we hang or crash.
+ setvbuf(stdout, nullptr, _IONBF, (size_t) 0);
+
+ printf("Test Timestamps V0.1.3\n");
+
+ // Legacy
+ aaudio_policy_t policy = AAUDIO_POLICY_NEVER;
+ result = testTimeStamps(policy,
+ AAUDIO_SHARING_MODE_SHARED,
+ AAUDIO_PERFORMANCE_MODE_NONE,
+ AAUDIO_DIRECTION_INPUT);
+ result = testTimeStamps(policy,
+ AAUDIO_SHARING_MODE_SHARED,
+ AAUDIO_PERFORMANCE_MODE_LOW_LATENCY,
+ AAUDIO_DIRECTION_INPUT);
+ result = testTimeStamps(policy,
+ AAUDIO_SHARING_MODE_SHARED,
+ AAUDIO_PERFORMANCE_MODE_NONE,
+ AAUDIO_DIRECTION_OUTPUT);
+ result = testTimeStamps(policy,
+ AAUDIO_SHARING_MODE_SHARED,
+ AAUDIO_PERFORMANCE_MODE_LOW_LATENCY,
+ AAUDIO_DIRECTION_OUTPUT);
+
+ // MMAP
+ policy = AAUDIO_POLICY_ALWAYS;
+ result = testTimeStamps(policy,
+ AAUDIO_SHARING_MODE_EXCLUSIVE,
+ AAUDIO_PERFORMANCE_MODE_LOW_LATENCY,
+ AAUDIO_DIRECTION_INPUT);
+ result = testTimeStamps(policy,
+ AAUDIO_SHARING_MODE_EXCLUSIVE,
+ AAUDIO_PERFORMANCE_MODE_LOW_LATENCY,
+ AAUDIO_DIRECTION_OUTPUT);
+ result = testTimeStamps(policy,
+ AAUDIO_SHARING_MODE_SHARED,
+ AAUDIO_PERFORMANCE_MODE_LOW_LATENCY,
+ AAUDIO_DIRECTION_INPUT);
+ result = testTimeStamps(policy,
+ AAUDIO_SHARING_MODE_SHARED,
+ AAUDIO_PERFORMANCE_MODE_LOW_LATENCY,
+ AAUDIO_DIRECTION_OUTPUT);
+
+ return (result == AAUDIO_OK) ? EXIT_SUCCESS : EXIT_FAILURE;
}
diff --git a/media/libaudioclient/AudioRecord.cpp b/media/libaudioclient/AudioRecord.cpp
index 6402bbb..26a320c 100644
--- a/media/libaudioclient/AudioRecord.cpp
+++ b/media/libaudioclient/AudioRecord.cpp
@@ -120,7 +120,7 @@
}
// No lock here: worst case we remove a NULL callback which will be a nop
if (mDeviceCallback != 0 && mInput != AUDIO_IO_HANDLE_NONE) {
- AudioSystem::removeAudioDeviceCallback(mDeviceCallback, mInput);
+ AudioSystem::removeAudioDeviceCallback(this, mInput);
}
IInterface::asBinder(mAudioRecord)->unlinkToDeath(mDeathNotifier, this);
mAudioRecord.clear();
@@ -274,7 +274,7 @@
mStatus = NO_ERROR;
mUserData = user;
// TODO: add audio hardware input latency here
- mLatency = (1000 * mFrameCount) / mSampleRate;
+ mLatency = (1000LL * mFrameCount) / mSampleRate;
mMarkerPosition = 0;
mMarkerReached = false;
mNewPosition = 0;
@@ -499,19 +499,26 @@
return mSelectedDeviceId;
}
+// must be called with mLock held
+void AudioRecord::updateRoutedDeviceId_l()
+{
+ // if the record is inactive, do not update actual device as the input stream maybe routed
+ // from a device not relevant to this client because of other active use cases.
+ if (!mActive) {
+ return;
+ }
+ if (mInput != AUDIO_IO_HANDLE_NONE) {
+ audio_port_handle_t deviceId = AudioSystem::getDeviceIdForIo(mInput);
+ if (deviceId != AUDIO_PORT_HANDLE_NONE) {
+ mRoutedDeviceId = deviceId;
+ }
+ }
+}
+
audio_port_handle_t AudioRecord::getRoutedDeviceId() {
AutoMutex lock(mLock);
- if (mInput == AUDIO_IO_HANDLE_NONE) {
- return AUDIO_PORT_HANDLE_NONE;
- }
- // if the input stream does not have an active audio patch, use either the device initially
- // selected by audio policy manager or the last routed device
- audio_port_handle_t deviceId = AudioSystem::getDeviceIdForIo(mInput);
- if (deviceId == AUDIO_PORT_HANDLE_NONE) {
- deviceId = mRoutedDeviceId;
- }
- mRoutedDeviceId = deviceId;
- return deviceId;
+ updateRoutedDeviceId_l();
+ return mRoutedDeviceId;
}
// -------------------------------------------------------------------------
@@ -537,9 +544,6 @@
return NO_INIT;
}
- if (mDeviceCallback != 0 && mInput != AUDIO_IO_HANDLE_NONE) {
- AudioSystem::removeAudioDeviceCallback(mDeviceCallback, mInput);
- }
audio_io_handle_t input;
// mFlags (not mOrigFlags) is modified depending on whether fast request is accepted.
@@ -744,6 +748,15 @@
}
mNotificationFramesAct = (uint32_t) notificationFrames;
+
+ //mInput != input includes the case where mInput == AUDIO_IO_HANDLE_NONE for first creation
+ if (mDeviceCallback != 0 && mInput != input) {
+ if (mInput != AUDIO_IO_HANDLE_NONE) {
+ AudioSystem::removeAudioDeviceCallback(this, mInput);
+ }
+ AudioSystem::addAudioDeviceCallback(this, input);
+ }
+
// We retain a copy of the I/O handle, but don't own the reference
mInput = input;
mRefreshRemaining = true;
@@ -763,10 +776,6 @@
mDeathNotifier = new DeathNotifier(this);
IInterface::asBinder(mAudioRecord)->linkToDeath(mDeathNotifier, this);
- if (mDeviceCallback != 0) {
- AudioSystem::addAudioDeviceCallback(mDeviceCallback, mInput);
- }
-
return NO_ERROR;
// End of retry loop.
@@ -1239,7 +1248,7 @@
return BAD_VALUE;
}
AutoMutex lock(mLock);
- if (mDeviceCallback == callback) {
+ if (mDeviceCallback.unsafe_get() == callback.get()) {
ALOGW("%s adding same callback!", __FUNCTION__);
return INVALID_OPERATION;
}
@@ -1247,9 +1256,9 @@
if (mInput != AUDIO_IO_HANDLE_NONE) {
if (mDeviceCallback != 0) {
ALOGW("%s callback already present!", __FUNCTION__);
- AudioSystem::removeAudioDeviceCallback(mDeviceCallback, mInput);
+ AudioSystem::removeAudioDeviceCallback(this, mInput);
}
- status = AudioSystem::addAudioDeviceCallback(callback, mInput);
+ status = AudioSystem::addAudioDeviceCallback(this, mInput);
}
mDeviceCallback = callback;
return status;
@@ -1263,17 +1272,38 @@
return BAD_VALUE;
}
AutoMutex lock(mLock);
- if (mDeviceCallback != callback) {
+ if (mDeviceCallback.unsafe_get() != callback.get()) {
ALOGW("%s removing different callback!", __FUNCTION__);
return INVALID_OPERATION;
}
+ mDeviceCallback.clear();
if (mInput != AUDIO_IO_HANDLE_NONE) {
- AudioSystem::removeAudioDeviceCallback(mDeviceCallback, mInput);
+ AudioSystem::removeAudioDeviceCallback(this, mInput);
}
- mDeviceCallback = 0;
return NO_ERROR;
}
+void AudioRecord::onAudioDeviceUpdate(audio_io_handle_t audioIo,
+ audio_port_handle_t deviceId)
+{
+ sp<AudioSystem::AudioDeviceCallback> callback;
+ {
+ AutoMutex lock(mLock);
+ if (audioIo != mInput) {
+ return;
+ }
+ callback = mDeviceCallback.promote();
+ // only update device if the record is active as route changes due to other use cases are
+ // irrelevant for this client
+ if (mActive) {
+ mRoutedDeviceId = deviceId;
+ }
+ }
+ if (callback.get() != nullptr) {
+ callback->onAudioDeviceUpdate(mInput, mRoutedDeviceId);
+ }
+}
+
// =========================================================================
void AudioRecord::DeathNotifier::binderDied(const wp<IBinder>& who __unused)
diff --git a/media/libaudioclient/AudioSystem.cpp b/media/libaudioclient/AudioSystem.cpp
index 211bd7d..cdc75ac 100644
--- a/media/libaudioclient/AudioSystem.cpp
+++ b/media/libaudioclient/AudioSystem.cpp
@@ -493,14 +493,16 @@
if (ioDesc == 0 || ioDesc->mIoHandle == AUDIO_IO_HANDLE_NONE) return;
audio_port_handle_t deviceId = AUDIO_PORT_HANDLE_NONE;
- Vector < sp<AudioDeviceCallback> > callbacks;
+ Vector < wp<AudioDeviceCallback> > callbacks;
{
Mutex::Autolock _l(mLock);
switch (event) {
case AUDIO_OUTPUT_OPENED:
- case AUDIO_INPUT_OPENED: {
+ case AUDIO_OUTPUT_REGISTERED:
+ case AUDIO_INPUT_OPENED:
+ case AUDIO_INPUT_REGISTERED: {
sp<AudioIoDescriptor> oldDesc = getIoDescriptor_l(ioDesc->mIoHandle);
if (oldDesc == 0) {
mIoDescriptors.add(ioDesc->mIoHandle, ioDesc);
@@ -511,13 +513,19 @@
if (ioDesc->getDeviceId() != AUDIO_PORT_HANDLE_NONE) {
deviceId = ioDesc->getDeviceId();
- ssize_t ioIndex = mAudioDeviceCallbacks.indexOfKey(ioDesc->mIoHandle);
- if (ioIndex >= 0) {
- callbacks = mAudioDeviceCallbacks.valueAt(ioIndex);
+ if (event == AUDIO_OUTPUT_OPENED || event == AUDIO_INPUT_OPENED) {
+ ssize_t ioIndex = mAudioDeviceCallbacks.indexOfKey(ioDesc->mIoHandle);
+ if (ioIndex >= 0) {
+ callbacks = mAudioDeviceCallbacks.valueAt(ioIndex);
+ }
}
}
- ALOGV("ioConfigChanged() new %s opened %d samplingRate %u, format %#x channel mask %#x "
- "frameCount %zu deviceId %d", event == AUDIO_OUTPUT_OPENED ? "output" : "input",
+ ALOGV("ioConfigChanged() new %s %s %d samplingRate %u, format %#x channel mask %#x "
+ "frameCount %zu deviceId %d",
+ event == AUDIO_OUTPUT_OPENED || event == AUDIO_OUTPUT_REGISTERED ?
+ "output" : "input",
+ event == AUDIO_OUTPUT_OPENED || event == AUDIO_INPUT_OPENED ?
+ "opened" : "registered",
ioDesc->mIoHandle, ioDesc->mSamplingRate, ioDesc->mFormat, ioDesc->mChannelMask,
ioDesc->mFrameCount, ioDesc->getDeviceId());
} break;
@@ -563,9 +571,23 @@
} break;
}
}
+ bool callbackRemoved = false;
// callbacks.size() != 0 => ioDesc->mIoHandle and deviceId are valid
- for (size_t i = 0; i < callbacks.size(); i++) {
- callbacks[i]->onAudioDeviceUpdate(ioDesc->mIoHandle, deviceId);
+ for (size_t i = 0; i < callbacks.size(); ) {
+ sp<AudioDeviceCallback> callback = callbacks[i].promote();
+ if (callback.get() != nullptr) {
+ callback->onAudioDeviceUpdate(ioDesc->mIoHandle, deviceId);
+ i++;
+ } else {
+ callbacks.removeAt(i);
+ callbackRemoved = true;
+ }
+ }
+ // clean up callback list while we are here if some clients have disappeared without
+ // unregistering their callback
+ if (callbackRemoved) {
+ Mutex::Autolock _l(mLock);
+ mAudioDeviceCallbacks.replaceValueFor(ioDesc->mIoHandle, callbacks);
}
}
@@ -618,17 +640,17 @@
}
status_t AudioSystem::AudioFlingerClient::addAudioDeviceCallback(
- const sp<AudioDeviceCallback>& callback, audio_io_handle_t audioIo)
+ const wp<AudioDeviceCallback>& callback, audio_io_handle_t audioIo)
{
Mutex::Autolock _l(mLock);
- Vector < sp<AudioDeviceCallback> > callbacks;
+ Vector < wp<AudioDeviceCallback> > callbacks;
ssize_t ioIndex = mAudioDeviceCallbacks.indexOfKey(audioIo);
if (ioIndex >= 0) {
callbacks = mAudioDeviceCallbacks.valueAt(ioIndex);
}
for (size_t cbIndex = 0; cbIndex < callbacks.size(); cbIndex++) {
- if (callbacks[cbIndex] == callback) {
+ if (callbacks[cbIndex].unsafe_get() == callback.unsafe_get()) {
return INVALID_OPERATION;
}
}
@@ -639,18 +661,18 @@
}
status_t AudioSystem::AudioFlingerClient::removeAudioDeviceCallback(
- const sp<AudioDeviceCallback>& callback, audio_io_handle_t audioIo)
+ const wp<AudioDeviceCallback>& callback, audio_io_handle_t audioIo)
{
Mutex::Autolock _l(mLock);
ssize_t ioIndex = mAudioDeviceCallbacks.indexOfKey(audioIo);
if (ioIndex < 0) {
return INVALID_OPERATION;
}
- Vector < sp<AudioDeviceCallback> > callbacks = mAudioDeviceCallbacks.valueAt(ioIndex);
+ Vector < wp<AudioDeviceCallback> > callbacks = mAudioDeviceCallbacks.valueAt(ioIndex);
size_t cbIndex;
for (cbIndex = 0; cbIndex < callbacks.size(); cbIndex++) {
- if (callbacks[cbIndex] == callback) {
+ if (callbacks[cbIndex].unsafe_get() == callback.unsafe_get()) {
break;
}
}
@@ -1128,7 +1150,7 @@
}
status_t AudioSystem::addAudioDeviceCallback(
- const sp<AudioDeviceCallback>& callback, audio_io_handle_t audioIo)
+ const wp<AudioDeviceCallback>& callback, audio_io_handle_t audioIo)
{
const sp<AudioFlingerClient> afc = getAudioFlingerClient();
if (afc == 0) {
@@ -1145,7 +1167,7 @@
}
status_t AudioSystem::removeAudioDeviceCallback(
- const sp<AudioDeviceCallback>& callback, audio_io_handle_t audioIo)
+ const wp<AudioDeviceCallback>& callback, audio_io_handle_t audioIo)
{
const sp<AudioFlingerClient> afc = getAudioFlingerClient();
if (afc == 0) {
diff --git a/media/libaudioclient/AudioTrack.cpp b/media/libaudioclient/AudioTrack.cpp
index 07875ac..6206be0 100644
--- a/media/libaudioclient/AudioTrack.cpp
+++ b/media/libaudioclient/AudioTrack.cpp
@@ -65,6 +65,14 @@
return tv.tv_sec * 1000000ll + tv.tv_nsec / 1000;
}
+// TODO move to audio_utils.
+static inline struct timespec convertNsToTimespec(int64_t ns) {
+ struct timespec tv;
+ tv.tv_sec = static_cast<time_t>(ns / NANOS_PER_SECOND);
+ tv.tv_nsec = static_cast<long>(ns % NANOS_PER_SECOND);
+ return tv;
+}
+
// current monotonic time in microseconds.
static int64_t getNowUs()
{
@@ -270,7 +278,7 @@
}
// No lock here: worst case we remove a NULL callback which will be a nop
if (mDeviceCallback != 0 && mOutput != AUDIO_IO_HANDLE_NONE) {
- AudioSystem::removeAudioDeviceCallback(mDeviceCallback, mOutput);
+ AudioSystem::removeAudioDeviceCallback(this, mOutput);
}
IInterface::asBinder(mAudioTrack)->unlinkToDeath(mDeathNotifier, this);
mAudioTrack.clear();
@@ -541,7 +549,8 @@
mUpdatePeriod = 0;
mPosition = 0;
mReleased = 0;
- mStartUs = 0;
+ mStartNs = 0;
+ mStartFromZeroUs = 0;
AudioSystem::acquireAudioSessionId(mSessionId, mClientPid);
mSequence = 1;
mObservedSequence = mSequence;
@@ -589,6 +598,7 @@
mStartEts.clear();
}
}
+ mStartNs = systemTime(); // save this for timestamp adjustment after starting.
if (previousState == STATE_STOPPED || previousState == STATE_FLUSHED) {
// reset current position as seen by client to 0
mPosition = 0;
@@ -607,7 +617,8 @@
+ mStartEts.mPosition[ExtendedTimestamp::LOCATION_SERVER]),
(long long)mStartEts.mFlushed,
(long long)mFramesWritten);
- mFramesWrittenServerOffset = -mStartEts.mPosition[ExtendedTimestamp::LOCATION_SERVER];
+ // mStartEts is already adjusted by mFramesWrittenServerOffset, so we delta adjust.
+ mFramesWrittenServerOffset -= mStartEts.mPosition[ExtendedTimestamp::LOCATION_SERVER];
}
mFramesWritten = 0;
mProxy->clearTimestamp(); // need new server push for valid timestamp
@@ -617,7 +628,7 @@
// since the flush is asynchronous and stop may not fully drain.
// We save the time when the track is started to later verify whether
// the counters are realistic (i.e. start from zero after this time).
- mStartUs = getNowUs();
+ mStartFromZeroUs = mStartNs / 1000;
// force refresh of remaining frames by processAudioBuffer() as last
// write before stop could be partial.
@@ -1220,19 +1231,26 @@
return mSelectedDeviceId;
}
+// must be called with mLock held
+void AudioTrack::updateRoutedDeviceId_l()
+{
+ // if the track is inactive, do not update actual device as the output stream maybe routed
+ // to a device not relevant to this client because of other active use cases.
+ if (mState != STATE_ACTIVE) {
+ return;
+ }
+ if (mOutput != AUDIO_IO_HANDLE_NONE) {
+ audio_port_handle_t deviceId = AudioSystem::getDeviceIdForIo(mOutput);
+ if (deviceId != AUDIO_PORT_HANDLE_NONE) {
+ mRoutedDeviceId = deviceId;
+ }
+ }
+}
+
audio_port_handle_t AudioTrack::getRoutedDeviceId() {
AutoMutex lock(mLock);
- if (mOutput == AUDIO_IO_HANDLE_NONE) {
- return AUDIO_PORT_HANDLE_NONE;
- }
- // if the output stream does not have an active audio patch, use either the device initially
- // selected by audio policy manager or the last routed device
- audio_port_handle_t deviceId = AudioSystem::getDeviceIdForIo(mOutput);
- if (deviceId == AUDIO_PORT_HANDLE_NONE) {
- deviceId = mRoutedDeviceId;
- }
- mRoutedDeviceId = deviceId;
- return deviceId;
+ updateRoutedDeviceId_l();
+ return mRoutedDeviceId;
}
status_t AudioTrack::attachAuxEffect(int effectId)
@@ -1270,7 +1288,7 @@
ALOGW("getLatency(%d) failed status %d", mOutput, status);
} else {
// FIXME don't believe this lie
- mLatency = mAfLatency + (1000 * mFrameCount) / mSampleRate;
+ mLatency = mAfLatency + (1000LL * mFrameCount) / mSampleRate;
}
}
@@ -1296,12 +1314,10 @@
return NO_INIT;
}
- if (mDeviceCallback != 0 && mOutput != AUDIO_IO_HANDLE_NONE) {
- AudioSystem::removeAudioDeviceCallback(mDeviceCallback, mOutput);
- }
audio_io_handle_t output;
audio_stream_type_t streamType = mStreamType;
audio_attributes_t *attr = (mStreamType == AUDIO_STREAM_DEFAULT) ? &mAttributes : NULL;
+ bool callbackAdded = false;
// mFlags (not mOrigFlags) is modified depending on whether fast request is accepted.
// After fast request is denied, we will request again if IAudioTrack is re-created.
@@ -1506,12 +1522,14 @@
sp<IMemory> iMem = track->getCblk();
if (iMem == 0) {
ALOGE("Could not get control block");
- return NO_INIT;
+ status = NO_INIT;
+ goto release;
}
void *iMemPointer = iMem->pointer();
if (iMemPointer == NULL) {
ALOGE("Could not get control block pointer");
- return NO_INIT;
+ status = NO_INIT;
+ goto release;
}
// invariant that mAudioTrack != 0 is true only after set() returns successfully
if (mAudioTrack != 0) {
@@ -1573,6 +1591,15 @@
}
}
+ //mOutput != output includes the case where mOutput == AUDIO_IO_HANDLE_NONE for first creation
+ if (mDeviceCallback != 0 && mOutput != output) {
+ if (mOutput != AUDIO_IO_HANDLE_NONE) {
+ AudioSystem::removeAudioDeviceCallback(this, mOutput);
+ }
+ AudioSystem::addAudioDeviceCallback(this, output);
+ callbackAdded = true;
+ }
+
// We retain a copy of the I/O handle, but don't own the reference
mOutput = output;
mRefreshRemaining = true;
@@ -1588,7 +1615,8 @@
buffers = mSharedBuffer->pointer();
if (buffers == NULL) {
ALOGE("Could not get buffer pointer");
- return NO_INIT;
+ status = NO_INIT;
+ goto release;
}
}
@@ -1633,15 +1661,15 @@
mDeathNotifier = new DeathNotifier(this);
IInterface::asBinder(mAudioTrack)->linkToDeath(mDeathNotifier, this);
- if (mDeviceCallback != 0) {
- AudioSystem::addAudioDeviceCallback(mDeviceCallback, mOutput);
- }
-
return NO_ERROR;
}
release:
AudioSystem::releaseOutput(output, streamType, mSessionId);
+ if (callbackAdded) {
+ // note: mOutput is always valid is callbackAdded is true
+ AudioSystem::removeAudioDeviceCallback(this, mOutput);
+ }
if (status == NO_ERROR) {
status = NO_INIT;
}
@@ -2088,7 +2116,14 @@
// Convert frame units to time units
nsecs_t ns = NS_WHENEVER;
if (minFrames != (uint32_t) ~0) {
- ns = framesToNanoseconds(minFrames, sampleRate, speed) + kWaitPeriodNs;
+ // AudioFlinger consumption of client data may be irregular when coming out of device
+ // standby since the kernel buffers require filling. This is throttled to no more than 2x
+ // the expected rate in the MixerThread. Hence, we reduce the estimated time to wait by one
+ // half (but no more than half a second) to improve callback accuracy during these temporary
+ // data surges.
+ const nsecs_t estimatedNs = framesToNanoseconds(minFrames, sampleRate, speed);
+ constexpr nsecs_t maxThrottleCompensationNs = 500000000LL;
+ ns = estimatedNs - min(estimatedNs / 2, maxThrottleCompensationNs) + kWaitPeriodNs;
ns -= (timeAfterCallbacks - timeBeforeCallbacks); // account for callback time
// TODO: Should we warn if the callback time is too long?
if (ns < 0) ns = 0;
@@ -2573,8 +2608,7 @@
if (at < limit) {
ALOGV("timestamp pause lag:%lld adjusting from %lld to %lld",
(long long)lag, (long long)at, (long long)limit);
- timestamp.mTime.tv_sec = limit / NANOS_PER_SECOND;
- timestamp.mTime.tv_nsec = limit % NANOS_PER_SECOND; // compiler opt.
+ timestamp.mTime = convertNsToTimespec(limit);
}
}
mPreviousLocation = location;
@@ -2617,18 +2651,18 @@
// the previous song under gapless playback.
// However, we sometimes see zero timestamps, then a glitch of
// the previous song's position, and then correct timestamps afterwards.
- if (mStartUs != 0 && mSampleRate != 0) {
+ if (mStartFromZeroUs != 0 && mSampleRate != 0) {
static const int kTimeJitterUs = 100000; // 100 ms
static const int k1SecUs = 1000000;
const int64_t timeNow = getNowUs();
- if (timeNow < mStartUs + k1SecUs) { // within first second of starting
+ if (timeNow < mStartFromZeroUs + k1SecUs) { // within first second of starting
const int64_t timestampTimeUs = convertTimespecToUs(timestamp.mTime);
- if (timestampTimeUs < mStartUs) {
+ if (timestampTimeUs < mStartFromZeroUs) {
return WOULD_BLOCK; // stale timestamp time, occurs before start.
}
- const int64_t deltaTimeUs = timestampTimeUs - mStartUs;
+ const int64_t deltaTimeUs = timestampTimeUs - mStartFromZeroUs;
const int64_t deltaPositionByUs = (double)timestamp.mPosition * 1000000
/ ((double)mSampleRate * mPlaybackRate.mSpeed);
@@ -2651,10 +2685,10 @@
return WOULD_BLOCK;
}
if (deltaPositionByUs != 0) {
- mStartUs = 0; // don't check again, we got valid nonzero position.
+ mStartFromZeroUs = 0; // don't check again, we got valid nonzero position.
}
} else {
- mStartUs = 0; // don't check again, start time expired.
+ mStartFromZeroUs = 0; // don't check again, start time expired.
}
mTimestampStartupGlitchReported = false;
}
@@ -2692,14 +2726,33 @@
// Prevent retrograde motion in timestamp.
// This is sometimes caused by erratic reports of the available space in the ALSA drivers.
if (status == NO_ERROR) {
+ // previousTimestampValid is set to false when starting after a stop or flush.
if (previousTimestampValid) {
const int64_t previousTimeNanos =
audio_utils_ns_from_timespec(&mPreviousTimestamp.mTime);
- const int64_t currentTimeNanos = audio_utils_ns_from_timespec(×tamp.mTime);
+ int64_t currentTimeNanos = audio_utils_ns_from_timespec(×tamp.mTime);
+
+ // Fix stale time when checking timestamp right after start().
+ //
+ // For offload compatibility, use a default lag value here.
+ // Any time discrepancy between this update and the pause timestamp is handled
+ // by the retrograde check afterwards.
+ const int64_t lagNs = int64_t(mAfLatency * 1000000LL);
+ const int64_t limitNs = mStartNs - lagNs;
+ if (currentTimeNanos < limitNs) {
+ ALOGD("correcting timestamp time for pause, "
+ "currentTimeNanos: %lld < limitNs: %lld < mStartNs: %lld",
+ (long long)currentTimeNanos, (long long)limitNs, (long long)mStartNs);
+ timestamp.mTime = convertNsToTimespec(limitNs);
+ currentTimeNanos = limitNs;
+ }
+
+ // retrograde check
if (currentTimeNanos < previousTimeNanos) {
ALOGW("retrograde timestamp time corrected, %lld < %lld",
(long long)currentTimeNanos, (long long)previousTimeNanos);
timestamp.mTime = mPreviousTimestamp.mTime;
+ // currentTimeNanos not used below.
}
// Looking at signed delta will work even when the timestamps
@@ -2817,7 +2870,7 @@
return BAD_VALUE;
}
AutoMutex lock(mLock);
- if (mDeviceCallback == callback) {
+ if (mDeviceCallback.unsafe_get() == callback.get()) {
ALOGW("%s adding same callback!", __FUNCTION__);
return INVALID_OPERATION;
}
@@ -2825,9 +2878,9 @@
if (mOutput != AUDIO_IO_HANDLE_NONE) {
if (mDeviceCallback != 0) {
ALOGW("%s callback already present!", __FUNCTION__);
- AudioSystem::removeAudioDeviceCallback(mDeviceCallback, mOutput);
+ AudioSystem::removeAudioDeviceCallback(this, mOutput);
}
- status = AudioSystem::addAudioDeviceCallback(callback, mOutput);
+ status = AudioSystem::addAudioDeviceCallback(this, mOutput);
}
mDeviceCallback = callback;
return status;
@@ -2841,17 +2894,39 @@
return BAD_VALUE;
}
AutoMutex lock(mLock);
- if (mDeviceCallback != callback) {
+ if (mDeviceCallback.unsafe_get() != callback.get()) {
ALOGW("%s removing different callback!", __FUNCTION__);
return INVALID_OPERATION;
}
+ mDeviceCallback.clear();
if (mOutput != AUDIO_IO_HANDLE_NONE) {
- AudioSystem::removeAudioDeviceCallback(mDeviceCallback, mOutput);
+ AudioSystem::removeAudioDeviceCallback(this, mOutput);
}
- mDeviceCallback = 0;
return NO_ERROR;
}
+
+void AudioTrack::onAudioDeviceUpdate(audio_io_handle_t audioIo,
+ audio_port_handle_t deviceId)
+{
+ sp<AudioSystem::AudioDeviceCallback> callback;
+ {
+ AutoMutex lock(mLock);
+ if (audioIo != mOutput) {
+ return;
+ }
+ callback = mDeviceCallback.promote();
+ // only update device if the track is active as route changes due to other use cases are
+ // irrelevant for this client
+ if (mState == STATE_ACTIVE) {
+ mRoutedDeviceId = deviceId;
+ }
+ }
+ if (callback.get() != nullptr) {
+ callback->onAudioDeviceUpdate(mOutput, mRoutedDeviceId);
+ }
+}
+
status_t AudioTrack::pendingDuration(int32_t *msec, ExtendedTimestamp::Location location)
{
if (msec == nullptr ||
@@ -2909,7 +2984,7 @@
case STATE_STOPPED:
if (isOffloadedOrDirect_l()) {
// check if we have started in the past to return true.
- return mStartUs > 0;
+ return mStartFromZeroUs > 0;
}
// A normal audio track may still be draining, so
// check if stream has ended. This covers fasttrack position
diff --git a/media/libaudioclient/ToneGenerator.cpp b/media/libaudioclient/ToneGenerator.cpp
index 9bc2594..78e392e 100644
--- a/media/libaudioclient/ToneGenerator.cpp
+++ b/media/libaudioclient/ToneGenerator.cpp
@@ -864,6 +864,13 @@
if (property_get("gsm.operator.iso-country", value, "") == 0) {
property_get("gsm.sim.operator.iso-country", value, "");
}
+ // If dual sim device has two SIM cards inserted and is not registerd to any network,
+ // "," is set to "gsm.operator.iso-country" prop.
+ // In this case, "gsm.sim.operator.iso-country" prop should be used.
+ if (strlen(value) == 1 && strstr(value, ",") != NULL) {
+ property_get("gsm.sim.operator.iso-country", value, "");
+ }
+
if (strstr(value, "us") != NULL ||
strstr(value, "ca") != NULL) {
mRegion = ANSI;
diff --git a/media/libaudioclient/include/media/AudioIoDescriptor.h b/media/libaudioclient/include/media/AudioIoDescriptor.h
index fed86c9..859f1a9 100644
--- a/media/libaudioclient/include/media/AudioIoDescriptor.h
+++ b/media/libaudioclient/include/media/AudioIoDescriptor.h
@@ -20,9 +20,11 @@
namespace android {
enum audio_io_config_event {
+ AUDIO_OUTPUT_REGISTERED,
AUDIO_OUTPUT_OPENED,
AUDIO_OUTPUT_CLOSED,
AUDIO_OUTPUT_CONFIG_CHANGED,
+ AUDIO_INPUT_REGISTERED,
AUDIO_INPUT_OPENED,
AUDIO_INPUT_CLOSED,
AUDIO_INPUT_CONFIG_CHANGED,
diff --git a/media/libaudioclient/include/media/AudioRecord.h b/media/libaudioclient/include/media/AudioRecord.h
index 74a626e..c6ad1b5 100644
--- a/media/libaudioclient/include/media/AudioRecord.h
+++ b/media/libaudioclient/include/media/AudioRecord.h
@@ -36,7 +36,7 @@
// ----------------------------------------------------------------------------
-class AudioRecord : public RefBase
+class AudioRecord : public AudioSystem::AudioDeviceCallback
{
public:
@@ -427,7 +427,12 @@
/* Returns the ID of the audio device actually used by the input to which this AudioRecord
* is attached.
- * A value of AUDIO_PORT_HANDLE_NONE indicates the AudioRecord is not attached to any input.
+ * The device ID is relevant only if the AudioRecord is active.
+ * When the AudioRecord is inactive, the device ID returned can be either:
+ * - AUDIO_PORT_HANDLE_NONE if the AudioRecord is not attached to any output.
+ * - The device ID used before paused or stopped.
+ * - The device ID selected by audio policy manager of setOutputDevice() if the AudioRecord
+ * has not been started yet.
*
* Parameters:
* none.
@@ -457,6 +462,10 @@
status_t removeAudioDeviceCallback(
const sp<AudioSystem::AudioDeviceCallback>& callback);
+ // AudioSystem::AudioDeviceCallback> virtuals
+ virtual void onAudioDeviceUpdate(audio_io_handle_t audioIo,
+ audio_port_handle_t deviceId);
+
private:
/* If nonContig is non-NULL, it is an output parameter that will be set to the number of
* additional non-contiguous frames that are predicted to be available immediately,
@@ -564,6 +573,8 @@
// FIXME enum is faster than strcmp() for parameter 'from'
status_t restoreRecord_l(const char *from);
+ void updateRoutedDeviceId_l();
+
sp<AudioRecordThread> mAudioRecordThread;
mutable Mutex mLock;
@@ -668,7 +679,7 @@
audio_port_handle_t mRoutedDeviceId; // Device actually selected by audio policy manager:
// May not match the app selection depending on other
// activity and connected devices
- sp<AudioSystem::AudioDeviceCallback> mDeviceCallback;
+ wp<AudioSystem::AudioDeviceCallback> mDeviceCallback;
audio_port_handle_t mPortId; // unique ID allocated by audio policy
};
diff --git a/media/libaudioclient/include/media/AudioSystem.h b/media/libaudioclient/include/media/AudioSystem.h
index 4d5b317..5a81d83 100644
--- a/media/libaudioclient/include/media/AudioSystem.h
+++ b/media/libaudioclient/include/media/AudioSystem.h
@@ -370,9 +370,9 @@
audio_port_handle_t deviceId) = 0;
};
- static status_t addAudioDeviceCallback(const sp<AudioDeviceCallback>& callback,
+ static status_t addAudioDeviceCallback(const wp<AudioDeviceCallback>& callback,
audio_io_handle_t audioIo);
- static status_t removeAudioDeviceCallback(const sp<AudioDeviceCallback>& callback,
+ static status_t removeAudioDeviceCallback(const wp<AudioDeviceCallback>& callback,
audio_io_handle_t audioIo);
static audio_port_handle_t getDeviceIdForIo(audio_io_handle_t audioIo);
@@ -403,9 +403,9 @@
const sp<AudioIoDescriptor>& ioDesc);
- status_t addAudioDeviceCallback(const sp<AudioDeviceCallback>& callback,
+ status_t addAudioDeviceCallback(const wp<AudioDeviceCallback>& callback,
audio_io_handle_t audioIo);
- status_t removeAudioDeviceCallback(const sp<AudioDeviceCallback>& callback,
+ status_t removeAudioDeviceCallback(const wp<AudioDeviceCallback>& callback,
audio_io_handle_t audioIo);
audio_port_handle_t getDeviceIdForIo(audio_io_handle_t audioIo);
@@ -413,7 +413,7 @@
private:
Mutex mLock;
DefaultKeyedVector<audio_io_handle_t, sp<AudioIoDescriptor> > mIoDescriptors;
- DefaultKeyedVector<audio_io_handle_t, Vector < sp<AudioDeviceCallback> > >
+ DefaultKeyedVector<audio_io_handle_t, Vector < wp<AudioDeviceCallback> > >
mAudioDeviceCallbacks;
// cached values for recording getInputBufferSize() queries
size_t mInBuffSize; // zero indicates cache is invalid
diff --git a/media/libaudioclient/include/media/AudioTrack.h b/media/libaudioclient/include/media/AudioTrack.h
index 014df20..2adacd7 100644
--- a/media/libaudioclient/include/media/AudioTrack.h
+++ b/media/libaudioclient/include/media/AudioTrack.h
@@ -35,7 +35,7 @@
// ----------------------------------------------------------------------------
-class AudioTrack : public RefBase
+class AudioTrack : public AudioSystem::AudioDeviceCallback
{
public:
@@ -605,7 +605,11 @@
/* Returns the ID of the audio device actually used by the output to which this AudioTrack is
* attached.
- * A value of AUDIO_PORT_HANDLE_NONE indicates the audio track is not attached to any output.
+ * When the AudioTrack is inactive, the device ID returned can be either:
+ * - AUDIO_PORT_HANDLE_NONE if the AudioTrack is not attached to any output.
+ * - The device ID used before paused or stopped.
+ * - The device ID selected by audio policy manager of setOutputDevice() if the AudioTrack
+ * has not been started yet.
*
* Parameters:
* none.
@@ -845,6 +849,12 @@
status_t removeAudioDeviceCallback(
const sp<AudioSystem::AudioDeviceCallback>& callback);
+ // AudioSystem::AudioDeviceCallback> virtuals
+ virtual void onAudioDeviceUpdate(audio_io_handle_t audioIo,
+ audio_port_handle_t deviceId);
+
+
+
/* Obtain the pending duration in milliseconds for playback of pure PCM
* (mixable without embedded timing) data remaining in AudioTrack.
*
@@ -974,6 +984,8 @@
void restartIfDisabled();
+ void updateRoutedDeviceId_l();
+
// Next 4 fields may be changed if IAudioTrack is re-created, but always != 0
sp<IAudioTrack> mAudioTrack;
sp<IMemory> mCblkMemory;
@@ -1085,8 +1097,10 @@
// reset by stop() but continues monotonically
// after new IAudioTrack to restore mPosition,
// and could be easily widened to uint64_t
- int64_t mStartUs; // the start time after flush or stop.
+ int64_t mStartFromZeroUs; // the start time after flush or stop,
+ // when position should be 0.
// only used for offloaded and direct tracks.
+ int64_t mStartNs; // the time when start() is called.
ExtendedTimestamp mStartEts; // Extended timestamp at start for normal
// AudioTracks.
AudioTimestamp mStartTs; // Timestamp at start for offloaded or direct
@@ -1163,7 +1177,7 @@
uid_t mClientUid;
pid_t mClientPid;
- sp<AudioSystem::AudioDeviceCallback> mDeviceCallback;
+ wp<AudioSystem::AudioDeviceCallback> mDeviceCallback;
audio_port_handle_t mPortId; // unique ID allocated by audio policy
};
diff --git a/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp b/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp
index 4f1fed5..aae80b6 100644
--- a/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp
+++ b/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp
@@ -35,6 +35,15 @@
// effect_handle_t interface implementation for bass boost
extern "C" const struct effect_interface_s gLvmEffectInterface;
+// Turn on VERY_VERY_VERBOSE_LOGGING to log parameter get and set for effects.
+
+//#define VERY_VERY_VERBOSE_LOGGING
+#ifdef VERY_VERY_VERBOSE_LOGGING
+#define ALOGVV ALOGV
+#else
+#define ALOGVV(a...) do { } while (false)
+#endif
+
#define LVM_ERROR_CHECK(LvmStatus, callingFunc, calledFunc){\
if ((LvmStatus) == LVM_NULLADDRESS){\
ALOGV("\tLVM_ERROR : Parameter error - "\
@@ -140,26 +149,43 @@
void LvmEffect_free (EffectContext *pContext);
int Effect_setConfig (EffectContext *pContext, effect_config_t *pConfig);
void Effect_getConfig (EffectContext *pContext, effect_config_t *pConfig);
-int BassBoost_setParameter (EffectContext *pContext, void *pParam, void *pValue);
+int BassBoost_setParameter (EffectContext *pContext,
+ uint32_t paramSize,
+ void *pParam,
+ uint32_t valueSize,
+ void *pValue);
int BassBoost_getParameter (EffectContext *pContext,
- void *pParam,
- uint32_t *pValueSize,
- void *pValue);
-int Virtualizer_setParameter (EffectContext *pContext, void *pParam, void *pValue);
-int Virtualizer_getParameter (EffectContext *pContext,
- void *pParam,
- uint32_t *pValueSize,
- void *pValue);
-int Equalizer_setParameter (EffectContext *pContext,
- void *pParam,
- uint32_t valueSize,
- void *pValue);
-int Equalizer_getParameter (EffectContext *pContext,
+ uint32_t paramSize,
void *pParam,
uint32_t *pValueSize,
void *pValue);
-int Volume_setParameter (EffectContext *pContext, void *pParam, void *pValue);
+int Virtualizer_setParameter (EffectContext *pContext,
+ uint32_t paramSize,
+ void *pParam,
+ uint32_t valueSize,
+ void *pValue);
+int Virtualizer_getParameter (EffectContext *pContext,
+ uint32_t paramSize,
+ void *pParam,
+ uint32_t *pValueSize,
+ void *pValue);
+int Equalizer_setParameter (EffectContext *pContext,
+ uint32_t paramSize,
+ void *pParam,
+ uint32_t valueSize,
+ void *pValue);
+int Equalizer_getParameter (EffectContext *pContext,
+ uint32_t paramSize,
+ void *pParam,
+ uint32_t *pValueSize,
+ void *pValue);
+int Volume_setParameter (EffectContext *pContext,
+ uint32_t paramSize,
+ void *pParam,
+ uint32_t valueSize,
+ void *pValue);
int Volume_getParameter (EffectContext *pContext,
+ uint32_t paramSize,
void *pParam,
uint32_t *pValueSize,
void *pValue);
@@ -985,8 +1011,12 @@
float energyBassBoost = 0;
float crossCorrection = 0;
+ bool eqEnabled = pContext->pBundledContext->bEqualizerEnabled == LVM_TRUE;
+ bool bbEnabled = pContext->pBundledContext->bBassEnabled == LVM_TRUE;
+ bool viEnabled = pContext->pBundledContext->bVirtualizerEnabled == LVM_TRUE;
+
//EQ contribution
- if (pContext->pBundledContext->bEqualizerEnabled == LVM_TRUE) {
+ if (eqEnabled) {
for (int i = 0; i < FIVEBAND_NUMBANDS; i++) {
float bandFactor = pContext->pBundledContext->bandGaindB[i]/15.0;
float bandCoefficient = LimitLevel_bandEnergyCoefficient[i];
@@ -1012,35 +1042,37 @@
}
bandFactorSum -= 1.0;
if (bandFactorSum > 0)
- crossCorrection = bandFactorSum * 0.7;
+ crossCorrection = bandFactorSum * 0.7;
}
//BassBoost contribution
- if (pContext->pBundledContext->bBassEnabled == LVM_TRUE) {
+ if (bbEnabled) {
float boostFactor = (pContext->pBundledContext->BassStrengthSaved)/1000.0;
float boostCoefficient = LimitLevel_bassBoostEnergyCoefficient;
energyContribution += boostFactor * boostCoefficient * boostCoefficient;
- for (int i = 0; i < FIVEBAND_NUMBANDS; i++) {
- float bandFactor = pContext->pBundledContext->bandGaindB[i]/15.0;
- float bandCrossCoefficient = LimitLevel_bassBoostEnergyCrossCoefficient[i];
- float bandEnergy = boostFactor * bandFactor *
+ if (eqEnabled) {
+ for (int i = 0; i < FIVEBAND_NUMBANDS; i++) {
+ float bandFactor = pContext->pBundledContext->bandGaindB[i]/15.0;
+ float bandCrossCoefficient = LimitLevel_bassBoostEnergyCrossCoefficient[i];
+ float bandEnergy = boostFactor * bandFactor *
bandCrossCoefficient;
- if (bandEnergy > 0)
- energyBassBoost += bandEnergy;
+ if (bandEnergy > 0)
+ energyBassBoost += bandEnergy;
+ }
}
}
//Virtualizer contribution
- if (pContext->pBundledContext->bVirtualizerEnabled == LVM_TRUE) {
+ if (viEnabled) {
energyContribution += LimitLevel_virtualizerContribution *
LimitLevel_virtualizerContribution;
}
double totalEnergyEstimation = sqrt(energyContribution + energyCross + energyBassBoost) -
crossCorrection;
- ALOGV(" TOTAL energy estimation: %0.2f", totalEnergyEstimation);
+ ALOGV(" TOTAL energy estimation: %0.2f dB", totalEnergyEstimation);
//roundoff
int maxLevelRound = (int)(totalEnergyEstimation + 0.99);
@@ -1058,6 +1090,8 @@
/* Activate the initial settings */
LvmStatus = LVM_SetControlParameters(pContext->pBundledContext->hInstance, &ActiveParams);
LVM_ERROR_CHECK(LvmStatus, "LVM_SetControlParameters", "LvmEffect_limitLevel")
+
+ ALOGV("LVM_SetControlParameters return:%d", (int)LvmStatus);
//ALOGV("\tLvmEffect_limitLevel just Set -> %d\n",
// ActiveParams.pEQNB_BandDefinition[band].Gain);
@@ -1308,6 +1342,8 @@
ALOGV("\tEffect_setConfig Succesfully called LVM_SetControlParameters\n");
pContext->pBundledContext->SampleRate = SampleRate;
+ LvmEffect_limitLevel(pContext);
+
}else{
//ALOGV("\tEffect_setConfig keep sampling rate at %d", SampleRate);
}
@@ -2165,59 +2201,54 @@
//
//----------------------------------------------------------------------------
-int BassBoost_getParameter(EffectContext *pContext,
- void *pParam,
- uint32_t *pValueSize,
- void *pValue){
+int BassBoost_getParameter(EffectContext *pContext,
+ uint32_t paramSize,
+ void *pParam,
+ uint32_t *pValueSize,
+ void *pValue) {
int status = 0;
- int32_t *pParamTemp = (int32_t *)pParam;
- int32_t param = *pParamTemp++;
+ int32_t *params = (int32_t *)pParam;
- //ALOGV("\tBassBoost_getParameter start");
+ ALOGVV("%s start", __func__);
- switch (param){
- case BASSBOOST_PARAM_STRENGTH_SUPPORTED:
- if (*pValueSize != sizeof(uint32_t)){
- ALOGV("\tLVM_ERROR : BassBoost_getParameter() invalid pValueSize1 %d", *pValueSize);
- return -EINVAL;
- }
- *pValueSize = sizeof(uint32_t);
- break;
- case BASSBOOST_PARAM_STRENGTH:
- if (*pValueSize != sizeof(int16_t)){
- ALOGV("\tLVM_ERROR : BassBoost_getParameter() invalid pValueSize2 %d", *pValueSize);
- return -EINVAL;
- }
- *pValueSize = sizeof(int16_t);
- break;
-
- default:
- ALOGV("\tLVM_ERROR : BassBoost_getParameter() invalid param %d", param);
- return -EINVAL;
+ if (paramSize < sizeof(int32_t)) {
+ ALOGV("%s invalid paramSize: %u", __func__, paramSize);
+ return -EINVAL;
}
-
- switch (param){
+ switch (params[0]) {
case BASSBOOST_PARAM_STRENGTH_SUPPORTED:
- *(uint32_t *)pValue = 1;
+ if (*pValueSize != sizeof(uint32_t)) { // legacy: check equality here.
+ ALOGV("%s BASSBOOST_PARAM_STRENGTH_SUPPORTED invalid *pValueSize %u",
+ __func__, *pValueSize);
+ status = -EINVAL;
+ break;
+ }
+ // no need to set *pValueSize
- //ALOGV("\tBassBoost_getParameter() BASSBOOST_PARAM_STRENGTH_SUPPORTED Value is %d",
- // *(uint32_t *)pValue);
+ *(uint32_t *)pValue = 1;
+ ALOGVV("%s BASSBOOST_PARAM_STRENGTH_SUPPORTED %u", __func__, *(uint32_t *)pValue);
break;
case BASSBOOST_PARAM_STRENGTH:
- *(int16_t *)pValue = BassGetStrength(pContext);
+ if (*pValueSize != sizeof(int16_t)) { // legacy: check equality here.
+ ALOGV("%s BASSBOOST_PARAM_STRENGTH invalid *pValueSize %u",
+ __func__, *pValueSize);
+ status = -EINVAL;
+ break;
+ }
+ // no need to set *pValueSize
- //ALOGV("\tBassBoost_getParameter() BASSBOOST_PARAM_STRENGTH Value is %d",
- // *(int16_t *)pValue);
+ *(int16_t *)pValue = BassGetStrength(pContext);
+ ALOGVV("%s BASSBOOST_PARAM_STRENGTH %d", __func__, *(int16_t *)pValue);
break;
default:
- ALOGV("\tLVM_ERROR : BassBoost_getParameter() invalid param %d", param);
+ ALOGV("%s invalid param %d", __func__, params[0]);
status = -EINVAL;
break;
}
- //ALOGV("\tBassBoost_getParameter end");
+ ALOGVV("%s end param: %d, status: %d", __func__, params[0], status);
return status;
} /* end BassBoost_getParameter */
@@ -2236,27 +2267,42 @@
//
//----------------------------------------------------------------------------
-int BassBoost_setParameter (EffectContext *pContext, void *pParam, void *pValue){
+int BassBoost_setParameter(EffectContext *pContext,
+ uint32_t paramSize,
+ void *pParam,
+ uint32_t valueSize,
+ void *pValue) {
int status = 0;
- int16_t strength;
- int32_t *pParamTemp = (int32_t *)pParam;
+ int32_t *params = (int32_t *)pParam;
- //ALOGV("\tBassBoost_setParameter start");
+ ALOGVV("%s start", __func__);
- switch (*pParamTemp){
- case BASSBOOST_PARAM_STRENGTH:
- strength = *(int16_t *)pValue;
- //ALOGV("\tBassBoost_setParameter() BASSBOOST_PARAM_STRENGTH value is %d", strength);
- //ALOGV("\tBassBoost_setParameter() Calling pBassBoost->BassSetStrength");
+ if (paramSize != sizeof(int32_t)) { // legacy: check equality here.
+ ALOGV("%s invalid paramSize: %u", __func__, paramSize);
+ return -EINVAL;
+ }
+ switch (params[0]) {
+ case BASSBOOST_PARAM_STRENGTH: {
+ if (valueSize < sizeof(int16_t)) {
+ ALOGV("%s BASSBOOST_PARAM_STRENGTH invalid valueSize: %u", __func__, valueSize);
+ status = -EINVAL;
+ break;
+ }
+
+ const int16_t strength = *(int16_t *)pValue;
+ ALOGVV("%s BASSBOOST_PARAM_STRENGTH %d", __func__, strength);
+ ALOGVV("%s BASSBOOST_PARAM_STRENGTH Calling BassSetStrength", __func__);
BassSetStrength(pContext, (int32_t)strength);
- //ALOGV("\tBassBoost_setParameter() Called pBassBoost->BassSetStrength");
- break;
+ ALOGVV("%s BASSBOOST_PARAM_STRENGTH Called BassSetStrength", __func__);
+ } break;
+
default:
- ALOGV("\tLVM_ERROR : BassBoost_setParameter() invalid param %d", *pParamTemp);
+ ALOGV("%s invalid param %d", __func__, params[0]);
+ status = -EINVAL;
break;
}
- //ALOGV("\tBassBoost_setParameter end");
+ ALOGVV("%s end param: %d, status: %d", __func__, params[0], status);
return status;
} /* end BassBoost_setParameter */
@@ -2281,92 +2327,97 @@
//
//----------------------------------------------------------------------------
-int Virtualizer_getParameter(EffectContext *pContext,
- void *pParam,
- uint32_t *pValueSize,
- void *pValue){
+int Virtualizer_getParameter(EffectContext *pContext,
+ uint32_t paramSize,
+ void *pParam,
+ uint32_t *pValueSize,
+ void *pValue) {
int status = 0;
- int32_t *pParamTemp = (int32_t *)pParam;
- int32_t param = *pParamTemp++;
+ int32_t *params = (int32_t *)pParam;
- //ALOGV("\tVirtualizer_getParameter start");
+ ALOGVV("%s start", __func__);
- switch (param){
- case VIRTUALIZER_PARAM_STRENGTH_SUPPORTED:
- if (*pValueSize != sizeof(uint32_t)){
- ALOGV("\tLVM_ERROR : Virtualizer_getParameter() invalid pValueSize %d",*pValueSize);
- return -EINVAL;
- }
- *pValueSize = sizeof(uint32_t);
- break;
- case VIRTUALIZER_PARAM_STRENGTH:
- if (*pValueSize != sizeof(int16_t)){
- ALOGV("\tLVM_ERROR : Virtualizer_getParameter() invalid pValueSize2 %d",*pValueSize);
- return -EINVAL;
- }
- *pValueSize = sizeof(int16_t);
- break;
- case VIRTUALIZER_PARAM_VIRTUAL_SPEAKER_ANGLES:
- // return value size can only be interpreted as relative to input value,
- // deferring validity check to below
- break;
- case VIRTUALIZER_PARAM_VIRTUALIZATION_MODE:
- if (*pValueSize != sizeof(uint32_t)){
- ALOGV("\tLVM_ERROR : Virtualizer_getParameter() invalid pValueSize %d",*pValueSize);
- return -EINVAL;
- }
- *pValueSize = sizeof(uint32_t);
- break;
- default:
- ALOGV("\tLVM_ERROR : Virtualizer_getParameter() invalid param %d", param);
- return -EINVAL;
+ if (paramSize < sizeof(int32_t)) {
+ ALOGV("%s invalid paramSize: %u", __func__, paramSize);
+ return -EINVAL;
}
-
- switch (param){
+ switch (params[0]) {
case VIRTUALIZER_PARAM_STRENGTH_SUPPORTED:
- *(uint32_t *)pValue = 1;
+ if (*pValueSize != sizeof(uint32_t)) { // legacy: check equality here.
+ ALOGV("%s VIRTUALIZER_PARAM_STRENGTH_SUPPORTED invalid *pValueSize %u",
+ __func__, *pValueSize);
+ status = -EINVAL;
+ break;
+ }
+ // no need to set *pValueSize
- //ALOGV("\tVirtualizer_getParameter() VIRTUALIZER_PARAM_STRENGTH_SUPPORTED Value is %d",
- // *(uint32_t *)pValue);
+ *(uint32_t *)pValue = 1;
+ ALOGVV("%s VIRTUALIZER_PARAM_STRENGTH_SUPPORTED %d", __func__, *(uint32_t *)pValue);
break;
case VIRTUALIZER_PARAM_STRENGTH:
+ if (*pValueSize != sizeof(int16_t)) { // legacy: check equality here.
+ ALOGV("%s VIRTUALIZER_PARAM_STRENGTH invalid *pValueSize %u",
+ __func__, *pValueSize);
+ status = -EINVAL;
+ break;
+ }
+ // no need to set *pValueSize
+
*(int16_t *)pValue = VirtualizerGetStrength(pContext);
- //ALOGV("\tVirtualizer_getParameter() VIRTUALIZER_PARAM_STRENGTH Value is %d",
- // *(int16_t *)pValue);
+ ALOGVV("%s VIRTUALIZER_PARAM_STRENGTH %d", __func__, *(int16_t *)pValue);
break;
case VIRTUALIZER_PARAM_VIRTUAL_SPEAKER_ANGLES: {
- const audio_channel_mask_t channelMask = (audio_channel_mask_t) *pParamTemp++;
- const audio_devices_t deviceType = (audio_devices_t) *pParamTemp;
- uint32_t nbChannels = audio_channel_count_from_out_mask(channelMask);
- if (*pValueSize < 3 * nbChannels * sizeof(int32_t)){
- ALOGV("\tLVM_ERROR : Virtualizer_getParameter() invalid pValueSize %d",*pValueSize);
- return -EINVAL;
+ if (paramSize < 3 * sizeof(int32_t)) {
+ ALOGV("%s VIRTUALIZER_PARAM_SPEAKER_ANGLES invalid paramSize: %u",
+ __func__, paramSize);
+ status = -EINVAL;
+ break;
}
+
+ const audio_channel_mask_t channelMask = (audio_channel_mask_t) params[1];
+ const audio_devices_t deviceType = (audio_devices_t) params[2];
+ const uint32_t nbChannels = audio_channel_count_from_out_mask(channelMask);
+ const uint32_t valueSizeRequired = 3 * nbChannels * sizeof(int32_t);
+ if (*pValueSize < valueSizeRequired) {
+ ALOGV("%s VIRTUALIZER_PARAM_SPEAKER_ANGLES invalid *pValueSize %u",
+ __func__, *pValueSize);
+ status = -EINVAL;
+ break;
+ }
+ *pValueSize = valueSizeRequired;
+
// verify the configuration is supported
status = VirtualizerIsConfigurationSupported(channelMask, deviceType);
if (status == 0) {
- ALOGV("VIRTUALIZER_PARAM_VIRTUAL_SPEAKER_ANGLES supports mask=0x%x device=0x%x",
- channelMask, deviceType);
+ ALOGV("%s VIRTUALIZER_PARAM_VIRTUAL_SPEAKER_ANGLES mask=0x%x device=0x%x",
+ __func__, channelMask, deviceType);
// configuration is supported, get the angles
VirtualizerGetSpeakerAngles(channelMask, deviceType, (int32_t *)pValue);
}
- }
- break;
+ } break;
case VIRTUALIZER_PARAM_VIRTUALIZATION_MODE:
- *(uint32_t *)pValue = (uint32_t) VirtualizerGetVirtualizationMode(pContext);
+ if (*pValueSize != sizeof(uint32_t)) { // legacy: check equality here.
+ ALOGV("%s VIRTUALIZER_PARAM_VIRTUALIZATION_MODE invalid *pValueSize %u",
+ __func__, *pValueSize);
+ status = -EINVAL;
+ break;
+ }
+ // no need to set *pValueSize
+
+ *(uint32_t *)pValue = (uint32_t) VirtualizerGetVirtualizationMode(pContext);
break;
default:
- ALOGV("\tLVM_ERROR : Virtualizer_getParameter() invalid param %d", param);
+ ALOGV("%s invalid param %d", __func__, params[0]);
status = -EINVAL;
break;
}
- ALOGV("\tVirtualizer_getParameter end returning status=%d", status);
+ ALOGVV("%s end param: %d, status: %d", __func__, params[0], status);
return status;
} /* end Virtualizer_getParameter */
@@ -2385,37 +2436,57 @@
//
//----------------------------------------------------------------------------
-int Virtualizer_setParameter (EffectContext *pContext, void *pParam, void *pValue){
+int Virtualizer_setParameter(EffectContext *pContext,
+ uint32_t paramSize,
+ void *pParam,
+ uint32_t valueSize,
+ void *pValue) {
int status = 0;
- int16_t strength;
- int32_t *pParamTemp = (int32_t *)pParam;
- int32_t param = *pParamTemp++;
+ int32_t *params = (int32_t *)pParam;
- //ALOGV("\tVirtualizer_setParameter start");
+ ALOGVV("%s start", __func__);
- switch (param){
- case VIRTUALIZER_PARAM_STRENGTH:
- strength = *(int16_t *)pValue;
- //ALOGV("\tVirtualizer_setParameter() VIRTUALIZER_PARAM_STRENGTH value is %d", strength);
- //ALOGV("\tVirtualizer_setParameter() Calling pVirtualizer->setStrength");
+ if (paramSize != sizeof(int32_t)) { // legacy: check equality here.
+ ALOGV("%s invalid paramSize: %u", __func__, paramSize);
+ return -EINVAL;
+ }
+ switch (params[0]) {
+ case VIRTUALIZER_PARAM_STRENGTH: {
+ if (valueSize < sizeof(int16_t)) {
+ ALOGV("%s VIRTUALIZER_PARAM_STRENGTH invalid valueSize: %u", __func__, valueSize);
+ status = -EINVAL;
+ break;
+ }
+
+ const int16_t strength = *(int16_t *)pValue;
+ ALOGVV("%s VIRTUALIZER_PARAM_STRENGTH %d", __func__, strength);
+ ALOGVV("%s VIRTUALIZER_PARAM_STRENGTH Calling VirtualizerSetStrength", __func__);
VirtualizerSetStrength(pContext, (int32_t)strength);
- //ALOGV("\tVirtualizer_setParameter() Called pVirtualizer->setStrength");
- break;
+ ALOGVV("%s VIRTUALIZER_PARAM_STRENGTH Called VirtualizerSetStrength", __func__);
+ } break;
case VIRTUALIZER_PARAM_FORCE_VIRTUALIZATION_MODE: {
- const audio_devices_t deviceType = *(audio_devices_t *) pValue;
- status = VirtualizerForceVirtualizationMode(pContext, deviceType);
- //ALOGV("VIRTUALIZER_PARAM_FORCE_VIRTUALIZATION_MODE device=0x%x result=%d",
- // deviceType, status);
+ if (valueSize < sizeof(int32_t)) {
+ ALOGV("%s VIRTUALIZER_PARAM_FORCE_VIRTUALIZATION_MODE invalid valueSize: %u",
+ __func__, valueSize);
+ android_errorWriteLog(0x534e4554, "64478003");
+ status = -EINVAL;
+ break;
}
- break;
+
+ const audio_devices_t deviceType = (audio_devices_t)*(int32_t *)pValue;
+ status = VirtualizerForceVirtualizationMode(pContext, deviceType);
+ ALOGVV("%s VIRTUALIZER_PARAM_FORCE_VIRTUALIZATION_MODE device=%#x result=%d",
+ __func__, deviceType, status);
+ } break;
default:
- ALOGV("\tLVM_ERROR : Virtualizer_setParameter() invalid param %d", param);
+ ALOGV("%s invalid param %d", __func__, params[0]);
+ status = -EINVAL;
break;
}
- //ALOGV("\tVirtualizer_setParameter end");
+ ALOGVV("%s end param: %d, status: %d", __func__, params[0], status);
return status;
} /* end Virtualizer_setParameter */
@@ -2439,174 +2510,215 @@
// Side Effects:
//
//----------------------------------------------------------------------------
-int Equalizer_getParameter(EffectContext *pContext,
- void *pParam,
- uint32_t *pValueSize,
- void *pValue){
+int Equalizer_getParameter(EffectContext *pContext,
+ uint32_t paramSize,
+ void *pParam,
+ uint32_t *pValueSize,
+ void *pValue) {
int status = 0;
- int32_t *pParamTemp = (int32_t *)pParam;
- int32_t param = *pParamTemp++;
- int32_t param2;
- char *name;
+ int32_t *params = (int32_t *)pParam;
- //ALOGV("\tEqualizer_getParameter start");
+ ALOGVV("%s start", __func__);
- switch (param) {
+ if (paramSize < sizeof(int32_t)) {
+ ALOGV("%s invalid paramSize: %u", __func__, paramSize);
+ return -EINVAL;
+ }
+ switch (params[0]) {
case EQ_PARAM_NUM_BANDS:
+ if (*pValueSize < sizeof(uint16_t)) {
+ ALOGV("%s EQ_PARAM_NUM_BANDS invalid *pValueSize %u", __func__, *pValueSize);
+ status = -EINVAL;
+ break;
+ }
+ *pValueSize = sizeof(uint16_t);
+
+ *(uint16_t *)pValue = (uint16_t)FIVEBAND_NUMBANDS;
+ ALOGVV("%s EQ_PARAM_NUM_BANDS %u", __func__, *(uint16_t *)pValue);
+ break;
+
case EQ_PARAM_CUR_PRESET:
+ if (*pValueSize < sizeof(uint16_t)) {
+ ALOGV("%s EQ_PARAM_CUR_PRESET invalid *pValueSize %u", __func__, *pValueSize);
+ status = -EINVAL;
+ break;
+ }
+ *pValueSize = sizeof(uint16_t);
+
+ *(uint16_t *)pValue = (uint16_t)EqualizerGetPreset(pContext);
+ ALOGVV("%s EQ_PARAM_CUR_PRESET %u", __func__, *(uint16_t *)pValue);
+ break;
+
case EQ_PARAM_GET_NUM_OF_PRESETS:
- case EQ_PARAM_BAND_LEVEL:
- case EQ_PARAM_GET_BAND:
+ if (*pValueSize < sizeof(uint16_t)) {
+ ALOGV("%s EQ_PARAM_GET_NUM_OF_PRESETS invalid *pValueSize %u", __func__, *pValueSize);
+ status = -EINVAL;
+ break;
+ }
+ *pValueSize = sizeof(uint16_t);
+
+ *(uint16_t *)pValue = (uint16_t)EqualizerGetNumPresets();
+ ALOGVV("%s EQ_PARAM_GET_NUM_OF_PRESETS %u", __func__, *(uint16_t *)pValue);
+ break;
+
+ case EQ_PARAM_GET_BAND: {
+ if (paramSize < 2 * sizeof(int32_t)) {
+ ALOGV("%s EQ_PARAM_GET_BAND invalid paramSize: %u", __func__, paramSize);
+ status = -EINVAL;
+ break;
+ }
+ if (*pValueSize < sizeof(uint16_t)) {
+ ALOGV("%s EQ_PARAM_GET_BAND invalid *pValueSize %u", __func__, *pValueSize);
+ status = -EINVAL;
+ break;
+ }
+ *pValueSize = sizeof(uint16_t);
+
+ const int32_t frequency = params[1];
+ *(uint16_t *)pValue = (uint16_t)EqualizerGetBand(pContext, frequency);
+ ALOGVV("%s EQ_PARAM_GET_BAND frequency %d, band %u",
+ __func__, frequency, *(uint16_t *)pValue);
+ } break;
+
+ case EQ_PARAM_BAND_LEVEL: {
+ if (paramSize < 2 * sizeof(int32_t)) {
+ ALOGV("%s EQ_PARAM_BAND_LEVEL invalid paramSize %u", __func__, paramSize);
+ status = -EINVAL;
+ break;
+ }
if (*pValueSize < sizeof(int16_t)) {
- ALOGV("\tLVM_ERROR : Equalizer_getParameter() invalid pValueSize 1 %d", *pValueSize);
- return -EINVAL;
+ ALOGV("%s EQ_PARAM_BAND_LEVEL invalid *pValueSize %u", __func__, *pValueSize);
+ status = -EINVAL;
+ break;
}
*pValueSize = sizeof(int16_t);
- break;
+
+ const int32_t band = params[1];
+ if (band < 0 || band >= FIVEBAND_NUMBANDS) {
+ if (band < 0) {
+ android_errorWriteLog(0x534e4554, "32438598");
+ ALOGW("%s EQ_PARAM_BAND_LEVEL invalid band %d", __func__, band);
+ }
+ status = -EINVAL;
+ break;
+ }
+ *(int16_t *)pValue = (int16_t)EqualizerGetBandLevel(pContext, band);
+ ALOGVV("%s EQ_PARAM_BAND_LEVEL band %d, level %d",
+ __func__, band, *(int16_t *)pValue);
+ } break;
case EQ_PARAM_LEVEL_RANGE:
if (*pValueSize < 2 * sizeof(int16_t)) {
- ALOGV("\tLVM_ERROR : Equalizer_getParameter() invalid pValueSize 2 %d", *pValueSize);
- return -EINVAL;
+ ALOGV("%s EQ_PARAM_LEVEL_RANGE invalid *pValueSize %u", __func__, *pValueSize);
+ status = -EINVAL;
+ break;
}
*pValueSize = 2 * sizeof(int16_t);
- break;
- case EQ_PARAM_BAND_FREQ_RANGE:
- if (*pValueSize < 2 * sizeof(int32_t)) {
- ALOGV("\tLVM_ERROR : Equalizer_getParameter() invalid pValueSize 3 %d", *pValueSize);
- return -EINVAL;
- }
- *pValueSize = 2 * sizeof(int32_t);
- break;
- case EQ_PARAM_CENTER_FREQ:
- if (*pValueSize < sizeof(int32_t)) {
- ALOGV("\tLVM_ERROR : Equalizer_getParameter() invalid pValueSize 5 %d", *pValueSize);
- return -EINVAL;
- }
- *pValueSize = sizeof(int32_t);
- break;
-
- case EQ_PARAM_GET_PRESET_NAME:
- break;
-
- case EQ_PARAM_PROPERTIES:
- if (*pValueSize < (2 + FIVEBAND_NUMBANDS) * sizeof(uint16_t)) {
- ALOGV("\tLVM_ERROR : Equalizer_getParameter() invalid pValueSize 1 %d", *pValueSize);
- return -EINVAL;
- }
- *pValueSize = (2 + FIVEBAND_NUMBANDS) * sizeof(uint16_t);
- break;
-
- default:
- ALOGV("\tLVM_ERROR : Equalizer_getParameter unknown param %d", param);
- return -EINVAL;
- }
-
- switch (param) {
- case EQ_PARAM_NUM_BANDS:
- *(uint16_t *)pValue = (uint16_t)FIVEBAND_NUMBANDS;
- //ALOGV("\tEqualizer_getParameter() EQ_PARAM_NUM_BANDS %d", *(int16_t *)pValue);
- break;
-
- case EQ_PARAM_LEVEL_RANGE:
*(int16_t *)pValue = -1500;
*((int16_t *)pValue + 1) = 1500;
- //ALOGV("\tEqualizer_getParameter() EQ_PARAM_LEVEL_RANGE min %d, max %d",
- // *(int16_t *)pValue, *((int16_t *)pValue + 1));
+ ALOGVV("%s EQ_PARAM_LEVEL_RANGE min %d, max %d",
+ __func__, *(int16_t *)pValue, *((int16_t *)pValue + 1));
break;
- case EQ_PARAM_BAND_LEVEL:
- param2 = *pParamTemp;
- if (param2 < 0 || param2 >= FIVEBAND_NUMBANDS) {
+ case EQ_PARAM_BAND_FREQ_RANGE: {
+ if (paramSize < 2 * sizeof(int32_t)) {
+ ALOGV("%s EQ_PARAM_BAND_FREQ_RANGE invalid paramSize: %u", __func__, paramSize);
status = -EINVAL;
- if (param2 < 0) {
- android_errorWriteLog(0x534e4554, "32438598");
- ALOGW("\tERROR Equalizer_getParameter() EQ_PARAM_BAND_LEVEL band %d", param2);
- }
break;
}
- *(int16_t *)pValue = (int16_t)EqualizerGetBandLevel(pContext, param2);
- //ALOGV("\tEqualizer_getParameter() EQ_PARAM_BAND_LEVEL band %d, level %d",
- // param2, *(int32_t *)pValue);
- break;
-
- case EQ_PARAM_CENTER_FREQ:
- param2 = *pParamTemp;
- if (param2 < 0 || param2 >= FIVEBAND_NUMBANDS) {
+ if (*pValueSize < 2 * sizeof(int32_t)) {
+ ALOGV("%s EQ_PARAM_BAND_FREQ_RANGE invalid *pValueSize %u", __func__, *pValueSize);
status = -EINVAL;
- if (param2 < 0) {
- android_errorWriteLog(0x534e4554, "32436341");
- ALOGW("\tERROR Equalizer_getParameter() EQ_PARAM_CENTER_FREQ band %d", param2);
- }
break;
}
- *(int32_t *)pValue = EqualizerGetCentreFrequency(pContext, param2);
- //ALOGV("\tEqualizer_getParameter() EQ_PARAM_CENTER_FREQ band %d, frequency %d",
- // param2, *(int32_t *)pValue);
- break;
+ *pValueSize = 2 * sizeof(int32_t);
- case EQ_PARAM_BAND_FREQ_RANGE:
- param2 = *pParamTemp;
- if (param2 < 0 || param2 >= FIVEBAND_NUMBANDS) {
- status = -EINVAL;
- if (param2 < 0) {
+ const int32_t band = params[1];
+ if (band < 0 || band >= FIVEBAND_NUMBANDS) {
+ if (band < 0) {
android_errorWriteLog(0x534e4554, "32247948");
- ALOGW("\tERROR Equalizer_getParameter() EQ_PARAM_BAND_FREQ_RANGE band %d", param2);
+ ALOGW("%s EQ_PARAM_BAND_FREQ_RANGE invalid band %d",
+ __func__, band);
}
- break;
- }
- EqualizerGetBandFreqRange(pContext, param2, (uint32_t *)pValue, ((uint32_t *)pValue + 1));
- //ALOGV("\tEqualizer_getParameter() EQ_PARAM_BAND_FREQ_RANGE band %d, min %d, max %d",
- // param2, *(int32_t *)pValue, *((int32_t *)pValue + 1));
- break;
-
- case EQ_PARAM_GET_BAND:
- param2 = *pParamTemp;
- *(uint16_t *)pValue = (uint16_t)EqualizerGetBand(pContext, param2);
- //ALOGV("\tEqualizer_getParameter() EQ_PARAM_GET_BAND frequency %d, band %d",
- // param2, *(uint16_t *)pValue);
- break;
-
- case EQ_PARAM_CUR_PRESET:
- *(uint16_t *)pValue = (uint16_t)EqualizerGetPreset(pContext);
- //ALOGV("\tEqualizer_getParameter() EQ_PARAM_CUR_PRESET %d", *(int32_t *)pValue);
- break;
-
- case EQ_PARAM_GET_NUM_OF_PRESETS:
- *(uint16_t *)pValue = (uint16_t)EqualizerGetNumPresets();
- //ALOGV("\tEqualizer_getParameter() EQ_PARAM_GET_NUM_OF_PRESETS %d", *(int16_t *)pValue);
- break;
-
- case EQ_PARAM_GET_PRESET_NAME:
- param2 = *pParamTemp;
- if ((param2 < 0 && param2 != PRESET_CUSTOM) || param2 >= EqualizerGetNumPresets()) {
status = -EINVAL;
- if (param2 < 0) {
- android_errorWriteLog(0x534e4554, "32448258");
- ALOGE("\tERROR Equalizer_getParameter() EQ_PARAM_GET_PRESET_NAME preset %d",
- param2);
+ break;
+ }
+ EqualizerGetBandFreqRange(pContext, band, (uint32_t *)pValue, ((uint32_t *)pValue + 1));
+ ALOGVV("%s EQ_PARAM_BAND_FREQ_RANGE band %d, min %d, max %d",
+ __func__, band, *(int32_t *)pValue, *((int32_t *)pValue + 1));
+
+ } break;
+
+ case EQ_PARAM_CENTER_FREQ: {
+ if (paramSize < 2 * sizeof(int32_t)) {
+ ALOGV("%s EQ_PARAM_CENTER_FREQ invalid paramSize: %u", __func__, paramSize);
+ status = -EINVAL;
+ break;
+ }
+ if (*pValueSize < sizeof(int32_t)) {
+ ALOGV("%s EQ_PARAM_CENTER_FREQ invalid *pValueSize %u", __func__, *pValueSize);
+ status = -EINVAL;
+ break;
+ }
+ *pValueSize = sizeof(int32_t);
+
+ const int32_t band = params[1];
+ if (band < 0 || band >= FIVEBAND_NUMBANDS) {
+ status = -EINVAL;
+ if (band < 0) {
+ android_errorWriteLog(0x534e4554, "32436341");
+ ALOGW("%s EQ_PARAM_CENTER_FREQ invalid band %d", __func__, band);
}
break;
}
+ *(int32_t *)pValue = EqualizerGetCentreFrequency(pContext, band);
+ ALOGVV("%s EQ_PARAM_CENTER_FREQ band %d, frequency %d",
+ __func__, band, *(int32_t *)pValue);
+ } break;
+ case EQ_PARAM_GET_PRESET_NAME: {
+ if (paramSize < 2 * sizeof(int32_t)) {
+ ALOGV("%s EQ_PARAM_PRESET_NAME invalid paramSize: %u", __func__, paramSize);
+ status = -EINVAL;
+ break;
+ }
if (*pValueSize < 1) {
- status = -EINVAL;
android_errorWriteLog(0x534e4554, "37536407");
+ status = -EINVAL;
break;
}
- name = (char *)pValue;
- strncpy(name, EqualizerGetPresetName(param2), *pValueSize - 1);
+ const int32_t preset = params[1];
+ if ((preset < 0 && preset != PRESET_CUSTOM) || preset >= EqualizerGetNumPresets()) {
+ if (preset < 0) {
+ android_errorWriteLog(0x534e4554, "32448258");
+ ALOGE("%s EQ_PARAM_GET_PRESET_NAME preset %d", __func__, preset);
+ }
+ status = -EINVAL;
+ break;
+ }
+
+ char * const name = (char *)pValue;
+ strncpy(name, EqualizerGetPresetName(preset), *pValueSize - 1);
name[*pValueSize - 1] = 0;
*pValueSize = strlen(name) + 1;
- //ALOGV("\tEqualizer_getParameter() EQ_PARAM_GET_PRESET_NAME preset %d, name %s len %d",
- // param2, gEqualizerPresets[param2].name, *pValueSize);
- break;
+ ALOGVV("%s EQ_PARAM_GET_PRESET_NAME preset %d, name %s len %d",
+ __func__, preset, gEqualizerPresets[preset].name, *pValueSize);
+
+ } break;
case EQ_PARAM_PROPERTIES: {
+ constexpr uint32_t requiredValueSize = (2 + FIVEBAND_NUMBANDS) * sizeof(uint16_t);
+ if (*pValueSize < requiredValueSize) {
+ ALOGV("%s EQ_PARAM_PROPERTIES invalid *pValueSize %u", __func__, *pValueSize);
+ status = -EINVAL;
+ break;
+ }
+ *pValueSize = requiredValueSize;
+
int16_t *p = (int16_t *)pValue;
- ALOGV("\tEqualizer_getParameter() EQ_PARAM_PROPERTIES");
+ ALOGV("%s EQ_PARAM_PROPERTIES", __func__);
p[0] = (int16_t)EqualizerGetPreset(pContext);
p[1] = (int16_t)FIVEBAND_NUMBANDS;
for (int i = 0; i < FIVEBAND_NUMBANDS; i++) {
@@ -2615,12 +2727,12 @@
} break;
default:
- ALOGV("\tLVM_ERROR : Equalizer_getParameter() invalid param %d", param);
+ ALOGV("%s invalid param %d", __func__, params[0]);
status = -EINVAL;
break;
}
- //GV("\tEqualizer_getParameter end\n");
+ ALOGVV("%s end param: %d, status: %d", __func__, params[0], status);
return status;
} /* end Equalizer_getParameter */
@@ -2640,74 +2752,89 @@
// Outputs:
//
//----------------------------------------------------------------------------
-int Equalizer_setParameter (EffectContext *pContext,
- void *pParam,
- uint32_t valueSize,
- void *pValue) {
+int Equalizer_setParameter(EffectContext *pContext,
+ uint32_t paramSize,
+ void *pParam,
+ uint32_t valueSize,
+ void *pValue) {
int status = 0;
- int32_t preset;
- int32_t band;
- int32_t level;
- int32_t *pParamTemp = (int32_t *)pParam;
- int32_t param = *pParamTemp++;
+ int32_t *params = (int32_t *)pParam;
+ ALOGVV("%s start", __func__);
- //ALOGV("\tEqualizer_setParameter start");
- switch (param) {
- case EQ_PARAM_CUR_PRESET:
+ if (paramSize < sizeof(int32_t)) {
+ ALOGV("%s invalid paramSize: %u", __func__, paramSize);
+ return -EINVAL;
+ }
+ switch (params[0]) {
+ case EQ_PARAM_CUR_PRESET: {
if (valueSize < sizeof(int16_t)) {
- status = -EINVAL;
- break;
+ ALOGV("%s EQ_PARAM_CUR_PRESET invalid valueSize %u", __func__, valueSize);
+ status = -EINVAL;
+ break;
}
- preset = (int32_t)(*(uint16_t *)pValue);
+ const int32_t preset = (int32_t)*(uint16_t *)pValue;
- //ALOGV("\tEqualizer_setParameter() EQ_PARAM_CUR_PRESET %d", preset);
- if ((preset >= EqualizerGetNumPresets())||(preset < 0)) {
+ ALOGVV("%s EQ_PARAM_CUR_PRESET %d", __func__, preset);
+ if (preset >= EqualizerGetNumPresets() || preset < 0) {
+ ALOGV("%s EQ_PARAM_CUR_PRESET invalid preset %d", __func__, preset);
status = -EINVAL;
break;
}
EqualizerSetPreset(pContext, preset);
- break;
- case EQ_PARAM_BAND_LEVEL:
- if (valueSize < sizeof(int16_t)) {
- status = -EINVAL;
- break;
- }
- band = *pParamTemp;
- level = (int32_t)(*(int16_t *)pValue);
- //ALOGV("\tEqualizer_setParameter() EQ_PARAM_BAND_LEVEL band %d, level %d", band, level);
- if (band < 0 || band >= FIVEBAND_NUMBANDS) {
+ } break;
+
+ case EQ_PARAM_BAND_LEVEL: {
+ if (paramSize < 2 * sizeof(int32_t)) {
+ ALOGV("%s EQ_PARAM_BAND_LEVEL invalid paramSize: %u", __func__, paramSize);
status = -EINVAL;
+ break;
+ }
+ if (valueSize < sizeof(int16_t)) {
+ ALOGV("%s EQ_PARAM_BAND_LEVEL invalid valueSize %u", __func__, valueSize);
+ status = -EINVAL;
+ break;
+ }
+ const int32_t band = params[1];
+ const int32_t level = (int32_t)*(int16_t *)pValue;
+ ALOGVV("%s EQ_PARAM_BAND_LEVEL band %d, level %d", __func__, band, level);
+ if (band < 0 || band >= FIVEBAND_NUMBANDS) {
if (band < 0) {
android_errorWriteLog(0x534e4554, "32095626");
- ALOGE("\tERROR Equalizer_setParameter() EQ_PARAM_BAND_LEVEL band %d", band);
+ ALOGE("%s EQ_PARAM_BAND_LEVEL invalid band %d", __func__, band);
}
+ status = -EINVAL;
break;
}
EqualizerSetBandLevel(pContext, band, level);
- break;
+ } break;
+
case EQ_PARAM_PROPERTIES: {
- //ALOGV("\tEqualizer_setParameter() EQ_PARAM_PROPERTIES");
+ ALOGVV("%s EQ_PARAM_PROPERTIES", __func__);
if (valueSize < sizeof(int16_t)) {
- status = -EINVAL;
- break;
+ ALOGV("%s EQ_PARAM_PROPERTIES invalid valueSize %u", __func__, valueSize);
+ status = -EINVAL;
+ break;
}
int16_t *p = (int16_t *)pValue;
if ((int)p[0] >= EqualizerGetNumPresets()) {
+ ALOGV("%s EQ_PARAM_PROPERTIES invalid preset %d", __func__, (int)p[0]);
status = -EINVAL;
break;
}
if (p[0] >= 0) {
EqualizerSetPreset(pContext, (int)p[0]);
} else {
- if (valueSize < (2 + FIVEBAND_NUMBANDS) * sizeof(int16_t)) {
+ constexpr uint32_t valueSizeRequired = (2 + FIVEBAND_NUMBANDS) * sizeof(int16_t);
+ if (valueSize < valueSizeRequired) {
android_errorWriteLog(0x534e4554, "37563371");
- ALOGE("\tERROR Equalizer_setParameter() EQ_PARAM_PROPERTIES valueSize %d < %d",
- (int)valueSize, (int)((2 + FIVEBAND_NUMBANDS) * sizeof(int16_t)));
+ ALOGE("%s EQ_PARAM_PROPERTIES invalid valueSize %u < %u",
+ __func__, valueSize, valueSizeRequired);
status = -EINVAL;
break;
}
if ((int)p[1] != FIVEBAND_NUMBANDS) {
+ ALOGV("%s EQ_PARAM_PROPERTIES invalid bands %d", __func__, (int)p[1]);
status = -EINVAL;
break;
}
@@ -2716,13 +2843,14 @@
}
}
} break;
+
default:
- ALOGV("\tLVM_ERROR : Equalizer_setParameter() invalid param %d", param);
+ ALOGV("%s invalid param %d", __func__, params[0]);
status = -EINVAL;
break;
}
- //ALOGV("\tEqualizer_setParameter end");
+ ALOGVV("%s end param: %d, status: %d", __func__, params[0], status);
return status;
} /* end Equalizer_setParameter */
@@ -2747,79 +2875,92 @@
//
//----------------------------------------------------------------------------
-int Volume_getParameter(EffectContext *pContext,
- void *pParam,
- uint32_t *pValueSize,
- void *pValue){
+int Volume_getParameter(EffectContext *pContext,
+ uint32_t paramSize,
+ void *pParam,
+ uint32_t *pValueSize,
+ void *pValue) {
int status = 0;
- int32_t *pParamTemp = (int32_t *)pParam;
- int32_t param = *pParamTemp++;;
+ int32_t *params = (int32_t *)pParam;
- //ALOGV("\tVolume_getParameter start");
+ ALOGVV("%s start", __func__);
- switch (param){
+ if (paramSize < sizeof(int32_t)) {
+ ALOGV("%s invalid paramSize: %u", __func__, paramSize);
+ return -EINVAL;
+ }
+ switch (params[0]) {
case VOLUME_PARAM_LEVEL:
- case VOLUME_PARAM_MAXLEVEL:
- case VOLUME_PARAM_STEREOPOSITION:
- if (*pValueSize != sizeof(int16_t)){
- ALOGV("\tLVM_ERROR : Volume_getParameter() invalid pValueSize 1 %d", *pValueSize);
- return -EINVAL;
+ if (*pValueSize != sizeof(int16_t)) { // legacy: check equality here.
+ ALOGV("%s VOLUME_PARAM_LEVEL invalid *pValueSize %u", __func__, *pValueSize);
+ status = -EINVAL;
+ break;
}
- *pValueSize = sizeof(int16_t);
+ // no need to set *pValueSize
+
+ status = VolumeGetVolumeLevel(pContext, (int16_t *)(pValue));
+ ALOGVV("%s VOLUME_PARAM_LEVEL %d", __func__, *(int16_t *)pValue);
+ break;
+
+ case VOLUME_PARAM_MAXLEVEL:
+ if (*pValueSize != sizeof(int16_t)) { // legacy: check equality here.
+ ALOGV("%s VOLUME_PARAM_MAXLEVEL invalid *pValueSize %u", __func__, *pValueSize);
+ status = -EINVAL;
+ break;
+ }
+ // no need to set *pValueSize
+
+ // in millibel
+ *(int16_t *)pValue = 0;
+ ALOGVV("%s VOLUME_PARAM_MAXLEVEL %d", __func__, *(int16_t *)pValue);
+ break;
+
+ case VOLUME_PARAM_STEREOPOSITION:
+ if (*pValueSize != sizeof(int16_t)) { // legacy: check equality here.
+ ALOGV("%s VOLUME_PARAM_STEREOPOSITION invalid *pValueSize %u",
+ __func__, *pValueSize);
+ status = -EINVAL;
+ break;
+ }
+ // no need to set *pValueSize
+
+ VolumeGetStereoPosition(pContext, (int16_t *)pValue);
+ ALOGVV("%s VOLUME_PARAM_STEREOPOSITION %d", __func__, *(int16_t *)pValue);
break;
case VOLUME_PARAM_MUTE:
+ if (*pValueSize < sizeof(uint32_t)) {
+ ALOGV("%s VOLUME_PARAM_MUTE invalid *pValueSize %u", __func__, *pValueSize);
+ status = -EINVAL;
+ break;
+ }
+ *pValueSize = sizeof(uint32_t);
+
+ status = VolumeGetMute(pContext, (uint32_t *)pValue);
+ ALOGV("%s VOLUME_PARAM_MUTE %u", __func__, *(uint32_t *)pValue);
+ break;
+
case VOLUME_PARAM_ENABLESTEREOPOSITION:
- if (*pValueSize < sizeof(int32_t)){
- ALOGV("\tLVM_ERROR : Volume_getParameter() invalid pValueSize 2 %d", *pValueSize);
- return -EINVAL;
+ if (*pValueSize < sizeof(int32_t)) {
+ ALOGV("%s VOLUME_PARAM_ENABLESTEREOPOSITION invalid *pValueSize %u",
+ __func__, *pValueSize);
+ status = -EINVAL;
+ break;
}
*pValueSize = sizeof(int32_t);
- break;
- default:
- ALOGV("\tLVM_ERROR : Volume_getParameter unknown param %d", param);
- return -EINVAL;
- }
-
- switch (param){
- case VOLUME_PARAM_LEVEL:
- status = VolumeGetVolumeLevel(pContext, (int16_t *)(pValue));
- //ALOGV("\tVolume_getParameter() VOLUME_PARAM_LEVEL Value is %d",
- // *(int16_t *)pValue);
- break;
-
- case VOLUME_PARAM_MAXLEVEL:
- *(int16_t *)pValue = 0;
- //ALOGV("\tVolume_getParameter() VOLUME_PARAM_MAXLEVEL Value is %d",
- // *(int16_t *)pValue);
- break;
-
- case VOLUME_PARAM_STEREOPOSITION:
- VolumeGetStereoPosition(pContext, (int16_t *)pValue);
- //ALOGV("\tVolume_getParameter() VOLUME_PARAM_STEREOPOSITION Value is %d",
- // *(int16_t *)pValue);
- break;
-
- case VOLUME_PARAM_MUTE:
- status = VolumeGetMute(pContext, (uint32_t *)pValue);
- ALOGV("\tVolume_getParameter() VOLUME_PARAM_MUTE Value is %d",
- *(uint32_t *)pValue);
- break;
-
- case VOLUME_PARAM_ENABLESTEREOPOSITION:
*(int32_t *)pValue = pContext->pBundledContext->bStereoPositionEnabled;
- //ALOGV("\tVolume_getParameter() VOLUME_PARAM_ENABLESTEREOPOSITION Value is %d",
- // *(uint32_t *)pValue);
+ ALOGVV("%s VOLUME_PARAM_ENABLESTEREOPOSITION %d", __func__, *(int32_t *)pValue);
+
break;
default:
- ALOGV("\tLVM_ERROR : Volume_getParameter() invalid param %d", param);
+ ALOGV("%s invalid param %d", __func__, params[0]);
status = -EINVAL;
break;
}
- //ALOGV("\tVolume_getParameter end");
+ ALOGVV("%s end param: %d, status: %d", __func__, params[0], status);
return status;
} /* end Volume_getParameter */
@@ -2839,55 +2980,87 @@
//
//----------------------------------------------------------------------------
-int Volume_setParameter (EffectContext *pContext, void *pParam, void *pValue){
- int status = 0;
- int16_t level;
- int16_t position;
- uint32_t mute;
- uint32_t positionEnabled;
- int32_t *pParamTemp = (int32_t *)pParam;
- int32_t param = *pParamTemp++;
+int Volume_setParameter(EffectContext *pContext,
+ uint32_t paramSize,
+ void *pParam,
+ uint32_t valueSize,
+ void *pValue) {
+ int status = 0;
+ int32_t *params = (int32_t *)pParam;
- //ALOGV("\tVolume_setParameter start");
+ ALOGVV("%s start", __func__);
- switch (param){
- case VOLUME_PARAM_LEVEL:
- level = *(int16_t *)pValue;
- //ALOGV("\tVolume_setParameter() VOLUME_PARAM_LEVEL value is %d", level);
- //ALOGV("\tVolume_setParameter() Calling pVolume->setVolumeLevel");
- status = VolumeSetVolumeLevel(pContext, (int16_t)level);
- //ALOGV("\tVolume_setParameter() Called pVolume->setVolumeLevel");
- break;
+ if (paramSize < sizeof(int32_t)) {
+ ALOGV("%s invalid paramSize: %u", __func__, paramSize);
+ return -EINVAL;
+ }
+ switch (params[0]) {
+ case VOLUME_PARAM_LEVEL: {
+ if (valueSize < sizeof(int16_t)) {
+ ALOGV("%s VOLUME_PARAM_LEVEL invalid valueSize %u", __func__, valueSize);
+ status = -EINVAL;
+ break;
+ }
- case VOLUME_PARAM_MUTE:
- mute = *(uint32_t *)pValue;
- //ALOGV("\tVolume_setParameter() Calling pVolume->setMute, mute is %d", mute);
- //ALOGV("\tVolume_setParameter() Calling pVolume->setMute");
+ const int16_t level = *(int16_t *)pValue;
+ ALOGVV("%s VOLUME_PARAM_LEVEL %d", __func__, level);
+ ALOGVV("%s VOLUME_PARAM_LEVEL Calling VolumeSetVolumeLevel", __func__);
+ status = VolumeSetVolumeLevel(pContext, level);
+ ALOGVV("%s VOLUME_PARAM_LEVEL Called VolumeSetVolumeLevel", __func__);
+ } break;
+
+ case VOLUME_PARAM_MUTE: {
+ if (valueSize < sizeof(uint32_t)) {
+ ALOGV("%s VOLUME_PARAM_MUTE invalid valueSize %u", __func__, valueSize);
+ android_errorWriteLog(0x534e4554, "64477217");
+ status = -EINVAL;
+ break;
+ }
+
+ const uint32_t mute = *(uint32_t *)pValue;
+ ALOGVV("%s VOLUME_PARAM_MUTE %d", __func__, mute);
+ ALOGVV("%s VOLUME_PARAM_MUTE Calling VolumeSetMute", __func__);
status = VolumeSetMute(pContext, mute);
- //ALOGV("\tVolume_setParameter() Called pVolume->setMute");
- break;
+ ALOGVV("%s VOLUME_PARAM_MUTE Called VolumeSetMute", __func__);
+ } break;
- case VOLUME_PARAM_ENABLESTEREOPOSITION:
- positionEnabled = *(uint32_t *)pValue;
- (void) VolumeEnableStereoPosition(pContext, positionEnabled);
- (void) VolumeSetStereoPosition(pContext, pContext->pBundledContext->positionSaved);
- //ALOGV("\tVolume_setParameter() VOLUME_PARAM_ENABLESTEREOPOSITION called");
- break;
+ case VOLUME_PARAM_ENABLESTEREOPOSITION: {
+ if (valueSize < sizeof(uint32_t)) {
+ ALOGV("%s VOLUME_PARAM_ENABLESTEREOPOSITION invalid valueSize %u",
+ __func__, valueSize);
+ status = -EINVAL;
+ break;
+ }
- case VOLUME_PARAM_STEREOPOSITION:
- position = *(int16_t *)pValue;
- //ALOGV("\tVolume_setParameter() VOLUME_PARAM_STEREOPOSITION value is %d", position);
- //ALOGV("\tVolume_setParameter() Calling pVolume->VolumeSetStereoPosition");
- status = VolumeSetStereoPosition(pContext, (int16_t)position);
- //ALOGV("\tVolume_setParameter() Called pVolume->VolumeSetStereoPosition");
- break;
+ const uint32_t positionEnabled = *(uint32_t *)pValue;
+ status = VolumeEnableStereoPosition(pContext, positionEnabled)
+ ?: VolumeSetStereoPosition(pContext, pContext->pBundledContext->positionSaved);
+ ALOGVV("%s VOLUME_PARAM_ENABLESTEREOPOSITION called", __func__);
+ } break;
+
+ case VOLUME_PARAM_STEREOPOSITION: {
+ if (valueSize < sizeof(int16_t)) {
+ ALOGV("%s VOLUME_PARAM_STEREOPOSITION invalid valueSize %u", __func__, valueSize);
+ status = -EINVAL;
+ break;
+ }
+
+ const int16_t position = *(int16_t *)pValue;
+ ALOGVV("%s VOLUME_PARAM_STEREOPOSITION %d", __func__, position);
+ ALOGVV("%s VOLUME_PARAM_STEREOPOSITION Calling VolumeSetStereoPosition",
+ __func__);
+ status = VolumeSetStereoPosition(pContext, position);
+ ALOGVV("%s VOLUME_PARAM_STEREOPOSITION Called VolumeSetStereoPosition",
+ __func__);
+ } break;
default:
- ALOGV("\tLVM_ERROR : Volume_setParameter() invalid param %d", param);
+ ALOGV("%s invalid param %d", __func__, params[0]);
+ status = -EINVAL;
break;
}
- //ALOGV("\tVolume_setParameter end");
+ ALOGVV("%s end param: %d, status: %d", __func__, params[0], status);
return status;
} /* end Volume_setParameter */
@@ -3205,6 +3378,13 @@
return status;
} /* end Effect_process */
+// The value offset of an effect parameter is computed by rounding up
+// the parameter size to the next 32 bit alignment.
+static inline uint32_t computeParamVOffset(const effect_param_t *p) {
+ return ((p->psize + sizeof(int32_t) - 1) / sizeof(int32_t)) *
+ sizeof(int32_t);
+}
+
/* Effect Control Interface Implementation: Command */
int Effect_command(effect_handle_t self,
uint32_t cmdCode,
@@ -3315,8 +3495,7 @@
ALOGV("\tLVM_ERROR : EFFECT_CMD_GET_PARAM: psize too big");
return -EINVAL;
}
- uint32_t paddedParamSize = ((p->psize + sizeof(int32_t) - 1) / sizeof(int32_t)) *
- sizeof(int32_t);
+ const uint32_t paddedParamSize = computeParamVOffset(p);
if ((EFFECT_PARAM_SIZE_MAX - sizeof(effect_param_t) < paddedParamSize) ||
(EFFECT_PARAM_SIZE_MAX - sizeof(effect_param_t) - paddedParamSize <
p->vsize)) {
@@ -3338,6 +3517,7 @@
uint32_t voffset = paddedParamSize;
if(pContext->EffectType == LVM_BASS_BOOST){
p->status = android::BassBoost_getParameter(pContext,
+ p->psize,
p->data,
&p->vsize,
p->data + voffset);
@@ -3350,6 +3530,7 @@
if(pContext->EffectType == LVM_VIRTUALIZER){
p->status = android::Virtualizer_getParameter(pContext,
+ p->psize,
(void *)p->data,
&p->vsize,
p->data + voffset);
@@ -3364,6 +3545,7 @@
//ALOGV("\tEqualizer_command cmdCode Case: "
// "EFFECT_CMD_GET_PARAM start");
p->status = android::Equalizer_getParameter(pContext,
+ p->psize,
p->data,
&p->vsize,
p->data + voffset);
@@ -3378,6 +3560,7 @@
if(pContext->EffectType == LVM_VOLUME){
//ALOGV("\tVolume_command cmdCode Case: EFFECT_CMD_GET_PARAM start");
p->status = android::Volume_getParameter(pContext,
+ p->psize,
(void *)p->data,
&p->vsize,
p->data + voffset);
@@ -3407,13 +3590,9 @@
"EFFECT_CMD_SET_PARAM: ERROR");
return -EINVAL;
}
- effect_param_t *p = (effect_param_t *) pCmdData;
- if (p->psize != sizeof(int32_t)){
- ALOGV("\tLVM_ERROR : BassBoost_command cmdCode Case: "
- "EFFECT_CMD_SET_PARAM: ERROR, psize is not sizeof(int32_t)");
- return -EINVAL;
- }
+ effect_param_t * const p = (effect_param_t *) pCmdData;
+ const uint32_t voffset = computeParamVOffset(p);
//ALOGV("\tnBassBoost_command cmdSize is %d\n"
// "\tsizeof(effect_param_t) is %d\n"
@@ -3423,8 +3602,10 @@
// cmdSize, sizeof(effect_param_t), p->psize, p->vsize );
*(int *)pReplyData = android::BassBoost_setParameter(pContext,
- (void *)p->data,
- p->data + p->psize);
+ p->psize,
+ (void *)p->data,
+ p->vsize,
+ p->data + voffset);
}
if(pContext->EffectType == LVM_VIRTUALIZER){
// Warning this log will fail to properly read an int32_t value, assumes int16_t
@@ -3442,13 +3623,9 @@
"EFFECT_CMD_SET_PARAM: ERROR");
return -EINVAL;
}
- effect_param_t *p = (effect_param_t *) pCmdData;
- if (p->psize != sizeof(int32_t)){
- ALOGV("\tLVM_ERROR : Virtualizer_command cmdCode Case: "
- "EFFECT_CMD_SET_PARAM: ERROR, psize is not sizeof(int32_t)");
- return -EINVAL;
- }
+ effect_param_t * const p = (effect_param_t *) pCmdData;
+ const uint32_t voffset = computeParamVOffset(p);
//ALOGV("\tnVirtualizer_command cmdSize is %d\n"
// "\tsizeof(effect_param_t) is %d\n"
@@ -3458,8 +3635,10 @@
// cmdSize, sizeof(effect_param_t), p->psize, p->vsize );
*(int *)pReplyData = android::Virtualizer_setParameter(pContext,
- (void *)p->data,
- p->data + p->psize);
+ p->psize,
+ (void *)p->data,
+ p->vsize,
+ p->data + voffset);
}
if(pContext->EffectType == LVM_EQUALIZER){
//ALOGV("\tEqualizer_command cmdCode Case: "
@@ -3475,12 +3654,15 @@
"EFFECT_CMD_SET_PARAM: ERROR");
return -EINVAL;
}
- effect_param_t *p = (effect_param_t *) pCmdData;
+
+ effect_param_t * const p = (effect_param_t *) pCmdData;
+ const uint32_t voffset = computeParamVOffset(p);
*(int *)pReplyData = android::Equalizer_setParameter(pContext,
- (void *)p->data,
- p->vsize,
- p->data + p->psize);
+ p->psize,
+ (void *)p->data,
+ p->vsize,
+ p->data + voffset);
}
if(pContext->EffectType == LVM_VOLUME){
//ALOGV("\tVolume_command cmdCode Case: EFFECT_CMD_SET_PARAM start");
@@ -3497,11 +3679,15 @@
"EFFECT_CMD_SET_PARAM: ERROR");
return -EINVAL;
}
- effect_param_t *p = (effect_param_t *) pCmdData;
+
+ effect_param_t * const p = (effect_param_t *) pCmdData;
+ const uint32_t voffset = computeParamVOffset(p);
*(int *)pReplyData = android::Volume_setParameter(pContext,
- (void *)p->data,
- p->data + p->psize);
+ p->psize,
+ (void *)p->data,
+ p->vsize,
+ p->data + voffset);
}
//ALOGV("\tEffect_command cmdCode Case: EFFECT_CMD_SET_PARAM end");
} break;
@@ -3648,10 +3834,10 @@
if(rightdB > maxdB){
maxdB = rightdB;
}
- //ALOGV("\tEFFECT_CMD_SET_VOLUME Session: %d, SessionID: %d VOLUME is %d dB (%d), "
+ //ALOGV("\tEFFECT_CMD_SET_VOLUME Session: %d, SessionID: %d VOLUME is %d dB, "
// "effect is %d",
//pContext->pBundledContext->SessionNo, pContext->pBundledContext->SessionId,
- //(int32_t)maxdB, maxVol<<7, pContext->EffectType);
+ //(int32_t)maxdB, pContext->EffectType);
//ALOGV("\tEFFECT_CMD_SET_VOLUME: Left is %d, Right is %d", leftVolume, rightVolume);
//ALOGV("\tEFFECT_CMD_SET_VOLUME: Left %ddB, Right %ddB, Position %ddB",
// leftdB, rightdB, pandB);
diff --git a/media/libeffects/lvm/wrapper/Bundle/EffectBundle.h b/media/libeffects/lvm/wrapper/Bundle/EffectBundle.h
index cad89fd..291383a 100644
--- a/media/libeffects/lvm/wrapper/Bundle/EffectBundle.h
+++ b/media/libeffects/lvm/wrapper/Bundle/EffectBundle.h
@@ -213,7 +213,7 @@
static const float LimitLevel_bassBoostEnergyCrossCoefficient[FIVEBAND_NUMBANDS] = {
221.21, 208.10, 28.16, 0.0, 0.0 };
-static const float LimitLevel_bassBoostEnergyCoefficient = 7.12;
+static const float LimitLevel_bassBoostEnergyCoefficient = 9.00;
static const float LimitLevel_virtualizerContribution = 1.9;
diff --git a/media/libeffects/lvm/wrapper/Reverb/EffectReverb.cpp b/media/libeffects/lvm/wrapper/Reverb/EffectReverb.cpp
index 1717b49..ee9406d 100644
--- a/media/libeffects/lvm/wrapper/Reverb/EffectReverb.cpp
+++ b/media/libeffects/lvm/wrapper/Reverb/EffectReverb.cpp
@@ -429,9 +429,7 @@
size_t ii;
LVM_INT32 temp;
- src += n-1;
- dst += n-1;
- for (ii = n; ii != 0; ii--) {
+ for (ii = 0; ii < n; ii++) {
temp = (LVM_INT32)((*src) * 32768.0f);
if (temp >= 32767) {
*dst = 32767;
@@ -440,8 +438,8 @@
} else {
*dst = (LVM_INT16)temp;
}
- src--;
- dst--;
+ src++;
+ dst++;
}
return;
}
diff --git a/media/libheif/HeifDecoderImpl.cpp b/media/libheif/HeifDecoderImpl.cpp
index 115baff..4b131a7 100644
--- a/media/libheif/HeifDecoderImpl.cpp
+++ b/media/libheif/HeifDecoderImpl.cpp
@@ -401,6 +401,10 @@
videoFrame->getFlattenedIccData());
}
mFrameDecoded = true;
+
+ // Aggressive clear to avoid holding on to resources
+ mRetriever.clear();
+ mDataSource.clear();
return true;
}
diff --git a/media/libmedia/Android.bp b/media/libmedia/Android.bp
index 12242b3..93656a8 100644
--- a/media/libmedia/Android.bp
+++ b/media/libmedia/Android.bp
@@ -63,18 +63,13 @@
},
shared_libs: [
- "android.hidl.memory@1.0",
"android.hidl.token@1.0-utils",
"android.hardware.media.omx@1.0",
- "android.hardware.media@1.0",
- "libbase",
"libbinder",
"libcutils",
"libgui",
"libhidlbase",
- "libhidlmemory",
"libhidltransport",
- "libhwbinder",
"liblog",
"libstagefright_foundation",
"libui",
@@ -82,11 +77,8 @@
],
export_shared_lib_headers: [
- "android.hidl.memory@1.0",
"android.hidl.token@1.0-utils",
"android.hardware.media.omx@1.0",
- "android.hardware.media@1.0",
- "libhidlmemory",
"libstagefright_foundation",
"libui",
],
@@ -137,7 +129,6 @@
srcs: [
"IDataSource.cpp",
- "IHDCP.cpp",
"BufferingSettings.cpp",
"mediaplayer.cpp",
"IMediaHTTPConnection.cpp",
@@ -190,17 +181,8 @@
"libdl",
"libaudioutils",
"libaudioclient",
- "libmedia_helper",
- "libmediadrm",
- "libmediametrics",
- "libbase",
"libhidlbase",
"libhidltransport",
- "libhwbinder",
- "libhidlmemory",
- "android.hidl.memory@1.0",
- "android.hardware.graphics.common@1.0",
- "android.hardware.graphics.bufferqueue@1.0",
],
export_shared_lib_headers: [
@@ -209,9 +191,6 @@
"libicuuc",
"libicui18n",
"libsonivox",
- "libmediadrm",
- "libmedia_helper",
- "android.hidl.memory@1.0",
],
// for memory heap analysis
diff --git a/media/libmedia/IHDCP.cpp b/media/libmedia/IHDCP.cpp
deleted file mode 100644
index a46017f..0000000
--- a/media/libmedia/IHDCP.cpp
+++ /dev/null
@@ -1,359 +0,0 @@
-/*
- * Copyright (C) 2012 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 "IHDCP"
-#include <utils/Log.h>
-
-#include <binder/Parcel.h>
-#include <media/IHDCP.h>
-#include <media/stagefright/MediaErrors.h>
-#include <media/stagefright/foundation/ADebug.h>
-
-namespace android {
-
-enum {
- OBSERVER_NOTIFY = IBinder::FIRST_CALL_TRANSACTION,
- HDCP_SET_OBSERVER,
- HDCP_INIT_ASYNC,
- HDCP_SHUTDOWN_ASYNC,
- HDCP_GET_CAPS,
- HDCP_ENCRYPT,
- HDCP_ENCRYPT_NATIVE,
- HDCP_DECRYPT,
-};
-
-struct BpHDCPObserver : public BpInterface<IHDCPObserver> {
- explicit BpHDCPObserver(const sp<IBinder> &impl)
- : BpInterface<IHDCPObserver>(impl) {
- }
-
- virtual void notify(
- int msg, int ext1, int ext2, const Parcel *obj) {
- Parcel data, reply;
- data.writeInterfaceToken(IHDCPObserver::getInterfaceDescriptor());
- data.writeInt32(msg);
- data.writeInt32(ext1);
- data.writeInt32(ext2);
- if (obj && obj->dataSize() > 0) {
- data.appendFrom(const_cast<Parcel *>(obj), 0, obj->dataSize());
- }
- remote()->transact(OBSERVER_NOTIFY, data, &reply, IBinder::FLAG_ONEWAY);
- }
-};
-
-IMPLEMENT_META_INTERFACE(HDCPObserver, "android.hardware.IHDCPObserver");
-
-struct BpHDCP : public BpInterface<IHDCP> {
- explicit BpHDCP(const sp<IBinder> &impl)
- : BpInterface<IHDCP>(impl) {
- }
-
- virtual status_t setObserver(const sp<IHDCPObserver> &observer) {
- Parcel data, reply;
- data.writeInterfaceToken(IHDCP::getInterfaceDescriptor());
- data.writeStrongBinder(IInterface::asBinder(observer));
- remote()->transact(HDCP_SET_OBSERVER, data, &reply);
- return reply.readInt32();
- }
-
- virtual status_t initAsync(const char *host, unsigned port) {
- Parcel data, reply;
- data.writeInterfaceToken(IHDCP::getInterfaceDescriptor());
- data.writeCString(host);
- data.writeInt32(port);
- remote()->transact(HDCP_INIT_ASYNC, data, &reply);
- return reply.readInt32();
- }
-
- virtual status_t shutdownAsync() {
- Parcel data, reply;
- data.writeInterfaceToken(IHDCP::getInterfaceDescriptor());
- remote()->transact(HDCP_SHUTDOWN_ASYNC, data, &reply);
- return reply.readInt32();
- }
-
- virtual uint32_t getCaps() {
- Parcel data, reply;
- data.writeInterfaceToken(IHDCP::getInterfaceDescriptor());
- remote()->transact(HDCP_GET_CAPS, data, &reply);
- return reply.readInt32();
- }
-
- virtual status_t encrypt(
- const void *inData, size_t size, uint32_t streamCTR,
- uint64_t *outInputCTR, void *outData) {
- Parcel data, reply;
- data.writeInterfaceToken(IHDCP::getInterfaceDescriptor());
- data.writeInt32(size);
- data.write(inData, size);
- data.writeInt32(streamCTR);
- remote()->transact(HDCP_ENCRYPT, data, &reply);
-
- status_t err = reply.readInt32();
-
- if (err != OK) {
- *outInputCTR = 0;
-
- return err;
- }
-
- *outInputCTR = reply.readInt64();
- reply.read(outData, size);
-
- return err;
- }
-
- virtual status_t encryptNative(
- const sp<GraphicBuffer> &graphicBuffer,
- size_t offset, size_t size, uint32_t streamCTR,
- uint64_t *outInputCTR, void *outData) {
- Parcel data, reply;
- data.writeInterfaceToken(IHDCP::getInterfaceDescriptor());
- data.write(*graphicBuffer);
- data.writeInt32(offset);
- data.writeInt32(size);
- data.writeInt32(streamCTR);
- remote()->transact(HDCP_ENCRYPT_NATIVE, data, &reply);
-
- status_t err = reply.readInt32();
-
- if (err != OK) {
- *outInputCTR = 0;
- return err;
- }
-
- *outInputCTR = reply.readInt64();
- reply.read(outData, size);
-
- return err;
- }
-
- virtual status_t decrypt(
- const void *inData, size_t size,
- uint32_t streamCTR, uint64_t inputCTR,
- void *outData) {
- Parcel data, reply;
- data.writeInterfaceToken(IHDCP::getInterfaceDescriptor());
- data.writeInt32(size);
- data.write(inData, size);
- data.writeInt32(streamCTR);
- data.writeInt64(inputCTR);
- remote()->transact(HDCP_DECRYPT, data, &reply);
-
- status_t err = reply.readInt32();
-
- if (err != OK) {
- return err;
- }
-
- reply.read(outData, size);
-
- return err;
- }
-};
-
-IMPLEMENT_META_INTERFACE(HDCP, "android.hardware.IHDCP");
-
-status_t BnHDCPObserver::onTransact(
- uint32_t code, const Parcel &data, Parcel *reply, uint32_t flags) {
- switch (code) {
- case OBSERVER_NOTIFY:
- {
- CHECK_INTERFACE(IHDCPObserver, data, reply);
-
- int msg = data.readInt32();
- int ext1 = data.readInt32();
- int ext2 = data.readInt32();
-
- Parcel obj;
- if (data.dataAvail() > 0) {
- obj.appendFrom(
- const_cast<Parcel *>(&data),
- data.dataPosition(),
- data.dataAvail());
- }
-
- notify(msg, ext1, ext2, &obj);
-
- return OK;
- }
-
- default:
- return BBinder::onTransact(code, data, reply, flags);
- }
-}
-
-status_t BnHDCP::onTransact(
- uint32_t code, const Parcel &data, Parcel *reply, uint32_t flags) {
- switch (code) {
- case HDCP_SET_OBSERVER:
- {
- CHECK_INTERFACE(IHDCP, data, reply);
-
- sp<IHDCPObserver> observer =
- interface_cast<IHDCPObserver>(data.readStrongBinder());
-
- reply->writeInt32(setObserver(observer));
- return OK;
- }
-
- case HDCP_INIT_ASYNC:
- {
- CHECK_INTERFACE(IHDCP, data, reply);
-
- const char *host = data.readCString();
- unsigned port = data.readInt32();
-
- reply->writeInt32(initAsync(host, port));
- return OK;
- }
-
- case HDCP_SHUTDOWN_ASYNC:
- {
- CHECK_INTERFACE(IHDCP, data, reply);
-
- reply->writeInt32(shutdownAsync());
- return OK;
- }
-
- case HDCP_GET_CAPS:
- {
- CHECK_INTERFACE(IHDCP, data, reply);
-
- reply->writeInt32(getCaps());
- return OK;
- }
-
- case HDCP_ENCRYPT:
- {
- CHECK_INTERFACE(IHDCP, data, reply);
-
- size_t size = data.readInt32();
- void *inData = NULL;
- // watch out for overflow
- if (size <= SIZE_MAX / 2) {
- inData = malloc(2 * size);
- }
- if (inData == NULL) {
- reply->writeInt32(ERROR_OUT_OF_RANGE);
- return OK;
- }
-
- void *outData = (uint8_t *)inData + size;
-
- status_t err = data.read(inData, size);
- if (err != OK) {
- free(inData);
- reply->writeInt32(err);
- return OK;
- }
-
- uint32_t streamCTR = data.readInt32();
- uint64_t inputCTR;
- err = encrypt(inData, size, streamCTR, &inputCTR, outData);
-
- reply->writeInt32(err);
-
- if (err == OK) {
- reply->writeInt64(inputCTR);
- reply->write(outData, size);
- }
-
- free(inData);
- inData = outData = NULL;
-
- return OK;
- }
-
- case HDCP_ENCRYPT_NATIVE:
- {
- CHECK_INTERFACE(IHDCP, data, reply);
-
- sp<GraphicBuffer> graphicBuffer = new GraphicBuffer();
- data.read(*graphicBuffer);
- size_t offset = data.readInt32();
- size_t size = data.readInt32();
- uint32_t streamCTR = data.readInt32();
- void *outData = NULL;
- uint64_t inputCTR;
-
- status_t err = ERROR_OUT_OF_RANGE;
-
- outData = malloc(size);
-
- if (outData != NULL) {
- err = encryptNative(graphicBuffer, offset, size,
- streamCTR, &inputCTR, outData);
- }
-
- reply->writeInt32(err);
-
- if (err == OK) {
- reply->writeInt64(inputCTR);
- reply->write(outData, size);
- }
-
- free(outData);
- outData = NULL;
-
- return OK;
- }
-
- case HDCP_DECRYPT:
- {
- CHECK_INTERFACE(IHDCP, data, reply);
-
- size_t size = data.readInt32();
- size_t bufSize = 2 * size;
-
- // watch out for overflow
- void *inData = NULL;
- if (bufSize > size) {
- inData = malloc(bufSize);
- }
-
- if (inData == NULL) {
- reply->writeInt32(ERROR_OUT_OF_RANGE);
- return OK;
- }
-
- void *outData = (uint8_t *)inData + size;
-
- data.read(inData, size);
-
- uint32_t streamCTR = data.readInt32();
- uint64_t inputCTR = data.readInt64();
- status_t err = decrypt(inData, size, streamCTR, inputCTR, outData);
-
- reply->writeInt32(err);
-
- if (err == OK) {
- reply->write(outData, size);
- }
-
- free(inData);
- inData = outData = NULL;
-
- return OK;
- }
-
- default:
- return BBinder::onTransact(code, data, reply, flags);
- }
-}
-
-} // namespace android
diff --git a/media/libmedia/IMediaCodecService.cpp b/media/libmedia/IMediaCodecService.cpp
index 2d62419..adfa93d 100644
--- a/media/libmedia/IMediaCodecService.cpp
+++ b/media/libmedia/IMediaCodecService.cpp
@@ -27,7 +27,8 @@
namespace android {
enum {
- GET_OMX = IBinder::FIRST_CALL_TRANSACTION
+ GET_OMX = IBinder::FIRST_CALL_TRANSACTION,
+ GET_OMX_STORE
};
class BpMediaCodecService : public BpInterface<IMediaCodecService>
@@ -45,6 +46,13 @@
return interface_cast<IOMX>(reply.readStrongBinder());
}
+ virtual sp<IOMXStore> getOMXStore() {
+ Parcel data, reply;
+ data.writeInterfaceToken(IMediaCodecService::getInterfaceDescriptor());
+ remote()->transact(GET_OMX_STORE, data, &reply);
+ return interface_cast<IOMXStore>(reply.readStrongBinder());
+ }
+
};
IMPLEMENT_META_INTERFACE(MediaCodecService, "android.media.IMediaCodecService");
@@ -62,6 +70,12 @@
reply->writeStrongBinder(IInterface::asBinder(omx));
return NO_ERROR;
}
+ case GET_OMX_STORE: {
+ CHECK_INTERFACE(IMediaCodecService, data, reply);
+ sp<IOMXStore> omxStore = getOMXStore();
+ reply->writeStrongBinder(IInterface::asBinder(omxStore));
+ return NO_ERROR;
+ }
default:
return BBinder::onTransact(code, data, reply, flags);
}
diff --git a/media/libmedia/IMediaExtractor.cpp b/media/libmedia/IMediaExtractor.cpp
index 19b00f3..3c43a72 100644
--- a/media/libmedia/IMediaExtractor.cpp
+++ b/media/libmedia/IMediaExtractor.cpp
@@ -38,7 +38,8 @@
SETMEDIACAS,
SETUID,
NAME,
- GETMETRICS
+ GETMETRICS,
+ RELEASE,
};
class BpMediaExtractor : public BpInterface<IMediaExtractor> {
@@ -138,6 +139,13 @@
ALOGV("name NOT IMPLEMENTED");
return NULL;
}
+
+ virtual void release() {
+ ALOGV("release");
+ Parcel data, reply;
+ data.writeInterfaceToken(BpMediaExtractor::getInterfaceDescriptor());
+ remote()->transact(RELEASE, data, &reply);
+ }
};
IMPLEMENT_META_INTERFACE(MediaExtractor, "android.media.IMediaExtractor");
@@ -215,6 +223,12 @@
reply->writeInt32(setMediaCas(casToken));
return OK;
}
+ case RELEASE: {
+ ALOGV("release");
+ CHECK_INTERFACE(IMediaExtractor, data, reply);
+ release();
+ return OK;
+ }
default:
return BBinder::onTransact(code, data, reply, flags);
}
diff --git a/media/libmedia/IMediaPlayer.cpp b/media/libmedia/IMediaPlayer.cpp
index cda3b3a..903e503 100644
--- a/media/libmedia/IMediaPlayer.cpp
+++ b/media/libmedia/IMediaPlayer.cpp
@@ -58,6 +58,7 @@
GET_CURRENT_POSITION,
GET_DURATION,
RESET,
+ NOTIFY_AT,
SET_AUDIO_STREAM_TYPE,
SET_LOOPING,
SET_VOLUME,
@@ -328,6 +329,15 @@
return reply.readInt32();
}
+ status_t notifyAt(int64_t mediaTimeUs)
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(IMediaPlayer::getInterfaceDescriptor());
+ data.writeInt64(mediaTimeUs);
+ remote()->transact(NOTIFY_AT, data, &reply);
+ return reply.readInt32();
+ }
+
status_t setAudioStreamType(audio_stream_type_t stream)
{
Parcel data, reply;
@@ -746,6 +756,11 @@
reply->writeInt32(reset());
return NO_ERROR;
} break;
+ case NOTIFY_AT: {
+ CHECK_INTERFACE(IMediaPlayer, data, reply);
+ reply->writeInt32(notifyAt(data.readInt64()));
+ return NO_ERROR;
+ } break;
case SET_AUDIO_STREAM_TYPE: {
CHECK_INTERFACE(IMediaPlayer, data, reply);
reply->writeInt32(setAudioStreamType((audio_stream_type_t) data.readInt32()));
diff --git a/media/libmedia/IMediaPlayerService.cpp b/media/libmedia/IMediaPlayerService.cpp
index a01852c..d135878 100644
--- a/media/libmedia/IMediaPlayerService.cpp
+++ b/media/libmedia/IMediaPlayerService.cpp
@@ -20,7 +20,6 @@
#include <binder/Parcel.h>
#include <binder/IMemory.h>
-#include <media/IHDCP.h>
#include <media/IMediaCodecList.h>
#include <media/IMediaHTTPService.h>
#include <media/IMediaPlayerService.h>
@@ -40,7 +39,6 @@
CREATE_MEDIA_RECORDER,
CREATE_METADATA_RETRIEVER,
GET_OMX,
- MAKE_HDCP,
ADD_BATTERY_DATA,
PULL_BATTERY_DATA,
LISTEN_FOR_REMOTE_DISPLAY,
@@ -90,14 +88,6 @@
return interface_cast<IOMX>(reply.readStrongBinder());
}
- virtual sp<IHDCP> makeHDCP(bool createEncryptionModule) {
- Parcel data, reply;
- data.writeInterfaceToken(IMediaPlayerService::getInterfaceDescriptor());
- data.writeInt32(createEncryptionModule);
- remote()->transact(MAKE_HDCP, data, &reply);
- return interface_cast<IHDCP>(reply.readStrongBinder());
- }
-
virtual void addBatteryData(uint32_t params) {
Parcel data, reply;
data.writeInterfaceToken(IMediaPlayerService::getInterfaceDescriptor());
@@ -167,13 +157,6 @@
reply->writeStrongBinder(IInterface::asBinder(omx));
return NO_ERROR;
} break;
- case MAKE_HDCP: {
- CHECK_INTERFACE(IMediaPlayerService, data, reply);
- bool createEncryptionModule = data.readInt32();
- sp<IHDCP> hdcp = makeHDCP(createEncryptionModule);
- reply->writeStrongBinder(IInterface::asBinder(hdcp));
- return NO_ERROR;
- } break;
case ADD_BATTERY_DATA: {
CHECK_INTERFACE(IMediaPlayerService, data, reply);
uint32_t params = data.readInt32();
diff --git a/media/libmedia/MediaCodecInfo.cpp b/media/libmedia/MediaCodecInfo.cpp
index 2a74512..a570ffe 100644
--- a/media/libmedia/MediaCodecInfo.cpp
+++ b/media/libmedia/MediaCodecInfo.cpp
@@ -101,42 +101,46 @@
return OK;
}
-void MediaCodecInfo::CapabilitiesBuilder::addProfileLevel(uint32_t profile, uint32_t level) {
+void MediaCodecInfo::CapabilitiesWriter::addDetail(
+ const char* key, const char* value) {
+ mCap->mDetails->setString(key, value);
+}
+
+void MediaCodecInfo::CapabilitiesWriter::addDetail(
+ const char* key, int32_t value) {
+ mCap->mDetails->setInt32(key, value);
+}
+
+void MediaCodecInfo::CapabilitiesWriter::addProfileLevel(
+ uint32_t profile, uint32_t level) {
ProfileLevel profileLevel;
profileLevel.mProfile = profile;
profileLevel.mLevel = level;
- if (mProfileLevelsSorted.indexOf(profileLevel) < 0) {
- mProfileLevels.push_back(profileLevel);
- mProfileLevelsSorted.add(profileLevel);
+ if (mCap->mProfileLevelsSorted.indexOf(profileLevel) < 0) {
+ mCap->mProfileLevels.push_back(profileLevel);
+ mCap->mProfileLevelsSorted.add(profileLevel);
}
}
-void MediaCodecInfo::CapabilitiesBuilder::addColorFormat(uint32_t format) {
- if (mColorFormatsSorted.indexOf(format) < 0) {
- mColorFormats.push(format);
- mColorFormatsSorted.add(format);
+void MediaCodecInfo::CapabilitiesWriter::addColorFormat(uint32_t format) {
+ if (mCap->mColorFormatsSorted.indexOf(format) < 0) {
+ mCap->mColorFormats.push(format);
+ mCap->mColorFormatsSorted.add(format);
}
}
-void MediaCodecInfo::CapabilitiesBuilder::addFlags(uint32_t flags) {
- mFlags |= flags;
+void MediaCodecInfo::CapabilitiesWriter::addFlags(uint32_t flags) {
+ mCap->mFlags |= flags;
+}
+
+MediaCodecInfo::CapabilitiesWriter::CapabilitiesWriter(
+ MediaCodecInfo::Capabilities* cap) : mCap(cap) {
}
bool MediaCodecInfo::isEncoder() const {
return mIsEncoder;
}
-bool MediaCodecInfo::hasQuirk(const char *name) const {
- if (name) {
- for (size_t ix = 0; ix < mQuirks.size(); ix++) {
- if (mQuirks.itemAt(ix).equalsIgnoreCase(name)) {
- return true;
- }
- }
- }
- return false;
-}
-
void MediaCodecInfo::getSupportedMimes(Vector<AString> *mimes) const {
mimes->clear();
for (size_t ix = 0; ix < mCaps.size(); ix++) {
@@ -157,20 +161,21 @@
return mName.c_str();
}
+const char *MediaCodecInfo::getOwnerName() const {
+ return mOwner.c_str();
+}
+
// static
sp<MediaCodecInfo> MediaCodecInfo::FromParcel(const Parcel &parcel) {
AString name = AString::FromParcel(parcel);
+ AString owner = AString::FromParcel(parcel);
bool isEncoder = static_cast<bool>(parcel.readInt32());
- sp<MediaCodecInfo> info = new MediaCodecInfo(name, isEncoder, NULL);
+ sp<MediaCodecInfo> info = new MediaCodecInfo;
+ info->mName = name;
+ info->mOwner = owner;
+ info->mIsEncoder = isEncoder;
size_t size = static_cast<size_t>(parcel.readInt32());
for (size_t i = 0; i < size; i++) {
- AString quirk = AString::FromParcel(parcel);
- if (info != NULL) {
- info->mQuirks.push_back(quirk);
- }
- }
- size = static_cast<size_t>(parcel.readInt32());
- for (size_t i = 0; i < size; i++) {
AString mime = AString::FromParcel(parcel);
sp<Capabilities> caps = Capabilities::FromParcel(parcel);
if (caps == NULL)
@@ -184,11 +189,8 @@
status_t MediaCodecInfo::writeToParcel(Parcel *parcel) const {
mName.writeToParcel(parcel);
+ mOwner.writeToParcel(parcel);
parcel->writeInt32(mIsEncoder);
- parcel->writeInt32(mQuirks.size());
- for (size_t i = 0; i < mQuirks.size(); i++) {
- mQuirks.itemAt(i).writeToParcel(parcel);
- }
parcel->writeInt32(mCaps.size());
for (size_t i = 0; i < mCaps.size(); i++) {
mCaps.keyAt(i).writeToParcel(parcel);
@@ -208,86 +210,46 @@
return -1;
}
-MediaCodecInfo::MediaCodecInfo(AString name, bool encoder, const char *mime)
- : mName(name),
- mIsEncoder(encoder),
- mHasSoleMime(false) {
- if (mime != NULL) {
- addMime(mime);
- mHasSoleMime = true;
- }
+MediaCodecInfo::MediaCodecInfo() {
}
-status_t MediaCodecInfo::addMime(const char *mime) {
- if (mHasSoleMime) {
- ALOGE("Codec '%s' already had its type specified", mName.c_str());
- return -EINVAL;
- }
- ssize_t ix = getCapabilityIndex(mime);
+void MediaCodecInfoWriter::setName(const char* name) {
+ mInfo->mName = name;
+}
+
+void MediaCodecInfoWriter::setOwner(const char* owner) {
+ mInfo->mOwner = owner;
+}
+
+void MediaCodecInfoWriter::setEncoder(bool isEncoder) {
+ mInfo->mIsEncoder = isEncoder;
+}
+
+std::unique_ptr<MediaCodecInfo::CapabilitiesWriter>
+ MediaCodecInfoWriter::addMime(const char *mime) {
+ ssize_t ix = mInfo->getCapabilityIndex(mime);
if (ix >= 0) {
- mCurrentCaps = mCaps.valueAt(ix);
- } else {
- mCurrentCaps = new Capabilities();
- mCaps.add(AString(mime), mCurrentCaps);
+ return std::unique_ptr<MediaCodecInfo::CapabilitiesWriter>(
+ new MediaCodecInfo::CapabilitiesWriter(
+ mInfo->mCaps.valueAt(ix).get()));
}
- return OK;
+ sp<MediaCodecInfo::Capabilities> caps = new MediaCodecInfo::Capabilities();
+ mInfo->mCaps.add(AString(mime), caps);
+ return std::unique_ptr<MediaCodecInfo::CapabilitiesWriter>(
+ new MediaCodecInfo::CapabilitiesWriter(caps.get()));
}
-status_t MediaCodecInfo::updateMime(const char *mime) {
- ssize_t ix = getCapabilityIndex(mime);
- if (ix < 0) {
- ALOGE("updateMime mime not found %s", mime);
- return -EINVAL;
- }
-
- mCurrentCaps = mCaps.valueAt(ix);
- return OK;
-}
-
-void MediaCodecInfo::removeMime(const char *mime) {
- ssize_t ix = getCapabilityIndex(mime);
+bool MediaCodecInfoWriter::removeMime(const char *mime) {
+ ssize_t ix = mInfo->getCapabilityIndex(mime);
if (ix >= 0) {
- mCaps.removeItemsAt(ix);
- // mCurrentCaps will be removed when completed
+ mInfo->mCaps.removeItemsAt(ix);
+ return true;
}
+ return false;
}
-status_t MediaCodecInfo::initializeCapabilities(const sp<Capabilities> &caps) {
- // TRICKY: copy data to mCurrentCaps as it is a reference to
- // an element of the capabilites map.
- mCurrentCaps->mColorFormats.clear();
- mCurrentCaps->mColorFormats.appendVector(caps->mColorFormats);
- mCurrentCaps->mProfileLevels.clear();
- mCurrentCaps->mProfileLevels.appendVector(caps->mProfileLevels);
- mCurrentCaps->mFlags = caps->mFlags;
- mCurrentCaps->mDetails = caps->mDetails;
- return OK;
-}
-
-void MediaCodecInfo::addQuirk(const char *name) {
- if (!hasQuirk(name)) {
- mQuirks.push(name);
- }
-}
-
-void MediaCodecInfo::complete() {
- mCurrentCaps = NULL;
-}
-
-void MediaCodecInfo::addDetail(const AString &key, const AString &value) {
- mCurrentCaps->mDetails->setString(key.c_str(), value.c_str());
-}
-
-void MediaCodecInfo::addFeature(const AString &key, int32_t value) {
- AString tag = "feature-";
- tag.append(key);
- mCurrentCaps->mDetails->setInt32(tag.c_str(), value);
-}
-
-void MediaCodecInfo::addFeature(const AString &key, const char *value) {
- AString tag = "feature-";
- tag.append(key);
- mCurrentCaps->mDetails->setString(tag.c_str(), value);
+MediaCodecInfoWriter::MediaCodecInfoWriter(MediaCodecInfo* info) :
+ mInfo(info) {
}
} // namespace android
diff --git a/media/libmedia/include/media/IHDCP.h b/media/libmedia/include/media/IHDCP.h
deleted file mode 100644
index 352561e..0000000
--- a/media/libmedia/include/media/IHDCP.h
+++ /dev/null
@@ -1,120 +0,0 @@
-/*
- * Copyright (C) 2012 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 <binder/IInterface.h>
-#include <media/hardware/HDCPAPI.h>
-#include <media/stagefright/foundation/ABase.h>
-#include <ui/GraphicBuffer.h>
-
-namespace android {
-
-struct IHDCPObserver : public IInterface {
- DECLARE_META_INTERFACE(HDCPObserver);
-
- virtual void notify(
- int msg, int ext1, int ext2, const Parcel *obj) = 0;
-
-private:
- DISALLOW_EVIL_CONSTRUCTORS(IHDCPObserver);
-};
-
-struct IHDCP : public IInterface {
- DECLARE_META_INTERFACE(HDCP);
-
- // Called to specify the observer that receives asynchronous notifications
- // from the HDCP implementation to signal completion/failure of asynchronous
- // operations (such as initialization) or out of band events.
- virtual status_t setObserver(const sp<IHDCPObserver> &observer) = 0;
-
- // Request to setup an HDCP session with the specified host listening
- // on the specified port.
- virtual status_t initAsync(const char *host, unsigned port) = 0;
-
- // Request to shutdown the active HDCP session.
- virtual status_t shutdownAsync() = 0;
-
- // Returns the capability bitmask of this HDCP session.
- // Possible return values (please refer to HDCAPAPI.h):
- // HDCP_CAPS_ENCRYPT: mandatory, meaning the HDCP module can encrypt
- // from an input byte-array buffer to an output byte-array buffer
- // HDCP_CAPS_ENCRYPT_NATIVE: the HDCP module supports encryption from
- // a native buffer to an output byte-array buffer. The format of the
- // input native buffer is specific to vendor's encoder implementation.
- // It is the same format as that used by the encoder when
- // "storeMetaDataInBuffers" extension is enabled on its output port.
- virtual uint32_t getCaps() = 0;
-
- // ENCRYPTION only:
- // Encrypt data according to the HDCP spec. "size" bytes of data are
- // available at "inData" (virtual address), "size" may not be a multiple
- // of 128 bits (16 bytes). An equal number of encrypted bytes should be
- // written to the buffer at "outData" (virtual address).
- // This operation is to be synchronous, i.e. this call does not return
- // until outData contains size bytes of encrypted data.
- // streamCTR will be assigned by the caller (to 0 for the first PES stream,
- // 1 for the second and so on)
- // inputCTR _will_be_maintained_by_the_callee_ for each PES stream.
- virtual status_t encrypt(
- const void *inData, size_t size, uint32_t streamCTR,
- uint64_t *outInputCTR, void *outData) = 0;
-
- // Encrypt data according to the HDCP spec. "size" bytes of data starting
- // at location "offset" are available in "buffer" (buffer handle). "size"
- // may not be a multiple of 128 bits (16 bytes). An equal number of
- // encrypted bytes should be written to the buffer at "outData" (virtual
- // address). This operation is to be synchronous, i.e. this call does not
- // return until outData contains size bytes of encrypted data.
- // streamCTR will be assigned by the caller (to 0 for the first PES stream,
- // 1 for the second and so on)
- // inputCTR _will_be_maintained_by_the_callee_ for each PES stream.
- virtual status_t encryptNative(
- const sp<GraphicBuffer> &graphicBuffer,
- size_t offset, size_t size, uint32_t streamCTR,
- uint64_t *outInputCTR, void *outData) = 0;
-
- // DECRYPTION only:
- // Decrypt data according to the HDCP spec.
- // "size" bytes of encrypted data are available at "inData"
- // (virtual address), "size" may not be a multiple of 128 bits (16 bytes).
- // An equal number of decrypted bytes should be written to the buffer
- // at "outData" (virtual address).
- // This operation is to be synchronous, i.e. this call does not return
- // until outData contains size bytes of decrypted data.
- // Both streamCTR and inputCTR will be provided by the caller.
- virtual status_t decrypt(
- const void *inData, size_t size,
- uint32_t streamCTR, uint64_t inputCTR,
- void *outData) = 0;
-
-private:
- DISALLOW_EVIL_CONSTRUCTORS(IHDCP);
-};
-
-struct BnHDCPObserver : public BnInterface<IHDCPObserver> {
- virtual status_t onTransact(
- uint32_t code, const Parcel &data, Parcel *reply,
- uint32_t flags = 0);
-};
-
-struct BnHDCP : public BnInterface<IHDCP> {
- virtual status_t onTransact(
- uint32_t code, const Parcel &data, Parcel *reply,
- uint32_t flags = 0);
-};
-
-} // namespace android
-
-
diff --git a/media/libmedia/include/media/IMediaCodecService.h b/media/libmedia/include/media/IMediaCodecService.h
index da3c5a03..59fb1c0 100644
--- a/media/libmedia/include/media/IMediaCodecService.h
+++ b/media/libmedia/include/media/IMediaCodecService.h
@@ -22,6 +22,7 @@
#include <binder/Parcel.h>
#include <media/IDataSource.h>
#include <media/IOMX.h>
+#include <media/IOMXStore.h>
namespace android {
@@ -31,6 +32,7 @@
DECLARE_META_INTERFACE(MediaCodecService);
virtual sp<IOMX> getOMX() = 0;
+ virtual sp<IOMXStore> getOMXStore() = 0;
};
class BnMediaCodecService: public BnInterface<IMediaCodecService>
diff --git a/media/libmedia/include/media/IMediaExtractor.h b/media/libmedia/include/media/IMediaExtractor.h
index 1e13b65..0ac7673 100644
--- a/media/libmedia/include/media/IMediaExtractor.h
+++ b/media/libmedia/include/media/IMediaExtractor.h
@@ -68,6 +68,8 @@
virtual void setUID(uid_t uid) = 0;
virtual const char * name() = 0;
+
+ virtual void release() = 0;
};
diff --git a/media/libmedia/include/media/IMediaPlayer.h b/media/libmedia/include/media/IMediaPlayer.h
index e657716..e181338 100644
--- a/media/libmedia/include/media/IMediaPlayer.h
+++ b/media/libmedia/include/media/IMediaPlayer.h
@@ -79,6 +79,7 @@
MediaPlayerSeekMode mode = MediaPlayerSeekMode::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;
diff --git a/media/libmedia/include/media/IMediaPlayerService.h b/media/libmedia/include/media/IMediaPlayerService.h
index f21bb3a..398a8c7 100644
--- a/media/libmedia/include/media/IMediaPlayerService.h
+++ b/media/libmedia/include/media/IMediaPlayerService.h
@@ -31,7 +31,6 @@
namespace android {
-struct IHDCP;
class IMediaCodecList;
struct IMediaHTTPService;
class IMediaRecorder;
@@ -50,7 +49,6 @@
virtual sp<IMediaPlayer> create(const sp<IMediaPlayerClient>& client,
audio_session_t audioSessionId = AUDIO_SESSION_ALLOCATE) = 0;
virtual sp<IOMX> getOMX() = 0;
- virtual sp<IHDCP> makeHDCP(bool createEncryptionModule) = 0;
virtual sp<IMediaCodecList> getCodecList() const = 0;
// Connects to a remote display.
diff --git a/media/libmedia/include/media/IOMXStore.h b/media/libmedia/include/media/IOMXStore.h
index f739c3b..628db70 100644
--- a/media/libmedia/include/media/IOMXStore.h
+++ b/media/libmedia/include/media/IOMXStore.h
@@ -27,11 +27,6 @@
#include <vector>
#include <string>
-/*
-#include <OMX_Core.h>
-#include <OMX_Video.h>
-*/
-
namespace android {
using hardware::media::omx::V1_0::IOmxStore;
diff --git a/media/libmedia/include/media/MediaCodecInfo.h b/media/libmedia/include/media/MediaCodecInfo.h
index ef641d2..ab2cd24 100644
--- a/media/libmedia/include/media/MediaCodecInfo.h
+++ b/media/libmedia/include/media/MediaCodecInfo.h
@@ -18,6 +18,7 @@
#define MEDIA_CODEC_INFO_H_
+#include <android-base/macros.h>
#include <binder/Parcel.h>
#include <media/stagefright/foundation/ABase.h>
#include <media/stagefright/foundation/AString.h>
@@ -36,6 +37,9 @@
typedef KeyedVector<AString, AString> CodecSettings;
+struct MediaCodecInfoWriter;
+struct MediaCodecListWriter;
+
struct MediaCodecInfo : public RefBase {
struct ProfileLevel {
uint32_t mProfile;
@@ -45,6 +49,8 @@
}
};
+ struct CapabilitiesWriter;
+
struct Capabilities : public RefBase {
enum {
// decoder flags
@@ -77,72 +83,191 @@
static sp<Capabilities> FromParcel(const Parcel &parcel);
status_t writeToParcel(Parcel *parcel) const;
- DISALLOW_EVIL_CONSTRUCTORS(Capabilities);
+ DISALLOW_COPY_AND_ASSIGN(Capabilities);
friend struct MediaCodecInfo;
+ friend struct MediaCodecInfoWriter;
+ friend struct CapabilitiesWriter;
};
- // Use a subclass to allow setting fields on construction without allowing
- // to do the same throughout the framework.
- struct CapabilitiesBuilder : public Capabilities {
+ /**
+ * This class is used for modifying information inside a `Capabilities`
+ * object. An object of type `CapabilitiesWriter` can be obtained by calling
+ * `MediaCodecInfoWriter::addMime()` or
+ * `MediaCodecInfoWriter::updateMime()`.
+ */
+ struct CapabilitiesWriter {
+ /**
+ * Add a key-value pair to the list of details. If the key already
+ * exists, the old value will be replaced.
+ *
+ * A pair added by this function will be accessible by
+ * `Capabilities::getDetails()`. Call `AMessage::getString()` with the
+ * same key to retrieve the value.
+ *
+ * @param key The key.
+ * @param value The string value.
+ */
+ void addDetail(const char* key, const char* value);
+ /**
+ * Add a key-value pair to the list of details. If the key already
+ * exists, the old value will be replaced.
+ *
+ * A pair added by this function will be accessible by
+ * `Capabilities::getDetails()`. Call `AMessage::getInt32()` with the
+ * same key to retrieve the value.
+ *
+ * @param key The key.
+ * @param value The `int32_t` value.
+ */
+ void addDetail(const char* key, int32_t value);
+ /**
+ * Add a profile-level pair. If this profile-level pair already exists,
+ * it will be ignored.
+ *
+ * @param profile The "profile" component.
+ * @param level The "level" component.
+ */
void addProfileLevel(uint32_t profile, uint32_t level);
+ /**
+ * Add a color format. If this color format already exists, it will be
+ * ignored.
+ *
+ * @param format The color format.
+ */
void addColorFormat(uint32_t format);
+ /**
+ * Add flags. The underlying operation is bitwise-or. In other words,
+ * bits that have already been set will be ignored.
+ *
+ * @param flags The additional flags.
+ */
void addFlags(uint32_t flags);
+ private:
+ /**
+ * The associated `Capabilities` object.
+ */
+ Capabilities* mCap;
+ /**
+ * Construct a writer for the given `Capabilities` object.
+ *
+ * @param cap The `Capabilities` object to be written to.
+ */
+ CapabilitiesWriter(Capabilities* cap);
+
+ friend MediaCodecInfoWriter;
};
bool isEncoder() const;
- bool hasQuirk(const char *name) const;
void getSupportedMimes(Vector<AString> *mimes) const;
const sp<Capabilities> getCapabilitiesFor(const char *mime) const;
const char *getCodecName() const;
/**
+ * Return the name of the service that hosts the codec. This value is not
+ * visible at the Java level.
+ *
+ * Currently, this is the "instance name" of the IOmx service.
+ */
+ const char *getOwnerName() const;
+
+ /**
* Serialization over Binder
*/
static sp<MediaCodecInfo> FromParcel(const Parcel &parcel);
status_t writeToParcel(Parcel *parcel) const;
private:
- // variable set only in constructor - these are accessed by MediaCodecList
- // to avoid duplication of same variables
AString mName;
+ AString mOwner;
bool mIsEncoder;
- bool mHasSoleMime; // was initialized with mime
-
- Vector<AString> mQuirks;
KeyedVector<AString, sp<Capabilities> > mCaps;
- sp<Capabilities> mCurrentCaps; // currently initalized capabilities
-
ssize_t getCapabilityIndex(const char *mime) const;
- /* Methods used by MediaCodecList to construct the info
- * object from XML.
- *
- * After info object is created:
- * - additional quirks can be added
- * - additional mimes can be added
- * - OMX codec capabilities can be set for the current mime-type
- * - a capability detail can be set for the current mime-type
- * - a feature can be set for the current mime-type
- * - info object can be completed when parsing of a mime-type is done
+ /**
+ * Construct an `MediaCodecInfo` object. After the construction, its
+ * information can be set via an `MediaCodecInfoWriter` object obtained from
+ * `MediaCodecListWriter::addMediaCodecInfo()`.
*/
- MediaCodecInfo(AString name, bool encoder, const char *mime);
- void addQuirk(const char *name);
- status_t addMime(const char *mime);
- status_t updateMime(const char *mime);
+ MediaCodecInfo();
- status_t initializeCapabilities(const sp<Capabilities> &caps);
- void addDetail(const AString &key, const AString &value);
- void addFeature(const AString &key, int32_t value);
- void addFeature(const AString &key, const char *value);
- void removeMime(const char *mime);
- void complete();
+ DISALLOW_COPY_AND_ASSIGN(MediaCodecInfo);
- DISALLOW_EVIL_CONSTRUCTORS(MediaCodecInfo);
-
- friend struct MediaCodecList;
friend class MediaCodecListOverridesTest;
+ friend struct MediaCodecInfoWriter;
+ friend struct MediaCodecListWriter;
+};
+
+/**
+ * This class is to be used by a `MediaCodecListBuilderBase` instance to
+ * populate information inside the associated `MediaCodecInfo` object.
+ *
+ * The only place where an instance of `MediaCodecInfoWriter` can be constructed
+ * is `MediaCodecListWriter::addMediaCodecInfo()`. A `MediaCodecListBuilderBase`
+ * instance should call `MediaCodecListWriter::addMediaCodecInfo()` on the given
+ * `MediaCodecListWriter` object given as an input to
+ * `MediaCodecListBuilderBase::buildMediaCodecList()`.
+ */
+struct MediaCodecInfoWriter {
+ /**
+ * Set the name of the codec.
+ *
+ * @param name The new name.
+ */
+ void setName(const char* name);
+ /**
+ * Set the owner name of the codec.
+ *
+ * This "owner name" is the name of the `IOmx` instance that supports this
+ * codec.
+ *
+ * @param owner The new owner name.
+ */
+ void setOwner(const char* owner);
+ /**
+ * Set whether this codec is an encoder or a decoder.
+ *
+ * @param isEncoder Whether this codec is an encoder or a decoder.
+ */
+ void setEncoder(bool isEncoder = true);
+ /**
+ * Add a mime to an indexed list and return a `CapabilitiesWriter` object
+ * that can be used for modifying the associated `Capabilities`.
+ *
+ * If the mime already exists, this function will return the
+ * `CapabilitiesWriter` associated with the mime.
+ *
+ * @param[in] mime The name of a new mime to add.
+ * @return writer The `CapabilitiesWriter` object for modifying the
+ * `Capabilities` associated with the mime. `writer` will be valid
+ * regardless of whether `mime` already exists or not.
+ */
+ std::unique_ptr<MediaCodecInfo::CapabilitiesWriter> addMime(
+ const char* mime);
+ /**
+ * Remove a mime.
+ *
+ * @param mime The name of the mime to remove.
+ * @return `true` if `mime` is removed; `false` if `mime` is not found.
+ */
+ bool removeMime(const char* mime);
+private:
+ /**
+ * The associated `MediaCodecInfo`.
+ */
+ MediaCodecInfo* mInfo;
+ /**
+ * Construct the `MediaCodecInfoWriter` object associated with the given
+ * `MediaCodecInfo` object.
+ *
+ * @param info The underlying `MediaCodecInfo` object.
+ */
+ MediaCodecInfoWriter(MediaCodecInfo* info);
+
+ DISALLOW_COPY_AND_ASSIGN(MediaCodecInfoWriter);
+
+ friend struct MediaCodecListWriter;
};
} // namespace android
diff --git a/media/libmedia/include/media/mediaplayer.h b/media/libmedia/include/media/mediaplayer.h
index cb0a99f..25741d3 100644
--- a/media/libmedia/include/media/mediaplayer.h
+++ b/media/libmedia/include/media/mediaplayer.h
@@ -50,6 +50,7 @@
MEDIA_PAUSED = 7,
MEDIA_STOPPED = 8,
MEDIA_SKIPPED = 9,
+ MEDIA_NOTIFY_TIME = 98,
MEDIA_TIMED_TEXT = 99,
MEDIA_ERROR = 100,
MEDIA_INFO = 200,
@@ -245,6 +246,7 @@
status_t seekTo(
int msec,
MediaPlayerSeekMode mode = MediaPlayerSeekMode::SEEK_PREVIOUS_SYNC);
+ status_t notifyAt(int64_t mediaTimeUs);
status_t getCurrentPosition(int *msec);
status_t getDuration(int *msec);
status_t reset();
diff --git a/media/libmedia/include/media/omx/1.0/Conversion.h b/media/libmedia/include/media/omx/1.0/Conversion.h
index 9816fe1..94f2e8d 100644
--- a/media/libmedia/include/media/omx/1.0/Conversion.h
+++ b/media/libmedia/include/media/omx/1.0/Conversion.h
@@ -24,7 +24,6 @@
#include <hidl/MQDescriptor.h>
#include <hidl/Status.h>
-#include <hidlmemory/mapping.h>
#include <binder/Binder.h>
#include <binder/Status.h>
@@ -36,7 +35,6 @@
#include <media/OMXBuffer.h>
#include <media/hardware/VideoAPI.h>
-#include <android/hidl/memory/1.0/IMemory.h>
#include <android/hardware/media/omx/1.0/types.h>
#include <android/hardware/media/omx/1.0/IOmx.h>
#include <android/hardware/media/omx/1.0/IOmxNode.h>
diff --git a/media/libmedia/mediaplayer.cpp b/media/libmedia/mediaplayer.cpp
index 49101d1..00084c1 100644
--- a/media/libmedia/mediaplayer.cpp
+++ b/media/libmedia/mediaplayer.cpp
@@ -610,6 +610,15 @@
return result;
}
+status_t MediaPlayer::notifyAt(int64_t mediaTimeUs)
+{
+ Mutex::Autolock _l(mLock);
+ if (mPlayer != 0) {
+ return mPlayer->notifyAt(mediaTimeUs);
+ }
+ return INVALID_OPERATION;
+}
+
status_t MediaPlayer::reset_l()
{
mLoop = false;
@@ -950,6 +959,9 @@
mVideoWidth = ext1;
mVideoHeight = ext2;
break;
+ case MEDIA_NOTIFY_TIME:
+ ALOGV("Received notify time message");
+ break;
case MEDIA_TIMED_TEXT:
ALOGV("Received timed text message");
break;
diff --git a/media/libmediametrics/MediaAnalyticsItem.cpp b/media/libmediametrics/MediaAnalyticsItem.cpp
index 47a147e..31ac4e5 100644
--- a/media/libmediametrics/MediaAnalyticsItem.cpp
+++ b/media/libmediametrics/MediaAnalyticsItem.cpp
@@ -59,6 +59,7 @@
MediaAnalyticsItem::MediaAnalyticsItem()
: mPid(-1),
mUid(-1),
+ mPkgVersionCode(0),
mSessionID(MediaAnalyticsItem::SessionIDNone),
mTimestamp(0),
mFinalized(0),
@@ -70,6 +71,7 @@
MediaAnalyticsItem::MediaAnalyticsItem(MediaAnalyticsItem::Key key)
: mPid(-1),
mUid(-1),
+ mPkgVersionCode(0),
mSessionID(MediaAnalyticsItem::SessionIDNone),
mTimestamp(0),
mFinalized(0),
@@ -98,7 +100,7 @@
// clean attributes
// contents of the attributes
- for (size_t i = 0 ; i < mPropSize; i++ ) {
+ for (size_t i = 0 ; i < mPropCount; i++ ) {
clearProp(&mProps[i]);
}
// the attribute records themselves
@@ -298,7 +300,8 @@
clearProp(prop);
if (i != mPropCount-1) {
// in the middle, bring last one down to fill gap
- mProps[i] = mProps[mPropCount-1];
+ copyProp(prop, &mProps[mPropCount-1]);
+ clearProp(&mProps[mPropCount-1]);
}
mPropCount--;
return true;
diff --git a/media/libmediaplayerservice/Android.mk b/media/libmediaplayerservice/Android.mk
index 1fc74a9..614a942 100644
--- a/media/libmediaplayerservice/Android.mk
+++ b/media/libmediaplayerservice/Android.mk
@@ -8,12 +8,10 @@
LOCAL_SRC_FILES:= \
ActivityManager.cpp \
- HDCP.cpp \
MediaPlayerFactory.cpp \
MediaPlayerService.cpp \
MediaRecorderClient.cpp \
MetadataRetrieverClient.cpp \
- RemoteDisplay.cpp \
StagefrightRecorder.cpp \
TestPlayerStub.cpp \
@@ -35,7 +33,6 @@
libstagefright_foundation \
libstagefright_httplive \
libstagefright_omx \
- libstagefright_wfd \
libutils \
libnativewindow \
libhidlbase \
@@ -51,7 +48,6 @@
LOCAL_C_INCLUDES := \
frameworks/av/media/libstagefright/include \
frameworks/av/media/libstagefright/rtsp \
- frameworks/av/media/libstagefright/wifi-display \
frameworks/av/media/libstagefright/webm \
$(LOCAL_PATH)/include/media \
frameworks/av/include/camera \
diff --git a/media/libmediaplayerservice/HDCP.cpp b/media/libmediaplayerservice/HDCP.cpp
deleted file mode 100644
index afe3936..0000000
--- a/media/libmediaplayerservice/HDCP.cpp
+++ /dev/null
@@ -1,175 +0,0 @@
-/*
- * Copyright (C) 2012 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 "HDCP"
-#include <utils/Log.h>
-
-#include "HDCP.h"
-
-#include <media/stagefright/foundation/ADebug.h>
-
-#include <dlfcn.h>
-
-namespace android {
-
-HDCP::HDCP(bool createEncryptionModule)
- : mIsEncryptionModule(createEncryptionModule),
- mLibHandle(NULL),
- mHDCPModule(NULL) {
- mLibHandle = dlopen("libstagefright_hdcp.so", RTLD_NOW);
-
- if (mLibHandle == NULL) {
- ALOGE("Unable to locate libstagefright_hdcp.so");
- return;
- }
-
- typedef HDCPModule *(*CreateHDCPModuleFunc)(
- void *, HDCPModule::ObserverFunc);
-
- CreateHDCPModuleFunc createHDCPModule =
- mIsEncryptionModule
- ? (CreateHDCPModuleFunc)dlsym(mLibHandle, "createHDCPModule")
- : (CreateHDCPModuleFunc)dlsym(
- mLibHandle, "createHDCPModuleForDecryption");
-
- if (createHDCPModule == NULL) {
- ALOGE("Unable to find symbol 'createHDCPModule'.");
- } else if ((mHDCPModule = createHDCPModule(
- this, &HDCP::ObserveWrapper)) == NULL) {
- ALOGE("createHDCPModule failed.");
- }
-}
-
-HDCP::~HDCP() {
- Mutex::Autolock autoLock(mLock);
-
- if (mHDCPModule != NULL) {
- delete mHDCPModule;
- mHDCPModule = NULL;
- }
-
- if (mLibHandle != NULL) {
- dlclose(mLibHandle);
- mLibHandle = NULL;
- }
-}
-
-status_t HDCP::setObserver(const sp<IHDCPObserver> &observer) {
- Mutex::Autolock autoLock(mLock);
-
- if (mHDCPModule == NULL) {
- return NO_INIT;
- }
-
- mObserver = observer;
-
- return OK;
-}
-
-status_t HDCP::initAsync(const char *host, unsigned port) {
- Mutex::Autolock autoLock(mLock);
-
- if (mHDCPModule == NULL) {
- return NO_INIT;
- }
-
- return mHDCPModule->initAsync(host, port);
-}
-
-status_t HDCP::shutdownAsync() {
- Mutex::Autolock autoLock(mLock);
-
- if (mHDCPModule == NULL) {
- return NO_INIT;
- }
-
- return mHDCPModule->shutdownAsync();
-}
-
-uint32_t HDCP::getCaps() {
- Mutex::Autolock autoLock(mLock);
-
- if (mHDCPModule == NULL) {
- return NO_INIT;
- }
-
- return mHDCPModule->getCaps();
-}
-
-status_t HDCP::encrypt(
- const void *inData, size_t size, uint32_t streamCTR,
- uint64_t *outInputCTR, void *outData) {
- Mutex::Autolock autoLock(mLock);
-
- CHECK(mIsEncryptionModule);
-
- if (mHDCPModule == NULL) {
- *outInputCTR = 0;
-
- return NO_INIT;
- }
-
- return mHDCPModule->encrypt(inData, size, streamCTR, outInputCTR, outData);
-}
-
-status_t HDCP::encryptNative(
- const sp<GraphicBuffer> &graphicBuffer,
- size_t offset, size_t size, uint32_t streamCTR,
- uint64_t *outInputCTR, void *outData) {
- Mutex::Autolock autoLock(mLock);
-
- CHECK(mIsEncryptionModule);
-
- if (mHDCPModule == NULL) {
- *outInputCTR = 0;
-
- return NO_INIT;
- }
-
- return mHDCPModule->encryptNative(graphicBuffer->handle,
- offset, size, streamCTR, outInputCTR, outData);
-}
-
-status_t HDCP::decrypt(
- const void *inData, size_t size,
- uint32_t streamCTR, uint64_t outInputCTR, void *outData) {
- Mutex::Autolock autoLock(mLock);
-
- CHECK(!mIsEncryptionModule);
-
- if (mHDCPModule == NULL) {
- return NO_INIT;
- }
-
- return mHDCPModule->decrypt(inData, size, streamCTR, outInputCTR, outData);
-}
-
-// static
-void HDCP::ObserveWrapper(void *me, int msg, int ext1, int ext2) {
- static_cast<HDCP *>(me)->observe(msg, ext1, ext2);
-}
-
-void HDCP::observe(int msg, int ext1, int ext2) {
- Mutex::Autolock autoLock(mLock);
-
- if (mObserver != NULL) {
- mObserver->notify(msg, ext1, ext2, NULL /* obj */);
- }
-}
-
-} // namespace android
-
diff --git a/media/libmediaplayerservice/HDCP.h b/media/libmediaplayerservice/HDCP.h
deleted file mode 100644
index 83c61b5..0000000
--- a/media/libmediaplayerservice/HDCP.h
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Copyright (C) 2012 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 HDCP_H_
-
-#define HDCP_H_
-
-#include <media/IHDCP.h>
-#include <utils/Mutex.h>
-
-namespace android {
-
-struct HDCP : public BnHDCP {
- explicit HDCP(bool createEncryptionModule);
- virtual ~HDCP();
-
- virtual status_t setObserver(const sp<IHDCPObserver> &observer);
- virtual status_t initAsync(const char *host, unsigned port);
- virtual status_t shutdownAsync();
- virtual uint32_t getCaps();
-
- virtual status_t encrypt(
- const void *inData, size_t size, uint32_t streamCTR,
- uint64_t *outInputCTR, void *outData);
-
- virtual status_t encryptNative(
- const sp<GraphicBuffer> &graphicBuffer,
- size_t offset, size_t size, uint32_t streamCTR,
- uint64_t *outInputCTR, void *outData);
-
- virtual status_t decrypt(
- const void *inData, size_t size,
- uint32_t streamCTR, uint64_t outInputCTR, void *outData);
-
-private:
- Mutex mLock;
-
- bool mIsEncryptionModule;
-
- void *mLibHandle;
- HDCPModule *mHDCPModule;
- sp<IHDCPObserver> mObserver;
-
- static void ObserveWrapper(void *me, int msg, int ext1, int ext2);
- void observe(int msg, int ext1, int ext2);
-
- DISALLOW_EVIL_CONSTRUCTORS(HDCP);
-};
-
-} // namespace android
-
-#endif // HDCP_H_
-
diff --git a/media/libmediaplayerservice/MediaPlayerService.cpp b/media/libmediaplayerservice/MediaPlayerService.cpp
index ba98f18..44ab5da 100644
--- a/media/libmediaplayerservice/MediaPlayerService.cpp
+++ b/media/libmediaplayerservice/MediaPlayerService.cpp
@@ -79,9 +79,7 @@
#include <media/stagefright/omx/OMX.h>
-#include "HDCP.h"
#include "HTTPBase.h"
-#include "RemoteDisplay.h"
static const int kDumpLockRetries = 50;
static const int kDumpLockSleepUs = 20000;
@@ -349,18 +347,13 @@
return mOMX;
}
-sp<IHDCP> MediaPlayerService::makeHDCP(bool createEncryptionModule) {
- return new HDCP(createEncryptionModule);
-}
-
sp<IRemoteDisplay> MediaPlayerService::listenForRemoteDisplay(
- const String16 &opPackageName,
- const sp<IRemoteDisplayClient>& client, const String8& iface) {
- if (!checkPermission("android.permission.CONTROL_WIFI_DISPLAY")) {
- return NULL;
- }
+ const String16 &/*opPackageName*/,
+ const sp<IRemoteDisplayClient>& /*client*/,
+ const String8& /*iface*/) {
+ ALOGE("listenForRemoteDisplay is no longer supported!");
- return new RemoteDisplay(opPackageName, client, iface.string());
+ return NULL;
}
status_t MediaPlayerService::AudioOutput::dump(int fd, const Vector<String16>& args) const
@@ -1269,6 +1262,14 @@
return p->reset();
}
+status_t MediaPlayerService::Client::notifyAt(int64_t mediaTimeUs)
+{
+ ALOGV("[%d] notifyAt(%lld)", mConnId, (long long)mediaTimeUs);
+ sp<MediaPlayerBase> p = getPlayer();
+ if (p == 0) return UNKNOWN_ERROR;
+ return p->notifyAt(mediaTimeUs);
+}
+
status_t MediaPlayerService::Client::setAudioStreamType(audio_stream_type_t type)
{
ALOGV("[%d] setAudioStreamType(%d)", mConnId, type);
diff --git a/media/libmediaplayerservice/MediaPlayerService.h b/media/libmediaplayerservice/MediaPlayerService.h
index 25691f9..d62dd52 100644
--- a/media/libmediaplayerservice/MediaPlayerService.h
+++ b/media/libmediaplayerservice/MediaPlayerService.h
@@ -229,7 +229,6 @@
virtual sp<IMediaCodecList> getCodecList() const;
virtual sp<IOMX> getOMX();
- virtual sp<IHDCP> makeHDCP(bool createEncryptionModule);
virtual sp<IRemoteDisplay> listenForRemoteDisplay(const String16 &opPackageName,
const sp<IRemoteDisplayClient>& client, const String8& iface);
@@ -327,6 +326,7 @@
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);
diff --git a/media/libmediaplayerservice/RemoteDisplay.cpp b/media/libmediaplayerservice/RemoteDisplay.cpp
deleted file mode 100644
index 0eb4b5d..0000000
--- a/media/libmediaplayerservice/RemoteDisplay.cpp
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Copyright 2012, 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 "RemoteDisplay.h"
-
-#include "source/WifiDisplaySource.h"
-
-#include <media/IRemoteDisplayClient.h>
-#include <media/stagefright/foundation/ADebug.h>
-#include <media/stagefright/foundation/AMessage.h>
-#include <media/stagefright/foundation/ANetworkSession.h>
-
-namespace android {
-
-RemoteDisplay::RemoteDisplay(
- const String16 &opPackageName,
- const sp<IRemoteDisplayClient> &client,
- const char *iface)
- : mLooper(new ALooper),
- mNetSession(new ANetworkSession) {
- mLooper->setName("wfd_looper");
-
- mSource = new WifiDisplaySource(opPackageName, mNetSession, client);
- mLooper->registerHandler(mSource);
-
- mNetSession->start();
- mLooper->start();
-
- mSource->start(iface);
-}
-
-RemoteDisplay::~RemoteDisplay() {
-}
-
-status_t RemoteDisplay::pause() {
- return mSource->pause();
-}
-
-status_t RemoteDisplay::resume() {
- return mSource->resume();
-}
-
-status_t RemoteDisplay::dispose() {
- mSource->stop();
- mSource.clear();
-
- mLooper->stop();
- mNetSession->stop();
-
- return OK;
-}
-
-} // namespace android
diff --git a/media/libmediaplayerservice/RemoteDisplay.h b/media/libmediaplayerservice/RemoteDisplay.h
deleted file mode 100644
index d4573e9..0000000
--- a/media/libmediaplayerservice/RemoteDisplay.h
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Copyright 2012, 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 REMOTE_DISPLAY_H_
-
-#define REMOTE_DISPLAY_H_
-
-#include <media/IMediaPlayerService.h>
-#include <media/IRemoteDisplay.h>
-#include <media/stagefright/foundation/ABase.h>
-#include <utils/Errors.h>
-#include <utils/RefBase.h>
-
-namespace android {
-
-struct ALooper;
-struct ANetworkSession;
-class IRemoteDisplayClient;
-struct WifiDisplaySource;
-
-struct RemoteDisplay : public BnRemoteDisplay {
- RemoteDisplay(
- const String16 &opPackageName,
- const sp<IRemoteDisplayClient> &client,
- const char *iface);
-
- virtual status_t pause();
- virtual status_t resume();
- virtual status_t dispose();
-
-protected:
- virtual ~RemoteDisplay();
-
-private:
- sp<ALooper> mNetLooper;
- sp<ALooper> mLooper;
- sp<ANetworkSession> mNetSession;
- sp<WifiDisplaySource> mSource;
-
- DISALLOW_EVIL_CONSTRUCTORS(RemoteDisplay);
-};
-
-} // namespace android
-
-#endif // REMOTE_DISPLAY_H_
-
diff --git a/media/libmediaplayerservice/include/MediaPlayerInterface.h b/media/libmediaplayerservice/include/MediaPlayerInterface.h
index bcdca37..764df70 100644
--- a/media/libmediaplayerservice/include/MediaPlayerInterface.h
+++ b/media/libmediaplayerservice/include/MediaPlayerInterface.h
@@ -225,6 +225,9 @@
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 */) {
+ return INVALID_OPERATION;
+ }
virtual status_t setLooping(int loop) = 0;
virtual player_type playerType() = 0;
virtual status_t setParameter(int key, const Parcel &request) = 0;
diff --git a/media/libmediaplayerservice/nuplayer/GenericSource.cpp b/media/libmediaplayerservice/nuplayer/GenericSource.cpp
index e0d253d..45a5f27 100644
--- a/media/libmediaplayerservice/nuplayer/GenericSource.cpp
+++ b/media/libmediaplayerservice/nuplayer/GenericSource.cpp
@@ -31,6 +31,7 @@
#include <media/stagefright/DataSource.h>
#include <media/stagefright/FileSource.h>
#include <media/stagefright/MediaBuffer.h>
+#include <media/stagefright/MediaClock.h>
#include <media/stagefright/MediaDefs.h>
#include <media/stagefright/MediaExtractor.h>
#include <media/stagefright/MediaSource.h>
@@ -51,7 +52,8 @@
NuPlayer::GenericSource::GenericSource(
const sp<AMessage> ¬ify,
bool uidValid,
- uid_t uid)
+ uid_t uid,
+ const sp<MediaClock> &mediaClock)
: Source(notify),
mAudioTimeUs(0),
mAudioLastDequeueTimeUs(0),
@@ -65,10 +67,12 @@
mIsStreaming(false),
mUIDValid(uidValid),
mUID(uid),
+ mMediaClock(mediaClock),
mFd(-1),
mBitrate(-1ll),
mPendingReadBufferTypes(0) {
ALOGV("GenericSource");
+ CHECK(mediaClock != NULL);
mBufferingMonitor = new BufferingMonitor(notify);
resetDataSource();
@@ -729,17 +733,20 @@
int64_t timeUs;
CHECK(msg->findInt64("timeUs", &timeUs));
- int64_t subTimeUs;
+ int64_t subTimeUs = 0;
readBuffer(type, timeUs, MediaPlayerSeekMode::SEEK_PREVIOUS_SYNC /* mode */, &subTimeUs);
- int64_t delayUs = subTimeUs - timeUs;
+ status_t eosResult;
+ if (!packets->hasBufferAvailable(&eosResult)) {
+ return;
+ }
+
if (msg->what() == kWhatFetchSubtitleData) {
- const int64_t oneSecUs = 1000000ll;
- delayUs -= oneSecUs;
+ subTimeUs -= 1000000ll; // send subtile data one second earlier
}
sp<AMessage> msg2 = new AMessage(sendWhat, this);
msg2->setInt32("generation", msgGeneration);
- msg2->post(delayUs < 0 ? 0 : delayUs);
+ mMediaClock->addTimer(msg2, subTimeUs);
}
void NuPlayer::GenericSource::sendTextData(
@@ -771,8 +778,10 @@
notify->setBuffer("buffer", buffer);
notify->post();
- const int64_t delayUs = nextSubTimeUs - subTimeUs;
- msg->post(delayUs < 0 ? 0 : delayUs);
+ if (msg->what() == kWhatSendSubtitleData) {
+ nextSubTimeUs -= 1000000ll; // send subtile data one second earlier
+ }
+ mMediaClock->addTimer(msg, nextSubTimeUs);
}
}
diff --git a/media/libmediaplayerservice/nuplayer/GenericSource.h b/media/libmediaplayerservice/nuplayer/GenericSource.h
index 4064133..381bcac 100644
--- a/media/libmediaplayerservice/nuplayer/GenericSource.h
+++ b/media/libmediaplayerservice/nuplayer/GenericSource.h
@@ -35,12 +35,14 @@
struct IMediaHTTPService;
struct MediaSource;
class MediaBuffer;
+struct MediaClock;
struct NuCachedSource2;
struct NuPlayer::GenericSource : public NuPlayer::Source,
public MediaBufferObserver // Modular DRM
{
- GenericSource(const sp<AMessage> ¬ify, bool uidValid, uid_t uid);
+ GenericSource(const sp<AMessage> ¬ify, bool uidValid, uid_t uid,
+ const sp<MediaClock> &mediaClock);
status_t setDataSource(
const sp<IMediaHTTPService> &httpService,
@@ -227,6 +229,7 @@
bool mIsStreaming;
bool mUIDValid;
uid_t mUID;
+ const sp<MediaClock> mMediaClock;
sp<IMediaHTTPService> mHTTPService;
AString mUri;
KeyedVector<String8, String8> mUriHeaders;
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
index 274f613..29b1781 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
@@ -281,7 +281,7 @@
ALOGV("setDataSourceAsync GenericSource %s", url);
sp<GenericSource> genericSource =
- new GenericSource(notify, mUIDValid, mUID);
+ new GenericSource(notify, mUIDValid, mUID, mMediaClock);
status_t err = genericSource->setDataSource(httpService, url, headers);
@@ -304,7 +304,7 @@
sp<AMessage> notify = new AMessage(kWhatSourceNotify, this);
sp<GenericSource> source =
- new GenericSource(notify, mUIDValid, mUID);
+ new GenericSource(notify, mUIDValid, mUID, mMediaClock);
ALOGV("setDataSourceAsync fd %d/%lld/%lld source: %p",
fd, (long long)offset, (long long)length, source.get());
@@ -325,7 +325,7 @@
sp<AMessage> msg = new AMessage(kWhatSetDataSource, this);
sp<AMessage> notify = new AMessage(kWhatSourceNotify, this);
- sp<GenericSource> source = new GenericSource(notify, mUIDValid, mUID);
+ sp<GenericSource> source = new GenericSource(notify, mUIDValid, mUID, mMediaClock);
status_t err = source->setDataSource(dataSource);
if (err != OK) {
@@ -473,6 +473,13 @@
(new AMessage(kWhatReset, this))->post();
}
+status_t NuPlayer::notifyAt(int64_t mediaTimeUs) {
+ sp<AMessage> notify = new AMessage(kWhatNotifyTime, this);
+ notify->setInt64("timerUs", mediaTimeUs);
+ mMediaClock->addTimer(notify, mediaTimeUs);
+ return OK;
+}
+
void NuPlayer::seekToAsync(int64_t seekTimeUs, MediaPlayerSeekMode mode, bool needNotify) {
sp<AMessage> msg = new AMessage(kWhatSeek, this);
msg->setInt64("seekTimeUs", seekTimeUs);
@@ -1315,6 +1322,16 @@
break;
}
+ case kWhatNotifyTime:
+ {
+ ALOGV("kWhatNotifyTime");
+ int64_t timerUs;
+ CHECK(msg->findInt64("timerUs", &timerUs));
+
+ notifyListener(MEDIA_NOTIFY_TIME, timerUs, 0);
+ break;
+ }
+
case kWhatSeek:
{
int64_t seekTimeUs;
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.h b/media/libmediaplayerservice/nuplayer/NuPlayer.h
index 5e3c48b..eefc2a6 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayer.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayer.h
@@ -73,6 +73,9 @@
// Will notify the driver through "notifyResetComplete" once finished.
void resetAsync();
+ // Request a notification when specified media time is reached.
+ status_t notifyAt(int64_t mediaTimeUs);
+
// Will notify the driver through "notifySeekComplete" once finished
// and needNotify is true.
void seekToAsync(
@@ -140,6 +143,7 @@
kWhatClosedCaptionNotify = 'capN',
kWhatRendererNotify = 'renN',
kWhatReset = 'rset',
+ kWhatNotifyTime = 'nfyT',
kWhatSeek = 'seek',
kWhatPause = 'paus',
kWhatResume = 'rsme',
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
index 19281cd..4005be8 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
@@ -78,6 +78,8 @@
ALOGD("NuPlayerDriver(%p) created, clientPid(%d)", this, pid);
mLooper->setName("NuPlayerDriver Looper");
+ mMediaClock->init();
+
// set up an analytics record
mAnalyticsItem = new MediaAnalyticsItem(kKeyPlayer);
mAnalyticsItem->generateSessionID();
@@ -662,6 +664,11 @@
return OK;
}
+status_t NuPlayerDriver::notifyAt(int64_t mediaTimeUs) {
+ ALOGV("notifyAt(%p), time:%lld", this, (long long)mediaTimeUs);
+ return mPlayer->notifyAt(mediaTimeUs);
+}
+
status_t NuPlayerDriver::setLooping(int loop) {
mLooping = loop != 0;
return OK;
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.h b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.h
index b797ca1..666359a 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.h
@@ -65,6 +65,7 @@
virtual status_t getCurrentPosition(int *msec);
virtual status_t getDuration(int *msec);
virtual status_t reset();
+ virtual status_t notifyAt(int64_t mediaTimeUs) override;
virtual status_t setLooping(int loop);
virtual player_type playerType();
virtual status_t invoke(const Parcel &request, Parcel *reply);
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
index 323b52d..50db4c9 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
@@ -549,8 +549,10 @@
CHECK_EQ(mAudioSink->getPosition(&numFramesPlayed),
(status_t)OK);
+ // Handle AudioTrack race when start is immediately called after flush.
uint32_t numFramesPendingPlayout =
- mNumFramesWritten - numFramesPlayed;
+ (mNumFramesWritten > numFramesPlayed ?
+ mNumFramesWritten - numFramesPlayed : 0);
// This is how long the audio sink will have data to
// play back.
@@ -1153,7 +1155,18 @@
return writtenAudioDurationUs - (mediaUs - mAudioFirstAnchorTimeMediaUs);
}
}
- return writtenAudioDurationUs - mAudioSink->getPlayedOutDurationUs(nowUs);
+
+ const int64_t audioSinkPlayedUs = mAudioSink->getPlayedOutDurationUs(nowUs);
+ int64_t pendingUs = writtenAudioDurationUs - audioSinkPlayedUs;
+ if (pendingUs < 0) {
+ // This shouldn't happen unless the timestamp is stale.
+ ALOGW("%s: pendingUs %lld < 0, clamping to zero, potential resume after pause "
+ "writtenAudioDurationUs: %lld, audioSinkPlayedUs: %lld",
+ __func__, (long long)pendingUs,
+ (long long)writtenAudioDurationUs, (long long)audioSinkPlayedUs);
+ pendingUs = 0;
+ }
+ return pendingUs;
}
int64_t NuPlayer::Renderer::getRealTimeUs(int64_t mediaTimeUs, int64_t nowUs) {
@@ -1235,82 +1248,48 @@
return;
}
- bool needRepostDrainVideoQueue = false;
- int64_t delayUs;
int64_t nowUs = ALooper::GetNowUs();
- int64_t realTimeUs;
if (mFlags & FLAG_REAL_TIME) {
- int64_t mediaTimeUs;
- CHECK(entry.mBuffer->meta()->findInt64("timeUs", &mediaTimeUs));
- realTimeUs = mediaTimeUs;
- } else {
- int64_t mediaTimeUs;
- CHECK(entry.mBuffer->meta()->findInt64("timeUs", &mediaTimeUs));
+ int64_t realTimeUs;
+ CHECK(entry.mBuffer->meta()->findInt64("timeUs", &realTimeUs));
- {
- Mutex::Autolock autoLock(mLock);
- if (mAnchorTimeMediaUs < 0) {
- mMediaClock->updateAnchor(mediaTimeUs, nowUs, mediaTimeUs);
- mAnchorTimeMediaUs = mediaTimeUs;
- realTimeUs = nowUs;
- } else if (!mVideoSampleReceived) {
- // Always render the first video frame.
- realTimeUs = nowUs;
- } else if (mAudioFirstAnchorTimeMediaUs < 0
- || mMediaClock->getRealTimeFor(mediaTimeUs, &realTimeUs) == OK) {
- realTimeUs = getRealTimeUs(mediaTimeUs, nowUs);
- } else if (mediaTimeUs - mAudioFirstAnchorTimeMediaUs >= 0) {
- needRepostDrainVideoQueue = true;
- realTimeUs = nowUs;
- } else {
- realTimeUs = nowUs;
- }
- }
- if (!mHasAudio) {
- // smooth out videos >= 10fps
- mMediaClock->updateMaxTimeMedia(mediaTimeUs + 100000);
- }
+ realTimeUs = mVideoScheduler->schedule(realTimeUs * 1000) / 1000;
- // Heuristics to handle situation when media time changed without a
- // discontinuity. If we have not drained an audio buffer that was
- // received after this buffer, repost in 10 msec. Otherwise repost
- // in 500 msec.
- delayUs = realTimeUs - nowUs;
- int64_t postDelayUs = -1;
- if (delayUs > 500000) {
- postDelayUs = 500000;
- if (mHasAudio && (mLastAudioBufferDrained - entry.mBufferOrdinal) <= 0) {
- postDelayUs = 10000;
- }
- } else if (needRepostDrainVideoQueue) {
- // CHECK(mPlaybackRate > 0);
- // CHECK(mAudioFirstAnchorTimeMediaUs >= 0);
- // CHECK(mediaTimeUs - mAudioFirstAnchorTimeMediaUs >= 0);
- postDelayUs = mediaTimeUs - mAudioFirstAnchorTimeMediaUs;
- postDelayUs /= mPlaybackRate;
- }
+ int64_t twoVsyncsUs = 2 * (mVideoScheduler->getVsyncPeriod() / 1000);
- if (postDelayUs >= 0) {
- msg->setWhat(kWhatPostDrainVideoQueue);
- msg->post(postDelayUs);
- mVideoScheduler->restart();
- ALOGI("possible video time jump of %dms (%lld : %lld) or uninitialized media clock,"
- " retrying in %dms",
- (int)(delayUs / 1000), (long long)mediaTimeUs,
- (long long)mAudioFirstAnchorTimeMediaUs, (int)(postDelayUs / 1000));
- mDrainVideoQueuePending = true;
- return;
- }
+ int64_t delayUs = realTimeUs - nowUs;
+
+ ALOGW_IF(delayUs > 500000, "unusually high delayUs: %lld", (long long)delayUs);
+ // post 2 display refreshes before rendering is due
+ msg->post(delayUs > twoVsyncsUs ? delayUs - twoVsyncsUs : 0);
+
+ mDrainVideoQueuePending = true;
+ return;
}
- realTimeUs = mVideoScheduler->schedule(realTimeUs * 1000) / 1000;
- int64_t twoVsyncsUs = 2 * (mVideoScheduler->getVsyncPeriod() / 1000);
+ int64_t mediaTimeUs;
+ CHECK(entry.mBuffer->meta()->findInt64("timeUs", &mediaTimeUs));
- delayUs = realTimeUs - nowUs;
+ {
+ Mutex::Autolock autoLock(mLock);
+ if (mAnchorTimeMediaUs < 0) {
+ mMediaClock->updateAnchor(mediaTimeUs, nowUs, mediaTimeUs);
+ mAnchorTimeMediaUs = mediaTimeUs;
+ }
+ }
+ if (!mHasAudio) {
+ // smooth out videos >= 10fps
+ mMediaClock->updateMaxTimeMedia(mediaTimeUs + 100000);
+ }
- ALOGW_IF(delayUs > 500000, "unusually high delayUs: %" PRId64, delayUs);
- // post 2 display refreshes before rendering is due
- msg->post(delayUs > twoVsyncsUs ? delayUs - twoVsyncsUs : 0);
+ if (!mVideoSampleReceived || mediaTimeUs < mAudioFirstAnchorTimeMediaUs) {
+ msg->post();
+ } else {
+ int64_t twoVsyncsUs = 2 * (mVideoScheduler->getVsyncPeriod() / 1000);
+
+ // post 2 display refreshes before rendering is due
+ mMediaClock->addTimer(msg, mediaTimeUs, -twoVsyncsUs);
+ }
mDrainVideoQueuePending = true;
}
@@ -1344,6 +1323,7 @@
realTimeUs = getRealTimeUs(mediaTimeUs, nowUs);
}
+ realTimeUs = mVideoScheduler->schedule(realTimeUs * 1000) / 1000;
bool tooLate = false;
diff --git a/media/libstagefright/AACExtractor.cpp b/media/libstagefright/AACExtractor.cpp
index 7449aa7..30fb5fe 100644
--- a/media/libstagefright/AACExtractor.cpp
+++ b/media/libstagefright/AACExtractor.cpp
@@ -213,7 +213,7 @@
return mInitCheck == OK ? 1 : 0;
}
-sp<IMediaSource> AACExtractor::getTrack(size_t index) {
+sp<MediaSource> AACExtractor::getTrack(size_t index) {
if (mInitCheck != OK || index != 0) {
return NULL;
}
diff --git a/media/libstagefright/AACWriter.cpp b/media/libstagefright/AACWriter.cpp
index 8b1e1c3..9d90dbd 100644
--- a/media/libstagefright/AACWriter.cpp
+++ b/media/libstagefright/AACWriter.cpp
@@ -67,7 +67,7 @@
}
-status_t AACWriter::addSource(const sp<IMediaSource> &source) {
+status_t AACWriter::addSource(const sp<MediaSource> &source) {
if (mInitCheck != OK) {
return mInitCheck;
}
diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp
index ec48561..c44e868 100644
--- a/media/libstagefright/ACodec.cpp
+++ b/media/libstagefright/ACodec.cpp
@@ -62,9 +62,6 @@
#include "include/SharedMemoryBuffer.h"
#include <media/stagefright/omx/OMXUtils.h>
-#include <android/hidl/allocator/1.0/IAllocator.h>
-#include <android/hidl/memory/1.0/IMemory.h>
-
namespace android {
using binder::Status;
@@ -6344,19 +6341,10 @@
CHECK(mCodec->mOMXNode == NULL);
- OMXClient client;
- bool trebleFlag;
- if (client.connect(&trebleFlag) != OK) {
- mCodec->signalError(OMX_ErrorUndefined, NO_INIT);
- return false;
- }
- mCodec->setTrebleFlag(trebleFlag);
-
- sp<IOMX> omx = client.interface();
-
sp<AMessage> notify = new AMessage(kWhatOMXDied, mCodec);
Vector<AString> matchingCodecs;
+ Vector<AString> owners;
AString mime;
@@ -6364,9 +6352,31 @@
int32_t encoder = false;
if (msg->findString("componentName", &componentName)) {
sp<IMediaCodecList> list = MediaCodecList::getInstance();
- if (list != NULL && list->findCodecByName(componentName.c_str()) >= 0) {
- matchingCodecs.add(componentName);
+ 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));
@@ -6378,10 +6388,12 @@
mime.c_str(),
encoder, // createEncoder
0, // flags
- &matchingCodecs);
+ &matchingCodecs,
+ &owners);
}
sp<CodecObserver> observer = new CodecObserver;
+ sp<IOMX> omx;
sp<IOMXNode> omxNode;
status_t err = NAME_NOT_FOUND;
@@ -6389,6 +6401,14 @@
++matchIndex) {
componentName = matchingCodecs[matchIndex];
+ OMXClient client;
+ bool trebleFlag;
+ if (client.connect(owners[matchIndex].c_str(), &trebleFlag) != 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);
@@ -6396,6 +6416,7 @@
androidSetThreadPriority(tid, prevPriority);
if (err == OK) {
+ mCodec->setTrebleFlag(trebleFlag);
break;
} else {
ALOGW("Allocating component '%s' failed, try next one.", componentName.c_str());
@@ -8219,16 +8240,15 @@
}
status_t ACodec::queryCapabilities(
- const AString &name, const AString &mime, bool isEncoder,
- sp<MediaCodecInfo::Capabilities> *caps) {
- (*caps).clear();
- const char *role = GetComponentRole(isEncoder, mime.c_str());
+ const char* owner, const char* name, const char* mime, bool isEncoder,
+ MediaCodecInfo::CapabilitiesWriter* caps) {
+ const char *role = GetComponentRole(isEncoder, mime);
if (role == NULL) {
return BAD_VALUE;
}
OMXClient client;
- status_t err = client.connect();
+ status_t err = client.connect(owner);
if (err != OK) {
return err;
}
@@ -8237,7 +8257,7 @@
sp<CodecObserver> observer = new CodecObserver;
sp<IOMXNode> omxNode;
- err = omx->allocateNode(name.c_str(), observer, &omxNode);
+ err = omx->allocateNode(name, observer, &omxNode);
if (err != OK) {
client.disconnect();
return err;
@@ -8250,8 +8270,7 @@
return err;
}
- sp<MediaCodecInfo::CapabilitiesBuilder> builder = new MediaCodecInfo::CapabilitiesBuilder();
- bool isVideo = mime.startsWithIgnoreCase("video/");
+ bool isVideo = strncasecmp(mime, "video/", 6) == 0;
if (isVideo) {
OMX_VIDEO_PARAM_PROFILELEVELTYPE param;
@@ -8266,22 +8285,22 @@
if (err != OK) {
break;
}
- builder->addProfileLevel(param.eProfile, param.eLevel);
+ caps->addProfileLevel(param.eProfile, param.eLevel);
// AVC components may not list the constrained profiles explicitly, but
// decoders that support a profile also support its constrained version.
// Encoders must explicitly support constrained profiles.
- if (!isEncoder && mime.equalsIgnoreCase(MEDIA_MIMETYPE_VIDEO_AVC)) {
+ if (!isEncoder && strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AVC) == 0) {
if (param.eProfile == OMX_VIDEO_AVCProfileHigh) {
- builder->addProfileLevel(OMX_VIDEO_AVCProfileConstrainedHigh, param.eLevel);
+ caps->addProfileLevel(OMX_VIDEO_AVCProfileConstrainedHigh, param.eLevel);
} else if (param.eProfile == OMX_VIDEO_AVCProfileBaseline) {
- builder->addProfileLevel(OMX_VIDEO_AVCProfileConstrainedBaseline, param.eLevel);
+ caps->addProfileLevel(OMX_VIDEO_AVCProfileConstrainedBaseline, param.eLevel);
}
}
if (index == kMaxIndicesToCheck) {
ALOGW("[%s] stopping checking profiles after %u: %x/%x",
- name.c_str(), index,
+ name, index,
param.eProfile, param.eLevel);
}
}
@@ -8305,17 +8324,17 @@
if (IsFlexibleColorFormat(
omxNode, portFormat.eColorFormat, false /* usingNativeWindow */,
&flexibleEquivalent)) {
- builder->addColorFormat(flexibleEquivalent);
+ caps->addColorFormat(flexibleEquivalent);
}
- builder->addColorFormat(portFormat.eColorFormat);
+ caps->addColorFormat(portFormat.eColorFormat);
if (index == kMaxIndicesToCheck) {
ALOGW("[%s] stopping checking formats after %u: %s(%x)",
- name.c_str(), index,
+ name, index,
asString(portFormat.eColorFormat), portFormat.eColorFormat);
}
}
- } else if (mime.equalsIgnoreCase(MEDIA_MIMETYPE_AUDIO_AAC)) {
+ } else if (strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AAC) == 0) {
// More audio codecs if they have profiles.
OMX_AUDIO_PARAM_ANDROID_PROFILETYPE param;
InitOMXParams(¶m);
@@ -8329,11 +8348,11 @@
break;
}
// For audio, level is ignored.
- builder->addProfileLevel(param.eProfile, 0 /* level */);
+ caps->addProfileLevel(param.eProfile, 0 /* level */);
if (index == kMaxIndicesToCheck) {
ALOGW("[%s] stopping checking profiles after %u: %x",
- name.c_str(), index,
+ name, index,
param.eProfile);
}
}
@@ -8341,7 +8360,7 @@
// NOTE: Without Android extensions, OMX does not provide a way to query
// AAC profile support
if (param.nProfileIndex == 0) {
- ALOGW("component %s doesn't support profile query.", name.c_str());
+ ALOGW("component %s doesn't support profile query.", name);
}
}
@@ -8350,14 +8369,14 @@
if (omxNode->configureVideoTunnelMode(
kPortIndexOutput, OMX_TRUE, 0, &sidebandHandle) == OK) {
// tunneled playback includes adaptive playback
- builder->addFlags(MediaCodecInfo::Capabilities::kFlagSupportsAdaptivePlayback
+ caps->addFlags(MediaCodecInfo::Capabilities::kFlagSupportsAdaptivePlayback
| MediaCodecInfo::Capabilities::kFlagSupportsTunneledPlayback);
} else if (omxNode->setPortMode(
kPortIndexOutput, IOMX::kPortModeDynamicANWBuffer) == OK ||
omxNode->prepareForAdaptivePlayback(
kPortIndexOutput, OMX_TRUE,
1280 /* width */, 720 /* height */) == OK) {
- builder->addFlags(MediaCodecInfo::Capabilities::kFlagSupportsAdaptivePlayback);
+ caps->addFlags(MediaCodecInfo::Capabilities::kFlagSupportsAdaptivePlayback);
}
}
@@ -8369,11 +8388,10 @@
if (omxNode->getConfig(
(OMX_INDEXTYPE)OMX_IndexConfigAndroidIntraRefresh,
¶ms, sizeof(params)) == OK) {
- builder->addFlags(MediaCodecInfo::Capabilities::kFlagSupportsIntraRefresh);
+ caps->addFlags(MediaCodecInfo::Capabilities::kFlagSupportsIntraRefresh);
}
}
- *caps = builder;
omxNode->freeNode();
client.disconnect();
return OK;
diff --git a/media/libstagefright/AMRExtractor.cpp b/media/libstagefright/AMRExtractor.cpp
index 2892520..8b4d14d 100644
--- a/media/libstagefright/AMRExtractor.cpp
+++ b/media/libstagefright/AMRExtractor.cpp
@@ -186,7 +186,7 @@
return mInitCheck == OK ? 1 : 0;
}
-sp<IMediaSource> AMRExtractor::getTrack(size_t index) {
+sp<MediaSource> AMRExtractor::getTrack(size_t index) {
if (mInitCheck != OK || index != 0) {
return NULL;
}
diff --git a/media/libstagefright/AMRWriter.cpp b/media/libstagefright/AMRWriter.cpp
index 961b57f..f53d7f0 100644
--- a/media/libstagefright/AMRWriter.cpp
+++ b/media/libstagefright/AMRWriter.cpp
@@ -54,7 +54,7 @@
return mInitCheck;
}
-status_t AMRWriter::addSource(const sp<IMediaSource> &source) {
+status_t AMRWriter::addSource(const sp<MediaSource> &source) {
if (mInitCheck != OK) {
return mInitCheck;
}
diff --git a/media/libstagefright/Android.bp b/media/libstagefright/Android.bp
index 130992a..5481ae0 100644
--- a/media/libstagefright/Android.bp
+++ b/media/libstagefright/Android.bp
@@ -19,6 +19,7 @@
"BufferImpl.cpp",
"CodecBase.cpp",
"CallbackDataSource.cpp",
+ "CallbackMediaSource.cpp",
"CameraSource.cpp",
"CameraSourceTimeLapse.cpp",
"DataConverter.cpp",
@@ -51,7 +52,10 @@
"NuCachedSource2.cpp",
"NuMediaExtractor.cpp",
"OMXClient.cpp",
+ "OmxInfoBuilder.cpp",
"OggExtractor.cpp",
+ "RemoteMediaExtractor.cpp",
+ "RemoteMediaSource.cpp",
"SampleIterator.cpp",
"SampleTable.cpp",
"SimpleDecodingSource.cpp",
@@ -77,7 +81,6 @@
"libcutils",
"libdl",
"libdrmframework",
- "libexpat",
"libgui",
"liblog",
"libmedia",
@@ -89,19 +92,15 @@
"libui",
"libutils",
"libvorbisidec",
- "libmediadrm",
- "libnativewindow",
-
"libmedia_helper",
+ "libstagefright_omx_utils",
"libstagefright_flacdec",
"libstagefright_foundation",
- "libstagefright_xmlparser",
"libdl",
"libRScpp",
"libhidlbase",
"libhidlmemory",
"android.hidl.allocator@1.0",
- "android.hidl.memory@1.0",
"android.hidl.token@1.0-utils",
"android.hardware.cas@1.0",
"android.hardware.cas.native@1.0",
@@ -114,7 +113,6 @@
"libstagefright_aacenc",
"libstagefright_matroska",
"libstagefright_mediafilter",
- "libstagefright_omx_utils",
"libstagefright_webm",
"libstagefright_timedtext",
"libvpx",
@@ -124,9 +122,12 @@
"libFLAC",
],
- export_shared_lib_headers: ["libmedia"],
+ export_shared_lib_headers: [
+ "libmedia",
+ "android.hidl.allocator@1.0",
+ ],
+
export_include_dirs: [
- ".",
"include",
],
@@ -173,6 +174,5 @@
"tests",
"timedtext",
"webm",
- "wifi-display",
"xmlparser",
]
diff --git a/media/libstagefright/AudioPlayer.cpp b/media/libstagefright/AudioPlayer.cpp
index b3fb8d4..1d441eb 100644
--- a/media/libstagefright/AudioPlayer.cpp
+++ b/media/libstagefright/AudioPlayer.cpp
@@ -67,7 +67,7 @@
}
}
-void AudioPlayer::setSource(const sp<IMediaSource> &source) {
+void AudioPlayer::setSource(const sp<MediaSource> &source) {
CHECK(mSource == NULL);
mSource = source;
}
@@ -363,7 +363,7 @@
// When offloading, the OMX component is not used so this hack
// is not needed
if (!useOffload()) {
- wp<IMediaSource> tmp = mSource;
+ wp<MediaSource> tmp = mSource;
mSource.clear();
while (tmp.promote() != NULL) {
usleep(1000);
diff --git a/media/libstagefright/CallbackMediaSource.cpp b/media/libstagefright/CallbackMediaSource.cpp
new file mode 100644
index 0000000..cc7147e
--- /dev/null
+++ b/media/libstagefright/CallbackMediaSource.cpp
@@ -0,0 +1,50 @@
+/*
+ * 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.
+ */
+
+#include <media/stagefright/CallbackMediaSource.h>
+
+namespace android {
+
+CallbackMediaSource::CallbackMediaSource(const sp<IMediaSource> &source)
+ :mSource(source) {}
+
+CallbackMediaSource::~CallbackMediaSource() {}
+
+status_t CallbackMediaSource::start(MetaData *params) {
+ return mSource->start(params);
+}
+
+status_t CallbackMediaSource::stop() {
+ return mSource->stop();
+}
+
+sp<MetaData> CallbackMediaSource::getFormat() {
+ return mSource->getFormat();
+}
+
+status_t CallbackMediaSource::read(MediaBuffer **buffer, const ReadOptions *options) {
+ return mSource->read(buffer, reinterpret_cast<const MediaSource::ReadOptions*>(options));
+}
+
+status_t CallbackMediaSource::pause() {
+ return mSource->pause();
+}
+
+status_t CallbackMediaSource::setBuffers(const Vector<MediaBuffer *> &buffers) {
+ return mSource->setBuffers(buffers);
+}
+
+} // namespace android
diff --git a/media/libstagefright/FLACExtractor.cpp b/media/libstagefright/FLACExtractor.cpp
index 1b88e5d..99315d6 100644
--- a/media/libstagefright/FLACExtractor.cpp
+++ b/media/libstagefright/FLACExtractor.cpp
@@ -811,7 +811,7 @@
return mInitCheck == OK ? 1 : 0;
}
-sp<IMediaSource> FLACExtractor::getTrack(size_t index)
+sp<MediaSource> FLACExtractor::getTrack(size_t index)
{
if (mInitCheck != OK || index > 0) {
return NULL;
diff --git a/media/libstagefright/MP3Extractor.cpp b/media/libstagefright/MP3Extractor.cpp
index 22df522..2c696e5 100644
--- a/media/libstagefright/MP3Extractor.cpp
+++ b/media/libstagefright/MP3Extractor.cpp
@@ -407,7 +407,7 @@
return mInitCheck != OK ? 0 : 1;
}
-sp<IMediaSource> MP3Extractor::getTrack(size_t index) {
+sp<MediaSource> MP3Extractor::getTrack(size_t index) {
if (mInitCheck != OK || index != 0) {
return NULL;
}
diff --git a/media/libstagefright/MPEG2TSWriter.cpp b/media/libstagefright/MPEG2TSWriter.cpp
index 03ea959..7ed8fce 100644
--- a/media/libstagefright/MPEG2TSWriter.cpp
+++ b/media/libstagefright/MPEG2TSWriter.cpp
@@ -35,7 +35,7 @@
namespace android {
struct MPEG2TSWriter::SourceInfo : public AHandler {
- explicit SourceInfo(const sp<IMediaSource> &source);
+ explicit SourceInfo(const sp<MediaSource> &source);
void start(const sp<AMessage> ¬ify, const sp<MetaData> ¶ms);
void stop();
@@ -69,7 +69,7 @@
kWhatRead = 'read',
};
- sp<IMediaSource> mSource;
+ sp<MediaSource> mSource;
sp<ALooper> mLooper;
sp<AMessage> mNotify;
@@ -91,7 +91,7 @@
DISALLOW_EVIL_CONSTRUCTORS(SourceInfo);
};
-MPEG2TSWriter::SourceInfo::SourceInfo(const sp<IMediaSource> &source)
+MPEG2TSWriter::SourceInfo::SourceInfo(const sp<MediaSource> &source)
: mSource(source),
mLooper(new ALooper),
mEOSReceived(false),
@@ -499,7 +499,7 @@
}
}
-status_t MPEG2TSWriter::addSource(const sp<IMediaSource> &source) {
+status_t MPEG2TSWriter::addSource(const sp<MediaSource> &source) {
CHECK(!mStarted);
sp<MetaData> meta = source->getFormat();
diff --git a/media/libstagefright/MPEG4Extractor.cpp b/media/libstagefright/MPEG4Extractor.cpp
index 6f59fac..cef4945 100644
--- a/media/libstagefright/MPEG4Extractor.cpp
+++ b/media/libstagefright/MPEG4Extractor.cpp
@@ -354,6 +354,10 @@
}
MPEG4Extractor::~MPEG4Extractor() {
+ release();
+}
+
+void MPEG4Extractor::release() {
Track *track = mFirstTrack;
while (track) {
Track *next = track->next;
@@ -375,6 +379,12 @@
for (size_t i = 0; i < mPssh.size(); i++) {
delete [] mPssh[i].data;
}
+ mPssh.clear();
+
+ if (mDataSource != NULL) {
+ mDataSource->close();
+ mDataSource.clear();
+ }
}
uint32_t MPEG4Extractor::flags() const {
@@ -431,6 +441,66 @@
return NULL;
}
+ int64_t duration;
+ int32_t samplerate;
+ if (track->has_elst && mHeaderTimescale != 0 &&
+ track->meta->findInt64(kKeyDuration, &duration) &&
+ track->meta->findInt32(kKeySampleRate, &samplerate)) {
+
+ track->has_elst = false;
+
+ if (track->elst_segment_duration > INT64_MAX) {
+ goto editlistoverflow;
+ }
+ int64_t segment_duration = track->elst_segment_duration;
+ int64_t media_time = track->elst_media_time;
+ int64_t halfscale = mHeaderTimescale / 2;
+
+ int64_t delay;
+ // delay = ((media_time * samplerate) + halfscale) / mHeaderTimescale;
+ if (__builtin_mul_overflow(media_time, samplerate, &delay) ||
+ __builtin_add_overflow(delay, halfscale, &delay) ||
+ (delay /= mHeaderTimescale, false) ||
+ delay > INT32_MAX ||
+ delay < INT32_MIN) {
+ goto editlistoverflow;
+ }
+ track->meta->setInt32(kKeyEncoderDelay, delay);
+
+ int64_t scaled_duration;
+ // scaled_duration = ((duration * mHeaderTimescale) + 500000) / 1000000;
+ if (__builtin_mul_overflow(duration, mHeaderTimescale, &scaled_duration) ||
+ __builtin_add_overflow(scaled_duration, 500000, &scaled_duration)) {
+ goto editlistoverflow;
+ }
+ scaled_duration /= 1000000;
+
+ int64_t segment_end;
+ int64_t padding;
+ if (__builtin_add_overflow(segment_duration, media_time, &segment_end) ||
+ __builtin_sub_overflow(scaled_duration, segment_end, &padding)) {
+ goto editlistoverflow;
+ }
+
+ if (padding < 0) {
+ // track duration from media header (which is what kKeyDuration is) might
+ // be slightly shorter than the segment duration, which would make the
+ // padding negative. Clamp to zero.
+ padding = 0;
+ }
+
+ int64_t paddingsamples;
+ // paddingsamples = ((padding * samplerate) + halfscale) / mHeaderTimescale;
+ if (__builtin_mul_overflow(padding, samplerate, &paddingsamples) ||
+ __builtin_add_overflow(paddingsamples, halfscale, &paddingsamples) ||
+ (paddingsamples /= mHeaderTimescale, false) ||
+ paddingsamples > INT32_MAX) {
+ goto editlistoverflow;
+ }
+ track->meta->setInt32(kKeyEncoderPadding, paddingsamples);
+ }
+ editlistoverflow:
+
if ((flags & kIncludeExtensiveMetaData)
&& !track->includes_expensive_metadata) {
track->includes_expensive_metadata = true;
@@ -980,6 +1050,7 @@
track->skipTrack = false;
track->timescale = 0;
track->meta->setCString(kKeyMIMEType, "application/octet-stream");
+ track->has_elst = false;
}
off64_t stop_offset = *offset + chunk_size;
@@ -1046,6 +1117,10 @@
{
*offset += chunk_size;
+ if (!mLastTrack) {
+ return ERROR_MALFORMED;
+ }
+
// See 14496-12 8.6.6
uint8_t version;
if (mDataSource->readAt(data_offset, &version, 1) < 1) {
@@ -1060,8 +1135,6 @@
if (entry_count != 1) {
// we only support a single entry at the moment, for gapless playback
ALOGW("ignoring edit list with %d entries", entry_count);
- } else if (mHeaderTimescale == 0) {
- ALOGW("ignoring edit list because timescale is 0");
} else {
off64_t entriesoffset = data_offset + 8;
uint64_t segment_duration;
@@ -1085,31 +1158,12 @@
return ERROR_IO;
}
- uint64_t halfscale = mHeaderTimescale / 2;
- segment_duration = (segment_duration * 1000000 + halfscale)/ mHeaderTimescale;
- media_time = (media_time * 1000000 + halfscale) / mHeaderTimescale;
-
- int64_t duration;
- int32_t samplerate;
- if (!mLastTrack) {
- return ERROR_MALFORMED;
- }
- if (mLastTrack->meta->findInt64(kKeyDuration, &duration) &&
- mLastTrack->meta->findInt32(kKeySampleRate, &samplerate)) {
-
- int64_t delay = (media_time * samplerate + 500000) / 1000000;
- mLastTrack->meta->setInt32(kKeyEncoderDelay, delay);
-
- int64_t paddingus = duration - (int64_t)(segment_duration + media_time);
- if (paddingus < 0) {
- // track duration from media header (which is what kKeyDuration is) might
- // be slightly shorter than the segment duration, which would make the
- // padding negative. Clamp to zero.
- paddingus = 0;
- }
- int64_t paddingsamples = (paddingus * samplerate + 500000) / 1000000;
- mLastTrack->meta->setInt32(kKeyEncoderPadding, paddingsamples);
- }
+ // save these for later, because the elst atom might precede
+ // the atoms that actually gives us the duration and sample rate
+ // needed to calculate the padding and delay values
+ mLastTrack->has_elst = true;
+ mLastTrack->elst_media_time = media_time;
+ mLastTrack->elst_segment_duration = segment_duration;
}
break;
}
@@ -3290,7 +3344,7 @@
}
}
-sp<IMediaSource> MPEG4Extractor::getTrack(size_t index) {
+sp<MediaSource> MPEG4Extractor::getTrack(size_t index) {
status_t err;
if ((err = readMetaData()) != OK) {
return NULL;
@@ -3489,6 +3543,18 @@
return ERROR_UNSUPPORTED;
}
+ if (mLastTrack != NULL) {
+ uint32_t maxBitrate = 0;
+ uint32_t avgBitrate = 0;
+ esds.getBitRate(&maxBitrate, &avgBitrate);
+ if (maxBitrate > 0 && maxBitrate < INT32_MAX) {
+ mLastTrack->meta->setInt32(kKeyMaxBitRate, (int32_t)maxBitrate);
+ }
+ if (avgBitrate > 0 && avgBitrate < INT32_MAX) {
+ mLastTrack->meta->setInt32(kKeyBitRate, (int32_t)avgBitrate);
+ }
+ }
+
const uint8_t *csd;
size_t csd_size;
if (esds.getCodecSpecificInfo(
diff --git a/media/libstagefright/MPEG4Writer.cpp b/media/libstagefright/MPEG4Writer.cpp
index 7786c4d..8c121b3 100644
--- a/media/libstagefright/MPEG4Writer.cpp
+++ b/media/libstagefright/MPEG4Writer.cpp
@@ -100,7 +100,7 @@
class MPEG4Writer::Track {
public:
- Track(MPEG4Writer *owner, const sp<IMediaSource> &source, size_t trackId);
+ Track(MPEG4Writer *owner, const sp<MediaSource> &source, size_t trackId);
~Track();
@@ -271,7 +271,7 @@
MPEG4Writer *mOwner;
sp<MetaData> mMeta;
- sp<IMediaSource> mSource;
+ sp<MediaSource> mSource;
volatile bool mDone;
volatile bool mPaused;
volatile bool mResumed;
@@ -572,7 +572,7 @@
return NULL;
}
-status_t MPEG4Writer::addSource(const sp<IMediaSource> &source) {
+status_t MPEG4Writer::addSource(const sp<MediaSource> &source) {
Mutex::Autolock l(mLock);
if (mStarted) {
ALOGE("Attempt to add source AFTER recording is started");
@@ -1626,7 +1626,7 @@
////////////////////////////////////////////////////////////////////////////////
MPEG4Writer::Track::Track(
- MPEG4Writer *owner, const sp<IMediaSource> &source, size_t trackId)
+ MPEG4Writer *owner, const sp<MediaSource> &source, size_t trackId)
: mOwner(owner),
mMeta(source->getFormat()),
mSource(source),
diff --git a/media/libstagefright/MediaClock.cpp b/media/libstagefright/MediaClock.cpp
index 3aa0061..15843a2 100644
--- a/media/libstagefright/MediaClock.cpp
+++ b/media/libstagefright/MediaClock.cpp
@@ -17,11 +17,12 @@
//#define LOG_NDEBUG 0
#define LOG_TAG "MediaClock"
#include <utils/Log.h>
+#include <map>
#include <media/stagefright/MediaClock.h>
#include <media/stagefright/foundation/ADebug.h>
-#include <media/stagefright/foundation/ALooper.h>
+#include <media/stagefright/foundation/AMessage.h>
namespace android {
@@ -29,15 +30,52 @@
// If larger than this threshold, it's treated as discontinuity.
static const int64_t kAnchorFluctuationAllowedUs = 10000ll;
+MediaClock::Timer::Timer(const sp<AMessage> ¬ify, int64_t mediaTimeUs, int64_t adjustRealUs)
+ : mNotify(notify),
+ mMediaTimeUs(mediaTimeUs),
+ mAdjustRealUs(adjustRealUs) {
+}
+
MediaClock::MediaClock()
: mAnchorTimeMediaUs(-1),
mAnchorTimeRealUs(-1),
mMaxTimeMediaUs(INT64_MAX),
mStartingTimeMediaUs(-1),
- mPlaybackRate(1.0) {
+ mPlaybackRate(1.0),
+ mGeneration(0) {
+ mLooper = new ALooper;
+ mLooper->setName("MediaClock");
+ mLooper->start(false /* runOnCallingThread */,
+ false /* canCallJava */,
+ ANDROID_PRIORITY_AUDIO);
+}
+
+void MediaClock::init() {
+ mLooper->registerHandler(this);
}
MediaClock::~MediaClock() {
+ reset();
+ if (mLooper != NULL) {
+ mLooper->unregisterHandler(id());
+ mLooper->stop();
+ }
+}
+
+void MediaClock::reset() {
+ Mutex::Autolock autoLock(mLock);
+ auto it = mTimers.begin();
+ while (it != mTimers.end()) {
+ it->mNotify->setInt32("reason", TIMER_REASON_RESET);
+ it->mNotify->post();
+ it = mTimers.erase(it);
+ }
+ mAnchorTimeMediaUs = -1;
+ mAnchorTimeRealUs = -1;
+ mMaxTimeMediaUs = INT64_MAX;
+ mStartingTimeMediaUs = -1;
+ mPlaybackRate = 1.0;
+ ++mGeneration;
}
void MediaClock::setStartingTimeMedia(int64_t startingTimeMediaUs) {
@@ -82,6 +120,9 @@
}
mAnchorTimeRealUs = nowUs;
mAnchorTimeMediaUs = nowMediaUs;
+
+ ++mGeneration;
+ processTimers_l();
}
void MediaClock::updateMaxTimeMedia(int64_t maxTimeMediaUs) {
@@ -105,6 +146,11 @@
}
mAnchorTimeRealUs = nowUs;
mPlaybackRate = rate;
+
+ if (rate > 0.0) {
+ ++mGeneration;
+ processTimers_l();
+ }
}
float MediaClock::getPlaybackRate() const {
@@ -165,4 +211,106 @@
return OK;
}
+void MediaClock::addTimer(const sp<AMessage> ¬ify, int64_t mediaTimeUs,
+ int64_t adjustRealUs) {
+ Mutex::Autolock autoLock(mLock);
+
+ bool updateTimer = (mPlaybackRate != 0.0);
+ if (updateTimer) {
+ auto it = mTimers.begin();
+ while (it != mTimers.end()) {
+ if (((it->mAdjustRealUs - (double)adjustRealUs) * (double)mPlaybackRate
+ + (it->mMediaTimeUs - mediaTimeUs)) <= 0) {
+ updateTimer = false;
+ break;
+ }
+ ++it;
+ }
+ }
+
+ mTimers.emplace_back(notify, mediaTimeUs, adjustRealUs);
+
+ if (updateTimer) {
+ ++mGeneration;
+ processTimers_l();
+ }
+}
+
+void MediaClock::onMessageReceived(const sp<AMessage> &msg) {
+ switch (msg->what()) {
+ case kWhatTimeIsUp:
+ {
+ int32_t generation;
+ CHECK(msg->findInt32("generation", &generation));
+
+ Mutex::Autolock autoLock(mLock);
+ if (generation != mGeneration) {
+ break;
+ }
+ processTimers_l();
+ break;
+ }
+
+ default:
+ TRESPASS();
+ break;
+ }
+}
+
+void MediaClock::processTimers_l() {
+ int64_t nowMediaTimeUs;
+ status_t status = getMediaTime_l(
+ ALooper::GetNowUs(), &nowMediaTimeUs, false /* allowPastMaxTime */);
+
+ if (status != OK) {
+ return;
+ }
+
+ int64_t nextLapseRealUs = INT64_MAX;
+ std::multimap<int64_t, Timer> notifyList;
+ auto it = mTimers.begin();
+ while (it != mTimers.end()) {
+ double diff = it->mAdjustRealUs * (double)mPlaybackRate
+ + it->mMediaTimeUs - nowMediaTimeUs;
+ int64_t diffMediaUs;
+ if (diff > (double)INT64_MAX) {
+ diffMediaUs = INT64_MAX;
+ } else if (diff < (double)INT64_MIN) {
+ diffMediaUs = INT64_MIN;
+ } else {
+ diffMediaUs = diff;
+ }
+
+ if (diffMediaUs <= 0) {
+ notifyList.emplace(diffMediaUs, *it);
+ it = mTimers.erase(it);
+ } else {
+ if (mPlaybackRate != 0.0
+ && (double)diffMediaUs < INT64_MAX * (double)mPlaybackRate) {
+ int64_t targetRealUs = diffMediaUs / (double)mPlaybackRate;
+ if (targetRealUs < nextLapseRealUs) {
+ nextLapseRealUs = targetRealUs;
+ }
+ }
+ ++it;
+ }
+ }
+
+ auto itNotify = notifyList.begin();
+ while (itNotify != notifyList.end()) {
+ itNotify->second.mNotify->setInt32("reason", TIMER_REASON_REACHED);
+ itNotify->second.mNotify->post();
+ itNotify = notifyList.erase(itNotify);
+ }
+
+ if (mTimers.empty() || mPlaybackRate == 0.0 || mAnchorTimeMediaUs < 0
+ || nextLapseRealUs == INT64_MAX) {
+ return;
+ }
+
+ sp<AMessage> msg = new AMessage(kWhatTimeIsUp, this);
+ msg->setInt32("generation", mGeneration);
+ msg->post(nextLapseRealUs);
+}
+
} // namespace android
diff --git a/media/libstagefright/MediaCodec.cpp b/media/libstagefright/MediaCodec.cpp
index 98d101a..759e42d 100644
--- a/media/libstagefright/MediaCodec.cpp
+++ b/media/libstagefright/MediaCodec.cpp
@@ -73,6 +73,13 @@
static const char *kCodecCrypto = "android.media.mediacodec.crypto"; /* 0,1 */
static const char *kCodecEncoder = "android.media.mediacodec.encoder"; /* 0,1 */
+static const char *kCodecBytesIn = "android.media.mediacodec.bytesin"; /* 0..n */
+static const char *kCodecProfile = "android.media.mediacodec.profile"; /* 0..n */
+static const char *kCodecLevel = "android.media.mediacodec.level"; /* 0..n */
+static const char *kCodecMaxWidth = "android.media.mediacodec.maxwidth"; /* 0..n */
+static const char *kCodecMaxHeight = "android.media.mediacodec.maxheight"; /* 0..n */
+static const char *kCodecError = "android.media.mediacodec.errcode";
+static const char *kCodecErrorState = "android.media.mediacodec.errstate";
static int64_t getId(const sp<IResourceManagerClient> &client) {
@@ -429,22 +436,6 @@
}
// static
-status_t MediaCodec::QueryCapabilities(
- const AString &name, const AString &mime, bool isEncoder,
- sp<MediaCodecInfo::Capabilities> *caps /* nonnull */) {
- // TRICKY: this method is used by MediaCodecList/Info during its
- // initialization. As such, we cannot create a MediaCodec instance
- // because that requires an initialized MediaCodecList.
-
- sp<CodecBase> codec = GetCodecBase(name);
- if (codec == NULL) {
- return NAME_NOT_FOUND;
- }
-
- return codec->queryCapabilities(name, mime, isEncoder, caps);
-}
-
-// static
sp<PersistentSurface> MediaCodec::CreatePersistentInputSurface() {
OMXClient client;
if (client.connect() != OK) {
@@ -476,6 +467,7 @@
mFlags(0),
mStickyError(OK),
mSoftRenderer(NULL),
+ mAnalyticsItem(NULL),
mResourceManagerClient(new ResourceManagerClient(this)),
mResourceManagerService(new ResourceManagerServiceProxy(pid)),
mBatteryStatNotified(false),
@@ -494,6 +486,18 @@
} else {
mUid = uid;
}
+ initAnalyticsItem();
+}
+
+MediaCodec::~MediaCodec() {
+ CHECK_EQ(mState, UNINITIALIZED);
+ mResourceManagerService->removeResource(getId(mResourceManagerClient));
+
+ flushAnalyticsItem();
+}
+
+void MediaCodec::initAnalyticsItem() {
+ CHECK(mAnalyticsItem == NULL);
// set up our new record, get a sessionID, put it into the in-progress list
mAnalyticsItem = new MediaAnalyticsItem(kCodecKeyName);
if (mAnalyticsItem != NULL) {
@@ -503,11 +507,9 @@
}
}
-MediaCodec::~MediaCodec() {
- CHECK_EQ(mState, UNINITIALIZED);
- mResourceManagerService->removeResource(getId(mResourceManagerClient));
-
- if (mAnalyticsItem != NULL ) {
+void MediaCodec::flushAnalyticsItem() {
+ if (mAnalyticsItem != NULL) {
+ // don't log empty records
if (mAnalyticsItem->count() > 0) {
mAnalyticsItem->setFinalized(true);
mAnalyticsItem->selfrecord();
@@ -700,10 +702,21 @@
uint32_t flags) {
sp<AMessage> msg = new AMessage(kWhatConfigure, this);
+ if (mAnalyticsItem != NULL) {
+ int32_t profile = 0;
+ if (format->findInt32("profile", &profile)) {
+ mAnalyticsItem->setInt32(kCodecProfile, profile);
+ }
+ int32_t level = 0;
+ if (format->findInt32("level", &level)) {
+ mAnalyticsItem->setInt32(kCodecLevel, level);
+ }
+ }
+
if (mIsVideo) {
format->findInt32("width", &mVideoWidth);
format->findInt32("height", &mVideoHeight);
- if (!format->findInt32(kCodecRotation, &mRotationDegrees)) {
+ if (!format->findInt32("rotation-degrees", &mRotationDegrees)) {
mRotationDegrees = 0;
}
@@ -711,6 +724,14 @@
mAnalyticsItem->setInt32(kCodecWidth, mVideoWidth);
mAnalyticsItem->setInt32(kCodecHeight, mVideoHeight);
mAnalyticsItem->setInt32(kCodecRotation, mRotationDegrees);
+ int32_t maxWidth = 0;
+ if (format->findInt32("max-width", &maxWidth)) {
+ mAnalyticsItem->setInt32(kCodecMaxWidth, maxWidth);
+ }
+ int32_t maxHeight = 0;
+ if (format->findInt32("max-height", &maxHeight)) {
+ mAnalyticsItem->setInt32(kCodecMaxHeight, maxHeight);
+ }
}
// Prevent possible integer overflow in downstream code.
@@ -1417,6 +1438,12 @@
case CONFIGURING:
{
+ if (actionCode == ACTION_CODE_FATAL) {
+ mAnalyticsItem->setInt32(kCodecError, err);
+ mAnalyticsItem->setInt32(kCodecErrorState, mState);
+ flushAnalyticsItem();
+ initAnalyticsItem();
+ }
setState(actionCode == ACTION_CODE_FATAL ?
UNINITIALIZED : INITIALIZED);
break;
@@ -1424,6 +1451,12 @@
case STARTING:
{
+ if (actionCode == ACTION_CODE_FATAL) {
+ mAnalyticsItem->setInt32(kCodecError, err);
+ mAnalyticsItem->setInt32(kCodecErrorState, mState);
+ flushAnalyticsItem();
+ initAnalyticsItem();
+ }
setState(actionCode == ACTION_CODE_FATAL ?
UNINITIALIZED : CONFIGURED);
break;
@@ -1460,6 +1493,11 @@
case FLUSHING:
{
if (actionCode == ACTION_CODE_FATAL) {
+ mAnalyticsItem->setInt32(kCodecError, err);
+ mAnalyticsItem->setInt32(kCodecErrorState, mState);
+ flushAnalyticsItem();
+ initAnalyticsItem();
+
setState(UNINITIALIZED);
} else {
setState(
@@ -1488,6 +1526,10 @@
setState(INITIALIZED);
break;
default:
+ mAnalyticsItem->setInt32(kCodecError, err);
+ mAnalyticsItem->setInt32(kCodecErrorState, mState);
+ flushAnalyticsItem();
+ initAnalyticsItem();
setState(UNINITIALIZED);
break;
}
@@ -1592,6 +1634,19 @@
}
setState(CONFIGURED);
(new AMessage)->postReply(mReplyID);
+
+ // augment our media metrics info, now that we know more things
+ if (mAnalyticsItem != NULL) {
+ sp<AMessage> format;
+ if (mConfigureMsg != NULL &&
+ mConfigureMsg->findMessage("format", &format)) {
+ // format includes: mime
+ AString mime;
+ if (format->findString("mime", &mime)) {
+ mAnalyticsItem->setCString(kCodecMime, mime.c_str());
+ }
+ }
+ }
break;
}
@@ -2817,6 +2872,9 @@
Mutex::Autolock al(mBufferLock);
info->mOwnedByClient = false;
info->mData.clear();
+ if (mAnalyticsItem != NULL) {
+ mAnalyticsItem->addInt64(kCodecBytesIn, size);
+ }
}
return err;
diff --git a/media/libstagefright/MediaCodecList.cpp b/media/libstagefright/MediaCodecList.cpp
index 1dcba29..32a9f0a 100644
--- a/media/libstagefright/MediaCodecList.cpp
+++ b/media/libstagefright/MediaCodecList.cpp
@@ -24,38 +24,37 @@
#include <media/IMediaCodecList.h>
#include <media/IMediaPlayerService.h>
-#include <media/IResourceManagerService.h>
+#include <media/IMediaCodecService.h>
#include <media/MediaCodecInfo.h>
-#include <media/MediaResourcePolicy.h>
+#include <media/MediaDefs.h>
#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/foundation/AMessage.h>
-#include <media/stagefright/ACodec.h>
-#include <media/stagefright/MediaCodec.h>
#include <media/stagefright/MediaCodecList.h>
#include <media/stagefright/MediaErrors.h>
-#include <media/stagefright/OMXClient.h>
+#include <media/stagefright/OmxInfoBuilder.h>
+#include <media/stagefright/omx/OMXUtils.h>
+#include <xmlparser/include/media/stagefright/xmlparser/MediaCodecsXmlParser.h>
#include <sys/stat.h>
#include <utils/threads.h>
#include <cutils/properties.h>
-#include <expat.h>
+
+#include <algorithm>
namespace android {
-static Mutex sInitMutex;
+namespace {
-static bool parseBoolean(const char *s) {
- if (!strcasecmp(s, "true") || !strcasecmp(s, "yes") || !strcasecmp(s, "y")) {
- return true;
- }
- char *end;
- unsigned long res = strtoul(s, &end, 10);
- return *s != '\0' && *end == '\0' && res > 0;
-}
+Mutex sInitMutex;
-static bool isProfilingNeeded() {
+Mutex sRemoteInitMutex;
+
+constexpr const char* kProfilingResults =
+ MediaCodecsXmlParser::defaultProfilingResultsXmlPath;
+
+bool isProfilingNeeded() {
int8_t value = property_get_bool("debug.stagefright.profilecodec", 0);
if (value == 0) {
return false;
@@ -78,6 +77,10 @@
return profilingNeeded;
}
+OmxInfoBuilder sOmxInfoBuilder;
+
+} // unnamed namespace
+
// static
sp<IMediaCodecList> MediaCodecList::sCodecList;
@@ -86,42 +89,42 @@
ALOGV("Enter profilerThreadWrapper.");
remove(kProfilingResults); // remove previous result so that it won't be loaded to
// the new MediaCodecList
- MediaCodecList *codecList = new MediaCodecList();
+ sp<MediaCodecList> codecList(new MediaCodecList(&sOmxInfoBuilder));
if (codecList->initCheck() != OK) {
ALOGW("Failed to create a new MediaCodecList, skipping codec profiling.");
- delete codecList;
- return NULL;
+ return nullptr;
}
- Vector<sp<MediaCodecInfo>> infos;
- for (size_t i = 0; i < codecList->countCodecs(); ++i) {
- infos.push_back(codecList->getCodecInfo(i));
- }
+ const auto& infos = codecList->mCodecInfos;
ALOGV("Codec profiling started.");
- profileCodecs(infos);
+ profileCodecs(infos, kProfilingResults);
ALOGV("Codec profiling completed.");
- codecList->parseTopLevelXMLFile(kProfilingResults, true /* ignore_errors */);
+ codecList = new MediaCodecList(&sOmxInfoBuilder);
+ if (codecList->initCheck() != OK) {
+ ALOGW("Failed to parse profiling results.");
+ return nullptr;
+ }
{
Mutex::Autolock autoLock(sInitMutex);
sCodecList = codecList;
}
- return NULL;
+ return nullptr;
}
// static
sp<IMediaCodecList> MediaCodecList::getLocalInstance() {
Mutex::Autolock autoLock(sInitMutex);
- if (sCodecList == NULL) {
- MediaCodecList *codecList = new MediaCodecList;
+ if (sCodecList == nullptr) {
+ MediaCodecList *codecList = new MediaCodecList(&sOmxInfoBuilder);
if (codecList->initCheck() == OK) {
sCodecList = codecList;
if (isProfilingNeeded()) {
ALOGV("Codec profiling needed, will be run in separated thread.");
pthread_t profiler;
- if (pthread_create(&profiler, NULL, profilerThreadWrapper, NULL) != 0) {
+ if (pthread_create(&profiler, nullptr, profilerThreadWrapper, nullptr) != 0) {
ALOGW("Failed to create thread for codec profiling.");
}
}
@@ -134,8 +137,6 @@
return sCodecList;
}
-static Mutex sRemoteInitMutex;
-
sp<IMediaCodecList> MediaCodecList::sRemoteList;
sp<MediaCodecList::BinderDeathObserver> MediaCodecList::sBinderDeathObserver;
@@ -149,19 +150,19 @@
// static
sp<IMediaCodecList> MediaCodecList::getInstance() {
Mutex::Autolock _l(sRemoteInitMutex);
- if (sRemoteList == NULL) {
+ if (sRemoteList == nullptr) {
sp<IBinder> binder =
defaultServiceManager()->getService(String16("media.player"));
sp<IMediaPlayerService> service =
interface_cast<IMediaPlayerService>(binder);
- if (service.get() != NULL) {
+ if (service.get() != nullptr) {
sRemoteList = service->getCodecList();
- if (sRemoteList != NULL) {
+ if (sRemoteList != nullptr) {
sBinderDeathObserver = new BinderDeathObserver();
binder->linkToDeath(sBinderDeathObserver.get());
}
}
- if (sRemoteList == NULL) {
+ if (sRemoteList == nullptr) {
// if failed to get remote list, create local list
sRemoteList = getLocalInstance();
}
@@ -169,175 +170,11 @@
return sRemoteList;
}
-// Treblized media codec list will be located in /odm/etc or /vendor/etc.
-static const char *kConfigLocationList[] =
- {"/odm/etc", "/vendor/etc", "/etc"};
-static const int kConfigLocationListSize =
- (sizeof(kConfigLocationList) / sizeof(kConfigLocationList[0]));
-
-#define MEDIA_CODECS_CONFIG_FILE_PATH_MAX_LENGTH 128
-
-static bool findMediaCodecListFileFullPath(const char *file_name, char *out_path) {
- for (int i = 0; i < kConfigLocationListSize; i++) {
- snprintf(out_path,
- MEDIA_CODECS_CONFIG_FILE_PATH_MAX_LENGTH,
- "%s/%s",
- kConfigLocationList[i],
- file_name);
- struct stat file_stat;
- if (stat(out_path, &file_stat) == 0 && S_ISREG(file_stat.st_mode)) {
- return true;
- }
- }
- return false;
-}
-
-MediaCodecList::MediaCodecList()
- : mInitCheck(NO_INIT),
- mUpdate(false),
- mGlobalSettings(new AMessage()) {
- char config_file_path[MEDIA_CODECS_CONFIG_FILE_PATH_MAX_LENGTH];
- if (findMediaCodecListFileFullPath("media_codecs.xml", config_file_path)) {
- parseTopLevelXMLFile(config_file_path);
- }
- if (findMediaCodecListFileFullPath("media_codecs_performance.xml",
- config_file_path)) {
- parseTopLevelXMLFile(config_file_path, true/* ignore_errors */);
- }
- parseTopLevelXMLFile(kProfilingResults, true/* ignore_errors */);
-}
-
-void MediaCodecList::parseTopLevelXMLFile(const char *codecs_xml, bool ignore_errors) {
- // get href_base
- const char *href_base_end = strrchr(codecs_xml, '/');
- if (href_base_end != NULL) {
- mHrefBase = AString(codecs_xml, href_base_end - codecs_xml + 1);
- }
-
- mInitCheck = OK; // keeping this here for safety
- mCurrentSection = SECTION_TOPLEVEL;
- mDepth = 0;
-
- OMXClient client;
- mInitCheck = client.connect();
- if (mInitCheck != OK) {
- return; // this may fail if IMediaPlayerService is not available.
- }
- parseXMLFile(codecs_xml);
-
- if (mInitCheck != OK) {
- if (ignore_errors) {
- mInitCheck = OK;
- return;
- }
- mCodecInfos.clear();
- return;
- }
-
- Vector<MediaResourcePolicy> policies;
- AString value;
- if (mGlobalSettings->findString(kPolicySupportsMultipleSecureCodecs, &value)) {
- policies.push_back(
- MediaResourcePolicy(
- String8(kPolicySupportsMultipleSecureCodecs),
- String8(value.c_str())));
- }
- if (mGlobalSettings->findString(kPolicySupportsSecureWithNonSecureCodec, &value)) {
- policies.push_back(
- MediaResourcePolicy(
- String8(kPolicySupportsSecureWithNonSecureCodec),
- String8(value.c_str())));
- }
- if (policies.size() > 0) {
- sp<IServiceManager> sm = defaultServiceManager();
- sp<IBinder> binder = sm->getService(String16("media.resource_manager"));
- sp<IResourceManagerService> service = interface_cast<IResourceManagerService>(binder);
- if (service == NULL) {
- ALOGE("MediaCodecList: failed to get ResourceManagerService");
- } else {
- service->config(policies);
- }
- }
-
- for (size_t i = mCodecInfos.size(); i > 0;) {
- i--;
- const MediaCodecInfo &info = *mCodecInfos.itemAt(i).get();
- if (info.mCaps.size() == 0) {
- // No types supported by this component???
- ALOGW("Component %s does not support any type of media?",
- info.mName.c_str());
-
- mCodecInfos.removeAt(i);
-#if LOG_NDEBUG == 0
- } else {
- for (size_t type_ix = 0; type_ix < info.mCaps.size(); ++type_ix) {
- AString mime = info.mCaps.keyAt(type_ix);
- const sp<MediaCodecInfo::Capabilities> &caps = info.mCaps.valueAt(type_ix);
-
- ALOGV("%s codec info for %s: %s", info.mName.c_str(), mime.c_str(),
- caps->getDetails()->debugString().c_str());
- ALOGV(" flags=%d", caps->getFlags());
- {
- Vector<uint32_t> colorFormats;
- caps->getSupportedColorFormats(&colorFormats);
- AString nice;
- for (size_t ix = 0; ix < colorFormats.size(); ix++) {
- if (ix > 0) {
- nice.append(", ");
- }
- nice.append(colorFormats.itemAt(ix));
- }
- ALOGV(" colors=[%s]", nice.c_str());
- }
- {
- Vector<MediaCodecInfo::ProfileLevel> profileLevels;
- caps->getSupportedProfileLevels(&profileLevels);
- AString nice;
- for (size_t ix = 0; ix < profileLevels.size(); ix++) {
- if (ix > 0) {
- nice.append(", ");
- }
- const MediaCodecInfo::ProfileLevel &pl =
- profileLevels.itemAt(ix);
- nice.append(pl.mProfile);
- nice.append("/");
- nice.append(pl.mLevel);
- }
- ALOGV(" levels=[%s]", nice.c_str());
- }
- {
- AString quirks;
- for (size_t ix = 0; ix < info.mQuirks.size(); ix++) {
- if (ix > 0) {
- quirks.append(", ");
- }
- quirks.append(info.mQuirks[ix]);
- }
- ALOGV(" quirks=[%s]", quirks.c_str());
- }
- }
-#endif
- }
- }
-
-#if 0
- for (size_t i = 0; i < mCodecInfos.size(); ++i) {
- const CodecInfo &info = mCodecInfos.itemAt(i);
-
- AString line = info.mName;
- line.append(" supports ");
- for (size_t j = 0; j < mTypes.size(); ++j) {
- uint32_t value = mTypes.valueAt(j);
-
- if (info.mTypes & (1ul << value)) {
- line.append(mTypes.keyAt(j));
- line.append(" ");
- }
- }
-
- ALOGI("%s", line.c_str());
- }
-#endif
+MediaCodecList::MediaCodecList(MediaCodecListBuilderBase* builder) {
+ mGlobalSettings = new AMessage();
+ mCodecInfos.clear();
+ MediaCodecListWriter writer(this);
+ mInitCheck = builder->buildMediaCodecList(&writer);
}
MediaCodecList::~MediaCodecList() {
@@ -347,531 +184,21 @@
return mInitCheck;
}
-void MediaCodecList::parseXMLFile(const char *path) {
- FILE *file = fopen(path, "r");
-
- if (file == NULL) {
- ALOGW("unable to open media codecs configuration xml file: %s", path);
- mInitCheck = NAME_NOT_FOUND;
- return;
- }
-
- XML_Parser parser = ::XML_ParserCreate(NULL);
- CHECK(parser != NULL);
-
- ::XML_SetUserData(parser, this);
- ::XML_SetElementHandler(
- parser, StartElementHandlerWrapper, EndElementHandlerWrapper);
-
- const int BUFF_SIZE = 512;
- while (mInitCheck == OK) {
- void *buff = ::XML_GetBuffer(parser, BUFF_SIZE);
- if (buff == NULL) {
- ALOGE("failed in call to XML_GetBuffer()");
- mInitCheck = UNKNOWN_ERROR;
- break;
- }
-
- int bytes_read = ::fread(buff, 1, BUFF_SIZE, file);
- if (bytes_read < 0) {
- ALOGE("failed in call to read");
- mInitCheck = ERROR_IO;
- break;
- }
-
- XML_Status status = ::XML_ParseBuffer(parser, bytes_read, bytes_read == 0);
- if (status != XML_STATUS_OK) {
- ALOGE("malformed (%s)", ::XML_ErrorString(::XML_GetErrorCode(parser)));
- mInitCheck = ERROR_MALFORMED;
- break;
- }
-
- if (bytes_read == 0) {
- break;
- }
- }
-
- ::XML_ParserFree(parser);
-
- fclose(file);
- file = NULL;
+MediaCodecListWriter::MediaCodecListWriter(MediaCodecList* list) :
+ mList(list) {
}
-// static
-void MediaCodecList::StartElementHandlerWrapper(
- void *me, const char *name, const char **attrs) {
- static_cast<MediaCodecList *>(me)->startElementHandler(name, attrs);
+void MediaCodecListWriter::addGlobalSetting(
+ const char* key, const char* value) {
+ mList->mGlobalSettings->setString(key, value);
}
-// static
-void MediaCodecList::EndElementHandlerWrapper(void *me, const char *name) {
- static_cast<MediaCodecList *>(me)->endElementHandler(name);
-}
-
-status_t MediaCodecList::includeXMLFile(const char **attrs) {
- const char *href = NULL;
- size_t i = 0;
- while (attrs[i] != NULL) {
- if (!strcmp(attrs[i], "href")) {
- if (attrs[i + 1] == NULL) {
- return -EINVAL;
- }
- href = attrs[i + 1];
- ++i;
- } else {
- return -EINVAL;
- }
- ++i;
- }
-
- // For security reasons and for simplicity, file names can only contain
- // [a-zA-Z0-9_.] and must start with media_codecs_ and end with .xml
- for (i = 0; href[i] != '\0'; i++) {
- if (href[i] == '.' || href[i] == '_' ||
- (href[i] >= '0' && href[i] <= '9') ||
- (href[i] >= 'A' && href[i] <= 'Z') ||
- (href[i] >= 'a' && href[i] <= 'z')) {
- continue;
- }
- ALOGE("invalid include file name: %s", href);
- return -EINVAL;
- }
-
- AString filename = href;
- if (!filename.startsWith("media_codecs_") ||
- !filename.endsWith(".xml")) {
- ALOGE("invalid include file name: %s", href);
- return -EINVAL;
- }
- filename.insert(mHrefBase, 0);
-
- parseXMLFile(filename.c_str());
- return mInitCheck;
-}
-
-void MediaCodecList::startElementHandler(
- const char *name, const char **attrs) {
- if (mInitCheck != OK) {
- return;
- }
-
- bool inType = true;
-
- if (!strcmp(name, "Include")) {
- mInitCheck = includeXMLFile(attrs);
- if (mInitCheck == OK) {
- mPastSections.push(mCurrentSection);
- mCurrentSection = SECTION_INCLUDE;
- }
- ++mDepth;
- return;
- }
-
- switch (mCurrentSection) {
- case SECTION_TOPLEVEL:
- {
- if (!strcmp(name, "Decoders")) {
- mCurrentSection = SECTION_DECODERS;
- } else if (!strcmp(name, "Encoders")) {
- mCurrentSection = SECTION_ENCODERS;
- } else if (!strcmp(name, "Settings")) {
- mCurrentSection = SECTION_SETTINGS;
- }
- break;
- }
-
- case SECTION_SETTINGS:
- {
- if (!strcmp(name, "Setting")) {
- mInitCheck = addSettingFromAttributes(attrs);
- }
- break;
- }
-
- case SECTION_DECODERS:
- {
- if (!strcmp(name, "MediaCodec")) {
- mInitCheck =
- addMediaCodecFromAttributes(false /* encoder */, attrs);
-
- mCurrentSection = SECTION_DECODER;
- }
- break;
- }
-
- case SECTION_ENCODERS:
- {
- if (!strcmp(name, "MediaCodec")) {
- mInitCheck =
- addMediaCodecFromAttributes(true /* encoder */, attrs);
-
- mCurrentSection = SECTION_ENCODER;
- }
- break;
- }
-
- case SECTION_DECODER:
- case SECTION_ENCODER:
- {
- if (!strcmp(name, "Quirk")) {
- mInitCheck = addQuirk(attrs);
- } else if (!strcmp(name, "Type")) {
- mInitCheck = addTypeFromAttributes(attrs);
- mCurrentSection =
- (mCurrentSection == SECTION_DECODER
- ? SECTION_DECODER_TYPE : SECTION_ENCODER_TYPE);
- }
- }
- inType = false;
- // fall through
-
- case SECTION_DECODER_TYPE:
- case SECTION_ENCODER_TYPE:
- {
- // ignore limits and features specified outside of type
- bool outside = !inType && !mCurrentInfo->mHasSoleMime;
- if (outside && (!strcmp(name, "Limit") || !strcmp(name, "Feature"))) {
- ALOGW("ignoring %s specified outside of a Type", name);
- } else if (!strcmp(name, "Limit")) {
- mInitCheck = addLimit(attrs);
- } else if (!strcmp(name, "Feature")) {
- mInitCheck = addFeature(attrs);
- }
- break;
- }
-
- default:
- break;
- }
-
- ++mDepth;
-}
-
-void MediaCodecList::endElementHandler(const char *name) {
- if (mInitCheck != OK) {
- return;
- }
-
- switch (mCurrentSection) {
- case SECTION_SETTINGS:
- {
- if (!strcmp(name, "Settings")) {
- mCurrentSection = SECTION_TOPLEVEL;
- }
- break;
- }
-
- case SECTION_DECODERS:
- {
- if (!strcmp(name, "Decoders")) {
- mCurrentSection = SECTION_TOPLEVEL;
- }
- break;
- }
-
- case SECTION_ENCODERS:
- {
- if (!strcmp(name, "Encoders")) {
- mCurrentSection = SECTION_TOPLEVEL;
- }
- break;
- }
-
- case SECTION_DECODER_TYPE:
- case SECTION_ENCODER_TYPE:
- {
- if (!strcmp(name, "Type")) {
- mCurrentSection =
- (mCurrentSection == SECTION_DECODER_TYPE
- ? SECTION_DECODER : SECTION_ENCODER);
-
- mCurrentInfo->complete();
- }
- break;
- }
-
- case SECTION_DECODER:
- {
- if (!strcmp(name, "MediaCodec")) {
- mCurrentSection = SECTION_DECODERS;
- mCurrentInfo->complete();
- mCurrentInfo = NULL;
- }
- break;
- }
-
- case SECTION_ENCODER:
- {
- if (!strcmp(name, "MediaCodec")) {
- mCurrentSection = SECTION_ENCODERS;
- mCurrentInfo->complete();;
- mCurrentInfo = NULL;
- }
- break;
- }
-
- case SECTION_INCLUDE:
- {
- if (!strcmp(name, "Include") && mPastSections.size() > 0) {
- mCurrentSection = mPastSections.top();
- mPastSections.pop();
- }
- break;
- }
-
- default:
- break;
- }
-
- --mDepth;
-}
-
-status_t MediaCodecList::addSettingFromAttributes(const char **attrs) {
- const char *name = NULL;
- const char *value = NULL;
- const char *update = NULL;
-
- size_t i = 0;
- while (attrs[i] != NULL) {
- if (!strcmp(attrs[i], "name")) {
- if (attrs[i + 1] == NULL) {
- return -EINVAL;
- }
- name = attrs[i + 1];
- ++i;
- } else if (!strcmp(attrs[i], "value")) {
- if (attrs[i + 1] == NULL) {
- return -EINVAL;
- }
- value = attrs[i + 1];
- ++i;
- } else if (!strcmp(attrs[i], "update")) {
- if (attrs[i + 1] == NULL) {
- return -EINVAL;
- }
- update = attrs[i + 1];
- ++i;
- } else {
- return -EINVAL;
- }
-
- ++i;
- }
-
- if (name == NULL || value == NULL) {
- return -EINVAL;
- }
-
- mUpdate = (update != NULL) && parseBoolean(update);
- if (mUpdate != mGlobalSettings->contains(name)) {
- return -EINVAL;
- }
-
- mGlobalSettings->setString(name, value);
- return OK;
-}
-
-void MediaCodecList::setCurrentCodecInfo(bool encoder, const char *name, const char *type) {
- for (size_t i = 0; i < mCodecInfos.size(); ++i) {
- if (AString(name) == mCodecInfos[i]->getCodecName()) {
- if (mCodecInfos[i]->getCapabilitiesFor(type) == NULL) {
- ALOGW("Overrides with an unexpected mime %s", type);
- // Create a new MediaCodecInfo (but don't add it to mCodecInfos) to hold the
- // overrides we don't want.
- mCurrentInfo = new MediaCodecInfo(name, encoder, type);
- } else {
- mCurrentInfo = mCodecInfos.editItemAt(i);
- mCurrentInfo->updateMime(type); // to set the current cap
- }
- return;
- }
- }
- mCurrentInfo = new MediaCodecInfo(name, encoder, type);
- // The next step involves trying to load the codec, which may
- // fail. Only list the codec if this succeeds.
- // However, keep mCurrentInfo object around until parsing
- // of full codec info is completed.
- if (initializeCapabilities(type) == OK) {
- mCodecInfos.push_back(mCurrentInfo);
- }
-}
-
-status_t MediaCodecList::addMediaCodecFromAttributes(
- bool encoder, const char **attrs) {
- const char *name = NULL;
- const char *type = NULL;
- const char *update = NULL;
-
- size_t i = 0;
- while (attrs[i] != NULL) {
- if (!strcmp(attrs[i], "name")) {
- if (attrs[i + 1] == NULL) {
- return -EINVAL;
- }
- name = attrs[i + 1];
- ++i;
- } else if (!strcmp(attrs[i], "type")) {
- if (attrs[i + 1] == NULL) {
- return -EINVAL;
- }
- type = attrs[i + 1];
- ++i;
- } else if (!strcmp(attrs[i], "update")) {
- if (attrs[i + 1] == NULL) {
- return -EINVAL;
- }
- update = attrs[i + 1];
- ++i;
- } else {
- return -EINVAL;
- }
-
- ++i;
- }
-
- if (name == NULL) {
- return -EINVAL;
- }
-
- mUpdate = (update != NULL) && parseBoolean(update);
- ssize_t index = -1;
- for (size_t i = 0; i < mCodecInfos.size(); ++i) {
- if (AString(name) == mCodecInfos[i]->getCodecName()) {
- index = i;
- }
- }
- if (mUpdate != (index >= 0)) {
- return -EINVAL;
- }
-
- if (index >= 0) {
- // existing codec
- mCurrentInfo = mCodecInfos.editItemAt(index);
- if (type != NULL) {
- // existing type
- if (mCodecInfos[index]->getCapabilitiesFor(type) == NULL) {
- return -EINVAL;
- }
- mCurrentInfo->updateMime(type);
- }
- } else {
- // new codec
- mCurrentInfo = new MediaCodecInfo(name, encoder, type);
- // The next step involves trying to load the codec, which may
- // fail. Only list the codec if this succeeds.
- // However, keep mCurrentInfo object around until parsing
- // of full codec info is completed.
- if (initializeCapabilities(type) == OK) {
- mCodecInfos.push_back(mCurrentInfo);
- }
- }
-
- return OK;
-}
-
-status_t MediaCodecList::initializeCapabilities(const char *type) {
- if (type == NULL) {
- return OK;
- }
-
- ALOGV("initializeCapabilities %s:%s",
- mCurrentInfo->mName.c_str(), type);
-
- sp<MediaCodecInfo::Capabilities> caps;
- status_t err = MediaCodec::QueryCapabilities(
- mCurrentInfo->mName,
- type,
- mCurrentInfo->mIsEncoder,
- &caps);
- if (err != OK) {
- return err;
- } else if (caps == NULL) {
- ALOGE("MediaCodec::QueryCapabilities returned OK but no capabilities for '%s':'%s':'%s'",
- mCurrentInfo->mName.c_str(), type,
- mCurrentInfo->mIsEncoder ? "encoder" : "decoder");
- return UNKNOWN_ERROR;
- }
-
- return mCurrentInfo->initializeCapabilities(caps);
-}
-
-status_t MediaCodecList::addQuirk(const char **attrs) {
- const char *name = NULL;
-
- size_t i = 0;
- while (attrs[i] != NULL) {
- if (!strcmp(attrs[i], "name")) {
- if (attrs[i + 1] == NULL) {
- return -EINVAL;
- }
- name = attrs[i + 1];
- ++i;
- } else {
- return -EINVAL;
- }
-
- ++i;
- }
-
- if (name == NULL) {
- return -EINVAL;
- }
-
- mCurrentInfo->addQuirk(name);
- return OK;
-}
-
-status_t MediaCodecList::addTypeFromAttributes(const char **attrs) {
- const char *name = NULL;
- const char *update = NULL;
-
- size_t i = 0;
- while (attrs[i] != NULL) {
- if (!strcmp(attrs[i], "name")) {
- if (attrs[i + 1] == NULL) {
- return -EINVAL;
- }
- name = attrs[i + 1];
- ++i;
- } else if (!strcmp(attrs[i], "update")) {
- if (attrs[i + 1] == NULL) {
- return -EINVAL;
- }
- update = attrs[i + 1];
- ++i;
- } else {
- return -EINVAL;
- }
-
- ++i;
- }
-
- if (name == NULL) {
- return -EINVAL;
- }
-
- bool isExistingType = (mCurrentInfo->getCapabilitiesFor(name) != NULL);
- if (mUpdate != isExistingType) {
- return -EINVAL;
- }
-
- status_t ret;
- if (mUpdate) {
- ret = mCurrentInfo->updateMime(name);
- } else {
- ret = mCurrentInfo->addMime(name);
- }
-
- if (ret != OK) {
- return ret;
- }
-
- // The next step involves trying to load the codec, which may
- // fail. Handle this gracefully (by not reporting such mime).
- if (!mUpdate && initializeCapabilities(name) != OK) {
- mCurrentInfo->removeMime(name);
- }
- return OK;
+std::unique_ptr<MediaCodecInfoWriter>
+ MediaCodecListWriter::addMediaCodecInfo() {
+ sp<MediaCodecInfo> info = new MediaCodecInfo();
+ mList->mCodecInfos.push_back(info);
+ return std::unique_ptr<MediaCodecInfoWriter>(
+ new MediaCodecInfoWriter(info.get()));
}
// legacy method for non-advanced codecs
@@ -882,15 +209,15 @@
"feature-tunneled-playback",
};
- size_t numCodecs = mCodecInfos.size();
- for (; startIndex < numCodecs; ++startIndex) {
- const MediaCodecInfo &info = *mCodecInfos.itemAt(startIndex).get();
+ size_t numCodecInfos = mCodecInfos.size();
+ for (; startIndex < numCodecInfos; ++startIndex) {
+ const MediaCodecInfo &info = *mCodecInfos[startIndex];
if (info.isEncoder() != encoder) {
continue;
}
sp<MediaCodecInfo::Capabilities> capabilities = info.getCapabilitiesFor(type);
- if (capabilities == NULL) {
+ if (capabilities == nullptr) {
continue;
}
const sp<AMessage> &details = capabilities->getDetails();
@@ -913,223 +240,9 @@
return -ENOENT;
}
-static status_t limitFoundMissingAttr(const AString &name, const char *attr, bool found = true) {
- ALOGE("limit '%s' with %s'%s' attribute", name.c_str(),
- (found ? "" : "no "), attr);
- return -EINVAL;
-}
-
-static status_t limitError(const AString &name, const char *msg) {
- ALOGE("limit '%s' %s", name.c_str(), msg);
- return -EINVAL;
-}
-
-static status_t limitInvalidAttr(const AString &name, const char *attr, const AString &value) {
- ALOGE("limit '%s' with invalid '%s' attribute (%s)", name.c_str(),
- attr, value.c_str());
- return -EINVAL;
-}
-
-status_t MediaCodecList::addLimit(const char **attrs) {
- sp<AMessage> msg = new AMessage();
-
- size_t i = 0;
- while (attrs[i] != NULL) {
- if (attrs[i + 1] == NULL) {
- return -EINVAL;
- }
-
- // attributes with values
- if (!strcmp(attrs[i], "name")
- || !strcmp(attrs[i], "default")
- || !strcmp(attrs[i], "in")
- || !strcmp(attrs[i], "max")
- || !strcmp(attrs[i], "min")
- || !strcmp(attrs[i], "range")
- || !strcmp(attrs[i], "ranges")
- || !strcmp(attrs[i], "scale")
- || !strcmp(attrs[i], "value")) {
- msg->setString(attrs[i], attrs[i + 1]);
- ++i;
- } else {
- return -EINVAL;
- }
- ++i;
- }
-
- AString name;
- if (!msg->findString("name", &name)) {
- ALOGE("limit with no 'name' attribute");
- return -EINVAL;
- }
-
- // size, blocks, bitrate, frame-rate, blocks-per-second, aspect-ratio,
- // measured-frame-rate, measured-blocks-per-second: range
- // quality: range + default + [scale]
- // complexity: range + default
- bool found;
-
- if (name == "aspect-ratio" || name == "bitrate" || name == "block-count"
- || name == "blocks-per-second" || name == "complexity"
- || name == "frame-rate" || name == "quality" || name == "size"
- || name == "measured-blocks-per-second" || name.startsWith("measured-frame-rate-")) {
- AString min, max;
- if (msg->findString("min", &min) && msg->findString("max", &max)) {
- min.append("-");
- min.append(max);
- if (msg->contains("range") || msg->contains("value")) {
- return limitError(name, "has 'min' and 'max' as well as 'range' or "
- "'value' attributes");
- }
- msg->setString("range", min);
- } else if (msg->contains("min") || msg->contains("max")) {
- return limitError(name, "has only 'min' or 'max' attribute");
- } else if (msg->findString("value", &max)) {
- min = max;
- min.append("-");
- min.append(max);
- if (msg->contains("range")) {
- return limitError(name, "has both 'range' and 'value' attributes");
- }
- msg->setString("range", min);
- }
-
- AString range, scale = "linear", def, in_;
- if (!msg->findString("range", &range)) {
- return limitError(name, "with no 'range', 'value' or 'min'/'max' attributes");
- }
-
- if ((name == "quality" || name == "complexity") ^
- (found = msg->findString("default", &def))) {
- return limitFoundMissingAttr(name, "default", found);
- }
- if (name != "quality" && msg->findString("scale", &scale)) {
- return limitFoundMissingAttr(name, "scale");
- }
- if ((name == "aspect-ratio") ^ (found = msg->findString("in", &in_))) {
- return limitFoundMissingAttr(name, "in", found);
- }
-
- if (name == "aspect-ratio") {
- if (!(in_ == "pixels") && !(in_ == "blocks")) {
- return limitInvalidAttr(name, "in", in_);
- }
- in_.erase(5, 1); // (pixel|block)-aspect-ratio
- in_.append("-");
- in_.append(name);
- name = in_;
- }
- if (name == "quality") {
- mCurrentInfo->addDetail("quality-scale", scale);
- }
- if (name == "quality" || name == "complexity") {
- AString tag = name;
- tag.append("-default");
- mCurrentInfo->addDetail(tag, def);
- }
- AString tag = name;
- tag.append("-range");
- mCurrentInfo->addDetail(tag, range);
- } else {
- AString max, value, ranges;
- if (msg->contains("default")) {
- return limitFoundMissingAttr(name, "default");
- } else if (msg->contains("in")) {
- return limitFoundMissingAttr(name, "in");
- } else if ((name == "channel-count" || name == "concurrent-instances") ^
- (found = msg->findString("max", &max))) {
- return limitFoundMissingAttr(name, "max", found);
- } else if (msg->contains("min")) {
- return limitFoundMissingAttr(name, "min");
- } else if (msg->contains("range")) {
- return limitFoundMissingAttr(name, "range");
- } else if ((name == "sample-rate") ^
- (found = msg->findString("ranges", &ranges))) {
- return limitFoundMissingAttr(name, "ranges", found);
- } else if (msg->contains("scale")) {
- return limitFoundMissingAttr(name, "scale");
- } else if ((name == "alignment" || name == "block-size") ^
- (found = msg->findString("value", &value))) {
- return limitFoundMissingAttr(name, "value", found);
- }
-
- if (max.size()) {
- AString tag = "max-";
- tag.append(name);
- mCurrentInfo->addDetail(tag, max);
- } else if (value.size()) {
- mCurrentInfo->addDetail(name, value);
- } else if (ranges.size()) {
- AString tag = name;
- tag.append("-ranges");
- mCurrentInfo->addDetail(tag, ranges);
- } else {
- ALOGW("Ignoring unrecognized limit '%s'", name.c_str());
- }
- }
- return OK;
-}
-
-status_t MediaCodecList::addFeature(const char **attrs) {
- size_t i = 0;
- const char *name = NULL;
- int32_t optional = -1;
- int32_t required = -1;
- const char *value = NULL;
-
- while (attrs[i] != NULL) {
- if (attrs[i + 1] == NULL) {
- return -EINVAL;
- }
-
- // attributes with values
- if (!strcmp(attrs[i], "name")) {
- name = attrs[i + 1];
- ++i;
- } else if (!strcmp(attrs[i], "optional") || !strcmp(attrs[i], "required")) {
- int value = (int)parseBoolean(attrs[i + 1]);
- if (!strcmp(attrs[i], "optional")) {
- optional = value;
- } else {
- required = value;
- }
- ++i;
- } else if (!strcmp(attrs[i], "value")) {
- value = attrs[i + 1];
- ++i;
- } else {
- return -EINVAL;
- }
- ++i;
- }
- if (name == NULL) {
- ALOGE("feature with no 'name' attribute");
- return -EINVAL;
- }
-
- if (optional == required && optional != -1) {
- ALOGE("feature '%s' is both/neither optional and required", name);
- return -EINVAL;
- }
-
- if ((optional != -1 || required != -1) && (value != NULL)) {
- ALOGE("feature '%s' has both a value and optional/required attribute", name);
- return -EINVAL;
- }
-
- if (value != NULL) {
- mCurrentInfo->addFeature(name, value);
- } else {
- mCurrentInfo->addFeature(name, (required == 1) || (optional == 0));
- }
- return OK;
-}
-
ssize_t MediaCodecList::findCodecByName(const char *name) const {
for (size_t i = 0; i < mCodecInfos.size(); ++i) {
- const MediaCodecInfo &info = *mCodecInfos.itemAt(i).get();
-
- if (info.mName == name) {
+ if (strcmp(mCodecInfos[i]->getCodecName(), name) == 0) {
return i;
}
}
@@ -1167,11 +280,15 @@
//static
void MediaCodecList::findMatchingCodecs(
- const char *mime, bool encoder, uint32_t flags, Vector<AString> *matches) {
+ const char *mime, bool encoder, uint32_t flags,
+ Vector<AString> *matches, Vector<AString> *owners) {
matches->clear();
+ if (owners != nullptr) {
+ owners->clear();
+ }
const sp<IMediaCodecList> list = getInstance();
- if (list == NULL) {
+ if (list == nullptr) {
return;
}
@@ -1187,45 +304,27 @@
index = matchIndex + 1;
const sp<MediaCodecInfo> info = list->getCodecInfo(matchIndex);
- CHECK(info != NULL);
+ CHECK(info != nullptr);
AString componentName = info->getCodecName();
if ((flags & kHardwareCodecsOnly) && isSoftwareCodec(componentName)) {
ALOGV("skipping SW codec '%s'", componentName.c_str());
} else {
matches->push(componentName);
+ if (owners != nullptr) {
+ owners->push(AString(info->getOwnerName()));
+ }
ALOGV("matching '%s'", componentName.c_str());
}
}
- if (flags & kPreferSoftwareCodecs || property_get_bool("debug.stagefright.swcodec", false)) {
+ if (flags & kPreferSoftwareCodecs ||
+ property_get_bool("debug.stagefright.swcodec", false)) {
matches->sort(compareSoftwareCodecsFirst);
}
}
-// static
-uint32_t MediaCodecList::getQuirksFor(const char *componentName) {
- const sp<IMediaCodecList> list = getInstance();
- if (list == NULL) {
- return 0;
- }
-
- ssize_t ix = list->findCodecByName(componentName);
- if (ix < 0) {
- return 0;
- }
-
- const sp<MediaCodecInfo> info = list->getCodecInfo(ix);
-
- uint32_t quirks = 0;
- if (info->hasQuirk("requires-allocate-on-input-ports")) {
- quirks |= ACodec::kRequiresAllocateBufferOnInputPorts;
- }
- if (info->hasQuirk("requires-allocate-on-output-ports")) {
- quirks |= ACodec::kRequiresAllocateBufferOnOutputPorts;
- }
-
- return quirks;
+MediaCodecListBuilderBase::~MediaCodecListBuilderBase() {
}
} // namespace android
diff --git a/media/libstagefright/MediaCodecListOverrides.cpp b/media/libstagefright/MediaCodecListOverrides.cpp
index 095fc6a..6920e51 100644
--- a/media/libstagefright/MediaCodecListOverrides.cpp
+++ b/media/libstagefright/MediaCodecListOverrides.cpp
@@ -33,8 +33,6 @@
namespace android {
-const char *kProfilingResults = "/data/misc/media/media_codecs_profiling_results.xml";
-
AString getProfilingVersionString() {
char val[PROPERTY_VALUE_MAX];
if (property_get("ro.build.display.id", val, NULL) && (strlen(val) > 0)) {
@@ -205,24 +203,24 @@
return true;
}
-void profileCodecs(const Vector<sp<MediaCodecInfo>> &infos) {
+void profileCodecs(const std::vector<sp<MediaCodecInfo>> &infos,
+ const char* profilingResults) {
CodecSettings global_results;
KeyedVector<AString, CodecSettings> encoder_results;
KeyedVector<AString, CodecSettings> decoder_results;
profileCodecs(infos, &global_results, &encoder_results, &decoder_results);
- exportResultsToXML(kProfilingResults, global_results, encoder_results, decoder_results);
+ exportResultsToXML(profilingResults, global_results, encoder_results, decoder_results);
}
void profileCodecs(
- const Vector<sp<MediaCodecInfo>> &infos,
+ const std::vector<sp<MediaCodecInfo>> &infos,
CodecSettings *global_results,
KeyedVector<AString, CodecSettings> *encoder_results,
KeyedVector<AString, CodecSettings> *decoder_results,
bool forceToMeasure) {
KeyedVector<AString, sp<MediaCodecInfo::Capabilities>> codecsNeedMeasure;
AString supportMultipleSecureCodecs = "true";
- for (size_t i = 0; i < infos.size(); ++i) {
- const sp<MediaCodecInfo> info = infos[i];
+ for (const auto& info : infos) {
AString name = info->getCodecName();
if (name.startsWith("OMX.google.") ||
// TODO: reenable below codecs once fixed
diff --git a/media/libstagefright/MediaCodecListOverrides.h b/media/libstagefright/MediaCodecListOverrides.h
index d4bb225..4f8c2f5 100644
--- a/media/libstagefright/MediaCodecListOverrides.h
+++ b/media/libstagefright/MediaCodecListOverrides.h
@@ -23,12 +23,10 @@
#include <utils/StrongPointer.h>
#include <utils/KeyedVector.h>
+#include <vector>
namespace android {
-extern const char *kProfilingVersionString;
-extern const char *kProfilingResults;
-
struct MediaCodecInfo;
AString getProfilingVersionString();
@@ -36,11 +34,12 @@
bool splitString(const AString &s, const AString &delimiter, AString *s1, AString *s2);
// profile codecs and save the result to xml file named kProfilingResults.
-void profileCodecs(const Vector<sp<MediaCodecInfo>> &infos);
+void profileCodecs(const std::vector<sp<MediaCodecInfo>> &infos,
+ const char* profilingResults);
// profile codecs and save the result to global_results, encoder_results and decoder_results.
void profileCodecs(
- const Vector<sp<MediaCodecInfo>> &infos,
+ const std::vector<sp<MediaCodecInfo>> &infos,
CodecSettings *global_results,
KeyedVector<AString, CodecSettings> *encoder_results,
KeyedVector<AString, CodecSettings> *decoder_results,
diff --git a/media/libstagefright/MediaExtractor.cpp b/media/libstagefright/MediaExtractor.cpp
index c91c82b..14e6251 100644
--- a/media/libstagefright/MediaExtractor.cpp
+++ b/media/libstagefright/MediaExtractor.cpp
@@ -43,7 +43,10 @@
#include <media/stagefright/MediaDefs.h>
#include <media/stagefright/MediaExtractor.h>
#include <media/stagefright/MetaData.h>
+#include <media/stagefright/RemoteMediaExtractor.h>
+#include <media/IMediaExtractor.h>
#include <media/IMediaExtractorService.h>
+#include <media/IMediaSource.h>
#include <cutils/properties.h>
#include <utils/String8.h>
#include <private/android_filesystem_config.h>
@@ -92,6 +95,10 @@
}
}
+sp<IMediaExtractor> MediaExtractor::asIMediaExtractor() {
+ return RemoteMediaExtractor::wrap(sp<MediaExtractor>(this));
+}
+
sp<MetaData> MediaExtractor::getMetaData() {
return new MetaData;
}
@@ -125,7 +132,8 @@
if (!property_get_bool("media.stagefright.extractremote", true)) {
// local extractor
ALOGW("creating media extractor in calling process");
- return CreateFromService(source, mime);
+ sp<MediaExtractor> extractor = CreateFromService(source, mime);
+ return (extractor.get() == nullptr) ? nullptr : extractor->asIMediaExtractor();
} else {
// remote extractor
ALOGV("get service manager");
@@ -196,27 +204,27 @@
}
if (ret != NULL) {
- // track the container format (mpeg, aac, wvm, etc)
- if (MEDIA_LOG) {
- if (ret->mAnalyticsItem != NULL) {
- size_t ntracks = ret->countTracks();
- ret->mAnalyticsItem->setCString(kExtractorFormat, ret->name());
- // tracks (size_t)
- ret->mAnalyticsItem->setInt32(kExtractorTracks, ntracks);
- // metadata
- sp<MetaData> pMetaData = ret->getMetaData();
- if (pMetaData != NULL) {
- String8 xx = pMetaData->toString();
- // 'titl' -- but this verges into PII
- // 'mime'
- const char *mime = NULL;
- if (pMetaData->findCString(kKeyMIMEType, &mime)) {
- ret->mAnalyticsItem->setCString(kExtractorMime, mime);
+ // track the container format (mpeg, aac, wvm, etc)
+ if (MEDIA_LOG) {
+ if (ret->mAnalyticsItem != NULL) {
+ size_t ntracks = ret->countTracks();
+ ret->mAnalyticsItem->setCString(kExtractorFormat, ret->name());
+ // tracks (size_t)
+ ret->mAnalyticsItem->setInt32(kExtractorTracks, ntracks);
+ // metadata
+ sp<MetaData> pMetaData = ret->getMetaData();
+ if (pMetaData != NULL) {
+ String8 xx = pMetaData->toString();
+ // 'titl' -- but this verges into PII
+ // 'mime'
+ const char *mime = NULL;
+ if (pMetaData->findCString(kKeyMIMEType, &mime)) {
+ ret->mAnalyticsItem->setCString(kExtractorMime, mime);
+ }
+ // what else is interesting and not already available?
}
- // what else is interesting and not already available?
- }
- }
- }
+ }
+ }
}
return ret;
diff --git a/media/libstagefright/MediaSource.cpp b/media/libstagefright/MediaSource.cpp
index a17757a..8b92a6b 100644
--- a/media/libstagefright/MediaSource.cpp
+++ b/media/libstagefright/MediaSource.cpp
@@ -14,7 +14,9 @@
* limitations under the License.
*/
+#include <media/stagefright/CallbackMediaSource.h>
#include <media/stagefright/MediaSource.h>
+#include <media/stagefright/RemoteMediaSource.h>
namespace android {
@@ -22,4 +24,16 @@
MediaSource::~MediaSource() {}
+// static
+sp<MediaSource> MediaSource::CreateFromIMediaSource(const sp<IMediaSource> &source) {
+ if (source == nullptr) {
+ return nullptr;
+ }
+ return new CallbackMediaSource(source);
+}
+
+sp<IMediaSource> MediaSource::asIMediaSource() {
+ return RemoteMediaSource::wrap(sp<MediaSource>(this));
+}
+
} // namespace android
diff --git a/media/libstagefright/MediaSync.cpp b/media/libstagefright/MediaSync.cpp
index 9278381..ba14e5d 100644
--- a/media/libstagefright/MediaSync.cpp
+++ b/media/libstagefright/MediaSync.cpp
@@ -61,6 +61,7 @@
mNextBufferItemMediaUs(-1),
mPlaybackRate(0.0) {
mMediaClock = new MediaClock;
+ mMediaClock->init();
// initialize settings
mPlaybackSettings = AUDIO_PLAYBACK_RATE_DEFAULT;
diff --git a/media/libstagefright/MidiExtractor.cpp b/media/libstagefright/MidiExtractor.cpp
index 7930bbb..89cfa3c 100644
--- a/media/libstagefright/MidiExtractor.cpp
+++ b/media/libstagefright/MidiExtractor.cpp
@@ -282,7 +282,7 @@
return mInitCheck == OK ? 1 : 0;
}
-sp<IMediaSource> MidiExtractor::getTrack(size_t index)
+sp<MediaSource> MidiExtractor::getTrack(size_t index)
{
if (mInitCheck != OK || index > 0) {
return NULL;
diff --git a/media/libstagefright/OMXClient.cpp b/media/libstagefright/OMXClient.cpp
index 5f9aa01..5f50e46 100644
--- a/media/libstagefright/OMXClient.cpp
+++ b/media/libstagefright/OMXClient.cpp
@@ -38,7 +38,7 @@
}
status_t OMXClient::connect() {
- return connect(nullptr);
+ return connect("default", nullptr);
}
status_t OMXClient::connect(bool* trebleFlag) {
@@ -54,6 +54,19 @@
return connectLegacy();
}
+status_t OMXClient::connect(const char* name, bool* trebleFlag) {
+ if (property_get_bool("persist.media.treble_omx", true)) {
+ if (trebleFlag != nullptr) {
+ *trebleFlag = true;
+ }
+ return connectTreble(name);
+ }
+ if (trebleFlag != nullptr) {
+ *trebleFlag = false;
+ }
+ return connectLegacy();
+}
+
status_t OMXClient::connectLegacy() {
sp<IServiceManager> sm = defaultServiceManager();
sp<IBinder> codecbinder = sm->getService(String16("media.codec"));
@@ -73,9 +86,12 @@
return OK;
}
-status_t OMXClient::connectTreble() {
+status_t OMXClient::connectTreble(const char* name) {
using namespace ::android::hardware::media::omx::V1_0;
- sp<IOmx> tOmx = IOmx::getService("default");
+ if (name == nullptr) {
+ name = "default";
+ }
+ sp<IOmx> tOmx = IOmx::getService(name);
if (tOmx.get() == nullptr) {
ALOGE("Cannot obtain Treble IOmx.");
return NO_INIT;
diff --git a/media/libstagefright/OggExtractor.cpp b/media/libstagefright/OggExtractor.cpp
index 810b0d6..58467db 100644
--- a/media/libstagefright/OggExtractor.cpp
+++ b/media/libstagefright/OggExtractor.cpp
@@ -1123,6 +1123,7 @@
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);
@@ -1344,7 +1345,7 @@
return mInitCheck != OK ? 0 : 1;
}
-sp<IMediaSource> OggExtractor::getTrack(size_t index) {
+sp<MediaSource> OggExtractor::getTrack(size_t index) {
if (index >= 1) {
return NULL;
}
diff --git a/media/libstagefright/OmxInfoBuilder.cpp b/media/libstagefright/OmxInfoBuilder.cpp
new file mode 100644
index 0000000..8717a79
--- /dev/null
+++ b/media/libstagefright/OmxInfoBuilder.cpp
@@ -0,0 +1,323 @@
+/*
+ * 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.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "OmxInfoBuilder"
+
+#ifdef __LP64__
+#define OMX_ANDROID_COMPILE_AS_32BIT_ON_64BIT_PLATFORMS
+#endif
+
+#include <utils/Log.h>
+#include <cutils/properties.h>
+
+#include <binder/IServiceManager.h>
+#include <media/IMediaCodecService.h>
+#include <media/stagefright/OmxInfoBuilder.h>
+#include <media/stagefright/ACodec.h>
+
+#include <android/hardware/media/omx/1.0/IOmxStore.h>
+#include <android/hardware/media/omx/1.0/IOmx.h>
+#include <android/hardware/media/omx/1.0/IOmxNode.h>
+#include <media/stagefright/omx/OMXUtils.h>
+
+#include <media/IOMXStore.h>
+#include <media/IOMX.h>
+#include <media/MediaDefs.h>
+#include <media/omx/1.0/WOmx.h>
+
+#include <media/openmax/OMX_Index.h>
+#include <media/openmax/OMX_IndexExt.h>
+#include <media/openmax/OMX_Audio.h>
+#include <media/openmax/OMX_AudioExt.h>
+#include <media/openmax/OMX_Video.h>
+#include <media/openmax/OMX_VideoExt.h>
+
+namespace android {
+
+namespace /* unnamed */ {
+
+status_t queryCapabilities(
+ const IOMXStore::NodeInfo& node, const char* mime, bool isEncoder,
+ MediaCodecInfo::CapabilitiesWriter* caps) {
+ sp<ACodec> codec = new ACodec();
+ status_t err = codec->queryCapabilities(
+ node.owner.c_str(), node.name.c_str(), mime, isEncoder, caps);
+ if (err != OK) {
+ return err;
+ }
+ for (const auto& attribute : node.attributes) {
+ // All features have an int32 value except
+ // "feature-bitrate-modes", which has a string value.
+ if ((attribute.key.compare(0, 8, "feature-") == 0) &&
+ (attribute.key.compare(8, 15, "bitrate-modes")
+ != 0)) {
+ // If this attribute.key is a feature that is not a bitrate
+ // control, add an int32 value.
+ caps->addDetail(
+ attribute.key.c_str(),
+ attribute.value == "1" ? 1 : 0);
+ } else {
+ // Non-feature attributes
+ caps->addDetail(
+ attribute.key.c_str(), attribute.value.c_str());
+ }
+ }
+ return OK;
+}
+
+} // unnamed namespace
+
+OmxInfoBuilder::OmxInfoBuilder() {
+}
+
+status_t OmxInfoBuilder::buildMediaCodecList(MediaCodecListWriter* writer) {
+ bool treble;
+ sp<IOMX> omx;
+ std::vector<IOMXStore::RoleInfo> roles;
+
+ treble = property_get_bool("persist.media.treble_omx", true);
+ if (treble) {
+ using namespace ::android::hardware::media::omx::V1_0;
+ using ::android::hardware::hidl_vec;
+ using ::android::hardware::hidl_string;
+
+ // Obtain IOmxStore
+ sp<IOmxStore> omxStore = IOmxStore::getService();
+ if (omxStore == nullptr) {
+ ALOGE("Cannot connect to an IOmxStore instance.");
+ return NO_INIT;
+ }
+
+ // List service attributes (global settings)
+ Status status;
+ hidl_vec<IOmxStore::ServiceAttribute> serviceAttributes;
+ auto transStatus = omxStore->listServiceAttributes(
+ [&status, &serviceAttributes]
+ (Status inStatus, const hidl_vec<IOmxStore::ServiceAttribute>&
+ inAttributes) {
+ status = inStatus;
+ serviceAttributes = inAttributes;
+ });
+ if (!transStatus.isOk()) {
+ ALOGE("Fail to obtain global settings from IOmxStore.");
+ return NO_INIT;
+ }
+ if (status != Status::OK) {
+ ALOGE("IOmxStore reports parsing error.");
+ return NO_INIT;
+ }
+ for (const auto& p : serviceAttributes) {
+ writer->addGlobalSetting(
+ p.key.c_str(), p.value.c_str());
+ }
+
+ // List roles and convert to IOMXStore's format
+ transStatus = omxStore->listRoles(
+ [&roles]
+ (const hidl_vec<IOmxStore::RoleInfo>& inRoleList) {
+ roles.reserve(inRoleList.size());
+ for (const auto& inRole : inRoleList) {
+ IOMXStore::RoleInfo role;
+ role.role = inRole.role;
+ role.type = inRole.type;
+ role.isEncoder = inRole.isEncoder;
+ role.preferPlatformNodes = inRole.preferPlatformNodes;
+ std::vector<IOMXStore::NodeInfo>& nodes =
+ role.nodes;
+ nodes.reserve(inRole.nodes.size());
+ for (const auto& inNode : inRole.nodes) {
+ IOMXStore::NodeInfo node;
+ node.name = inNode.name;
+ node.owner = inNode.owner;
+ std::vector<IOMXStore::Attribute>& attributes =
+ node.attributes;
+ attributes.reserve(inNode.attributes.size());
+ for (const auto& inAttr : inNode.attributes) {
+ IOMXStore::Attribute attr;
+ attr.key = inAttr.key;
+ attr.value = inAttr.value;
+ attributes.push_back(std::move(attr));
+ }
+ nodes.push_back(std::move(node));
+ }
+ roles.push_back(std::move(role));
+ }
+ });
+ if (!transStatus.isOk()) {
+ ALOGE("Fail to obtain codec roles from IOmxStore.");
+ return NO_INIT;
+ }
+ } else {
+ // Obtain IOMXStore
+ sp<IServiceManager> sm = defaultServiceManager();
+ if (sm == nullptr) {
+ ALOGE("Cannot obtain the default service manager.");
+ return NO_INIT;
+ }
+ sp<IBinder> codecBinder = sm->getService(String16("media.codec"));
+ if (codecBinder == nullptr) {
+ ALOGE("Cannot obtain the media codec service.");
+ return NO_INIT;
+ }
+ sp<IMediaCodecService> codecService =
+ interface_cast<IMediaCodecService>(codecBinder);
+ if (codecService == nullptr) {
+ ALOGE("Wrong type of media codec service obtained.");
+ return NO_INIT;
+ }
+ omx = codecService->getOMX();
+ if (omx == nullptr) {
+ ALOGE("Cannot connect to an IOMX instance.");
+ }
+ sp<IOMXStore> omxStore = codecService->getOMXStore();
+ if (omxStore == nullptr) {
+ ALOGE("Cannot connect to an IOMXStore instance.");
+ return NO_INIT;
+ }
+
+ // List service attributes (global settings)
+ std::vector<IOMXStore::Attribute> serviceAttributes;
+ status_t status = omxStore->listServiceAttributes(&serviceAttributes);
+ if (status != OK) {
+ ALOGE("Fail to obtain global settings from IOMXStore.");
+ return NO_INIT;
+ }
+ for (const auto& p : serviceAttributes) {
+ writer->addGlobalSetting(
+ p.key.c_str(), p.value.c_str());
+ }
+
+ // List roles
+ status = omxStore->listRoles(&roles);
+ if (status != OK) {
+ ALOGE("Fail to obtain codec roles from IOMXStore.");
+ return NO_INIT;
+ }
+ }
+
+ // Convert roles to lists of codecs
+
+ // codec name -> index into swCodecs
+ std::map<std::string, std::unique_ptr<MediaCodecInfoWriter> >
+ swCodecName2Info;
+ // codec name -> index into hwCodecs
+ std::map<std::string, std::unique_ptr<MediaCodecInfoWriter> >
+ 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;
+ 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;
+ bool isSoftware = nodeName.compare(0, 10, "OMX.google") == 0;
+ MediaCodecInfoWriter* info;
+ if (isSoftware) {
+ auto c2i = swCodecName2Info.find(nodeName);
+ if (c2i == swCodecName2Info.end()) {
+ // Create a new MediaCodecInfo for a new node.
+ c2i = swCodecName2Info.insert(std::make_pair(
+ nodeName, writer->addMediaCodecInfo())).first;
+ info = c2i->second.get();
+ info->setName(nodeName.c_str());
+ info->setOwner(node.owner.c_str());
+ info->setEncoder(isEncoder);
+ } else {
+ // The node has been seen before. Simply retrieve the
+ // existing MediaCodecInfoWriter.
+ info = c2i->second.get();
+ }
+ } else {
+ auto c2i = hwCodecName2Info.find(nodeName);
+ if (c2i == hwCodecName2Info.end()) {
+ // Create a new MediaCodecInfo for a new node.
+ if (!preferPlatformNodes) {
+ c2i = hwCodecName2Info.insert(std::make_pair(
+ nodeName, writer->addMediaCodecInfo())).first;
+ info = c2i->second.get();
+ info->setName(nodeName.c_str());
+ info->setOwner(node.owner.c_str());
+ info->setEncoder(isEncoder);
+ } else {
+ // If preferPlatformNodes is true, this node must be
+ // added after all software nodes.
+ hwCodecs.push_back(&node);
+ continue;
+ }
+ } else {
+ // The node has been seen before. Simply retrieve the
+ // existing MediaCodecInfoWriter.
+ info = c2i->second.get();
+ }
+ }
+ std::unique_ptr<MediaCodecInfo::CapabilitiesWriter> caps =
+ info->addMime(typeName.c_str());
+ if (queryCapabilities(
+ node, typeName.c_str(), isEncoder, caps.get()) != OK) {
+ ALOGW("Fail to add mime %s to codec %s",
+ typeName.c_str(), nodeName.c_str());
+ info->removeMime(typeName.c_str());
+ }
+ }
+
+ // If preferPlatformNodes is true, hardware nodes will not have been
+ // added in the loop above, but rather saved in hwCodecs. They are
+ // going to be added here.
+ if (preferPlatformNodes) {
+ for (const auto& node : hwCodecs) {
+ MediaCodecInfoWriter* info;
+ const auto& nodeName = node->name;
+ auto c2i = hwCodecName2Info.find(nodeName);
+ if (c2i == hwCodecName2Info.end()) {
+ // Create a new MediaCodecInfo for a new node.
+ c2i = hwCodecName2Info.insert(std::make_pair(
+ nodeName, writer->addMediaCodecInfo())).first;
+ info = c2i->second.get();
+ info->setName(nodeName.c_str());
+ info->setOwner(node->owner.c_str());
+ info->setEncoder(isEncoder);
+ } else {
+ // The node has been seen before. Simply retrieve the
+ // existing MediaCodecInfoWriter.
+ info = c2i->second.get();
+ }
+ std::unique_ptr<MediaCodecInfo::CapabilitiesWriter> caps =
+ info->addMime(typeName.c_str());
+ if (queryCapabilities(
+ *node, typeName.c_str(), isEncoder, caps.get()) != OK) {
+ ALOGW("Fail to add mime %s to codec %s "
+ "after software codecs",
+ typeName.c_str(), nodeName.c_str());
+ info->removeMime(typeName.c_str());
+ }
+ }
+ }
+ }
+ return OK;
+}
+
+} // namespace android
+
diff --git a/media/libstagefright/RemoteMediaExtractor.cpp b/media/libstagefright/RemoteMediaExtractor.cpp
new file mode 100644
index 0000000..db2c20c
--- /dev/null
+++ b/media/libstagefright/RemoteMediaExtractor.cpp
@@ -0,0 +1,82 @@
+/*
+ * 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.
+ */
+
+#include <media/stagefright/RemoteMediaExtractor.h>
+#include <media/stagefright/MediaSource.h>
+
+namespace android {
+
+RemoteMediaExtractor::RemoteMediaExtractor(const sp<MediaExtractor> &extractor)
+ :mExtractor(extractor) {}
+
+RemoteMediaExtractor::~RemoteMediaExtractor() {}
+
+size_t RemoteMediaExtractor::countTracks() {
+ return mExtractor->countTracks();
+}
+
+sp<IMediaSource> RemoteMediaExtractor::getTrack(size_t index) {
+ sp<MediaSource> source = mExtractor->getTrack(index);
+ return (source.get() == nullptr) ? nullptr : source->asIMediaSource();
+}
+
+sp<MetaData> RemoteMediaExtractor::getTrackMetaData(size_t index, uint32_t flags) {
+ return mExtractor->getTrackMetaData(index, flags);
+}
+
+sp<MetaData> RemoteMediaExtractor::getMetaData() {
+ return mExtractor->getMetaData();
+}
+
+status_t RemoteMediaExtractor::getMetrics(Parcel *reply) {
+ return mExtractor->getMetrics(reply);
+}
+
+uint32_t RemoteMediaExtractor::flags() const {
+ 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(casToken);
+}
+
+const char * RemoteMediaExtractor::name() {
+ return mExtractor->name();
+}
+
+void RemoteMediaExtractor::release() {
+ return mExtractor->release();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+// static
+sp<IMediaExtractor> RemoteMediaExtractor::wrap(const sp<MediaExtractor> &extractor) {
+ if (extractor.get() == nullptr) {
+ return nullptr;
+ }
+ return new RemoteMediaExtractor(extractor);
+}
+
+} // namespace android
diff --git a/media/libstagefright/RemoteMediaSource.cpp b/media/libstagefright/RemoteMediaSource.cpp
new file mode 100644
index 0000000..4060526
--- /dev/null
+++ b/media/libstagefright/RemoteMediaSource.cpp
@@ -0,0 +1,64 @@
+/*
+ * 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.
+ */
+
+#include <media/stagefright/RemoteMediaSource.h>
+
+namespace android {
+
+RemoteMediaSource::RemoteMediaSource(const sp<MediaSource> &source)
+ :mSource(source) {}
+
+RemoteMediaSource::~RemoteMediaSource() {}
+
+status_t RemoteMediaSource::start(MetaData *params) {
+ return mSource->start(params);
+}
+
+status_t RemoteMediaSource::stop() {
+ return mSource->stop();
+}
+
+sp<MetaData> RemoteMediaSource::getFormat() {
+ return mSource->getFormat();
+}
+
+status_t RemoteMediaSource::read(MediaBuffer **buffer, const ReadOptions *options) {
+ return mSource->read(buffer, reinterpret_cast<const MediaSource::ReadOptions*>(options));
+}
+
+status_t RemoteMediaSource::pause() {
+ return mSource->pause();
+}
+
+status_t RemoteMediaSource::setBuffers(const Vector<MediaBuffer *> &buffers) {
+ return mSource->setBuffers(buffers);
+}
+
+status_t RemoteMediaSource::setStopTimeUs(int64_t stopTimeUs) {
+ return mSource->setStopTimeUs(stopTimeUs);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+// static
+sp<IMediaSource> RemoteMediaSource::wrap(const sp<MediaSource> &source) {
+ if (source.get() == nullptr) {
+ return nullptr;
+ }
+ return new RemoteMediaSource(source);
+}
+
+} // namespace android
diff --git a/media/libstagefright/SampleIterator.cpp b/media/libstagefright/SampleIterator.cpp
index 4f31c5f..75f744d 100644
--- a/media/libstagefright/SampleIterator.cpp
+++ b/media/libstagefright/SampleIterator.cpp
@@ -172,7 +172,7 @@
if (mSampleToChunkIndex + 1 < mTable->mNumSampleToChunkOffsets) {
mStopChunk = entry[1].startChunk;
- if (mStopChunk < mFirstChunk ||
+ if (mSamplesPerChunk == 0 || mStopChunk < mFirstChunk ||
(mStopChunk - mFirstChunk) > UINT32_MAX / mSamplesPerChunk ||
((mStopChunk - mFirstChunk) * mSamplesPerChunk >
UINT32_MAX - mFirstChunkSampleIndex)) {
diff --git a/media/libstagefright/SimpleDecodingSource.cpp b/media/libstagefright/SimpleDecodingSource.cpp
index 90b8603..67e6748 100644
--- a/media/libstagefright/SimpleDecodingSource.cpp
+++ b/media/libstagefright/SimpleDecodingSource.cpp
@@ -36,13 +36,13 @@
//static
sp<SimpleDecodingSource> SimpleDecodingSource::Create(
- const sp<IMediaSource> &source, uint32_t flags) {
+ const sp<MediaSource> &source, uint32_t flags) {
return SimpleDecodingSource::Create(source, flags, nullptr, nullptr);
}
//static
sp<SimpleDecodingSource> SimpleDecodingSource::Create(
- const sp<IMediaSource> &source, uint32_t flags, const sp<ANativeWindow> &nativeWindow,
+ const sp<MediaSource> &source, uint32_t flags, const sp<ANativeWindow> &nativeWindow,
const char *desiredCodec) {
sp<Surface> surface = static_cast<Surface*>(nativeWindow.get());
const char *mime = NULL;
@@ -99,7 +99,7 @@
}
SimpleDecodingSource::SimpleDecodingSource(
- const sp<MediaCodec> &codec, const sp<IMediaSource> &source, const sp<ALooper> &looper,
+ const sp<MediaCodec> &codec, const sp<MediaSource> &source, const sp<ALooper> &looper,
bool usingSurface, bool isVorbis, const sp<AMessage> &format)
: mCodec(codec),
mSource(source),
@@ -212,7 +212,7 @@
status_t res;
// flush codec on seek
- IMediaSource::ReadOptions::SeekMode mode;
+ MediaSource::ReadOptions::SeekMode mode;
if (options != NULL && options->getSeekTo(&out_pts, &mode)) {
me->mQueuedInputEOS = false;
me->mGotOutputEOS = false;
diff --git a/media/libstagefright/StagefrightMetadataRetriever.cpp b/media/libstagefright/StagefrightMetadataRetriever.cpp
index a53897f..103da95 100644
--- a/media/libstagefright/StagefrightMetadataRetriever.cpp
+++ b/media/libstagefright/StagefrightMetadataRetriever.cpp
@@ -60,6 +60,12 @@
StagefrightMetadataRetriever::~StagefrightMetadataRetriever() {
ALOGV("~StagefrightMetadataRetriever()");
clearMetadata();
+ // Explicitly release extractor before continuing with the destructor,
+ // some extractors might need to callback to close off the DataSource
+ // and we need to make sure it's still there.
+ if (mExtractor != NULL) {
+ mExtractor->release();
+ }
if (mSource != NULL) {
mSource->close();
}
diff --git a/media/libstagefright/WAVExtractor.cpp b/media/libstagefright/WAVExtractor.cpp
index 780b746..18bf4ae 100644
--- a/media/libstagefright/WAVExtractor.cpp
+++ b/media/libstagefright/WAVExtractor.cpp
@@ -120,7 +120,7 @@
return mInitCheck == OK ? 1 : 0;
}
-sp<IMediaSource> WAVExtractor::getTrack(size_t index) {
+sp<MediaSource> WAVExtractor::getTrack(size_t index) {
if (mInitCheck != OK || index > 0) {
return NULL;
}
diff --git a/media/libstagefright/avc_utils.cpp b/media/libstagefright/avc_utils.cpp
index a745692..b75b468 100644
--- a/media/libstagefright/avc_utils.cpp
+++ b/media/libstagefright/avc_utils.cpp
@@ -200,10 +200,22 @@
frame_crop_top_offset, frame_crop_bottom_offset,
cropUnitX, cropUnitY);
- *width -=
- (frame_crop_left_offset + frame_crop_right_offset) * cropUnitX;
- *height -=
- (frame_crop_top_offset + frame_crop_bottom_offset) * cropUnitY;
+
+ // *width -= (frame_crop_left_offset + frame_crop_right_offset) * cropUnitX;
+ if(__builtin_add_overflow(frame_crop_left_offset, frame_crop_right_offset, &frame_crop_left_offset) ||
+ __builtin_mul_overflow(frame_crop_left_offset, cropUnitX, &frame_crop_left_offset) ||
+ __builtin_sub_overflow(*width, frame_crop_left_offset, width) ||
+ *width < 0) {
+ *width = 0;
+ }
+
+ //*height -= (frame_crop_top_offset + frame_crop_bottom_offset) * cropUnitY;
+ if(__builtin_add_overflow(frame_crop_top_offset, frame_crop_bottom_offset, &frame_crop_top_offset) ||
+ __builtin_mul_overflow(frame_crop_top_offset, cropUnitY, &frame_crop_top_offset) ||
+ __builtin_sub_overflow(*height, frame_crop_top_offset, height) ||
+ *height < 0) {
+ *height = 0;
+ }
}
if (sarWidth != NULL) {
diff --git a/media/libstagefright/codec2/include/C2Buffer.h b/media/libstagefright/codec2/include/C2Buffer.h
index 88f1db3..b5ea381 100644
--- a/media/libstagefright/codec2/include/C2Buffer.h
+++ b/media/libstagefright/codec2/include/C2Buffer.h
@@ -767,10 +767,10 @@
uint32_t mWidth;
uint32_t mHeight;
- inline C2Rect(uint32_t width, uint32_t height)
+ constexpr inline C2Rect(uint32_t width, uint32_t height)
: C2Rect(width, height, 0, 0) { }
- inline C2Rect(uint32_t width, uint32_t height, uint32_t left, uint32_t top)
+ constexpr inline C2Rect(uint32_t width, uint32_t height, uint32_t left, uint32_t top)
: mLeft(left), mTop(top), mWidth(width), mHeight(height) { }
// utility methods
@@ -921,29 +921,51 @@
public:
// crop can be an empty rect, does not have to line up with subsampling
// NOTE: we do not support floating-point crop
- inline const C2Rect crop() { return mCrop; }
+ inline const C2Rect crop() const { return mCrop; }
/**
* Sets crop to crop intersected with [(0,0) .. (width, height)]
*/
- inline void setCrop_be(const C2Rect &crop);
+ inline void setCrop_be(const C2Rect &crop) {
+ mCrop.mLeft = std::min(width(), crop.mLeft);
+ mCrop.mTop = std::min(height(), crop.mTop);
+ // It's guaranteed that mCrop.mLeft <= width() && mCrop.mTop <= height()
+ mCrop.mWidth = std::min(width() - mCrop.mLeft, crop.mWidth);
+ mCrop.mHeight = std::min(height() - mCrop.mTop, crop.mHeight);
+ }
/**
* If crop is within the dimensions of this object, it sets crop to it.
*
* \return true iff crop is within the dimensions of this object
*/
- inline bool setCrop(const C2Rect &crop);
+ inline bool setCrop(const C2Rect &crop) {
+ if (width() < crop.mWidth || height() < crop.mHeight
+ || width() - crop.mWidth < crop.mLeft || height() - crop.mHeight < crop.mTop) {
+ return false;
+ }
+ mCrop = crop;
+ return true;
+ }
+
+protected:
+ inline _C2PlanarSection(const _C2PlanarCapacityAspect *parent)
+ : _C2PlanarCapacityAspect(parent), mCrop(width(), height()) {}
private:
C2Rect mCrop;
/// @}
};
+class C2GraphicAllocation;
+
class C2Block2D : public _C2PlanarSection {
public:
const C2Handle *handle() const;
+protected:
+ C2Block2D(const std::shared_ptr<C2GraphicAllocation> &alloc);
+
private:
class Impl;
std::shared_ptr<Impl> mImpl;
@@ -961,14 +983,25 @@
class C2GraphicView : public _C2PlanarSection {
public:
/**
- * \return pointer to the start of the block or nullptr on error.
+ * \return array of pointers to the start of the planes or nullptr on error.
+ * Regardless of crop rect, they always point to the top-left corner of
+ * each plane. Access outside of the crop rect results in an undefined
+ * behavior.
*/
- const uint8_t *data() const;
+ const uint8_t *const *data() const;
/**
- * \return pointer to the start of the block or nullptr on error.
+ * \return array of pointers to the start of the planes or nullptr on error.
+ * Regardless of crop rect, they always point to the top-left corner of
+ * each plane. Access outside of the crop rect results in an undefined
+ * behavior.
*/
- uint8_t *data();
+ uint8_t *const *data();
+
+ /**
+ * \return layout of the graphic block to interpret the returned data.
+ */
+ const C2PlaneLayout layout() const;
/**
* Returns a section of this view.
@@ -985,6 +1018,13 @@
*/
C2Error error() const;
+protected:
+ C2GraphicView(
+ const _C2PlanarCapacityAspect *parent,
+ uint8_t *const *data,
+ const C2PlaneLayout& layout);
+ explicit C2GraphicView(C2Error error);
+
private:
class Impl;
std::shared_ptr<Impl> mImpl;
@@ -1022,7 +1062,12 @@
*/
C2Fence fence() const { return mFence; }
+protected:
+ C2ConstGraphicBlock(const std::shared_ptr<C2GraphicAllocation> &alloc, C2Fence fence);
+
private:
+ class Impl;
+ std::shared_ptr<Impl> mImpl;
C2Fence mFence;
};
@@ -1050,6 +1095,13 @@
* The block shall be modified only until firing the event for the fence.
*/
C2ConstGraphicBlock share(const C2Rect &crop, C2Fence fence);
+
+protected:
+ explicit C2GraphicBlock(const std::shared_ptr<C2GraphicAllocation> &alloc);
+
+private:
+ class Impl;
+ std::shared_ptr<Impl> mImpl;
};
/// @}
@@ -1115,7 +1167,8 @@
protected:
// no public constructor
- // C2BufferData(const std::shared_ptr<const Impl> &impl) : mImpl(impl) {}
+ explicit C2BufferData(const std::list<C2ConstLinearBlock> &blocks);
+ explicit C2BufferData(const std::list<C2ConstGraphicBlock> &blocks);
};
/**
@@ -1171,7 +1224,7 @@
* \retval C2_NO_MEMORY not enough memory to register for this callback
* \retval C2_CORRUPTED an unknown error prevented the registration (unexpected)
*/
- C2Error registerOnDestroyNotify(OnDestroyNotify *onDestroyNotify, void *arg = nullptr);
+ C2Error registerOnDestroyNotify(OnDestroyNotify onDestroyNotify, void *arg = nullptr);
/**
* Unregisters a previously registered pre-destroy notification.
@@ -1183,7 +1236,7 @@
* \retval C2_NOT_FOUND the notification was not found
* \retval C2_CORRUPTED an unknown error prevented the registration (unexpected)
*/
- C2Error unregisterOnDestroyNotify(OnDestroyNotify *onDestroyNotify, void *arg = nullptr);
+ C2Error unregisterOnDestroyNotify(OnDestroyNotify onDestroyNotify);
///@}
@@ -1219,14 +1272,17 @@
* \return true iff there is a metadata with the parameter type attached to this buffer.
*/
bool hasInfo(C2Param::Type index) const;
- std::shared_ptr<C2Info> removeInfo(C2Param::Type index) const;
+ std::shared_ptr<C2Info> removeInfo(C2Param::Type index);
///@}
protected:
// no public constructor
- inline C2Buffer() = default;
+ explicit C2Buffer(const std::list<C2ConstLinearBlock> &blocks);
+ explicit C2Buffer(const std::list<C2ConstGraphicBlock> &blocks);
private:
+ class Impl;
+ std::shared_ptr<Impl> mImpl;
// Type _mType;
};
@@ -1451,10 +1507,11 @@
/**
* Returns true if this is the same allocation as |other|.
*/
- virtual bool equals(const std::shared_ptr<const C2GraphicAllocation> &other) = 0;
+ virtual bool equals(const std::shared_ptr<const C2GraphicAllocation> &other) const = 0;
protected:
- virtual ~C2GraphicAllocation();
+ using _C2PlanarCapacityAspect::_C2PlanarCapacityAspect;
+ virtual ~C2GraphicAllocation() = default;
};
/**
diff --git a/media/libstagefright/codec2/include/C2Component.h b/media/libstagefright/codec2/include/C2Component.h
index f071423..94312fb 100644
--- a/media/libstagefright/codec2/include/C2Component.h
+++ b/media/libstagefright/codec2/include/C2Component.h
@@ -52,7 +52,7 @@
// virtual void onComponentReleased(<id>) = 0;
protected:
- virtual ~C2ComponentListener();
+ virtual ~C2ComponentListener() = default;
};
/**
diff --git a/media/libstagefright/codec2/include/C2Work.h b/media/libstagefright/codec2/include/C2Work.h
index a42d11a..a378623 100644
--- a/media/libstagefright/codec2/include/C2Work.h
+++ b/media/libstagefright/codec2/include/C2Work.h
@@ -60,9 +60,9 @@
typedef uint32_t node_id;
enum flags_t : uint32_t {
- BUFFERFLAG_CODEC_CONFIG,
- BUFFERFLAG_DROP_FRAME,
- BUFFERFLAG_END_OF_STREAM,
+ BUFFERFLAG_CODEC_CONFIG = (1 << 0),
+ BUFFERFLAG_DROP_FRAME = (1 << 1),
+ BUFFERFLAG_END_OF_STREAM = (1 << 2),
};
enum {
diff --git a/media/libstagefright/codec2/tests/Android.bp b/media/libstagefright/codec2/tests/Android.bp
index a8a6565..8a18f7d 100644
--- a/media/libstagefright/codec2/tests/Android.bp
+++ b/media/libstagefright/codec2/tests/Android.bp
@@ -15,14 +15,49 @@
include_dirs: [
"frameworks/av/media/libstagefright/codec2/include",
"frameworks/av/media/libstagefright/codec2/vndk/include",
+ ],
+
+ shared_libs: [
+ "android.hardware.graphics.allocator@2.0",
+ "android.hardware.graphics.mapper@2.0",
+ "libcutils",
+ "libhidlbase",
+ "libion",
+ "liblog",
+ "libstagefright_codec2",
+ "libutils",
+ ],
+
+ static_libs: [
+ "libstagefright_codec2_vndk",
+ ],
+
+ cflags: [
+ "-Werror",
+ "-Wall",
+ "-std=c++14",
+ ],
+}
+
+cc_test {
+ name: "codec2_interface_test",
+
+ tags: [
+ "tests",
+ ],
+
+ srcs: [
+ "C2ComponentInterface_test.cpp",
+ ],
+
+ include_dirs: [
+ "frameworks/av/media/libstagefright/codec2/include",
+ "frameworks/av/media/libstagefright/codec2/vndk/include",
"frameworks/native/include/media/openmax",
],
shared_libs: [
"libcutils",
- "liblog",
- "libstagefright_codec2",
- "libcutils",
"libhidlbase",
"libion",
"liblog",
diff --git a/media/libstagefright/codec2/tests/C2ComponentInterface_test.cpp b/media/libstagefright/codec2/tests/C2ComponentInterface_test.cpp
new file mode 100644
index 0000000..0c8ca3e
--- /dev/null
+++ b/media/libstagefright/codec2/tests/C2ComponentInterface_test.cpp
@@ -0,0 +1,708 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "C2ComponentInterface_test"
+
+#include <dlfcn.h>
+#include <stdio.h>
+
+#include <gtest/gtest.h>
+#include <utils/Log.h>
+
+#include <C2Component.h>
+#include <C2Param.h>
+
+#if !defined(UNUSED)
+#define UNUSED(expr) \
+ do { \
+ (void)(expr); \
+ } while (0)
+
+#endif //!defined(UNUSED)
+
+namespace android {
+
+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);
+ memcpy(ptr->m.mValue, cstr, len);
+ return ptr;
+}
+
+class C2CompIntfTest : public ::testing::Test {
+protected:
+ C2CompIntfTest() {}
+ ~C2CompIntfTest() override {}
+
+ void setComponent(std::shared_ptr<C2ComponentInterface> intf) {
+ mIntf = intf;
+ }
+
+ void resetResults() {
+ mIntf = nullptr;
+ mParamResults.clear();
+ }
+
+ template <typename T> void testUnsupportedParam();
+
+ template <typename T> void testSupportedParam();
+
+ // testReadOnlyParam() and testWritableParam() are the main functions for testing a parameter.
+ // A caller should find out if a tested parameter is read-only or writable before calling them
+ // and it must call one of the corresponded them.
+
+ // If a parameter is read-only this is called.
+ // Test read-only parameter |preParam|. The test expects failure while config() with |newParam|,
+ // and make sure |preParam| stay unchanged.
+ template <typename T>
+ void testReadOnlyParam(const T &preParam, const T &newParam);
+
+ // If a parameter is writable this is called.
+ // Test one filed |writableField| for given writable parameter |param|.
+ // |validValues| contains all values obtained from getSupportedValues() for |writableField|.
+ // The test checks validity for config() with each value, and make sure values are config-ed
+ // by query() them out. |invalidValues| contains some values which are not in |validValues|.
+ // The test expects C2_BAD_VALUE while config() with these values,
+ // and |param| should stay unchanged.
+ template <typename TParam, typename TRealField, typename TField>
+ void testWritableParam(TParam *const param, TRealField *const writableField,
+ const std::vector<TField> &validValues,
+ const std::vector<TField> &invalidValues);
+
+ // Test all the defined parameters in C2Param.h.
+ void testMain(std::shared_ptr<C2ComponentInterface> intf,
+ const std::string &componentName);
+
+ // Check permission of parameter type |T| for testing interface.
+ // This should be called first of the testing per parameter type,
+ // therefore different testing process is applied according to the permission type.
+ template <typename T>
+ void checkParamPermission(
+ int *const writable,
+ const std::vector<std::shared_ptr<C2ParamDescriptor>> &supportedParams);
+
+private:
+ enum ParamPermission : int {
+ WRITABLE,
+ READONLY,
+ UNSUPPORTED,
+ };
+
+ struct paramTestInfo {
+ std::string name;
+ int result;
+ paramTestInfo(const char *name_, int result_)
+ : name(name_), result(result_) {}
+ };
+
+ // queryOnStack() and queryonHeap() both call an interface's query_nb() and
+ // check if a component has a parameter whose type is |T|.
+ // If a component has, the value should be copied into an argument, that is
+ // |p| in queryOnStack() and |heapParams| in queryOnHeap().
+ // The return value is status_t (e.g. C2_OK).
+ template <typename T> status_t queryOnStack(T *const p);
+
+ template <typename T>
+ status_t queryOnHeap(const T &p,
+ std::vector<std::unique_ptr<C2Param>> *const heapParams);
+
+ // Get a value whose type is |T| in a component. The value is copied to |param|.
+ // This should be called only if a component has the parameter.
+ template <typename T> void getValue(T *const param);
+
+ // Check if the parameter's value in component is equal to |expected| and
+ // queryOnStack() and queryOnHeap() are succeeded. When this function called,
+ // it should be guaranteed a component has the parameter.
+ template <typename T> void queryParamAsExpected(const T &expected);
+
+ // Test if query functions works correctly for supported parameters.
+ // "Support" means here a component has the parameter.
+ template <typename T> void querySupportedParam();
+
+ // Test query functions works correctly for unsupported parameters.
+ // "Unsupport" means here a component doesn't have the parameter.
+ template <typename T> void queryUnsupportedParam();
+
+ // Execute an interface's config_nb(). |T| is a single parameter type, not std::vector.
+ // config() creates std::vector<C2Param *const> {p} and passes it to config_nb().
+ template <typename T>
+ status_t
+ config(T *const p,
+ std::vector<std::unique_ptr<C2SettingResult>> *const failures);
+
+ // Test if config works correctly for read-only parameters.
+ // Because the failure of config() is assumed, |newParam| doesn't matter.
+ template <typename T> void configReadOnlyParam(const T &newParam);
+
+ // Test if config works correctly for writable parameters.
+ // This changes the parameter's value to |newParam|.
+ // |stConfig| is a return value of config().
+ template <typename T> void configWritableParamValidValue(const T &newParam, status_t *stConfig);
+
+ // Test if config works correctly in the case an invalid value |newParam| is tried to write
+ // to an writable parameter.
+ template <typename T> void configWritableParamInvalidValue(const T &newParam);
+
+ // Create values for testing from |validValueInfos|. The values are returned as arguments.
+ // |validValues| : valid values, which can be written for the parameter.
+ // |InvalidValues| : invalid values, which cannot be written for the parameter.
+ // config() should be failed if these values are used as new values.
+ // This function should be called only for writable and supported parameters.
+ template <typename TField>
+ void getTestValues(const std::vector<C2FieldSupportedValues> &validValueInfos,
+ std::vector<TField> *const validValues,
+ std::vector<TField> *const invalidValues);
+
+ // Output the summary of test results. Categorizes parameters with their configuration.
+ void outputResults(const std::string &name);
+
+ std::shared_ptr<C2ComponentInterface> mIntf;
+ std::vector<paramTestInfo> mParamResults;
+ std::string mCurrentParamName;
+};
+
+// factory function
+// TODO(hiroh): Add factory functions for other types.
+template <typename T> std::unique_ptr<T> makeParam() {
+ return std::make_unique<T>();
+}
+
+template <> std::unique_ptr<C2PortMimeConfig::input> makeParam() {
+ // TODO(hiroh): Set more precise length.
+ return C2PortMimeConfig::input::alloc_unique(100);
+}
+
+#define TRACED_FAILURE(func) \
+ do { \
+ SCOPED_TRACE(mCurrentParamName); \
+ func; \
+ if (::testing::Test::HasFatalFailure()) { \
+ return; \
+ } \
+ } while (false)
+
+template <typename T> status_t C2CompIntfTest::queryOnStack(T *const p) {
+ std::vector<C2Param *const> stackParams{p};
+ return mIntf->query_nb(stackParams, {}, nullptr);
+}
+
+template <typename T>
+status_t C2CompIntfTest::queryOnHeap(
+ const T &p, std::vector<std::unique_ptr<C2Param>> *const heapParams) {
+ uint32_t index = p.type();
+ if (p.forStream()) {
+ index |= ((p.stream() << 17) & 0x01FE0000) | 0x02000000;
+ }
+ return mIntf->query_nb({}, {index}, heapParams);
+}
+
+template <typename T> void C2CompIntfTest::getValue(T *const param) {
+ // When getValue() is called, a component has to have the parameter.
+ ASSERT_EQ(C2_OK, queryOnStack(param));
+}
+
+template <typename T>
+void C2CompIntfTest::queryParamAsExpected(const T &expected) {
+ // TODO(hiroh): Don't create param on stack and call queryOnStack for flex params.
+ // Note that all the current supported parameters are non-flex params.
+ T stack;
+ std::unique_ptr<T> pHeap = makeParam<T>();
+ std::vector<std::unique_ptr<C2Param>> heapParams;
+
+ ASSERT_EQ(C2_OK, queryOnStack(&stack));
+
+ // |stack| is a parameter value. The parameter size shouldn't be 0.
+ EXPECT_NE(0u, stack.size());
+ EXPECT_EQ(stack, expected);
+
+ ASSERT_EQ(C2_OK, queryOnHeap(*pHeap, &heapParams));
+
+ // |*heapParams[0]| is a parameter value. The size of |heapParams| has to be one.
+ ASSERT_EQ(1u, heapParams.size());
+ EXPECT_TRUE(heapParams[0]);
+ EXPECT_EQ(*heapParams[0], expected);
+}
+
+template <typename T> void C2CompIntfTest::querySupportedParam() {
+ std::unique_ptr<T> param = makeParam<T>();
+ // The current parameter's value is acquired by getValue(), which should be succeeded.
+ getValue(param.get());
+ queryParamAsExpected(*param);
+}
+
+template <typename T> void C2CompIntfTest::queryUnsupportedParam() {
+ // TODO(hiroh): Don't create param on stack and call queryOnStack for flex params.
+ // Note that all the current supported parameters are non-flex params.
+ T stack;
+ std::unique_ptr<T> pHeap = makeParam<T>();
+ std::vector<std::unique_ptr<C2Param>> heapParams;
+ // If a component doesn't have the parameter, queryOnStack() and queryOnHeap()
+ // should return C2_BAD_INDEX.
+ ASSERT_EQ(C2_BAD_INDEX, queryOnStack(&stack));
+ EXPECT_FALSE(stack);
+ ASSERT_EQ(C2_BAD_INDEX, queryOnHeap(*pHeap, &heapParams));
+ EXPECT_EQ(0u, heapParams.size());
+}
+
+template <typename T>
+status_t C2CompIntfTest::config(
+ T *const p, std::vector<std::unique_ptr<C2SettingResult>> *const failures) {
+ std::vector<C2Param *const> params{p};
+ return mIntf->config_nb(params, failures);
+}
+
+// Create a new parameter copied from |p|.
+template <typename T> std::unique_ptr<T> makeParamFrom(const T &p) {
+ std::unique_ptr<T> retP = makeParam<T>();
+ EXPECT_TRUE(retP->updateFrom(p));
+ EXPECT_TRUE(memcmp(retP.get(), &p, sizeof(T)) == 0);
+ return retP;
+}
+
+template <typename T>
+void C2CompIntfTest::configReadOnlyParam(const T &newParam) {
+ std::unique_ptr<T> p = makeParamFrom(newParam);
+
+ std::vector<C2Param *const> params{p.get()};
+ std::vector<std::unique_ptr<C2SettingResult>> failures;
+
+ // config_nb should be failed because a parameter is read-only.
+ ASSERT_EQ(C2_BAD_VALUE, mIntf->config_nb(params, &failures));
+ ASSERT_EQ(1u, failures.size());
+ EXPECT_EQ(C2SettingResult::READ_ONLY, failures[0]->failure);
+}
+
+template <typename T>
+void C2CompIntfTest::configWritableParamValidValue(const T &newParam, status_t *configResult) {
+ std::unique_ptr<T> p = makeParamFrom(newParam);
+
+ std::vector<C2Param *const> params{p.get()};
+ std::vector<std::unique_ptr<C2SettingResult>> failures;
+ // In most cases, config_nb return C2_OK and the parameter's value should be changed
+ // to |newParam|, which is confirmed in a caller of configWritableParamValueValue().
+ // However, this can return ~~~~ and the parameter's values is not changed,
+ // because there may be dependent limitations between fields or between parameters.
+ // TODO(hiroh): I have to fill the return value. Comments in C2Component.h doesn't mention
+ // about the return value when conflict happens. I set C2_BAD_VALUE to it temporarily now.
+ status_t stConfig = mIntf->config_nb(params, &failures);
+ if (stConfig == C2_OK) {
+ EXPECT_EQ(0u, failures.size());
+ } else {
+ ASSERT_EQ(C2_BAD_VALUE, stConfig);
+ EXPECT_EQ(1u, failures.size());
+ EXPECT_EQ(C2SettingResult::CONFLICT, failures[0]->failure);
+ }
+ *configResult = stConfig;
+}
+
+template <typename T>
+void C2CompIntfTest::configWritableParamInvalidValue(const T &newParam) {
+ std::unique_ptr<T> p = makeParamFrom(newParam);
+
+ std::vector<C2Param *const> params{p.get()};
+ std::vector<std::unique_ptr<C2SettingResult>> failures;
+ // Although a parameter is writable, config_nb should be failed,
+ // because a new value is invalid.
+ ASSERT_EQ(C2_BAD_VALUE, mIntf->config_nb(params, &failures));
+ ASSERT_EQ(1u, failures.size());
+ EXPECT_EQ(C2SettingResult::BAD_VALUE, failures[0]->failure);
+}
+
+// There is only used enum type for the field type, that is C2DomainKind.
+// If another field type is added, it is necessary to add function for that.
+template <>
+void C2CompIntfTest::getTestValues(
+ const std::vector<C2FieldSupportedValues> &validValueInfos,
+ std::vector<C2DomainKind> *const validValues,
+ std::vector<C2DomainKind> *const invalidValues) {
+ UNUSED(validValueInfos);
+ validValues->emplace_back(C2DomainVideo);
+ validValues->emplace_back(C2DomainAudio);
+ validValues->emplace_back(C2DomainOther);
+
+ // There is no invalid value.
+ UNUSED(invalidValues);
+}
+
+template <typename TField>
+void C2CompIntfTest::getTestValues(
+ const std::vector<C2FieldSupportedValues> &validValueInfos,
+ std::vector<TField> *const validValues,
+ std::vector<TField> *const invalidValues) {
+
+ // The supported values are represented by C2Values. C2Value::Primitive needs to
+ // be transformed to a primitive value. This function is one to do that.
+ auto prim2Value = [](const C2Value::Primitive &prim) -> TField {
+ if (std::is_same<TField, int32_t>::value) {
+ return prim.i32;
+ } else if (std::is_same<TField, uint32_t>::value) {
+ return prim.u32;
+ } else if (std::is_same<TField, int64_t>::value) {
+ return prim.i64;
+ } else if (std::is_same<TField, uint64_t>::value) {
+ return prim.u64;
+ } else if (std::is_same<TField, float>::value) {
+ return prim.fp;
+ }
+ static_assert(std::is_same<TField, int32_t>::value ||
+ std::is_same<TField, uint32_t>::value ||
+ std::is_same<TField, int64_t>::value ||
+ std::is_same<TField, uint64_t>::value ||
+ std::is_same<TField, float>::value, "Invalid TField type.");
+ return 0;
+ };
+
+ // The size of validValueInfos is one.
+ const auto &c2FSV = validValueInfos[0];
+
+ switch (c2FSV.type) {
+ case C2FieldSupportedValues::Type::RANGE: {
+ const auto &range = c2FSV.range;
+ auto rmin = prim2Value(range.min);
+ auto rmax = prim2Value(range.max);
+ auto rstep = prim2Value(range.step);
+
+ ASSERT_LE(rmin, rmax);
+
+ if (rstep != 0) {
+ // Increase linear
+ for (auto v = rmin; v <= rmax; v += rstep) {
+ validValues->emplace_back(v);
+ }
+ if (rmin > std::numeric_limits<TField>::min()) {
+ invalidValues->emplace_back(rmin - 1);
+ }
+ if (rmax < std::numeric_limits<TField>::max()) {
+ invalidValues->emplace_back(rmax + 1);
+ }
+ const unsigned int N = validValues->size();
+ if (N >= 2) {
+ if (std::is_same<TField, float>::value) {
+ invalidValues->emplace_back((validValues->at(0) + validValues->at(1)) / 2);
+ invalidValues->emplace_back((validValues->at(N - 2) + validValues->at(N - 1)) / 2);
+ } else {
+ if (rstep > 1) {
+ invalidValues->emplace_back(validValues->at(0) + 1);
+ invalidValues->emplace_back(validValues->at(N - 1) - 1);
+ }
+ }
+ }
+ } else {
+ // There should be two cases, except linear case.
+ // 1. integer geometric case
+ // 2. float geometric case
+
+ auto nom = prim2Value(range.nom);
+ auto denom = prim2Value(range.denom);
+
+ // If both range.nom and range.denom are 1 and step is 0, we should use
+ // VALUES, shouldn't we?
+ ASSERT_FALSE(nom == 1 && denom == 1);
+
+ // (nom / denom) is not less than 1.
+ ASSERT_FALSE(denom == 0);
+ ASSERT_LE(denom, nom);
+ for (auto v = rmin; v <= rmax; v = v * nom / denom) {
+ validValues->emplace_back(v);
+ }
+
+ if (rmin > std::numeric_limits<TField>::min()) {
+ invalidValues->emplace_back(rmin - 1);
+ }
+ if (rmax < std::numeric_limits<TField>::max()) {
+ invalidValues->emplace_back(rmax + 1);
+ }
+
+ const unsigned int N = validValues->size();
+ if (N >= 2) {
+ if (std::is_same<TField, float>::value) {
+ invalidValues->emplace_back((validValues->at(0) + validValues->at(1)) / 2);
+ invalidValues->emplace_back((validValues->at(N - 2) + validValues->at(N - 1)) / 2);
+ } else {
+ if (validValues->at(1) - validValues->at(0) > 1) {
+ invalidValues->emplace_back(validValues->at(0) + 1);
+ }
+ if (validValues->at(N - 1) - validValues->at(N - 2) > 1) {
+ invalidValues->emplace_back(validValues->at(N - 1) - 1);
+ }
+ }
+ }
+ }
+ break;
+ }
+ case C2FieldSupportedValues::Type::VALUES: {
+ for (const C2Value::Primitive &prim : c2FSV.values) {
+ validValues->emplace_back(prim2Value(prim));
+ }
+ auto minv = *std::min_element(validValues->begin(), validValues->end());
+ auto maxv = *std::max_element(validValues->begin(), validValues->end());
+ if (minv - 1 > std::numeric_limits<TField>::min()) {
+ invalidValues->emplace_back(minv - 1);
+ }
+ if (maxv + 1 < std::numeric_limits<TField>::max()) {
+ invalidValues->emplace_back(maxv + 1);
+ }
+ break;
+ }
+ case C2FieldSupportedValues::Type::FLAGS: {
+ // TODO(hiroh) : Implement the case that param.type is FLAGS.
+ break;
+ }
+ }
+}
+
+template <typename T>
+void C2CompIntfTest::testReadOnlyParam(const T &preParam, const T &newParam) {
+ TRACED_FAILURE(configReadOnlyParam(newParam));
+ // Parameter value must not be changed
+ TRACED_FAILURE(queryParamAsExpected(preParam));
+}
+
+template <typename TParam, typename TRealField, typename TField>
+void C2CompIntfTest::testWritableParam(
+ TParam *const param, TRealField *const writableField,
+ const std::vector<TField> &validValues,
+ const std::vector<TField> &invalidValues) {
+ status_t stConfig;
+
+ // Get the parameter's value in the beginning in order to reset the value at the end.
+ TRACED_FAILURE(getValue(param));
+ std::unique_ptr<TParam> defaultParam = makeParamFrom(*param);
+
+ // Test valid values
+ for (const auto &val : validValues) {
+ std::unique_ptr<TParam> preParam = makeParamFrom(*param);
+
+ // Param is try to be changed
+ *writableField = val;
+ TRACED_FAILURE(configWritableParamValidValue(*param, &stConfig));
+ if (stConfig == C2_OK) {
+ TRACED_FAILURE(queryParamAsExpected(*param));
+ } else {
+ // Param is unchanged because a field value conflicts with other field or parameter.
+ TRACED_FAILURE(queryParamAsExpected(*preParam));
+ }
+ }
+
+ // Store the current parameter in order to test |param| is unchanged
+ // after trying to write an invalid value.
+ std::unique_ptr<TParam> lastValidParam = makeParamFrom(*param);
+
+ // Test invalid values
+ for (const auto &val : invalidValues) {
+ // Param is changed
+ *writableField = val;
+ TRACED_FAILURE(configWritableParamInvalidValue(*param));
+ TRACED_FAILURE(queryParamAsExpected(*lastValidParam));
+ }
+ // Reset the parameter by config().
+ TRACED_FAILURE(configWritableParamValidValue(*defaultParam, &stConfig));
+}
+
+template <typename T> void C2CompIntfTest::testUnsupportedParam() {
+ TRACED_FAILURE(queryUnsupportedParam<T>());
+}
+
+template <typename T> void C2CompIntfTest::testSupportedParam() {
+ TRACED_FAILURE(querySupportedParam<T>());
+}
+
+bool isSupportedParam(
+ const C2Param ¶m,
+ const std::vector<std::shared_ptr<C2ParamDescriptor>> &sParams) {
+ for (const auto &pd : sParams) {
+ if (param.type() == pd->type().type()) {
+ return true;
+ }
+ }
+ return false;
+}
+
+template <typename T>
+void C2CompIntfTest::checkParamPermission(
+ int *const result,
+ const std::vector<std::shared_ptr<C2ParamDescriptor>> &supportedParams) {
+ std::unique_ptr<T> param = makeParam<T>();
+
+ if (!isSupportedParam(*param, supportedParams)) {
+ // If a parameter isn't supported, it just finish after calling testUnsupportedParam().
+ testUnsupportedParam<T>();
+ *result = ParamPermission::UNSUPPORTED;
+ return;
+ }
+
+ testSupportedParam<T>();
+
+ TRACED_FAILURE(getValue(param.get()));
+ std::vector<std::unique_ptr<C2SettingResult>> failures;
+ // Config does not change the parameter, because param is the present param.
+ // This config is executed to find out if a parameter is read-only or writable.
+ status_t stStack = config(param.get(), &failures);
+ if (stStack == C2_BAD_VALUE) {
+ // Read-only
+ std::unique_ptr<T> newParam = makeParam<T>();
+ testReadOnlyParam(*param, *newParam);
+ *result = ParamPermission::READONLY;
+ } else {
+ // Writable
+ EXPECT_EQ(stStack, C2_OK);
+ *result = ParamPermission::WRITABLE;
+ }
+}
+
+void C2CompIntfTest::outputResults(const std::string &name) {
+ std::vector<std::string> params[3];
+ for (const auto &testInfo : mParamResults) {
+ int result = testInfo.result;
+ ASSERT_TRUE(0 <= result && result <= 2);
+ params[result].emplace_back(testInfo.name);
+ }
+ const char *resultString[] = {"Writable", "Read-Only", "Unsupported"};
+ printf("\n----TEST RESULTS (%s)----\n\n", name.c_str());
+ for (int i = 0; i < 3; i++) {
+ printf("[ %s ]\n", resultString[i]);
+ for (const auto &t : params[i]) {
+ printf("%s\n", t.c_str());
+ }
+ printf("\n");
+ }
+}
+
+#define TEST_GENERAL_WRITABLE_FIELD(TParam_, field_type_name_, field_name_) \
+ do { \
+ std::unique_ptr<TParam_> param = makeParam<TParam_>(); \
+ std::vector<C2FieldSupportedValues> validValueInfos; \
+ ASSERT_EQ(C2_OK, \
+ mIntf->getSupportedValues( \
+ {C2ParamField(param.get(), &field_type_name_::field_name_)}, \
+ &validValueInfos)); \
+ ASSERT_EQ(1u, validValueInfos.size()); \
+ std::vector<decltype(param->field_name_)> validValues; \
+ std::vector<decltype(param->field_name_)> invalidValues; \
+ getTestValues(validValueInfos, &validValues, &invalidValues); \
+ testWritableParam(param.get(), ¶m->field_name_, validValues,\
+ invalidValues); \
+ } while (0)
+
+#define TEST_VSSTRUCT_WRITABLE_FIELD(TParam_, field_type_name_) \
+ do { \
+ TEST_GENERAL_WRITABLE_FIELD(TParam_, field_type_name_, mWidth); \
+ TEST_GENERAL_WRITABLE_FIELD(TParam_, field_type_name_, mHeight);\
+ } while (0)
+
+#define TEST_U32_WRITABLE_FIELD(TParam_, field_type_name_) \
+ TEST_GENERAL_WRITABLE_FIELD(TParam_, field_type_name_, mValue)
+
+#define TEST_ENUM_WRITABLE_FIELD(TParam_, field_type_name_) \
+ TEST_GENERAL_WRITABLE_FIELD(TParam_, field_type_name_, mValue)
+
+// TODO(hiroh): Support parameters based on char[] and uint32_t[].
+//#define TEST_STRING_WRITABLE_FIELD(TParam_, field_type_name_)
+// TEST_GENERAL_WRITABLE_FIELD(TParam_, field_type_name_, m.mValue)
+//#define TEST_U32ARRAY_WRITABLE_FIELD(Tparam_, field_type_name_)
+// TEST_GENERAL_WRITABLE_FIELD(Tparam_, uint32_t[], field_type_name_, mValues)
+
+#define EACH_TEST(TParam_, field_type_name_, test_name) \
+ do { \
+ int result = 0; \
+ this->mCurrentParamName = #TParam_; \
+ checkParamPermission<TParam_>(&result, supportedParams); \
+ if (result == ParamPermission::WRITABLE) { \
+ test_name(TParam_, field_type_name_); \
+ } \
+ mParamResults.emplace_back(#TParam_, result); \
+ } while (0)
+
+#define EACH_TEST_SELF(type_, test_name) EACH_TEST(type_, type_, test_name)
+#define EACH_TEST_INPUT(type_, test_name) EACH_TEST(type_::input, type_, test_name)
+#define EACH_TEST_OUTPUT(type_, test_name) EACH_TEST(type_::output, type_, test_name)
+void C2CompIntfTest::testMain(std::shared_ptr<C2ComponentInterface> intf,
+ const std::string &componentName) {
+ setComponent(intf);
+
+ std::vector<std::shared_ptr<C2ParamDescriptor>> supportedParams;
+ ASSERT_EQ(C2_OK, mIntf->getSupportedParams(&supportedParams));
+
+ EACH_TEST_SELF(C2ComponentLatencyInfo, TEST_U32_WRITABLE_FIELD);
+ EACH_TEST_SELF(C2ComponentTemporalInfo, TEST_U32_WRITABLE_FIELD);
+ EACH_TEST_INPUT(C2PortLatencyInfo, TEST_U32_WRITABLE_FIELD);
+ EACH_TEST_OUTPUT(C2PortLatencyInfo, TEST_U32_WRITABLE_FIELD);
+ EACH_TEST_INPUT(C2StreamFormatConfig, TEST_U32_WRITABLE_FIELD);
+ EACH_TEST_OUTPUT(C2StreamFormatConfig, TEST_U32_WRITABLE_FIELD);
+ EACH_TEST_INPUT(C2PortStreamCountConfig, TEST_U32_WRITABLE_FIELD);
+ EACH_TEST_OUTPUT(C2PortStreamCountConfig, TEST_U32_WRITABLE_FIELD);
+
+ EACH_TEST_SELF(C2ComponentDomainInfo, TEST_ENUM_WRITABLE_FIELD);
+
+ // TODO(hiroh): Support parameters based on uint32_t[] and char[].
+ // EACH_TEST_INPUT(C2PortMimeConfig, TEST_STRING_WRITABLE_FIELD);
+ // EACH_TEST_OUTPUT(C2PortMimeConfig, TEST_STRING_WRITABLE_FIELD);
+ // EACH_TEST_INPUT(C2StreamMimeConfig, TEST_STRING_WRITABLE_FIELD);
+ // EACH_TEST_OUTPUT(C2StreamMimeConfig, TEST_STRING_WRITABLE_FIELD);
+
+ // EACH_TEST_SELF(C2SupportedParamsInfo, TEST_U32ARRAY_WRITABLE_FIELD);
+ // EACH_TEST_SELF(C2RequiredParamsInfo, TEST_U32ARRAY_WRITABLE_FIELD);
+ // EACH_TEST_SELF(C2ReadOnlyParamsInfo, TEST_U32ARRAY_WRITABLE_FIELD);
+ // EACH_TEST_SELF(C2RequestedInfosInfo, TEST_U32ARRAY_WRITABLE_FIELD);
+
+ EACH_TEST_INPUT(C2VideoSizeStreamInfo, TEST_VSSTRUCT_WRITABLE_FIELD);
+ EACH_TEST_OUTPUT(C2VideoSizeStreamInfo, TEST_VSSTRUCT_WRITABLE_FIELD);
+ EACH_TEST_INPUT(C2VideoSizeStreamTuning, TEST_VSSTRUCT_WRITABLE_FIELD);
+ EACH_TEST_OUTPUT(C2VideoSizeStreamTuning, TEST_VSSTRUCT_WRITABLE_FIELD);
+ EACH_TEST_INPUT(C2MaxVideoSizeHintPortSetting, TEST_VSSTRUCT_WRITABLE_FIELD);
+ EACH_TEST_OUTPUT(C2MaxVideoSizeHintPortSetting, TEST_VSSTRUCT_WRITABLE_FIELD);
+
+ outputResults(componentName);
+ resetResults();
+}
+
+TEST_F(C2CompIntfTest, C2V4L2CodecIntf) {
+
+ // Read a shared object library.
+ void* compLib = dlopen("system/lib/libv4l2_codec2.so", RTLD_NOW);
+
+ if (!compLib) {
+ printf("Cannot open library: %s.\n", dlerror());
+ FAIL();
+ return;
+ }
+
+ typedef C2ComponentStore* create_t();
+ create_t* create_store= (create_t*) dlsym(compLib, "create_store");
+ const char* dlsym_error = dlerror();
+ if (dlsym_error) {
+ printf("Cannot load symbol create: %s.\n", dlsym_error);
+ FAIL();
+ return;
+ }
+
+ typedef void destroy_t(C2ComponentStore*);
+ destroy_t* destroy_store = (destroy_t*) dlsym(compLib, "destroy_store");
+ dlsym_error = dlerror();
+ if (dlsym_error) {
+ printf("Cannot load symbol destroy: %s.\n", dlsym_error);
+ FAIL();
+ return;
+ }
+
+ std::shared_ptr<C2ComponentStore> componentStore(create_store(), destroy_store);
+ std::shared_ptr<C2ComponentInterface> componentIntf;
+ componentStore->createInterface("v4l2.decoder", &componentIntf);
+ auto componentName = "C2V4L2Codec";
+ testMain(componentIntf, componentName);
+}
+
+} // namespace android
diff --git a/media/libstagefright/codec2/tests/vndk/C2BufferTest.cpp b/media/libstagefright/codec2/tests/vndk/C2BufferTest.cpp
index 0ba3cad..97c4a7d 100644
--- a/media/libstagefright/codec2/tests/vndk/C2BufferTest.cpp
+++ b/media/libstagefright/codec2/tests/vndk/C2BufferTest.cpp
@@ -18,6 +18,7 @@
#include <C2Buffer.h>
#include <C2BufferPriv.h>
+#include <C2ParamDef.h>
#include <system/graphics.h>
@@ -26,27 +27,28 @@
class C2BufferTest : public ::testing::Test {
public:
C2BufferTest()
- : mAllocator(std::make_shared<C2AllocatorIon>()),
+ : mLinearAllocator(std::make_shared<C2AllocatorIon>()),
mSize(0u),
- mAddr(nullptr) {
+ mAddr(nullptr),
+ mGraphicAllocator(std::make_shared<C2AllocatorGralloc>()) {
}
~C2BufferTest() = default;
- void allocate(size_t capacity) {
- C2Error err = mAllocator->allocateLinearBuffer(
+ void allocateLinear(size_t capacity) {
+ C2Error err = mLinearAllocator->allocateLinearBuffer(
capacity,
{ C2MemoryUsage::kSoftwareRead, C2MemoryUsage::kSoftwareWrite },
- &mAllocation);
+ &mLinearAllocation);
if (err != C2_OK) {
- mAllocation.reset();
+ mLinearAllocation.reset();
FAIL() << "C2Allocator::allocateLinearBuffer() failed: " << err;
}
}
- void map(size_t offset, size_t size, uint8_t **addr) {
- ASSERT_TRUE(mAllocation);
- C2Error err = mAllocation->map(
+ void mapLinear(size_t offset, size_t size, uint8_t **addr) {
+ ASSERT_TRUE(mLinearAllocation);
+ C2Error err = mLinearAllocation->map(
offset,
size,
{ C2MemoryUsage::kSoftwareRead, C2MemoryUsage::kSoftwareWrite },
@@ -62,45 +64,89 @@
*addr = (uint8_t *)mAddr;
}
- void unmap() {
- ASSERT_TRUE(mAllocation);
+ void unmapLinear() {
+ ASSERT_TRUE(mLinearAllocation);
ASSERT_NE(nullptr, mAddr);
ASSERT_NE(0u, mSize);
// TODO: fence
- ASSERT_EQ(C2_OK, mAllocation->unmap(mAddr, mSize, nullptr));
+ ASSERT_EQ(C2_OK, mLinearAllocation->unmap(mAddr, mSize, nullptr));
mSize = 0u;
mAddr = nullptr;
}
- std::shared_ptr<C2BlockAllocator> makeBlockAllocator() {
- return std::make_shared<C2DefaultBlockAllocator>(mAllocator);
+ std::shared_ptr<C2BlockAllocator> makeLinearBlockAllocator() {
+ return std::make_shared<C2DefaultBlockAllocator>(mLinearAllocator);
+ }
+
+ void allocateGraphic(uint32_t width, uint32_t height) {
+ C2Error err = mGraphicAllocator->allocateGraphicBuffer(
+ width,
+ height,
+ HAL_PIXEL_FORMAT_YCBCR_420_888,
+ { C2MemoryUsage::kSoftwareRead, C2MemoryUsage::kSoftwareWrite },
+ &mGraphicAllocation);
+ if (err != C2_OK) {
+ mGraphicAllocation.reset();
+ FAIL() << "C2Allocator::allocateLinearBuffer() failed: " << err;
+ }
+ }
+
+ void mapGraphic(C2Rect rect, C2PlaneLayout *layout, uint8_t **addr) {
+ ASSERT_TRUE(mGraphicAllocation);
+ C2Error err = mGraphicAllocation->map(
+ rect,
+ { C2MemoryUsage::kSoftwareRead, C2MemoryUsage::kSoftwareWrite },
+ // TODO: fence
+ nullptr,
+ layout,
+ addr);
+ if (err != C2_OK) {
+ addr[C2PlaneLayout::Y] = nullptr;
+ addr[C2PlaneLayout::U] = nullptr;
+ addr[C2PlaneLayout::V] = nullptr;
+ FAIL() << "C2GraphicAllocation::map() failed: " << err;
+ }
+ }
+
+ void unmapGraphic() {
+ ASSERT_TRUE(mGraphicAllocation);
+
+ // TODO: fence
+ ASSERT_EQ(C2_OK, mGraphicAllocation->unmap(nullptr));
+ }
+
+ std::shared_ptr<C2BlockAllocator> makeGraphicBlockAllocator() {
+ return std::make_shared<C2DefaultGraphicBlockAllocator>(mGraphicAllocator);
}
private:
- std::shared_ptr<C2Allocator> mAllocator;
- std::shared_ptr<C2LinearAllocation> mAllocation;
+ std::shared_ptr<C2Allocator> mLinearAllocator;
+ std::shared_ptr<C2LinearAllocation> mLinearAllocation;
size_t mSize;
void *mAddr;
+
+ std::shared_ptr<C2Allocator> mGraphicAllocator;
+ std::shared_ptr<C2GraphicAllocation> mGraphicAllocation;
};
TEST_F(C2BufferTest, LinearAllocationTest) {
constexpr size_t kCapacity = 1024u * 1024u;
- allocate(kCapacity);
+ allocateLinear(kCapacity);
uint8_t *addr = nullptr;
- map(0u, kCapacity, &addr);
+ mapLinear(0u, kCapacity, &addr);
ASSERT_NE(nullptr, addr);
for (size_t i = 0; i < kCapacity; ++i) {
addr[i] = i % 100u;
}
- unmap();
+ unmapLinear();
addr = nullptr;
- map(kCapacity / 3, kCapacity / 3, &addr);
+ mapLinear(kCapacity / 3, kCapacity / 3, &addr);
ASSERT_NE(nullptr, addr);
for (size_t i = 0; i < kCapacity / 3; ++i) {
ASSERT_EQ((i + kCapacity / 3) % 100, addr[i]) << " at i = " << i;
@@ -110,7 +156,7 @@
TEST_F(C2BufferTest, BlockAllocatorTest) {
constexpr size_t kCapacity = 1024u * 1024u;
- std::shared_ptr<C2BlockAllocator> blockAllocator(makeBlockAllocator());
+ std::shared_ptr<C2BlockAllocator> blockAllocator(makeLinearBlockAllocator());
std::shared_ptr<C2LinearBlock> block;
ASSERT_EQ(C2_OK, blockAllocator->allocateLinearBlock(
@@ -161,4 +207,324 @@
}
}
+void fillPlane(const C2Rect rect, const C2PlaneInfo info, uint8_t *addr, uint8_t value) {
+ for (uint32_t row = 0; row < rect.mHeight / info.mVertSubsampling; ++row) {
+ int32_t rowOffset = (row + rect.mTop / info.mVertSubsampling) * info.mRowInc;
+ for (uint32_t col = 0; col < rect.mWidth / info.mHorizSubsampling; ++col) {
+ int32_t colOffset = (col + rect.mLeft / info.mHorizSubsampling) * info.mColInc;
+ addr[rowOffset + colOffset] = value;
+ }
+ }
+}
+
+bool verifyPlane(const C2Rect rect, const C2PlaneInfo info, const uint8_t *addr, uint8_t value) {
+ for (uint32_t row = 0; row < rect.mHeight / info.mVertSubsampling; ++row) {
+ int32_t rowOffset = (row + rect.mTop / info.mVertSubsampling) * info.mRowInc;
+ for (uint32_t col = 0; col < rect.mWidth / info.mHorizSubsampling; ++col) {
+ int32_t colOffset = (col + rect.mLeft / info.mHorizSubsampling) * info.mColInc;
+ if (addr[rowOffset + colOffset] != value) {
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+TEST_F(C2BufferTest, GraphicAllocationTest) {
+ constexpr uint32_t kWidth = 320;
+ constexpr uint32_t kHeight = 240;
+
+ allocateGraphic(kWidth, kHeight);
+
+ uint8_t *addr[C2PlaneLayout::MAX_NUM_PLANES];
+ C2Rect rect{ 0, 0, kWidth, kHeight };
+ C2PlaneLayout layout;
+ mapGraphic(rect, &layout, addr);
+ ASSERT_NE(nullptr, addr[C2PlaneLayout::Y]);
+ ASSERT_NE(nullptr, addr[C2PlaneLayout::U]);
+ ASSERT_NE(nullptr, addr[C2PlaneLayout::V]);
+
+ uint8_t *y = addr[C2PlaneLayout::Y];
+ C2PlaneInfo yInfo = layout.mPlanes[C2PlaneLayout::Y];
+ uint8_t *u = addr[C2PlaneLayout::U];
+ C2PlaneInfo uInfo = layout.mPlanes[C2PlaneLayout::U];
+ uint8_t *v = addr[C2PlaneLayout::V];
+ C2PlaneInfo vInfo = layout.mPlanes[C2PlaneLayout::V];
+
+ fillPlane(rect, yInfo, y, 0);
+ fillPlane(rect, uInfo, u, 0);
+ fillPlane(rect, vInfo, v, 0);
+ fillPlane({ kWidth / 4, kHeight / 4, kWidth / 2, kHeight / 2 }, yInfo, y, 0x12);
+ fillPlane({ kWidth / 4, kHeight / 4, kWidth / 2, kHeight / 2 }, uInfo, u, 0x34);
+ fillPlane({ kWidth / 4, kHeight / 4, kWidth / 2, kHeight / 2 }, vInfo, v, 0x56);
+
+ unmapGraphic();
+
+ mapGraphic(rect, &layout, addr);
+ ASSERT_NE(nullptr, addr[C2PlaneLayout::Y]);
+ ASSERT_NE(nullptr, addr[C2PlaneLayout::U]);
+ ASSERT_NE(nullptr, addr[C2PlaneLayout::V]);
+
+ y = addr[C2PlaneLayout::Y];
+ yInfo = layout.mPlanes[C2PlaneLayout::Y];
+ u = addr[C2PlaneLayout::U];
+ uInfo = layout.mPlanes[C2PlaneLayout::U];
+ v = addr[C2PlaneLayout::V];
+ vInfo = layout.mPlanes[C2PlaneLayout::V];
+
+ ASSERT_TRUE(verifyPlane({ kWidth / 4, kHeight / 4, kWidth / 2, kHeight / 2 }, yInfo, y, 0x12));
+ ASSERT_TRUE(verifyPlane({ kWidth / 4, kHeight / 4, kWidth / 2, kHeight / 2 }, uInfo, u, 0x34));
+ ASSERT_TRUE(verifyPlane({ kWidth / 4, kHeight / 4, kWidth / 2, kHeight / 2 }, vInfo, v, 0x56));
+ ASSERT_TRUE(verifyPlane({ 0, 0, kWidth, kHeight / 4 }, yInfo, y, 0));
+ ASSERT_TRUE(verifyPlane({ 0, 0, kWidth, kHeight / 4 }, uInfo, u, 0));
+ ASSERT_TRUE(verifyPlane({ 0, 0, kWidth, kHeight / 4 }, vInfo, v, 0));
+ ASSERT_TRUE(verifyPlane({ 0, 0, kWidth / 4, kHeight }, yInfo, y, 0));
+ ASSERT_TRUE(verifyPlane({ 0, 0, kWidth / 4, kHeight }, uInfo, u, 0));
+ ASSERT_TRUE(verifyPlane({ 0, 0, kWidth / 4, kHeight }, vInfo, v, 0));
+}
+
+TEST_F(C2BufferTest, GraphicBlockAllocatorTest) {
+ constexpr uint32_t kWidth = 320;
+ constexpr uint32_t kHeight = 240;
+
+ std::shared_ptr<C2BlockAllocator> blockAllocator(makeGraphicBlockAllocator());
+
+ std::shared_ptr<C2GraphicBlock> block;
+ ASSERT_EQ(C2_OK, blockAllocator->allocateGraphicBlock(
+ kWidth,
+ kHeight,
+ HAL_PIXEL_FORMAT_YCBCR_420_888,
+ { C2MemoryUsage::kSoftwareRead, C2MemoryUsage::kSoftwareWrite },
+ &block));
+ ASSERT_TRUE(block);
+
+ C2Acquirable<C2GraphicView> graphicViewHolder = block->map();
+ C2GraphicView graphicView = graphicViewHolder.get();
+ ASSERT_EQ(C2_OK, graphicView.error());
+ ASSERT_EQ(kWidth, graphicView.width());
+ ASSERT_EQ(kHeight, graphicView.height());
+
+ uint8_t *const *data = graphicView.data();
+ C2PlaneLayout layout = graphicView.layout();
+ ASSERT_NE(nullptr, data);
+
+ uint8_t *y = data[C2PlaneLayout::Y];
+ C2PlaneInfo yInfo = layout.mPlanes[C2PlaneLayout::Y];
+ uint8_t *u = data[C2PlaneLayout::U];
+ C2PlaneInfo uInfo = layout.mPlanes[C2PlaneLayout::U];
+ uint8_t *v = data[C2PlaneLayout::V];
+ C2PlaneInfo vInfo = layout.mPlanes[C2PlaneLayout::V];
+
+ fillPlane({ 0, 0, kWidth, kHeight }, yInfo, y, 0);
+ fillPlane({ 0, 0, kWidth, kHeight }, uInfo, u, 0);
+ fillPlane({ 0, 0, kWidth, kHeight }, vInfo, v, 0);
+ fillPlane({ kWidth / 4, kHeight / 4, kWidth / 2, kHeight / 2 }, yInfo, y, 0x12);
+ fillPlane({ kWidth / 4, kHeight / 4, kWidth / 2, kHeight / 2 }, uInfo, u, 0x34);
+ fillPlane({ kWidth / 4, kHeight / 4, kWidth / 2, kHeight / 2 }, vInfo, v, 0x56);
+
+ C2Fence fence;
+ C2ConstGraphicBlock constBlock = block->share(
+ { 0, 0, kWidth, kHeight }, fence);
+ block.reset();
+
+ C2Acquirable<const C2GraphicView> constViewHolder = constBlock.map();
+ const C2GraphicView constGraphicView = constViewHolder.get();
+ ASSERT_EQ(C2_OK, constGraphicView.error());
+ ASSERT_EQ(kWidth, constGraphicView.width());
+ ASSERT_EQ(kHeight, constGraphicView.height());
+
+ const uint8_t *const *constData = constGraphicView.data();
+ layout = graphicView.layout();
+ ASSERT_NE(nullptr, constData);
+
+ const uint8_t *cy = constData[C2PlaneLayout::Y];
+ yInfo = layout.mPlanes[C2PlaneLayout::Y];
+ const uint8_t *cu = constData[C2PlaneLayout::U];
+ uInfo = layout.mPlanes[C2PlaneLayout::U];
+ const uint8_t *cv = constData[C2PlaneLayout::V];
+ vInfo = layout.mPlanes[C2PlaneLayout::V];
+
+ ASSERT_TRUE(verifyPlane({ kWidth / 4, kHeight / 4, kWidth / 2, kHeight / 2 }, yInfo, cy, 0x12));
+ ASSERT_TRUE(verifyPlane({ kWidth / 4, kHeight / 4, kWidth / 2, kHeight / 2 }, uInfo, cu, 0x34));
+ ASSERT_TRUE(verifyPlane({ kWidth / 4, kHeight / 4, kWidth / 2, kHeight / 2 }, vInfo, cv, 0x56));
+ ASSERT_TRUE(verifyPlane({ 0, 0, kWidth, kHeight / 4 }, yInfo, cy, 0));
+ ASSERT_TRUE(verifyPlane({ 0, 0, kWidth, kHeight / 4 }, uInfo, cu, 0));
+ ASSERT_TRUE(verifyPlane({ 0, 0, kWidth, kHeight / 4 }, vInfo, cv, 0));
+ ASSERT_TRUE(verifyPlane({ 0, 0, kWidth / 4, kHeight }, yInfo, cy, 0));
+ ASSERT_TRUE(verifyPlane({ 0, 0, kWidth / 4, kHeight }, uInfo, cu, 0));
+ ASSERT_TRUE(verifyPlane({ 0, 0, kWidth / 4, kHeight }, vInfo, cv, 0));
+}
+
+class BufferData : public C2BufferData {
+public:
+ explicit BufferData(const std::list<C2ConstLinearBlock> &blocks) : C2BufferData(blocks) {}
+ explicit BufferData(const std::list<C2ConstGraphicBlock> &blocks) : C2BufferData(blocks) {}
+};
+
+class Buffer : public C2Buffer {
+public:
+ explicit Buffer(const std::list<C2ConstLinearBlock> &blocks) : C2Buffer(blocks) {}
+ explicit Buffer(const std::list<C2ConstGraphicBlock> &blocks) : C2Buffer(blocks) {}
+};
+
+TEST_F(C2BufferTest, BufferDataTest) {
+ std::shared_ptr<C2BlockAllocator> linearBlockAllocator(makeLinearBlockAllocator());
+ std::shared_ptr<C2BlockAllocator> graphicBlockAllocator(makeGraphicBlockAllocator());
+
+ constexpr uint32_t kWidth1 = 320;
+ constexpr uint32_t kHeight1 = 240;
+ constexpr C2Rect kCrop1(kWidth1, kHeight1);
+ constexpr uint32_t kWidth2 = 176;
+ constexpr uint32_t kHeight2 = 144;
+ constexpr C2Rect kCrop2(kWidth2, kHeight2);
+ constexpr size_t kCapacity1 = 1024u;
+ constexpr size_t kCapacity2 = 2048u;
+
+ std::shared_ptr<C2LinearBlock> linearBlock1;
+ std::shared_ptr<C2LinearBlock> linearBlock2;
+ ASSERT_EQ(C2_OK, linearBlockAllocator->allocateLinearBlock(
+ kCapacity1,
+ { C2MemoryUsage::kSoftwareRead, C2MemoryUsage::kSoftwareWrite },
+ &linearBlock1));
+ ASSERT_EQ(C2_OK, linearBlockAllocator->allocateLinearBlock(
+ kCapacity2,
+ { C2MemoryUsage::kSoftwareRead, C2MemoryUsage::kSoftwareWrite },
+ &linearBlock2));
+ std::shared_ptr<C2GraphicBlock> graphicBlock1;
+ std::shared_ptr<C2GraphicBlock> graphicBlock2;
+ ASSERT_EQ(C2_OK, graphicBlockAllocator->allocateGraphicBlock(
+ kWidth1,
+ kHeight1,
+ HAL_PIXEL_FORMAT_YCBCR_420_888,
+ { C2MemoryUsage::kSoftwareRead, C2MemoryUsage::kSoftwareWrite },
+ &graphicBlock1));
+ ASSERT_EQ(C2_OK, graphicBlockAllocator->allocateGraphicBlock(
+ kWidth2,
+ kHeight2,
+ HAL_PIXEL_FORMAT_YCBCR_420_888,
+ { C2MemoryUsage::kSoftwareRead, C2MemoryUsage::kSoftwareWrite },
+ &graphicBlock2));
+
+ std::shared_ptr<C2BufferData> data(new BufferData({ linearBlock1->share(0, kCapacity1, C2Fence()) }));
+ EXPECT_EQ(C2BufferData::LINEAR, data->type());
+ ASSERT_EQ(1u, data->linearBlocks().size());
+ EXPECT_EQ(linearBlock1->handle(), data->linearBlocks().front().handle());
+ EXPECT_TRUE(data->graphicBlocks().empty());
+
+ data.reset(new BufferData({
+ linearBlock1->share(0, kCapacity1, C2Fence()),
+ linearBlock2->share(0, kCapacity2, C2Fence()),
+ }));
+ EXPECT_EQ(C2BufferData::LINEAR_CHUNKS, data->type());
+ ASSERT_EQ(2u, data->linearBlocks().size());
+ EXPECT_EQ(linearBlock1->handle(), data->linearBlocks().front().handle());
+ EXPECT_EQ(linearBlock2->handle(), data->linearBlocks().back().handle());
+ EXPECT_TRUE(data->graphicBlocks().empty());
+
+ data.reset(new BufferData({ graphicBlock1->share(kCrop1, C2Fence()) }));
+ EXPECT_EQ(C2BufferData::GRAPHIC, data->type());
+ ASSERT_EQ(1u, data->graphicBlocks().size());
+ EXPECT_EQ(graphicBlock1->handle(), data->graphicBlocks().front().handle());
+ EXPECT_TRUE(data->linearBlocks().empty());
+
+ data.reset(new BufferData({
+ graphicBlock1->share(kCrop1, C2Fence()),
+ graphicBlock2->share(kCrop2, C2Fence()),
+ }));
+ EXPECT_EQ(C2BufferData::GRAPHIC_CHUNKS, data->type());
+ ASSERT_EQ(2u, data->graphicBlocks().size());
+ EXPECT_EQ(graphicBlock1->handle(), data->graphicBlocks().front().handle());
+ EXPECT_EQ(graphicBlock2->handle(), data->graphicBlocks().back().handle());
+ EXPECT_TRUE(data->linearBlocks().empty());
+}
+
+void DestroyCallback(const C2Buffer * /* buf */, void *arg) {
+ std::function<void(void)> *cb = (std::function<void(void)> *)arg;
+ (*cb)();
+}
+
+enum : uint32_t {
+ kParamIndexNumber1,
+ kParamIndexNumber2,
+};
+
+typedef C2GlobalParam<C2Info, C2Int32Value, kParamIndexNumber1> C2Number1Info;
+typedef C2GlobalParam<C2Info, C2Int32Value, kParamIndexNumber2> C2Number2Info;
+
+TEST_F(C2BufferTest, BufferTest) {
+ std::shared_ptr<C2BlockAllocator> alloc(makeLinearBlockAllocator());
+ constexpr size_t kCapacity = 1024u;
+ std::shared_ptr<C2LinearBlock> block;
+
+ ASSERT_EQ(C2_OK, alloc->allocateLinearBlock(
+ kCapacity,
+ { C2MemoryUsage::kSoftwareRead, C2MemoryUsage::kSoftwareWrite },
+ &block));
+
+ std::atomic_bool destroyed(false);
+ std::function<void(void)> arg = [&destroyed](){ destroyed = true; };
+
+ std::shared_ptr<C2Buffer> buffer(new Buffer( { block->share(0, kCapacity, C2Fence()) }));
+ ASSERT_EQ(C2_OK, buffer->registerOnDestroyNotify(&DestroyCallback, &arg));
+ EXPECT_FALSE(destroyed);
+ ASSERT_EQ(C2_DUPLICATE, buffer->registerOnDestroyNotify(&DestroyCallback, &arg));
+ buffer.reset();
+ EXPECT_TRUE(destroyed);
+
+ buffer.reset(new Buffer( { block->share(0, kCapacity, C2Fence()) }));
+ destroyed = false;
+ ASSERT_EQ(C2_OK, buffer->registerOnDestroyNotify(&DestroyCallback, &arg));
+ EXPECT_FALSE(destroyed);
+ ASSERT_EQ(C2_OK, buffer->unregisterOnDestroyNotify(&DestroyCallback));
+ EXPECT_FALSE(destroyed);
+ ASSERT_EQ(C2_NOT_FOUND, buffer->unregisterOnDestroyNotify(&DestroyCallback));
+ buffer.reset();
+ EXPECT_FALSE(destroyed);
+
+ std::shared_ptr<C2Info> info1(new C2Number1Info(1));
+ std::shared_ptr<C2Info> info2(new C2Number2Info(2));
+ buffer.reset(new Buffer( { block->share(0, kCapacity, C2Fence()) }));
+ EXPECT_TRUE(buffer->infos().empty());
+ EXPECT_FALSE(buffer->hasInfo(info1->type()));
+ EXPECT_FALSE(buffer->hasInfo(info2->type()));
+
+ ASSERT_EQ(C2_OK, buffer->setInfo(info1));
+ EXPECT_EQ(1u, buffer->infos().size());
+ EXPECT_EQ(*info1, *buffer->infos().front());
+ EXPECT_TRUE(buffer->hasInfo(info1->type()));
+ EXPECT_FALSE(buffer->hasInfo(info2->type()));
+
+ ASSERT_EQ(C2_OK, buffer->setInfo(info2));
+ EXPECT_EQ(2u, buffer->infos().size());
+ EXPECT_TRUE(buffer->hasInfo(info1->type()));
+ EXPECT_TRUE(buffer->hasInfo(info2->type()));
+
+ std::shared_ptr<C2Info> removed = buffer->removeInfo(info1->type());
+ ASSERT_TRUE(removed);
+ EXPECT_EQ(*removed, *info1);
+ EXPECT_EQ(1u, buffer->infos().size());
+ EXPECT_EQ(*info2, *buffer->infos().front());
+ EXPECT_FALSE(buffer->hasInfo(info1->type()));
+ EXPECT_TRUE(buffer->hasInfo(info2->type()));
+
+ removed = buffer->removeInfo(info1->type());
+ ASSERT_FALSE(removed);
+ EXPECT_EQ(1u, buffer->infos().size());
+ EXPECT_FALSE(buffer->hasInfo(info1->type()));
+ EXPECT_TRUE(buffer->hasInfo(info2->type()));
+
+ std::shared_ptr<C2Info> info3(new C2Number2Info(3));
+ ASSERT_EQ(C2_OK, buffer->setInfo(info3));
+ EXPECT_EQ(1u, buffer->infos().size());
+ EXPECT_FALSE(buffer->hasInfo(info1->type()));
+ EXPECT_TRUE(buffer->hasInfo(info2->type()));
+
+ removed = buffer->removeInfo(info2->type());
+ ASSERT_TRUE(removed);
+ EXPECT_EQ(*info3, *removed);
+ EXPECT_TRUE(buffer->infos().empty());
+ EXPECT_FALSE(buffer->hasInfo(info1->type()));
+ EXPECT_FALSE(buffer->hasInfo(info2->type()));
+}
+
} // namespace android
diff --git a/media/libstagefright/codec2/vndk/Android.bp b/media/libstagefright/codec2/vndk/Android.bp
index 9426b4e..916a6a9 100644
--- a/media/libstagefright/codec2/vndk/Android.bp
+++ b/media/libstagefright/codec2/vndk/Android.bp
@@ -10,6 +10,8 @@
],
shared_libs: [
+ "android.hardware.graphics.allocator@2.0",
+ "android.hardware.graphics.mapper@2.0",
"libbinder",
"libcutils",
"libdl",
@@ -19,6 +21,7 @@
"liblog",
"libmedia",
"libstagefright_foundation",
+ "libui",
"libutils",
],
diff --git a/media/libstagefright/codec2/vndk/C2Buffer.cpp b/media/libstagefright/codec2/vndk/C2Buffer.cpp
index ffb6c2e..1a0b55c 100644
--- a/media/libstagefright/codec2/vndk/C2Buffer.cpp
+++ b/media/libstagefright/codec2/vndk/C2Buffer.cpp
@@ -20,11 +20,25 @@
#include <C2BufferPriv.h>
+#include <android/hardware/graphics/allocator/2.0/IAllocator.h>
+#include <android/hardware/graphics/mapper/2.0/IMapper.h>
+
#include <ion/ion.h>
+#include <hardware/gralloc.h>
#include <sys/mman.h>
namespace android {
+using ::android::hardware::graphics::allocator::V2_0::IAllocator;
+using ::android::hardware::graphics::common::V1_0::BufferUsage;
+using ::android::hardware::graphics::common::V1_0::PixelFormat;
+using ::android::hardware::graphics::mapper::V2_0::BufferDescriptor;
+using ::android::hardware::graphics::mapper::V2_0::Error;
+using ::android::hardware::graphics::mapper::V2_0::IMapper;
+using ::android::hardware::graphics::mapper::V2_0::YCbCrLayout;
+using ::android::hardware::hidl_handle;
+using ::android::hardware::hidl_vec;
+
// standard ERRNO mappings
template<int N> constexpr C2Error _c2_errno2error_impl();
template<> constexpr C2Error _c2_errno2error_impl<0>() { return C2_OK; }
@@ -95,6 +109,37 @@
friend class ::android::C2DefaultBlockAllocator;
};
+class C2DefaultGraphicView : public C2GraphicView {
+ using C2GraphicView::C2GraphicView;
+ friend class ::android::C2ConstGraphicBlock;
+ friend class ::android::C2GraphicBlock;
+};
+
+class C2AcquirableConstGraphicView : public C2Acquirable<const C2GraphicView> {
+ using C2Acquirable::C2Acquirable;
+ friend class ::android::C2ConstGraphicBlock;
+};
+
+class C2AcquirableGraphicView : public C2Acquirable<C2GraphicView> {
+ using C2Acquirable::C2Acquirable;
+ friend class ::android::C2GraphicBlock;
+};
+
+class C2DefaultConstGraphicBlock : public C2ConstGraphicBlock {
+ using C2ConstGraphicBlock::C2ConstGraphicBlock;
+ friend class ::android::C2GraphicBlock;
+};
+
+class C2DefaultGraphicBlock : public C2GraphicBlock {
+ using C2GraphicBlock::C2GraphicBlock;
+ friend class ::android::C2DefaultGraphicBlockAllocator;
+};
+
+class C2DefaultBufferData : public C2BufferData {
+ using C2BufferData::C2BufferData;
+ friend class ::android::C2Buffer;
+};
+
} // namespace
/* ======================================= ION ALLOCATION ====================================== */
@@ -717,4 +762,680 @@
return C2_OK;
}
+/* ===================================== GRALLOC ALLOCATION ==================================== */
+
+static C2Error maperr2error(Error maperr) {
+ switch (maperr) {
+ case Error::NONE: return C2_OK;
+ case Error::BAD_DESCRIPTOR: return C2_BAD_VALUE;
+ case Error::BAD_BUFFER: return C2_BAD_VALUE;
+ case Error::BAD_VALUE: return C2_BAD_VALUE;
+ case Error::NO_RESOURCES: return C2_NO_MEMORY;
+ case Error::UNSUPPORTED: return C2_UNSUPPORTED;
+ }
+ return C2_CORRUPTED;
+}
+
+class C2AllocationGralloc : public C2GraphicAllocation {
+public:
+ virtual ~C2AllocationGralloc();
+
+ virtual C2Error map(
+ C2Rect rect, C2MemoryUsage usage, int *fenceFd,
+ C2PlaneLayout *layout /* nonnull */, uint8_t **addr /* nonnull */) override;
+ virtual C2Error unmap(C2Fence *fenceFd /* nullable */) override;
+ virtual bool isValid() const override { return true; }
+ virtual const C2Handle *handle() const override { return mHandle; }
+ virtual bool equals(const std::shared_ptr<const C2GraphicAllocation> &other) const override;
+
+ // internal methods
+ // |handle| will be moved.
+ C2AllocationGralloc(
+ const IMapper::BufferDescriptorInfo &info,
+ const sp<IMapper> &mapper,
+ hidl_handle &handle);
+ int dup() const;
+ C2Error status() const;
+
+private:
+ const IMapper::BufferDescriptorInfo mInfo;
+ const sp<IMapper> mMapper;
+ const hidl_handle mHandle;
+ buffer_handle_t mBuffer;
+ bool mLocked;
+};
+
+C2AllocationGralloc::C2AllocationGralloc(
+ const IMapper::BufferDescriptorInfo &info,
+ const sp<IMapper> &mapper,
+ hidl_handle &handle)
+ : C2GraphicAllocation(info.width, info.height),
+ mInfo(info),
+ mMapper(mapper),
+ mHandle(std::move(handle)),
+ mBuffer(nullptr),
+ mLocked(false) {}
+
+C2AllocationGralloc::~C2AllocationGralloc() {
+ if (!mBuffer) {
+ return;
+ }
+ if (mLocked) {
+ unmap(nullptr);
+ }
+ mMapper->freeBuffer(const_cast<native_handle_t *>(mBuffer));
+}
+
+C2Error C2AllocationGralloc::map(
+ C2Rect rect, C2MemoryUsage usage, int *fenceFd,
+ C2PlaneLayout *layout /* nonnull */, uint8_t **addr /* nonnull */) {
+ // TODO
+ (void) fenceFd;
+ (void) usage;
+
+ if (mBuffer && mLocked) {
+ return C2_DUPLICATE;
+ }
+ if (!layout || !addr) {
+ return C2_BAD_VALUE;
+ }
+
+ C2Error err = C2_OK;
+ if (!mBuffer) {
+ mMapper->importBuffer(
+ mHandle, [&err, this](const auto &maperr, const auto &buffer) {
+ err = maperr2error(maperr);
+ if (err == C2_OK) {
+ mBuffer = static_cast<buffer_handle_t>(buffer);
+ }
+ });
+ if (err != C2_OK) {
+ return err;
+ }
+ }
+
+ if (mInfo.format == PixelFormat::YCBCR_420_888 || mInfo.format == PixelFormat::YV12) {
+ YCbCrLayout ycbcrLayout;
+ mMapper->lockYCbCr(
+ const_cast<native_handle_t *>(mBuffer),
+ BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN,
+ { (int32_t)rect.mLeft, (int32_t)rect.mTop, (int32_t)rect.mWidth, (int32_t)rect.mHeight },
+ // TODO: fence
+ hidl_handle(),
+ [&err, &ycbcrLayout](const auto &maperr, const auto &mapLayout) {
+ err = maperr2error(maperr);
+ if (err == C2_OK) {
+ ycbcrLayout = mapLayout;
+ }
+ });
+ if (err != C2_OK) {
+ return err;
+ }
+ addr[C2PlaneLayout::Y] = (uint8_t *)ycbcrLayout.y;
+ addr[C2PlaneLayout::U] = (uint8_t *)ycbcrLayout.cb;
+ addr[C2PlaneLayout::V] = (uint8_t *)ycbcrLayout.cr;
+ layout->mType = C2PlaneLayout::MEDIA_IMAGE_TYPE_YUV;
+ layout->mNumPlanes = 3;
+ layout->mPlanes[C2PlaneLayout::Y] = {
+ C2PlaneInfo::Y, // mChannel
+ 1, // mColInc
+ (int32_t)ycbcrLayout.yStride, // mRowInc
+ 1, // mHorizSubsampling
+ 1, // mVertSubsampling
+ 8, // mBitDepth
+ 8, // mAllocatedDepth
+ };
+ layout->mPlanes[C2PlaneLayout::U] = {
+ C2PlaneInfo::Cb, // mChannel
+ (int32_t)ycbcrLayout.chromaStep, // mColInc
+ (int32_t)ycbcrLayout.cStride, // mRowInc
+ 2, // mHorizSubsampling
+ 2, // mVertSubsampling
+ 8, // mBitDepth
+ 8, // mAllocatedDepth
+ };
+ layout->mPlanes[C2PlaneLayout::V] = {
+ C2PlaneInfo::Cr, // mChannel
+ (int32_t)ycbcrLayout.chromaStep, // mColInc
+ (int32_t)ycbcrLayout.cStride, // mRowInc
+ 2, // mHorizSubsampling
+ 2, // mVertSubsampling
+ 8, // mBitDepth
+ 8, // mAllocatedDepth
+ };
+ } else {
+ void *pointer = nullptr;
+ mMapper->lock(
+ const_cast<native_handle_t *>(mBuffer),
+ BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN,
+ { (int32_t)rect.mLeft, (int32_t)rect.mTop, (int32_t)rect.mWidth, (int32_t)rect.mHeight },
+ // TODO: fence
+ hidl_handle(),
+ [&err, &pointer](const auto &maperr, const auto &mapPointer) {
+ err = maperr2error(maperr);
+ if (err == C2_OK) {
+ pointer = mapPointer;
+ }
+ });
+ if (err != C2_OK) {
+ return err;
+ }
+ // TODO
+ return C2_UNSUPPORTED;
+ }
+ mLocked = true;
+
+ return C2_OK;
+}
+
+C2Error C2AllocationGralloc::unmap(C2Fence *fenceFd /* nullable */) {
+ // TODO: fence
+ C2Error err = C2_OK;
+ mMapper->unlock(
+ const_cast<native_handle_t *>(mBuffer),
+ [&err, &fenceFd](const auto &maperr, const auto &releaseFence) {
+ // TODO
+ (void) fenceFd;
+ (void) releaseFence;
+ err = maperr2error(maperr);
+ if (err == C2_OK) {
+ // TODO: fence
+ }
+ });
+ if (err == C2_OK) {
+ mLocked = false;
+ }
+ return err;
+}
+
+bool C2AllocationGralloc::equals(const std::shared_ptr<const C2GraphicAllocation> &other) const {
+ return other && other->handle() == handle();
+}
+
+/* ===================================== GRALLOC ALLOCATOR ==================================== */
+
+class C2AllocatorGralloc::Impl {
+public:
+ Impl();
+
+ C2Error allocateGraphicBuffer(
+ uint32_t width, uint32_t height, uint32_t format, const C2MemoryUsage &usage,
+ std::shared_ptr<C2GraphicAllocation> *allocation);
+
+ C2Error recreateGraphicBuffer(
+ const C2Handle *handle,
+ std::shared_ptr<C2GraphicAllocation> *allocation);
+
+ C2Error status() const { return mInit; }
+
+private:
+ C2Error mInit;
+ sp<IAllocator> mAllocator;
+ sp<IMapper> mMapper;
+};
+
+C2AllocatorGralloc::Impl::Impl() : mInit(C2_OK) {
+ mAllocator = IAllocator::getService();
+ mMapper = IMapper::getService();
+ if (mAllocator == nullptr || mMapper == nullptr) {
+ mInit = C2_CORRUPTED;
+ }
+}
+
+C2Error C2AllocatorGralloc::Impl::allocateGraphicBuffer(
+ uint32_t width, uint32_t height, uint32_t format, const C2MemoryUsage &usage,
+ std::shared_ptr<C2GraphicAllocation> *allocation) {
+ // TODO: buffer usage should be determined according to |usage|
+ (void) usage;
+
+ IMapper::BufferDescriptorInfo info = {
+ width,
+ height,
+ 1u, // layerCount
+ (PixelFormat)format,
+ BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN,
+ };
+ C2Error err = C2_OK;
+ BufferDescriptor desc;
+ mMapper->createDescriptor(
+ info, [&err, &desc](const auto &maperr, const auto &descriptor) {
+ err = maperr2error(maperr);
+ if (err == C2_OK) {
+ desc = descriptor;
+ }
+ });
+ if (err != C2_OK) {
+ return err;
+ }
+
+ // IAllocator shares IMapper error codes.
+ hidl_handle buffer;
+ mAllocator->allocate(
+ desc,
+ 1u,
+ [&err, &buffer](const auto &maperr, const auto &stride, auto &buffers) {
+ (void) stride;
+ err = maperr2error(maperr);
+ if (err != C2_OK) {
+ return;
+ }
+ if (buffers.size() != 1u) {
+ err = C2_CORRUPTED;
+ return;
+ }
+ buffer = std::move(buffers[0]);
+ });
+ if (err != C2_OK) {
+ return err;
+ }
+
+ allocation->reset(new C2AllocationGralloc(info, mMapper, buffer));
+ return C2_OK;
+}
+
+C2Error C2AllocatorGralloc::Impl::recreateGraphicBuffer(
+ const C2Handle *handle,
+ std::shared_ptr<C2GraphicAllocation> *allocation) {
+ (void) handle;
+
+ // TODO: need to figure out BufferDescriptorInfo from the handle.
+ allocation->reset();
+ return C2_UNSUPPORTED;
+}
+
+C2AllocatorGralloc::C2AllocatorGralloc() : mImpl(new Impl) {}
+C2AllocatorGralloc::~C2AllocatorGralloc() { delete mImpl; }
+
+C2Error C2AllocatorGralloc::allocateGraphicBuffer(
+ uint32_t width, uint32_t height, uint32_t format, C2MemoryUsage usage,
+ std::shared_ptr<C2GraphicAllocation> *allocation) {
+ return mImpl->allocateGraphicBuffer(width, height, format, usage, allocation);
+}
+
+C2Error C2AllocatorGralloc::recreateGraphicBuffer(
+ const C2Handle *handle,
+ std::shared_ptr<C2GraphicAllocation> *allocation) {
+ return mImpl->recreateGraphicBuffer(handle, allocation);
+}
+
+C2Error C2AllocatorGralloc::status() const { return mImpl->status(); }
+
+/* ========================================== 2D BLOCK ========================================= */
+
+class C2Block2D::Impl {
+public:
+ const C2Handle *handle() const {
+ return mAllocation->handle();
+ }
+
+ Impl(const std::shared_ptr<C2GraphicAllocation> &alloc)
+ : mAllocation(alloc) {}
+
+private:
+ std::shared_ptr<C2GraphicAllocation> mAllocation;
+};
+
+C2Block2D::C2Block2D(const std::shared_ptr<C2GraphicAllocation> &alloc)
+ : _C2PlanarSection(alloc.get()), mImpl(new Impl(alloc)) {}
+
+const C2Handle *C2Block2D::handle() const {
+ return mImpl->handle();
+}
+
+class C2GraphicView::Impl {
+public:
+ Impl(uint8_t *const *data, const C2PlaneLayout &layout)
+ : mData(data), mLayout(layout), mError(C2_OK) {}
+ explicit Impl(C2Error error) : mData(nullptr), mError(error) {}
+
+ uint8_t *const *data() const { return mData; }
+ const C2PlaneLayout &layout() const { return mLayout; }
+ C2Error error() const { return mError; }
+
+private:
+ uint8_t *const *mData;
+ C2PlaneLayout mLayout;
+ C2Error mError;
+};
+
+C2GraphicView::C2GraphicView(
+ const _C2PlanarCapacityAspect *parent,
+ uint8_t *const *data,
+ const C2PlaneLayout& layout)
+ : _C2PlanarSection(parent), mImpl(new Impl(data, layout)) {}
+
+C2GraphicView::C2GraphicView(C2Error error)
+ : _C2PlanarSection(nullptr), mImpl(new Impl(error)) {}
+
+const uint8_t *const *C2GraphicView::data() const {
+ return mImpl->data();
+}
+
+uint8_t *const *C2GraphicView::data() {
+ return mImpl->data();
+}
+
+const C2PlaneLayout C2GraphicView::layout() const {
+ return mImpl->layout();
+}
+
+const C2GraphicView C2GraphicView::subView(const C2Rect &rect) const {
+ C2GraphicView view(this, mImpl->data(), mImpl->layout());
+ view.setCrop_be(rect);
+ return view;
+}
+
+C2GraphicView C2GraphicView::subView(const C2Rect &rect) {
+ C2GraphicView view(this, mImpl->data(), mImpl->layout());
+ view.setCrop_be(rect);
+ return view;
+}
+
+C2Error C2GraphicView::error() const {
+ return mImpl->error();
+}
+
+class C2ConstGraphicBlock::Impl {
+public:
+ explicit Impl(const std::shared_ptr<C2GraphicAllocation> &alloc)
+ : mAllocation(alloc), mData{ nullptr } {}
+
+ ~Impl() {
+ if (mData[0] != nullptr) {
+ // TODO: fence
+ mAllocation->unmap(nullptr);
+ }
+ }
+
+ C2Error map(C2Rect rect) {
+ if (mData[0] != nullptr) {
+ // Already mapped.
+ return C2_OK;
+ }
+ C2Error err = mAllocation->map(
+ rect,
+ { C2MemoryUsage::kSoftwareRead, 0 },
+ nullptr,
+ &mLayout,
+ mData);
+ if (err != C2_OK) {
+ memset(mData, 0, sizeof(mData));
+ }
+ return err;
+ }
+
+ C2ConstGraphicBlock subBlock(const C2Rect &rect, C2Fence fence) const {
+ C2ConstGraphicBlock block(mAllocation, fence);
+ block.setCrop_be(rect);
+ return block;
+ }
+
+ uint8_t *const *data() const {
+ return mData[0] == nullptr ? nullptr : &mData[0];
+ }
+
+ const C2PlaneLayout &layout() const { return mLayout; }
+
+private:
+ std::shared_ptr<C2GraphicAllocation> mAllocation;
+ C2PlaneLayout mLayout;
+ uint8_t *mData[C2PlaneLayout::MAX_NUM_PLANES];
+};
+
+C2ConstGraphicBlock::C2ConstGraphicBlock(
+ const std::shared_ptr<C2GraphicAllocation> &alloc, C2Fence fence)
+ : C2Block2D(alloc), mImpl(new Impl(alloc)), mFence(fence) {}
+
+C2Acquirable<const C2GraphicView> C2ConstGraphicBlock::map() const {
+ C2Error err = mImpl->map(crop());
+ if (err != C2_OK) {
+ C2DefaultGraphicView view(err);
+ return C2AcquirableConstGraphicView(err, mFence, view);
+ }
+ C2DefaultGraphicView view(this, mImpl->data(), mImpl->layout());
+ return C2AcquirableConstGraphicView(err, mFence, view);
+}
+
+C2ConstGraphicBlock C2ConstGraphicBlock::subBlock(const C2Rect &rect) const {
+ return mImpl->subBlock(rect, mFence);
+}
+
+class C2GraphicBlock::Impl {
+public:
+ explicit Impl(const std::shared_ptr<C2GraphicAllocation> &alloc)
+ : mAllocation(alloc), mData{ nullptr } {}
+
+ ~Impl() {
+ if (mData[0] != nullptr) {
+ // TODO: fence
+ mAllocation->unmap(nullptr);
+ }
+ }
+
+ C2Error map(C2Rect rect) {
+ if (mData[0] != nullptr) {
+ // Already mapped.
+ return C2_OK;
+ }
+ uint8_t *data[C2PlaneLayout::MAX_NUM_PLANES];
+ C2Error err = mAllocation->map(
+ rect,
+ { C2MemoryUsage::kSoftwareRead, C2MemoryUsage::kSoftwareWrite },
+ nullptr,
+ &mLayout,
+ data);
+ if (err == C2_OK) {
+ memcpy(mData, data, sizeof(mData));
+ } else {
+ memset(mData, 0, sizeof(mData));
+ }
+ return err;
+ }
+
+ C2ConstGraphicBlock share(const C2Rect &crop, C2Fence fence) const {
+ C2DefaultConstGraphicBlock block(mAllocation, fence);
+ block.setCrop_be(crop);
+ return block;
+ }
+
+ uint8_t *const *data() const {
+ return mData[0] == nullptr ? nullptr : mData;
+ }
+
+ const C2PlaneLayout &layout() const { return mLayout; }
+
+private:
+ std::shared_ptr<C2GraphicAllocation> mAllocation;
+ C2PlaneLayout mLayout;
+ uint8_t *mData[C2PlaneLayout::MAX_NUM_PLANES];
+};
+
+C2GraphicBlock::C2GraphicBlock(const std::shared_ptr<C2GraphicAllocation> &alloc)
+ : C2Block2D(alloc), mImpl(new Impl(alloc)) {}
+
+C2Acquirable<C2GraphicView> C2GraphicBlock::map() {
+ C2Error err = mImpl->map(crop());
+ if (err != C2_OK) {
+ C2DefaultGraphicView view(err);
+ // TODO: fence
+ return C2AcquirableGraphicView(err, C2Fence(), view);
+ }
+ C2DefaultGraphicView view(this, mImpl->data(), mImpl->layout());
+ // TODO: fence
+ return C2AcquirableGraphicView(err, C2Fence(), view);
+}
+
+C2ConstGraphicBlock C2GraphicBlock::share(const C2Rect &crop, C2Fence fence) {
+ return mImpl->share(crop, fence);
+}
+
+C2DefaultGraphicBlockAllocator::C2DefaultGraphicBlockAllocator(
+ const std::shared_ptr<C2Allocator> &allocator)
+ : mAllocator(allocator) {}
+
+C2Error C2DefaultGraphicBlockAllocator::allocateGraphicBlock(
+ uint32_t width,
+ uint32_t height,
+ uint32_t format,
+ C2MemoryUsage usage,
+ std::shared_ptr<C2GraphicBlock> *block /* nonnull */) {
+ block->reset();
+
+ std::shared_ptr<C2GraphicAllocation> alloc;
+ C2Error err = mAllocator->allocateGraphicBuffer(width, height, format, usage, &alloc);
+ if (err != C2_OK) {
+ return err;
+ }
+
+ block->reset(new C2DefaultGraphicBlock(alloc));
+
+ return C2_OK;
+}
+
+/* ========================================== BUFFER ========================================= */
+
+class C2BufferData::Impl {
+public:
+ explicit Impl(const std::list<C2ConstLinearBlock> &blocks)
+ : mType(blocks.size() == 1 ? LINEAR : LINEAR_CHUNKS),
+ mLinearBlocks(blocks) {
+ }
+
+ explicit Impl(const std::list<C2ConstGraphicBlock> &blocks)
+ : mType(blocks.size() == 1 ? GRAPHIC : GRAPHIC_CHUNKS),
+ mGraphicBlocks(blocks) {
+ }
+
+ Type type() const { return mType; }
+ const std::list<C2ConstLinearBlock> &linearBlocks() const { return mLinearBlocks; }
+ const std::list<C2ConstGraphicBlock> &graphicBlocks() const { return mGraphicBlocks; }
+
+private:
+ Type mType;
+ std::list<C2ConstLinearBlock> mLinearBlocks;
+ std::list<C2ConstGraphicBlock> mGraphicBlocks;
+};
+
+C2BufferData::C2BufferData(const std::list<C2ConstLinearBlock> &blocks) : mImpl(new Impl(blocks)) {}
+C2BufferData::C2BufferData(const std::list<C2ConstGraphicBlock> &blocks) : mImpl(new Impl(blocks)) {}
+
+C2BufferData::Type C2BufferData::type() const { return mImpl->type(); }
+
+const std::list<C2ConstLinearBlock> C2BufferData::linearBlocks() const {
+ return mImpl->linearBlocks();
+}
+
+const std::list<C2ConstGraphicBlock> C2BufferData::graphicBlocks() const {
+ return mImpl->graphicBlocks();
+}
+
+class C2Buffer::Impl {
+public:
+ Impl(C2Buffer *thiz, const std::list<C2ConstLinearBlock> &blocks)
+ : mThis(thiz), mData(blocks) {}
+ Impl(C2Buffer *thiz, const std::list<C2ConstGraphicBlock> &blocks)
+ : mThis(thiz), mData(blocks) {}
+
+ ~Impl() {
+ for (const auto &pair : mNotify) {
+ pair.first(mThis, pair.second);
+ }
+ }
+
+ const C2BufferData &data() const { return mData; }
+
+ C2Error registerOnDestroyNotify(OnDestroyNotify onDestroyNotify, void *arg = nullptr) {
+ auto it = std::find_if(
+ mNotify.begin(), mNotify.end(),
+ [onDestroyNotify, arg] (const auto &pair) {
+ return pair.first == onDestroyNotify && pair.second == arg;
+ });
+ if (it != mNotify.end()) {
+ return C2_DUPLICATE;
+ }
+ mNotify.emplace_back(onDestroyNotify, arg);
+ return C2_OK;
+ }
+
+ C2Error unregisterOnDestroyNotify(OnDestroyNotify onDestroyNotify) {
+ auto it = std::find_if(
+ mNotify.begin(), mNotify.end(),
+ [onDestroyNotify] (const auto &pair) {
+ return pair.first == onDestroyNotify;
+ });
+ if (it == mNotify.end()) {
+ return C2_NOT_FOUND;
+ }
+ mNotify.erase(it);
+ return C2_OK;
+ }
+
+ std::list<std::shared_ptr<const C2Info>> infos() const {
+ std::list<std::shared_ptr<const C2Info>> result(mInfos.size());
+ std::transform(
+ mInfos.begin(), mInfos.end(), result.begin(),
+ [] (const auto &elem) { return elem.second; });
+ return result;
+ }
+
+ C2Error setInfo(const std::shared_ptr<C2Info> &info) {
+ // To "update" you need to erase the existing one if any, and then insert.
+ (void) mInfos.erase(info->type());
+ (void) mInfos.insert({ info->type(), info });
+ return C2_OK;
+ }
+
+ bool hasInfo(C2Param::Type index) const {
+ return mInfos.count(index.type()) > 0;
+ }
+
+ std::shared_ptr<C2Info> removeInfo(C2Param::Type index) {
+ auto it = mInfos.find(index.type());
+ if (it == mInfos.end()) {
+ return nullptr;
+ }
+ std::shared_ptr<C2Info> ret = it->second;
+ (void) mInfos.erase(it);
+ return ret;
+ }
+
+private:
+ C2Buffer * const mThis;
+ C2DefaultBufferData mData;
+ std::map<uint32_t, std::shared_ptr<C2Info>> mInfos;
+ std::list<std::pair<OnDestroyNotify, void *>> mNotify;
+};
+
+C2Buffer::C2Buffer(const std::list<C2ConstLinearBlock> &blocks)
+ : mImpl(new Impl(this, blocks)) {}
+
+C2Buffer::C2Buffer(const std::list<C2ConstGraphicBlock> &blocks)
+ : mImpl(new Impl(this, blocks)) {}
+
+const C2BufferData C2Buffer::data() const { return mImpl->data(); }
+
+C2Error C2Buffer::registerOnDestroyNotify(OnDestroyNotify onDestroyNotify, void *arg) {
+ return mImpl->registerOnDestroyNotify(onDestroyNotify, arg);
+}
+
+C2Error C2Buffer::unregisterOnDestroyNotify(OnDestroyNotify onDestroyNotify) {
+ return mImpl->unregisterOnDestroyNotify(onDestroyNotify);
+}
+
+const std::list<std::shared_ptr<const C2Info>> C2Buffer::infos() const {
+ return mImpl->infos();
+}
+
+C2Error C2Buffer::setInfo(const std::shared_ptr<C2Info> &info) {
+ return mImpl->setInfo(info);
+}
+
+bool C2Buffer::hasInfo(C2Param::Type index) const {
+ return mImpl->hasInfo(index);
+}
+
+std::shared_ptr<C2Info> C2Buffer::removeInfo(C2Param::Type index) {
+ return mImpl->removeInfo(index);
+}
+
} // namespace android
diff --git a/media/libstagefright/codec2/vndk/include/C2BufferPriv.h b/media/libstagefright/codec2/vndk/include/C2BufferPriv.h
index bfb069c..a536710 100644
--- a/media/libstagefright/codec2/vndk/include/C2BufferPriv.h
+++ b/media/libstagefright/codec2/vndk/include/C2BufferPriv.h
@@ -65,6 +65,49 @@
const std::shared_ptr<C2Allocator> mAllocator;
};
+class C2AllocatorGralloc : public C2Allocator {
+public:
+ // (usage, capacity) => (align, heapMask, flags)
+ typedef std::function<int (C2MemoryUsage, size_t,
+ /* => */ size_t*, unsigned*, unsigned*)> usage_mapper_fn;
+
+ virtual C2Error allocateGraphicBuffer(
+ uint32_t width, uint32_t height, uint32_t format, C2MemoryUsage usage,
+ std::shared_ptr<C2GraphicAllocation> *allocation) override;
+
+ virtual C2Error recreateGraphicBuffer(
+ const C2Handle *handle,
+ std::shared_ptr<C2GraphicAllocation> *allocation) override;
+
+ C2AllocatorGralloc();
+
+ C2Error status() const;
+
+ virtual ~C2AllocatorGralloc();
+
+private:
+ class Impl;
+ Impl *mImpl;
+};
+
+class C2DefaultGraphicBlockAllocator : public C2BlockAllocator {
+public:
+ explicit C2DefaultGraphicBlockAllocator(const std::shared_ptr<C2Allocator> &allocator);
+
+ virtual ~C2DefaultGraphicBlockAllocator() = default;
+
+ virtual C2Error allocateGraphicBlock(
+ uint32_t width,
+ uint32_t height,
+ uint32_t format,
+ C2MemoryUsage usage,
+ std::shared_ptr<C2GraphicBlock> *block /* nonnull */) override;
+
+private:
+ const std::shared_ptr<C2Allocator> mAllocator;
+};
+
+
#if 0
class C2Allocation::Impl {
public:
diff --git a/media/libstagefright/codecs/aacenc/Android.bp b/media/libstagefright/codecs/aacenc/Android.bp
index fb368c2..1b4e35c 100644
--- a/media/libstagefright/codecs/aacenc/Android.bp
+++ b/media/libstagefright/codecs/aacenc/Android.bp
@@ -56,7 +56,7 @@
instruction_set: "arm",
- armv7_a_neon: {
+ neon: {
exclude_srcs: [
"src/asm/ARMV5E/PrePostMDCT_v5.s",
"src/asm/ARMV5E/R4R8First_v5.s",
diff --git a/media/libstagefright/codecs/amrwbenc/Android.bp b/media/libstagefright/codecs/amrwbenc/Android.bp
index 8968991..b6f637f 100644
--- a/media/libstagefright/codecs/amrwbenc/Android.bp
+++ b/media/libstagefright/codecs/amrwbenc/Android.bp
@@ -76,7 +76,7 @@
instruction_set: "arm",
- armv7_a_neon: {
+ neon: {
exclude_srcs: [
"src/asm/ARMV5E/convolve_opt.s",
"src/asm/ARMV5E/cor_h_vec_opt.s",
diff --git a/media/libstagefright/codecs/avcdec/Android.bp b/media/libstagefright/codecs/avcdec/Android.bp
index 1f43803..44c882c 100644
--- a/media/libstagefright/codecs/avcdec/Android.bp
+++ b/media/libstagefright/codecs/avcdec/Android.bp
@@ -36,3 +36,44 @@
ldflags: ["-Wl,-Bsymbolic"],
compile_multilib: "32",
}
+
+cc_library_shared {
+ name: "libstagefright_soft_c2avcdec",
+
+ static_libs: [
+ "libavcdec",
+ "libstagefright_codec2_vndk",
+ ],
+ srcs: ["C2SoftAvcDec.cpp"],
+
+ include_dirs: [
+ "external/libavc/decoder",
+ "external/libavc/common",
+ "frameworks/av/media/libstagefright/codec2/include",
+ "frameworks/av/media/libstagefright/codec2/vndk/include",
+ ],
+
+ shared_libs: [
+ "android.hardware.graphics.allocator@2.0",
+ "android.hardware.graphics.mapper@2.0",
+ "libhidlbase",
+ "libion",
+ "liblog",
+ "libmedia",
+ "libstagefright_codec2",
+ "libstagefright_foundation",
+ "libutils",
+ ],
+
+ sanitize: {
+ misc_undefined: [
+ "signed-integer-overflow",
+ ],
+ cfi: true,
+ diag: {
+ cfi: true,
+ },
+ },
+
+ ldflags: ["-Wl,-Bsymbolic"],
+}
diff --git a/media/libstagefright/codecs/avcdec/C2AvcConfig.h b/media/libstagefright/codecs/avcdec/C2AvcConfig.h
new file mode 100644
index 0000000..a7e0d95
--- /dev/null
+++ b/media/libstagefright/codecs/avcdec/C2AvcConfig.h
@@ -0,0 +1,87 @@
+/*
+ * 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.
+ */
+
+#ifndef C2AVCCONFIG_H_
+#define C2AVCCONFIG_H_
+
+#include <C2Config.h>
+
+namespace android {
+
+enum : uint32_t {
+ kParamIndexAvcProfile = kParamIndexParamStart + 1,
+ kParamIndexAvcLevel,
+ kParamIndexBlockSize,
+ kParamIndexAlignment,
+ kParamIndexFramerate,
+ kParamIndexBlocksPerSecond,
+};
+
+enum C2AvcProfileIdc : uint32_t {
+ kAvcProfileUnknown = 0,
+ kAvcProfileBaseline = 66,
+ kAvcProfileMain = 77,
+ kAvcProfileExtended = 88,
+ kAvcProfileHigh = 100,
+ kAvcProfileHigh10 = 110,
+ kAvcProfileHigh422 = 122,
+ kAvcProfileHigh444 = 144,
+};
+
+enum C2AvcLevelIdc : uint32_t {
+ kAvcLevelUnknown = 0,
+ kAvcLevel10 = 10,
+ kAvcLevel1b = 9,
+ kAvcLevel11 = 11,
+ kAvcLevel12 = 12,
+ kAvcLevel13 = 13,
+ kAvcLevel20 = 20,
+ kAvcLevel21 = 21,
+ kAvcLevel22 = 22,
+ kAvcLevel30 = 30,
+ kAvcLevel31 = 31,
+ kAvcLevel32 = 32,
+ kAvcLevel40 = 40,
+ kAvcLevel41 = 41,
+ kAvcLevel42 = 42,
+ kAvcLevel50 = 50,
+ kAvcLevel51 = 51,
+ kAvcLevel52 = 52,
+};
+
+// profile for AVC video decoder [IN]
+typedef C2StreamParam<C2Info, C2SimpleValueStruct<C2AvcProfileIdc>, kParamIndexAvcProfile>
+ C2AvcProfileInfo;
+
+// level for AVC video decoder [IN]
+typedef C2StreamParam<C2Info, C2SimpleValueStruct<C2AvcLevelIdc>, kParamIndexAvcLevel>
+ C2AvcLevelInfo;
+
+// block size [OUT]
+typedef C2StreamParam<C2Info, C2VideoSizeStruct, kParamIndexBlockSize> C2BlockSizeInfo;
+
+// alignment [OUT]
+typedef C2StreamParam<C2Info, C2VideoSizeStruct, kParamIndexAlignment> C2AlignmentInfo;
+
+// frame rate [OUT, hint]
+typedef C2StreamParam<C2Info, C2Uint32Value, kParamIndexFramerate> C2FrameRateInfo;
+
+// blocks-per-second [OUT, hint]
+typedef C2StreamParam<C2Info, C2Uint32Value, kParamIndexBlocksPerSecond> C2BlocksPerSecondInfo;
+
+} // namespace android
+
+#endif // C2AVCCONFIG_H_
diff --git a/media/libstagefright/codecs/avcdec/C2SoftAvcDec.cpp b/media/libstagefright/codecs/avcdec/C2SoftAvcDec.cpp
new file mode 100644
index 0000000..3aa8c38
--- /dev/null
+++ b/media/libstagefright/codecs/avcdec/C2SoftAvcDec.cpp
@@ -0,0 +1,1409 @@
+/*
+ * Copyright 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_NDEBUG 0
+#define LOG_TAG "C2SoftAvcDec"
+#include <utils/Log.h>
+
+#include <cmath>
+#include <thread>
+
+#include "ih264_typedefs.h"
+#include "iv.h"
+#include "ivd.h"
+#include "ih264d.h"
+#include "C2SoftAvcDec.h"
+
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/MediaDefs.h>
+#include <utils/misc.h>
+
+#include "ih264d_defs.h"
+
+namespace {
+
+template <class T>
+inline int32_t floor32(T arg) {
+ return (int32_t) std::llround(std::floor(arg));
+}
+
+} // namespace
+
+namespace android {
+
+struct iv_obj_t : public ::iv_obj_t {};
+struct ivd_video_decode_ip_t : public ::ivd_video_decode_ip_t {};
+struct ivd_video_decode_op_t : public ::ivd_video_decode_op_t {};
+
+#define PRINT_TIME ALOGV
+
+#define componentName "video_decoder.avc"
+// #define codingType OMX_VIDEO_CodingAVC
+#define CODEC_MIME_TYPE MEDIA_MIMETYPE_VIDEO_AVC
+
+/** Function and structure definitions to keep code similar for each codec */
+#define ivdec_api_function ih264d_api_function
+#define ivdext_create_ip_t ih264d_create_ip_t
+#define ivdext_create_op_t ih264d_create_op_t
+#define ivdext_delete_ip_t ih264d_delete_ip_t
+#define ivdext_delete_op_t ih264d_delete_op_t
+#define ivdext_ctl_set_num_cores_ip_t ih264d_ctl_set_num_cores_ip_t
+#define ivdext_ctl_set_num_cores_op_t ih264d_ctl_set_num_cores_op_t
+
+#define IVDEXT_CMD_CTL_SET_NUM_CORES \
+ (IVD_CONTROL_API_COMMAND_TYPE_T)IH264D_CMD_CTL_SET_NUM_CORES
+
+namespace {
+
+using SupportedValuesWithFields = C2SoftAvcDecIntf::SupportedValuesWithFields;
+
+uint32_t restoreIndex(const C2Param *param) {
+ return (param->forStream() ? (0x02000000 | ((param->stream() << 17) & 0x01FE0000)) : 0)
+ | param->type();
+}
+
+struct ValidateParam {
+ explicit ValidateParam(
+ const std::map<C2ParamField, SupportedValuesWithFields> &supportedValues)
+ : mSupportedValues(supportedValues) {}
+
+ template <class T, bool SIGNED = std::is_signed<T>::value, size_t SIZE = sizeof(T)>
+ struct Getter {
+ static T get(const C2Value::Primitive &) {
+ static_assert(!std::is_arithmetic<T>::value, "non-arithmetic type");
+ static_assert(!std::is_floating_point<T>::value || std::is_same<T, float>::value,
+ "float is the only supported floating point type");
+ static_assert(sizeof(T) <= 8, "type exceeds 64-bit");
+ }
+ };
+
+ template <class T>
+ bool validateField(
+ const C2FieldSupportedValues &supportedValues, const T &value) {
+ switch (supportedValues.type) {
+ case C2FieldSupportedValues::RANGE:
+ {
+ // TODO: handle step, nom, denom
+ return Getter<T>::get(supportedValues.range.min) < value
+ && value < Getter<T>::get(supportedValues.range.max);
+ }
+ case C2FieldSupportedValues::VALUES:
+ {
+ for (const auto &val : supportedValues.values) {
+ if (Getter<T>::get(val) == value) {
+ return true;
+ }
+ }
+ return false;
+ }
+ case C2FieldSupportedValues::FLAGS:
+ // TODO
+ return false;
+ }
+ return false;
+ }
+
+protected:
+ const std::map<C2ParamField, SupportedValuesWithFields> &mSupportedValues;
+};
+
+template <>
+struct ValidateParam::Getter<float> {
+ static float get(const C2Value::Primitive &value) { return value.fp; }
+};
+template <class T>
+struct ValidateParam::Getter<T, true, 8u> {
+ static int64_t get(const C2Value::Primitive &value) { return value.i64; }
+};
+template <class T>
+struct ValidateParam::Getter<T, true, 4u> {
+ static int32_t get(const C2Value::Primitive &value) { return value.i32; }
+};
+template <class T>
+struct ValidateParam::Getter<T, false, 8u> {
+ static uint64_t get(const C2Value::Primitive &value) { return value.u64; }
+};
+template <class T>
+struct ValidateParam::Getter<T, false, 4u> {
+ static uint32_t get(const C2Value::Primitive &value) { return value.u32; }
+};
+
+template <class T>
+struct ValidateSimpleParam : public ValidateParam {
+ explicit ValidateSimpleParam(
+ const std::map<C2ParamField, SupportedValuesWithFields> &supportedValues)
+ : ValidateParam(supportedValues) {}
+
+ std::unique_ptr<C2SettingResult> operator() (C2Param *c2param) {
+ T* param = (T*)c2param;
+ C2ParamField field(param, &T::mValue);
+ const C2FieldSupportedValues &supportedValues = mSupportedValues.at(field).supported;
+ if (!validateField(supportedValues, param->mValue)) {
+ return std::unique_ptr<C2SettingResult>(
+ new C2SettingResult {field, C2SettingResult::BAD_VALUE, nullptr, {}});
+ }
+ return nullptr;
+ }
+};
+
+template <class T>
+struct ValidateVideoSize : public ValidateParam {
+ explicit ValidateVideoSize(
+ const std::map<C2ParamField, SupportedValuesWithFields> &supportedValues)
+ : ValidateParam(supportedValues) {}
+
+ std::unique_ptr<C2SettingResult> operator() (C2Param *c2param) {
+ T* param = (T*)c2param;
+ C2ParamField field(param, &T::mWidth);
+ const C2FieldSupportedValues &supportedWidth = mSupportedValues.at(field).supported;
+ if (!validateField(supportedWidth, param->mWidth)) {
+ return std::unique_ptr<C2SettingResult>(
+ new C2SettingResult {field, C2SettingResult::BAD_VALUE, nullptr, {}});
+ }
+ field = C2ParamField(param, &T::mHeight);
+ const C2FieldSupportedValues &supportedHeight = mSupportedValues.at(field).supported;
+ if (!validateField(supportedHeight, param->mHeight)) {
+ return std::unique_ptr<C2SettingResult>(
+ new C2SettingResult {field, C2SettingResult::BAD_VALUE, nullptr, {}});
+ }
+ return nullptr;
+ }
+};
+
+template <class T>
+struct ValidateCString {
+ explicit ValidateCString(const char *expected) : mExpected(expected) {}
+
+ std::unique_ptr<C2SettingResult> operator() (C2Param *c2param) {
+ T* param = (T*)c2param;
+ if (strncmp(param->m.mValue, mExpected, param->flexCount()) != 0) {
+ return std::unique_ptr<C2SettingResult>(
+ new C2SettingResult {C2ParamField(param, &T::m), C2SettingResult::BAD_VALUE, nullptr, {}});
+ }
+ return nullptr;
+ }
+
+private:
+ const char *mExpected;
+};
+
+class GraphicBuffer : public C2Buffer {
+public:
+ explicit GraphicBuffer(const std::shared_ptr<C2GraphicBlock> &block)
+ : C2Buffer({ block->share(C2Rect(block->width(), block->height()), ::android::C2Fence()) }) {}
+};
+
+} // namespace
+
+#define CASE(member) \
+ case decltype(component->member)::baseIndex: \
+ return std::unique_ptr<C2StructDescriptor>(new C2StructDescriptor( \
+ static_cast<decltype(component->member) *>(nullptr)))
+
+class C2SoftAvcDecIntf::ParamReflector : public C2ParamReflector {
+public:
+ virtual std::unique_ptr<C2StructDescriptor> describe(C2Param::BaseIndex paramIndex) override {
+ constexpr C2SoftAvcDecIntf *component = nullptr;
+ switch (paramIndex.baseIndex()) {
+ CASE(mDomainInfo);
+ CASE(mInputStreamCount);
+ CASE(mInputStreamFormat);
+ // Output counterparts for the above would be redundant.
+ CASE(mVideoSize);
+ CASE(mMaxVideoSizeHint);
+
+ // port mime configs are stored as unique_ptr.
+ case C2PortMimeConfig::baseIndex:
+ return std::unique_ptr<C2StructDescriptor>(new C2StructDescriptor(
+ static_cast<C2PortMimeConfig *>(nullptr)));
+ }
+ return nullptr;
+ }
+};
+#undef CASE
+
+// static const CodecProfileLevel kProfileLevels[] = {
+// { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel52 },
+// { OMX_VIDEO_AVCProfileMain, OMX_VIDEO_AVCLevel52 },
+// { OMX_VIDEO_AVCProfileHigh, OMX_VIDEO_AVCLevel52 },
+// };
+C2SoftAvcDecIntf::C2SoftAvcDecIntf(const char *name, node_id id)
+ : mName(name),
+ mId(id),
+ mDomainInfo(C2DomainVideo),
+ mInputStreamCount(1u),
+ mOutputStreamCount(1u),
+ mInputStreamFormat(0u, C2FormatCompressed),
+ mOutputStreamFormat(0u, C2FormatVideo),
+ mProfile(0u, kAvcProfileUnknown),
+ mLevel(0u, kAvcLevelUnknown),
+ mBlockSize(0u),
+ mAlignment(0u),
+ mFrameRate(0u, 0),
+ mBlocksPerSecond(0u, 0),
+ mParamReflector(new ParamReflector) {
+
+ mInputPortMime = C2PortMimeConfig::input::alloc_unique(strlen(CODEC_MIME_TYPE) + 1);
+ strcpy(mInputPortMime->m.mValue, CODEC_MIME_TYPE);
+ mOutputPortMime = C2PortMimeConfig::output::alloc_unique(strlen(MEDIA_MIMETYPE_VIDEO_RAW) + 1);
+ strcpy(mOutputPortMime->m.mValue, MEDIA_MIMETYPE_VIDEO_RAW);
+
+ mVideoSize.mWidth = 320;
+ mVideoSize.mHeight = 240;
+ mBlockSize.mWidth = 16;
+ mBlockSize.mHeight = 16;
+ mAlignment.mWidth = 2;
+ mAlignment.mHeight = 2;
+
+ mMaxVideoSizeHint.mWidth = H264_MAX_FRAME_WIDTH;
+ mMaxVideoSizeHint.mHeight = H264_MAX_FRAME_HEIGHT;
+
+ auto insertParam = [¶ms = mParams] (C2Param *param) {
+ params[restoreIndex(param)] = param;
+ };
+
+ auto markReadOnly = [&supported = mSupportedValues] (auto *param) {
+ supported.emplace(
+ C2ParamField(param, &std::remove_pointer<decltype(param)>::type::mValue),
+ C2FieldSupportedValues(false /* flags */, {}));
+ };
+
+ auto markReadOnlyVideoSize = [&supported = mSupportedValues] (auto *param) {
+ supported.emplace(
+ C2ParamField(param, &std::remove_pointer<decltype(param)>::type::mWidth),
+ C2FieldSupportedValues(false /* flags */, {}));
+ supported.emplace(
+ C2ParamField(param, &std::remove_pointer<decltype(param)>::type::mHeight),
+ C2FieldSupportedValues(false /* flags */, {}));
+ };
+
+ insertParam(&mDomainInfo);
+ markReadOnly(&mDomainInfo);
+ mFieldVerifiers[restoreIndex(&mDomainInfo)] =
+ ValidateSimpleParam<decltype(mDomainInfo)>(mSupportedValues);
+
+ insertParam(mInputPortMime.get());
+ mFieldVerifiers[restoreIndex(mInputPortMime.get())] =
+ ValidateCString<std::remove_reference<decltype(*mInputPortMime)>::type>(CODEC_MIME_TYPE);
+
+ insertParam(&mInputStreamCount);
+ markReadOnly(&mInputStreamCount);
+ mFieldVerifiers[restoreIndex(&mInputStreamCount)] =
+ ValidateSimpleParam<decltype(mInputStreamCount)>(mSupportedValues);
+
+ insertParam(mOutputPortMime.get());
+ mFieldVerifiers[restoreIndex(mOutputPortMime.get())] =
+ ValidateCString<std::remove_reference<decltype(*mOutputPortMime)>::type>(MEDIA_MIMETYPE_VIDEO_RAW);
+
+ insertParam(&mOutputStreamCount);
+ markReadOnly(&mOutputStreamCount);
+ mFieldVerifiers[restoreIndex(&mOutputStreamCount)] =
+ ValidateSimpleParam<decltype(mOutputStreamCount)>(mSupportedValues);
+
+ insertParam(&mInputStreamFormat);
+ markReadOnly(&mInputStreamFormat);
+ mFieldVerifiers[restoreIndex(&mInputStreamFormat)] =
+ ValidateSimpleParam<decltype(mInputStreamFormat)>(mSupportedValues);
+
+ insertParam(&mOutputStreamFormat);
+ markReadOnly(&mOutputStreamFormat);
+ mFieldVerifiers[restoreIndex(&mOutputStreamFormat)] =
+ ValidateSimpleParam<decltype(mOutputStreamFormat)>(mSupportedValues);
+
+ insertParam(&mVideoSize);
+ markReadOnlyVideoSize(&mVideoSize);
+ mFieldVerifiers[restoreIndex(&mVideoSize)] =
+ ValidateVideoSize<decltype(mVideoSize)>(mSupportedValues);
+
+ insertParam(&mMaxVideoSizeHint);
+ mSupportedValues.emplace(
+ C2ParamField(&mMaxVideoSizeHint, &C2MaxVideoSizeHintPortSetting::mWidth),
+ C2FieldSupportedValues(H264_MIN_FRAME_WIDTH, H264_MAX_FRAME_WIDTH, mAlignment.mWidth));
+ mSupportedValues.emplace(
+ C2ParamField(&mMaxVideoSizeHint, &C2MaxVideoSizeHintPortSetting::mHeight),
+ C2FieldSupportedValues(H264_MIN_FRAME_HEIGHT, H264_MAX_FRAME_HEIGHT, mAlignment.mHeight));
+ mFieldVerifiers[restoreIndex(&mMaxVideoSizeHint)] =
+ ValidateVideoSize<decltype(mMaxVideoSizeHint)>(mSupportedValues);
+
+ insertParam(&mProfile);
+ mSupportedValues.emplace(
+ C2ParamField(&mProfile, &C2AvcProfileInfo::mValue),
+ C2FieldSupportedValues(false /* flags */, {
+ kAvcProfileUnknown,
+ kAvcProfileBaseline,
+ kAvcProfileMain,
+ kAvcProfileHigh,
+ }));
+ mFieldVerifiers[restoreIndex(&mProfile)] =
+ ValidateSimpleParam<decltype(mProfile)>(mSupportedValues);
+
+ insertParam(&mLevel);
+ mSupportedValues.emplace(
+ C2ParamField(&mLevel, &C2AvcLevelInfo::mValue),
+ C2FieldSupportedValues(false /* flags */, {
+ kAvcLevelUnknown,
+ kAvcLevel10,
+ kAvcLevel1b,
+ kAvcLevel11,
+ kAvcLevel12,
+ kAvcLevel13,
+ kAvcLevel20,
+ kAvcLevel21,
+ kAvcLevel22,
+ kAvcLevel30,
+ kAvcLevel31,
+ kAvcLevel32,
+ kAvcLevel40,
+ kAvcLevel41,
+ kAvcLevel42,
+ kAvcLevel50,
+ kAvcLevel51,
+ kAvcLevel52,
+ }));
+ mFieldVerifiers[restoreIndex(&mLevel)] =
+ ValidateSimpleParam<decltype(mLevel)>(mSupportedValues);
+
+ insertParam(&mBlockSize);
+ markReadOnlyVideoSize(&mBlockSize);
+ mFieldVerifiers[restoreIndex(&mBlockSize)] =
+ ValidateVideoSize<decltype(mBlockSize)>(mSupportedValues);
+
+ insertParam(&mAlignment);
+ markReadOnlyVideoSize(&mAlignment);
+ mFieldVerifiers[restoreIndex(&mAlignment)] =
+ ValidateVideoSize<decltype(mAlignment)>(mSupportedValues);
+
+ insertParam(&mFrameRate);
+ mSupportedValues.emplace(
+ C2ParamField(&mFrameRate, &C2FrameRateInfo::mValue),
+ C2FieldSupportedValues(0, 240));
+ mFieldVerifiers[restoreIndex(&mFrameRate)] =
+ ValidateSimpleParam<decltype(mFrameRate)>(mSupportedValues);
+
+ insertParam(&mBlocksPerSecond);
+ mSupportedValues.emplace(
+ C2ParamField(&mFrameRate, &C2BlocksPerSecondInfo::mValue),
+ C2FieldSupportedValues(0, 244800));
+ mFieldVerifiers[restoreIndex(&mBlocksPerSecond)] =
+ ValidateSimpleParam<decltype(mBlocksPerSecond)>(mSupportedValues);
+
+ mParamDescs.push_back(std::make_shared<C2ParamDescriptor>(
+ true, "_domain", &mDomainInfo));
+ mParamDescs.push_back(std::make_shared<C2ParamDescriptor>(
+ true, "_input_port_mime", mInputPortMime.get()));
+ mParamDescs.push_back(std::make_shared<C2ParamDescriptor>(
+ true, "_input_stream_count", &mInputStreamCount));
+ mParamDescs.push_back(std::make_shared<C2ParamDescriptor>(
+ true, "_output_port_mime", mOutputPortMime.get()));
+ mParamDescs.push_back(std::make_shared<C2ParamDescriptor>(
+ true, "_output_stream_count", &mOutputStreamCount));
+ mParamDescs.push_back(std::make_shared<C2ParamDescriptor>(
+ true, "_input_stream_format", &mInputStreamFormat));
+ mParamDescs.push_back(std::make_shared<C2ParamDescriptor>(
+ true, "_output_stream_format", &mOutputStreamFormat));
+ mParamDescs.push_back(std::make_shared<C2ParamDescriptor>(
+ false, "_video_size", &mVideoSize));
+ mParamDescs.push_back(std::make_shared<C2ParamDescriptor>(
+ false, "_max_video_size_hint", &mMaxVideoSizeHint));
+}
+
+C2String C2SoftAvcDecIntf::getName() const {
+ return mName;
+}
+
+node_id C2SoftAvcDecIntf::getId() const {
+ return mId;
+}
+
+status_t C2SoftAvcDecIntf::query_nb(
+ const std::vector<C2Param* const> & stackParams,
+ const std::vector<C2Param::Index> & heapParamIndices,
+ std::vector<std::unique_ptr<C2Param>>* const heapParams) const {
+ for (C2Param* const param : stackParams) {
+ if (!*param) {
+ continue;
+ }
+
+ uint32_t index = restoreIndex(param);
+ if (!mParams.count(index)) {
+ continue;
+ }
+
+ C2Param *myParam = mParams.find(index)->second;
+ if (myParam->size() != param->size()) {
+ param->invalidate();
+ continue;
+ }
+
+ param->updateFrom(*myParam);
+ }
+
+ for (const C2Param::Index index : heapParamIndices) {
+ if (mParams.count(index)) {
+ C2Param *myParam = mParams.find(index)->second;
+ heapParams->emplace_back(C2Param::Copy(*myParam));
+ }
+ }
+
+ return C2_OK;
+}
+
+status_t C2SoftAvcDecIntf::config_nb(
+ const std::vector<C2Param* const> ¶ms,
+ std::vector<std::unique_ptr<C2SettingResult>>* const failures) {
+ status_t err = C2_OK;
+ for (C2Param *param : params) {
+ uint32_t index = restoreIndex(param);
+ if (mParams.count(index) == 0) {
+ // We can't create C2SettingResult with no field, so just skipping in this case.
+ err = C2_BAD_INDEX;
+ continue;
+ }
+ C2Param *myParam = mParams.find(index)->second;
+ std::unique_ptr<C2SettingResult> result;
+ if (!(result = mFieldVerifiers[index](param))) {
+ myParam->updateFrom(*param);
+ updateSupportedValues();
+ } else {
+ failures->push_back(std::move(result));
+ err = C2_BAD_VALUE;
+ }
+ }
+ return err;
+}
+
+status_t C2SoftAvcDecIntf::commit_sm(
+ const std::vector<C2Param* const> ¶ms,
+ std::vector<std::unique_ptr<C2SettingResult>>* const failures) {
+ // TODO
+ return config_nb(params, failures);
+}
+
+status_t C2SoftAvcDecIntf::createTunnel_sm(node_id targetComponent) {
+ // Tunneling is not supported
+ (void) targetComponent;
+ return C2_UNSUPPORTED;
+}
+
+status_t C2SoftAvcDecIntf::releaseTunnel_sm(node_id targetComponent) {
+ // Tunneling is not supported
+ (void) targetComponent;
+ return C2_UNSUPPORTED;
+}
+
+std::shared_ptr<C2ParamReflector> C2SoftAvcDecIntf::getParamReflector() const {
+ return mParamReflector;
+}
+
+status_t C2SoftAvcDecIntf::getSupportedParams(
+ std::vector<std::shared_ptr<C2ParamDescriptor>> * const params) const {
+ params->insert(params->begin(), mParamDescs.begin(), mParamDescs.end());
+ return C2_OK;
+}
+
+status_t C2SoftAvcDecIntf::getSupportedValues(
+ const std::vector<const C2ParamField> &fields,
+ std::vector<C2FieldSupportedValues>* const values) const {
+ for (const auto &field : fields) {
+ if (mSupportedValues.count(field) == 0) {
+ return BAD_VALUE;
+ }
+ values->push_back(mSupportedValues.at(field).supported);
+ }
+ return C2_OK;
+}
+
+void C2SoftAvcDecIntf::updateSupportedValues() {
+ int32_t maxWidth = H264_MAX_FRAME_WIDTH;
+ int32_t maxHeight = H264_MAX_FRAME_HEIGHT;
+ // cf: Rec. ITU-T H.264 A.3
+ int maxFrameRate = 172;
+ std::vector<C2ParamField> fields;
+ if (mLevel.mValue != kAvcLevelUnknown) {
+ // cf: Rec. ITU-T H.264 Table A-1
+ constexpr int MaxFS[] = {
+ // 0 1 2 3 4 5 6 7 8 9
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 99,
+ 99, 396, 396, 396, 0, 0, 0, 0, 0, 0,
+ 396, 792, 1620, 0, 0, 0, 0, 0, 0, 0,
+ 1620, 3600, 5120, 0, 0, 0, 0, 0, 0, 0,
+ 8192, 8192, 8704, 0, 0, 0, 0, 0, 0, 0,
+ 22080, 36864, 36864,
+ };
+ constexpr int MaxMBPS[] = {
+ // 0 1 2 3 4 5 6 7 8 9
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 1485,
+ 1485, 3000, 6000, 11880, 0, 0, 0, 0, 0, 0,
+ 11880, 19800, 20250, 0, 0, 0, 0, 0, 0, 0,
+ 40500, 108000, 216000, 0, 0, 0, 0, 0, 0, 0,
+ 245760, 245760, 522240, 0, 0, 0, 0, 0, 0, 0,
+ 589824, 983040, 2073600,
+ };
+
+ // cf: Rec. ITU-T H.264 A.3.1
+ maxWidth = std::min(maxWidth, floor32(std::sqrt(MaxFS[mLevel.mValue] * 8)) * MB_SIZE);
+ maxHeight = std::min(maxHeight, floor32(std::sqrt(MaxFS[mLevel.mValue] * 8)) * MB_SIZE);
+ int32_t MBs = ((mVideoSize.mWidth + 15) / 16) * ((mVideoSize.mHeight + 15) / 16);
+ maxFrameRate = std::min(maxFrameRate, MaxMBPS[mLevel.mValue] / MBs);
+ fields.push_back(C2ParamField(&mLevel, &C2AvcLevelInfo::mValue));
+ }
+
+ SupportedValuesWithFields &maxWidthVals = mSupportedValues.at(
+ C2ParamField(&mMaxVideoSizeHint, &C2MaxVideoSizeHintPortSetting::mWidth));
+ maxWidthVals.supported.range.max = maxWidth;
+ maxWidthVals.restrictingFields.clear();
+ maxWidthVals.restrictingFields.insert(fields.begin(), fields.end());
+
+ SupportedValuesWithFields &maxHeightVals = mSupportedValues.at(
+ C2ParamField(&mMaxVideoSizeHint, &C2MaxVideoSizeHintPortSetting::mHeight));
+ maxHeightVals.supported.range.max = maxHeight;
+ maxHeightVals.restrictingFields.clear();
+ maxHeightVals.restrictingFields.insert(fields.begin(), fields.end());
+
+ SupportedValuesWithFields &frameRate = mSupportedValues.at(
+ C2ParamField(&mFrameRate, &C2FrameRateInfo::mValue));
+ frameRate.supported.range.max = maxFrameRate;
+ frameRate.restrictingFields.clear();
+ frameRate.restrictingFields.insert(fields.begin(), fields.end());
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+class C2SoftAvcDec::QueueProcessThread {
+public:
+ QueueProcessThread() : mExitRequested(false), mRunning(false) {}
+
+ ~QueueProcessThread() {
+ if (mThread && mThread->joinable()) {
+ mExitRequested = true;
+ mThread->join();
+ }
+ }
+
+ void start(std::weak_ptr<C2SoftAvcDec> component) {
+ mThread.reset(new std::thread([this, component] () {
+ mRunning = true;
+ while (auto comp = component.lock()) {
+ if (mExitRequested) break;
+ comp->processQueue();
+ }
+ mRunning = false;
+ }));
+ }
+
+ void requestExit() {
+ mExitRequested = true;
+ }
+
+ bool isRunning() {
+ return mRunning;
+ }
+
+private:
+ std::atomic_bool mExitRequested;
+ std::atomic_bool mRunning;
+ std::unique_ptr<std::thread> mThread;
+};
+
+C2SoftAvcDec::C2SoftAvcDec(
+ const char *name,
+ node_id id,
+ const std::shared_ptr<C2ComponentListener> &listener)
+ : mIntf(std::make_shared<C2SoftAvcDecIntf>(name, id)),
+ mListener(listener),
+ mThread(new QueueProcessThread),
+ mCodecCtx(NULL),
+ mFlushOutBuffer(NULL),
+ mIvColorFormat(IV_YUV_420P),
+ mChangingResolution(false),
+ mSignalledError(false),
+ mWidth(320),
+ mHeight(240),
+ mInputOffset(0) {
+ GETTIME(&mTimeStart, NULL);
+
+ // If input dump is enabled, then open create an empty file
+ GENERATE_FILE_NAMES();
+ CREATE_DUMP_FILE(mInFile);
+}
+
+C2SoftAvcDec::~C2SoftAvcDec() {
+ CHECK_EQ(deInitDecoder(), (status_t)OK);
+}
+
+status_t C2SoftAvcDec::queue_nb(
+ std::list<std::unique_ptr<C2Work>>* const items) {
+ if (!mThread->isRunning()) {
+ return C2_CORRUPTED;
+ }
+ std::unique_lock<std::mutex> lock(mQueueLock);
+ while (!items->empty()) {
+ // TODO: examine item and update width/height?
+ mQueue.emplace_back(std::move(items->front()));
+ items->pop_front();
+ }
+ mQueueCond.notify_all();
+ return C2_OK;
+}
+
+status_t C2SoftAvcDec::announce_nb(const std::vector<C2WorkOutline> &items) {
+ // Tunneling is not supported
+ (void) items;
+ return C2_UNSUPPORTED;
+}
+
+status_t C2SoftAvcDec::flush_sm(
+ bool flushThrough, std::list<std::unique_ptr<C2Work>>* const flushedWork) {
+ // Tunneling is not supported
+ (void) flushThrough;
+
+ if (!mThread->isRunning()) {
+ return C2_CORRUPTED;
+ }
+ {
+ std::unique_lock<std::mutex> lock(mQueueLock);
+ while (!mQueue.empty()) {
+ flushedWork->emplace_back(std::move(mQueue.front()));
+ mQueue.pop_front();
+ }
+ mQueueCond.notify_all();
+ }
+ {
+ std::unique_lock<std::mutex> lock(mPendingLock);
+ for (auto &elem : mPendingWork) {
+ flushedWork->emplace_back(std::move(elem.second));
+ }
+ mPendingWork.clear();
+ }
+ return C2_OK;
+}
+
+status_t C2SoftAvcDec::drain_nb(bool drainThrough) {
+ // Tunneling is not supported
+ (void) drainThrough;
+
+ if (!mThread->isRunning()) {
+ return C2_CORRUPTED;
+ }
+ std::unique_lock<std::mutex> lock(mQueueLock);
+ if (!mQueue.empty()) {
+ C2BufferPack &lastInput = mQueue.back()->input;
+ lastInput.flags = (flags_t)(lastInput.flags | BUFFERFLAG_END_OF_STREAM);
+ mQueueCond.notify_all();
+ }
+ return C2_OK;
+}
+
+status_t C2SoftAvcDec::start() {
+ if (!mThread->isRunning()) {
+ mThread->start(shared_from_this());
+ }
+ return C2_OK;
+}
+
+status_t C2SoftAvcDec::stop() {
+ ALOGV("stop");
+ std::chrono::system_clock::time_point now = std::chrono::system_clock::now();
+ std::chrono::system_clock::time_point deadline = now + std::chrono::milliseconds(500);
+
+ mThread->requestExit();
+ while (mThread->isRunning() && (now = std::chrono::system_clock::now()) < deadline) {
+ std::this_thread::yield();
+ std::unique_lock<std::mutex> lock(mQueueLock);
+ mQueueCond.notify_all();
+ }
+ if (mThread->isRunning()) {
+ return C2_TIMED_OUT;
+ }
+
+ mSignalledError = false;
+ resetDecoder();
+ resetPlugin();
+
+ return C2_OK;
+}
+
+void C2SoftAvcDec::reset() {
+ if (mThread->isRunning()) {
+ stop();
+ }
+ // TODO
+}
+
+void C2SoftAvcDec::release() {
+ if (mThread->isRunning()) {
+ stop();
+ }
+ // TODO
+}
+
+std::shared_ptr<C2ComponentInterface> C2SoftAvcDec::intf() {
+ return mIntf;
+}
+
+void C2SoftAvcDec::processQueue() {
+ if (mIsInFlush) {
+ setFlushMode();
+
+ /* Allocate a picture buffer to flushed data */
+ uint32_t displayStride = mWidth;
+ uint32_t displayHeight = mHeight;
+
+ uint32_t bufferSize = displayStride * displayHeight * 3 / 2;
+ mFlushOutBuffer = (uint8_t *)memalign(128, bufferSize);
+ if (NULL == mFlushOutBuffer) {
+ ALOGE("Could not allocate flushOutputBuffer of size %u", bufferSize);
+ return;
+ }
+
+ while (true) {
+ ivd_video_decode_ip_t s_dec_ip;
+ ivd_video_decode_op_t s_dec_op;
+ IV_API_CALL_STATUS_T status;
+ size_t sizeY, sizeUV;
+
+ setDecodeArgs(&s_dec_ip, &s_dec_op, NULL, NULL, 0, 0u);
+
+ status = ivdec_api_function(mCodecCtx, (void *)&s_dec_ip, (void *)&s_dec_op);
+ if (0 == s_dec_op.u4_output_present) {
+ resetPlugin();
+ break;
+ }
+ }
+
+ if (mFlushOutBuffer) {
+ free(mFlushOutBuffer);
+ mFlushOutBuffer = NULL;
+ }
+ mIsInFlush = false;
+ }
+
+ std::unique_ptr<C2Work> work;
+ {
+ std::unique_lock<std::mutex> lock(mQueueLock);
+ if (mQueue.empty()) {
+ mQueueCond.wait(lock);
+ }
+ if (mQueue.empty()) {
+ ALOGV("empty queue");
+ return;
+ }
+ work.swap(mQueue.front());
+ mQueue.pop_front();
+ }
+
+ // Process the work
+ process(work);
+
+ std::vector<std::unique_ptr<C2Work>> done;
+ {
+ std::unique_lock<std::mutex> lock(mPendingLock);
+ uint32_t index = work->input.ordinal.frame_index;
+ mPendingWork[index].swap(work);
+
+ if (work) {
+ work->result = C2_CORRUPTED;
+ done.emplace_back(std::move(work));
+ }
+ }
+
+ if (!done.empty()) {
+ mListener->onWorkDone(shared_from_this(), std::move(done));
+ }
+}
+
+
+static void *ivd_aligned_malloc(void *ctxt, WORD32 alignment, WORD32 size) {
+ UNUSED(ctxt);
+ return memalign(alignment, size);
+}
+
+static void ivd_aligned_free(void *ctxt, void *buf) {
+ UNUSED(ctxt);
+ free(buf);
+ return;
+}
+
+static size_t GetCPUCoreCount() {
+ long cpuCoreCount = 1;
+#if defined(_SC_NPROCESSORS_ONLN)
+ cpuCoreCount = sysconf(_SC_NPROCESSORS_ONLN);
+#else
+ // _SC_NPROC_ONLN must be defined...
+ cpuCoreCount = sysconf(_SC_NPROC_ONLN);
+#endif
+ CHECK(cpuCoreCount >= 1);
+ ALOGV("Number of CPU cores: %ld", cpuCoreCount);
+ return (size_t)cpuCoreCount;
+}
+
+void C2SoftAvcDec::logVersion() {
+ ivd_ctl_getversioninfo_ip_t s_ctl_ip;
+ ivd_ctl_getversioninfo_op_t s_ctl_op;
+ UWORD8 au1_buf[512];
+ IV_API_CALL_STATUS_T status;
+
+ s_ctl_ip.e_cmd = IVD_CMD_VIDEO_CTL;
+ s_ctl_ip.e_sub_cmd = IVD_CMD_CTL_GETVERSION;
+ s_ctl_ip.u4_size = sizeof(ivd_ctl_getversioninfo_ip_t);
+ s_ctl_op.u4_size = sizeof(ivd_ctl_getversioninfo_op_t);
+ s_ctl_ip.pv_version_buffer = au1_buf;
+ s_ctl_ip.u4_version_buffer_size = sizeof(au1_buf);
+
+ status =
+ ivdec_api_function(mCodecCtx, (void *)&s_ctl_ip, (void *)&s_ctl_op);
+
+ if (status != IV_SUCCESS) {
+ ALOGE("Error in getting version number: 0x%x",
+ s_ctl_op.u4_error_code);
+ } else {
+ ALOGV("Ittiam decoder version number: %s",
+ (char *)s_ctl_ip.pv_version_buffer);
+ }
+ return;
+}
+
+status_t C2SoftAvcDec::setParams(size_t stride) {
+ ivd_ctl_set_config_ip_t s_ctl_ip;
+ ivd_ctl_set_config_op_t s_ctl_op;
+ IV_API_CALL_STATUS_T status;
+ s_ctl_ip.u4_disp_wd = (UWORD32)stride;
+ s_ctl_ip.e_frm_skip_mode = IVD_SKIP_NONE;
+
+ s_ctl_ip.e_frm_out_mode = IVD_DISPLAY_FRAME_OUT;
+ s_ctl_ip.e_vid_dec_mode = IVD_DECODE_FRAME;
+ s_ctl_ip.e_cmd = IVD_CMD_VIDEO_CTL;
+ s_ctl_ip.e_sub_cmd = IVD_CMD_CTL_SETPARAMS;
+ s_ctl_ip.u4_size = sizeof(ivd_ctl_set_config_ip_t);
+ s_ctl_op.u4_size = sizeof(ivd_ctl_set_config_op_t);
+
+ ALOGV("Set the run-time (dynamic) parameters stride = %zu", stride);
+ status = ivdec_api_function(mCodecCtx, (void *)&s_ctl_ip, (void *)&s_ctl_op);
+
+ if (status != IV_SUCCESS) {
+ ALOGE("Error in setting the run-time parameters: 0x%x",
+ s_ctl_op.u4_error_code);
+
+ return UNKNOWN_ERROR;
+ }
+ return OK;
+}
+
+status_t C2SoftAvcDec::resetPlugin() {
+ mReceivedEOS = false;
+ mInputOffset = 0;
+
+ /* Initialize both start and end times */
+ gettimeofday(&mTimeStart, NULL);
+ gettimeofday(&mTimeEnd, NULL);
+
+ return OK;
+}
+
+status_t C2SoftAvcDec::resetDecoder() {
+ ivd_ctl_reset_ip_t s_ctl_ip;
+ ivd_ctl_reset_op_t s_ctl_op;
+ IV_API_CALL_STATUS_T status;
+
+ s_ctl_ip.e_cmd = IVD_CMD_VIDEO_CTL;
+ s_ctl_ip.e_sub_cmd = IVD_CMD_CTL_RESET;
+ s_ctl_ip.u4_size = sizeof(ivd_ctl_reset_ip_t);
+ s_ctl_op.u4_size = sizeof(ivd_ctl_reset_op_t);
+
+ status = ivdec_api_function(mCodecCtx, (void *)&s_ctl_ip, (void *)&s_ctl_op);
+ if (IV_SUCCESS != status) {
+ ALOGE("Error in reset: 0x%x", s_ctl_op.u4_error_code);
+ return UNKNOWN_ERROR;
+ }
+ mSignalledError = false;
+
+ /* Set number of cores/threads to be used by the codec */
+ setNumCores();
+
+ mStride = 0;
+ return OK;
+}
+
+status_t C2SoftAvcDec::setNumCores() {
+ ivdext_ctl_set_num_cores_ip_t s_set_cores_ip;
+ ivdext_ctl_set_num_cores_op_t s_set_cores_op;
+ IV_API_CALL_STATUS_T status;
+ s_set_cores_ip.e_cmd = IVD_CMD_VIDEO_CTL;
+ s_set_cores_ip.e_sub_cmd = IVDEXT_CMD_CTL_SET_NUM_CORES;
+ s_set_cores_ip.u4_num_cores = MIN(mNumCores, CODEC_MAX_NUM_CORES);
+ s_set_cores_ip.u4_size = sizeof(ivdext_ctl_set_num_cores_ip_t);
+ s_set_cores_op.u4_size = sizeof(ivdext_ctl_set_num_cores_op_t);
+ status = ivdec_api_function(
+ mCodecCtx, (void *)&s_set_cores_ip, (void *)&s_set_cores_op);
+ if (IV_SUCCESS != status) {
+ ALOGE("Error in setting number of cores: 0x%x",
+ s_set_cores_op.u4_error_code);
+ return UNKNOWN_ERROR;
+ }
+ return OK;
+}
+
+status_t C2SoftAvcDec::setFlushMode() {
+ IV_API_CALL_STATUS_T status;
+ ivd_ctl_flush_ip_t s_video_flush_ip;
+ ivd_ctl_flush_op_t s_video_flush_op;
+
+ s_video_flush_ip.e_cmd = IVD_CMD_VIDEO_CTL;
+ s_video_flush_ip.e_sub_cmd = IVD_CMD_CTL_FLUSH;
+ s_video_flush_ip.u4_size = sizeof(ivd_ctl_flush_ip_t);
+ s_video_flush_op.u4_size = sizeof(ivd_ctl_flush_op_t);
+
+ /* Set the decoder in Flush mode, subsequent decode() calls will flush */
+ status = ivdec_api_function(
+ mCodecCtx, (void *)&s_video_flush_ip, (void *)&s_video_flush_op);
+
+ if (status != IV_SUCCESS) {
+ ALOGE("Error in setting the decoder in flush mode: (%d) 0x%x", status,
+ s_video_flush_op.u4_error_code);
+ return UNKNOWN_ERROR;
+ }
+
+ return OK;
+}
+
+status_t C2SoftAvcDec::initDecoder() {
+ IV_API_CALL_STATUS_T status;
+
+ mNumCores = GetCPUCoreCount();
+ mCodecCtx = NULL;
+
+ mStride = mWidth;
+
+ /* Initialize the decoder */
+ {
+ ivdext_create_ip_t s_create_ip;
+ ivdext_create_op_t s_create_op;
+
+ void *dec_fxns = (void *)ivdec_api_function;
+
+ s_create_ip.s_ivd_create_ip_t.u4_size = sizeof(ivdext_create_ip_t);
+ s_create_ip.s_ivd_create_ip_t.e_cmd = IVD_CMD_CREATE;
+ s_create_ip.s_ivd_create_ip_t.u4_share_disp_buf = 0;
+ s_create_op.s_ivd_create_op_t.u4_size = sizeof(ivdext_create_op_t);
+ s_create_ip.s_ivd_create_ip_t.e_output_format = (IV_COLOR_FORMAT_T)mIvColorFormat;
+ s_create_ip.s_ivd_create_ip_t.pf_aligned_alloc = ivd_aligned_malloc;
+ s_create_ip.s_ivd_create_ip_t.pf_aligned_free = ivd_aligned_free;
+ s_create_ip.s_ivd_create_ip_t.pv_mem_ctxt = NULL;
+
+ status = ivdec_api_function(mCodecCtx, (void *)&s_create_ip, (void *)&s_create_op);
+
+ mCodecCtx = (iv_obj_t*)s_create_op.s_ivd_create_op_t.pv_handle;
+ mCodecCtx->pv_fxns = dec_fxns;
+ mCodecCtx->u4_size = sizeof(iv_obj_t);
+
+ if (status != IV_SUCCESS) {
+ ALOGE("Error in create: 0x%x",
+ s_create_op.s_ivd_create_op_t.u4_error_code);
+ deInitDecoder();
+ mCodecCtx = NULL;
+ return UNKNOWN_ERROR;
+ }
+ }
+
+ /* Reset the plugin state */
+ resetPlugin();
+
+ /* Set the run time (dynamic) parameters */
+ setParams(mStride);
+
+ /* Set number of cores/threads to be used by the codec */
+ setNumCores();
+
+ /* Get codec version */
+ logVersion();
+
+ mFlushNeeded = false;
+ return OK;
+}
+
+status_t C2SoftAvcDec::deInitDecoder() {
+ size_t i;
+ IV_API_CALL_STATUS_T status;
+
+ if (mCodecCtx) {
+ ivdext_delete_ip_t s_delete_ip;
+ ivdext_delete_op_t s_delete_op;
+
+ s_delete_ip.s_ivd_delete_ip_t.u4_size = sizeof(ivdext_delete_ip_t);
+ s_delete_ip.s_ivd_delete_ip_t.e_cmd = IVD_CMD_DELETE;
+
+ s_delete_op.s_ivd_delete_op_t.u4_size = sizeof(ivdext_delete_op_t);
+
+ status = ivdec_api_function(mCodecCtx, (void *)&s_delete_ip, (void *)&s_delete_op);
+ if (status != IV_SUCCESS) {
+ ALOGE("Error in delete: 0x%x",
+ s_delete_op.s_ivd_delete_op_t.u4_error_code);
+ return UNKNOWN_ERROR;
+ }
+ }
+
+
+ mChangingResolution = false;
+
+ return OK;
+}
+
+bool C2SoftAvcDec::getVUIParams() {
+ IV_API_CALL_STATUS_T status;
+ ih264d_ctl_get_vui_params_ip_t s_ctl_get_vui_params_ip;
+ ih264d_ctl_get_vui_params_op_t s_ctl_get_vui_params_op;
+
+ s_ctl_get_vui_params_ip.e_cmd = IVD_CMD_VIDEO_CTL;
+ s_ctl_get_vui_params_ip.e_sub_cmd =
+ (IVD_CONTROL_API_COMMAND_TYPE_T)IH264D_CMD_CTL_GET_VUI_PARAMS;
+
+ s_ctl_get_vui_params_ip.u4_size =
+ sizeof(ih264d_ctl_get_vui_params_ip_t);
+
+ s_ctl_get_vui_params_op.u4_size = sizeof(ih264d_ctl_get_vui_params_op_t);
+
+ status = ivdec_api_function(
+ (iv_obj_t *)mCodecCtx, (void *)&s_ctl_get_vui_params_ip,
+ (void *)&s_ctl_get_vui_params_op);
+
+ if (status != IV_SUCCESS) {
+ ALOGW("Error in getting VUI params: 0x%x",
+ s_ctl_get_vui_params_op.u4_error_code);
+ return false;
+ }
+
+ int32_t primaries = s_ctl_get_vui_params_op.u1_colour_primaries;
+ int32_t transfer = s_ctl_get_vui_params_op.u1_tfr_chars;
+ int32_t coeffs = s_ctl_get_vui_params_op.u1_matrix_coeffs;
+ bool fullRange = s_ctl_get_vui_params_op.u1_video_full_range_flag;
+
+ ColorAspects colorAspects;
+ ColorUtils::convertIsoColorAspectsToCodecAspects(
+ primaries, transfer, coeffs, fullRange, colorAspects);
+
+ // Update color aspects if necessary.
+ if (colorAspectsDiffer(colorAspects, mBitstreamColorAspects)) {
+ mBitstreamColorAspects = colorAspects;
+ status_t err = handleColorAspectsChange();
+ CHECK(err == OK);
+ }
+ return true;
+}
+
+bool C2SoftAvcDec::setDecodeArgs(
+ ivd_video_decode_ip_t *ps_dec_ip,
+ ivd_video_decode_op_t *ps_dec_op,
+ C2ReadView *inBuffer,
+ C2GraphicView *outBuffer,
+ uint32_t workIndex,
+ size_t inOffset) {
+ size_t width = mWidth;
+ size_t height = mHeight;
+ size_t sizeY = width * height;
+ size_t sizeUV;
+
+ ps_dec_ip->u4_size = sizeof(ivd_video_decode_ip_t);
+ ps_dec_op->u4_size = sizeof(ivd_video_decode_op_t);
+
+ ps_dec_ip->e_cmd = IVD_CMD_VIDEO_DECODE;
+
+ /* When in flush and after EOS with zero byte input,
+ * inBuffer is set to zero. Hence check for non-null */
+ if (inBuffer) {
+ ps_dec_ip->u4_ts = workIndex;
+ ps_dec_ip->pv_stream_buffer = const_cast<uint8_t *>(inBuffer->data()) + inOffset;
+ ps_dec_ip->u4_num_Bytes = inBuffer->capacity() - inOffset;
+ } else {
+ ps_dec_ip->u4_ts = 0;
+ ps_dec_ip->pv_stream_buffer = NULL;
+ ps_dec_ip->u4_num_Bytes = 0;
+ }
+
+ sizeUV = sizeY / 4;
+ ps_dec_ip->s_out_buffer.u4_min_out_buf_size[0] = sizeY;
+ ps_dec_ip->s_out_buffer.u4_min_out_buf_size[1] = sizeUV;
+ ps_dec_ip->s_out_buffer.u4_min_out_buf_size[2] = sizeUV;
+
+ if (outBuffer) {
+ if (outBuffer->width() < width ||
+ outBuffer->height() < height) {
+ ALOGE("Output buffer too small: provided (%dx%d) required (%zux%zu)",
+ outBuffer->width(), outBuffer->height(), width, height);
+ return false;
+ }
+ ps_dec_ip->s_out_buffer.pu1_bufs[0] = outBuffer->data()[0];
+ ps_dec_ip->s_out_buffer.pu1_bufs[1] = outBuffer->data()[1];
+ ps_dec_ip->s_out_buffer.pu1_bufs[2] = outBuffer->data()[2];
+ } else {
+ // mFlushOutBuffer always has the right size.
+ ps_dec_ip->s_out_buffer.pu1_bufs[0] = mFlushOutBuffer;
+ ps_dec_ip->s_out_buffer.pu1_bufs[1] = mFlushOutBuffer + sizeY;
+ ps_dec_ip->s_out_buffer.pu1_bufs[2] = mFlushOutBuffer + sizeY + sizeUV;
+ }
+
+ ps_dec_ip->s_out_buffer.u4_num_bufs = 3;
+ return true;
+}
+
+void C2SoftAvcDec::process(std::unique_ptr<C2Work> &work) {
+ if (mSignalledError) {
+ return;
+ }
+
+ if (NULL == mCodecCtx) {
+ if (OK != initDecoder()) {
+ ALOGE("Failed to initialize decoder");
+ // TODO: notify(OMX_EventError, OMX_ErrorUnsupportedSetting, 0, NULL);
+ mSignalledError = true;
+ return;
+ }
+ }
+ if (mWidth != mStride) {
+ /* Set the run-time (dynamic) parameters */
+ mStride = mWidth;
+ setParams(mStride);
+ }
+
+ const C2ConstLinearBlock &buffer =
+ work->input.buffers[0]->data().linearBlocks().front();
+ if (buffer.capacity() == 0) {
+ // TODO: result?
+
+ std::vector<std::unique_ptr<C2Work>> done;
+ done.emplace_back(std::move(work));
+ mListener->onWorkDone(shared_from_this(), std::move(done));
+ if (!(work->input.flags & BUFFERFLAG_END_OF_STREAM)) {
+ return;
+ }
+
+ mReceivedEOS = true;
+ // TODO: flush
+ } else if (work->input.flags & BUFFERFLAG_END_OF_STREAM) {
+ mReceivedEOS = true;
+ }
+
+ C2ReadView input = work->input.buffers[0]->data().linearBlocks().front().map().get();
+ uint32_t workIndex = work->input.ordinal.frame_index & 0xFFFFFFFF;
+
+ // TODO: populate --- assume display order?
+ if (!mAllocatedBlock) {
+ // TODO: error handling
+ // TODO: format & usage
+ uint32_t format = HAL_PIXEL_FORMAT_YV12;
+ C2MemoryUsage usage = { C2MemoryUsage::kSoftwareRead, C2MemoryUsage::kSoftwareWrite };
+ (void) work->worklets.front()->allocators[0]->allocateGraphicBlock(
+ mWidth, mHeight, format, usage, &mAllocatedBlock);
+ ALOGE("provided (%dx%d) required (%dx%d)", mAllocatedBlock->width(), mAllocatedBlock->height(), mWidth, mHeight);
+ }
+ C2GraphicView output = mAllocatedBlock->map().get();
+ ALOGE("mapped err = %d", output.error());
+
+ size_t inOffset = 0u;
+ while (inOffset < input.capacity()) {
+ ivd_video_decode_ip_t s_dec_ip;
+ ivd_video_decode_op_t s_dec_op;
+ WORD32 timeDelay, timeTaken;
+ size_t sizeY, sizeUV;
+
+ if (!setDecodeArgs(&s_dec_ip, &s_dec_op, &input, &output, workIndex, inOffset)) {
+ ALOGE("Decoder arg setup failed");
+ // TODO: notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL);
+ mSignalledError = true;
+ return;
+ }
+ ALOGE("Decoder arg setup succeeded");
+ // If input dump is enabled, then write to file
+ DUMP_TO_FILE(mInFile, s_dec_ip.pv_stream_buffer, s_dec_ip.u4_num_Bytes, mInputOffset);
+
+ GETTIME(&mTimeStart, NULL);
+ /* Compute time elapsed between end of previous decode()
+ * to start of current decode() */
+ TIME_DIFF(mTimeEnd, mTimeStart, timeDelay);
+
+ IV_API_CALL_STATUS_T status;
+ status = ivdec_api_function(mCodecCtx, (void *)&s_dec_ip, (void *)&s_dec_op);
+
+ bool unsupportedResolution =
+ (IVD_STREAM_WIDTH_HEIGHT_NOT_SUPPORTED == (s_dec_op.u4_error_code & 0xFF));
+
+ /* Check for unsupported dimensions */
+ if (unsupportedResolution) {
+ ALOGE("Unsupported resolution : %dx%d", mWidth, mHeight);
+ // TODO: notify(OMX_EventError, OMX_ErrorUnsupportedSetting, 0, NULL);
+ mSignalledError = true;
+ return;
+ }
+
+ bool allocationFailed = (IVD_MEM_ALLOC_FAILED == (s_dec_op.u4_error_code & 0xFF));
+ if (allocationFailed) {
+ ALOGE("Allocation failure in decoder");
+ // TODO: notify(OMX_EventError, OMX_ErrorUnsupportedSetting, 0, NULL);
+ mSignalledError = true;
+ return;
+ }
+
+ bool resChanged = (IVD_RES_CHANGED == (s_dec_op.u4_error_code & 0xFF));
+
+ getVUIParams();
+
+ GETTIME(&mTimeEnd, NULL);
+ /* Compute time taken for decode() */
+ TIME_DIFF(mTimeStart, mTimeEnd, timeTaken);
+
+ PRINT_TIME("timeTaken=%6d delay=%6d numBytes=%6d", timeTaken, timeDelay,
+ s_dec_op.u4_num_bytes_consumed);
+ ALOGI("bytes total=%u", input.capacity());
+ if (s_dec_op.u4_frame_decoded_flag && !mFlushNeeded) {
+ mFlushNeeded = true;
+ }
+
+ if (1 != s_dec_op.u4_frame_decoded_flag) {
+ /* If the input did not contain picture data, then ignore
+ * the associated timestamp */
+ //mTimeStampsValid[workIndex] = false;
+ }
+
+ // If the decoder is in the changing resolution mode and there is no output present,
+ // that means the switching is done and it's ready to reset the decoder and the plugin.
+ if (mChangingResolution && !s_dec_op.u4_output_present) {
+ ALOGV("changing resolution");
+ mChangingResolution = false;
+ resetDecoder();
+ resetPlugin();
+ mStride = mWidth;
+ setParams(mStride);
+ return;
+ }
+
+ if (resChanged) {
+ ALOGV("res changed");
+ mChangingResolution = true;
+ if (mFlushNeeded) {
+ setFlushMode();
+ }
+ return;
+ }
+
+ // Combine the resolution change and coloraspects change in one PortSettingChange event
+ // if necessary.
+ if ((0 < s_dec_op.u4_pic_wd) && (0 < s_dec_op.u4_pic_ht)) {
+ uint32_t width = s_dec_op.u4_pic_wd;
+ uint32_t height = s_dec_op.u4_pic_ht;
+ ALOGV("width = %u height = %u", width, height);
+ if (width != mWidth || height != mHeight) {
+ mAllocatedBlock.reset();
+ mWidth = width;
+ mHeight = height;
+ }
+ } else if (mUpdateColorAspects) {
+ //notify(OMX_EventPortSettingsChanged, kOutputPortIndex,
+ // kDescribeColorAspectsIndex, NULL);
+ ALOGV("update color aspect");
+ mUpdateColorAspects = false;
+ return;
+ }
+
+ if (s_dec_op.u4_output_present) {
+ ALOGV("output_present");
+ // TODO: outHeader->nFilledLen = (mWidth * mHeight * 3) / 2;
+ std::vector<std::unique_ptr<C2Work>> done;
+ done.push_back(std::move(mPendingWork[s_dec_op.u4_ts]));
+ done[0]->worklets.front()->output.buffers.clear();
+ done[0]->worklets.front()->output.buffers.emplace_back(
+ std::make_shared<GraphicBuffer>(std::move(mAllocatedBlock)));
+ done[0]->worklets.front()->output.ordinal = done[0]->input.ordinal;
+ mListener->onWorkDone(shared_from_this(), std::move(done));
+ } else if (mIsInFlush) {
+ ALOGV("flush");
+ /* If in flush mode and no output is returned by the codec,
+ * then come out of flush mode */
+ mIsInFlush = false;
+
+ /* If EOS was recieved on input port and there is no output
+ * from the codec, then signal EOS on output port */
+ if (mReceivedEOS) {
+ // TODO
+ // outHeader->nFilledLen = 0;
+ // outHeader->nFlags |= OMX_BUFFERFLAG_EOS;
+
+ // outInfo->mOwnedByUs = false;
+ // outQueue.erase(outQueue.begin());
+ // outInfo = NULL;
+ // notifyFillBufferDone(outHeader);
+ // outHeader = NULL;
+ resetPlugin();
+ }
+ }
+ inOffset += s_dec_op.u4_num_bytes_consumed;
+ }
+ /* If input EOS is seen and decoder is not in flush mode,
+ * set the decoder in flush mode.
+ * There can be a case where EOS is sent along with last picture data
+ * In that case, only after decoding that input data, decoder has to be
+ * put in flush. This case is handled here */
+
+ if (mReceivedEOS && !mIsInFlush) {
+ setFlushMode();
+ }
+}
+
+bool C2SoftAvcDec::colorAspectsDiffer(
+ const ColorAspects &a, const ColorAspects &b) {
+ if (a.mRange != b.mRange
+ || a.mPrimaries != b.mPrimaries
+ || a.mTransfer != b.mTransfer
+ || a.mMatrixCoeffs != b.mMatrixCoeffs) {
+ return true;
+ }
+ return false;
+}
+
+void C2SoftAvcDec::updateFinalColorAspects(
+ const ColorAspects &otherAspects, const ColorAspects &preferredAspects) {
+ Mutex::Autolock autoLock(mColorAspectsLock);
+ ColorAspects newAspects;
+ newAspects.mRange = preferredAspects.mRange != ColorAspects::RangeUnspecified ?
+ preferredAspects.mRange : otherAspects.mRange;
+ newAspects.mPrimaries = preferredAspects.mPrimaries != ColorAspects::PrimariesUnspecified ?
+ preferredAspects.mPrimaries : otherAspects.mPrimaries;
+ newAspects.mTransfer = preferredAspects.mTransfer != ColorAspects::TransferUnspecified ?
+ preferredAspects.mTransfer : otherAspects.mTransfer;
+ newAspects.mMatrixCoeffs = preferredAspects.mMatrixCoeffs != ColorAspects::MatrixUnspecified ?
+ preferredAspects.mMatrixCoeffs : otherAspects.mMatrixCoeffs;
+
+ // Check to see if need update mFinalColorAspects.
+ if (colorAspectsDiffer(mFinalColorAspects, newAspects)) {
+ mFinalColorAspects = newAspects;
+ mUpdateColorAspects = true;
+ }
+}
+
+status_t C2SoftAvcDec::handleColorAspectsChange() {
+// int perference = getColorAspectPreference();
+// ALOGD("Color Aspects preference: %d ", perference);
+//
+// if (perference == kPreferBitstream) {
+// updateFinalColorAspects(mDefaultColorAspects, mBitstreamColorAspects);
+// } else if (perference == kPreferContainer) {
+// updateFinalColorAspects(mBitstreamColorAspects, mDefaultColorAspects);
+// } else {
+// return OMX_ErrorUnsupportedSetting;
+// }
+ updateFinalColorAspects(mDefaultColorAspects, mBitstreamColorAspects);
+ return C2_OK;
+}
+
+} // namespace android
diff --git a/media/libstagefright/codecs/avcdec/C2SoftAvcDec.h b/media/libstagefright/codecs/avcdec/C2SoftAvcDec.h
new file mode 100644
index 0000000..6a83d1d
--- /dev/null
+++ b/media/libstagefright/codecs/avcdec/C2SoftAvcDec.h
@@ -0,0 +1,312 @@
+/*
+ * Copyright 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 C2_SOFT_H264_DEC_H_
+
+#define C2_SOFT_H264_DEC_H_
+
+#include <condition_variable>
+#include <map>
+#include <memory>
+#include <mutex>
+#include <set>
+#include <unordered_map>
+
+#include <util/C2ParamUtils.h>
+
+#include <C2Component.h>
+#include <C2Param.h>
+
+#include "C2AvcConfig.h"
+
+#include <media/stagefright/foundation/ABase.h>
+#include <media/stagefright/foundation/ColorUtils.h>
+
+#include <sys/time.h>
+
+namespace android {
+
+struct iv_obj_t;
+struct ivd_video_decode_ip_t;
+struct ivd_video_decode_op_t;
+
+/** Number of entries in the time-stamp array */
+#define MAX_PENDING_WORKS 64
+
+/** Maximum number of cores supported by the codec */
+#define CODEC_MAX_NUM_CORES 4
+
+#define CODEC_MAX_WIDTH 1920
+
+#define CODEC_MAX_HEIGHT 1088
+
+/** Input buffer size */
+#define INPUT_BUF_SIZE (1024 * 1024)
+
+#define MIN(a, b) ((a) < (b)) ? (a) : (b)
+
+/** Used to remove warnings about unused parameters */
+#define UNUSED(x) ((void)(x))
+
+/** Get time */
+#define GETTIME(a, b) gettimeofday(a, b);
+
+/** Compute difference between start and end */
+#define TIME_DIFF(start, end, diff) \
+ diff = (((end).tv_sec - (start).tv_sec) * 1000000) + \
+ ((end).tv_usec - (start).tv_usec);
+
+
+class C2SoftAvcDecIntf : public C2ComponentInterface {
+public:
+ struct SupportedValuesWithFields {
+ C2FieldSupportedValues supported;
+ std::set<C2ParamField> restrictingFields;
+
+ SupportedValuesWithFields(const C2FieldSupportedValues &supported) : supported(supported) {}
+ };
+
+ C2SoftAvcDecIntf(const char *name, node_id id);
+ virtual ~C2SoftAvcDecIntf() = default;
+
+ // From C2ComponentInterface
+ virtual C2String getName() const override;
+ virtual node_id getId() const override;
+ virtual status_t query_nb(
+ const std::vector<C2Param* const> &stackParams,
+ const std::vector<C2Param::Index> &heapParamIndices,
+ std::vector<std::unique_ptr<C2Param>>* const heapParams) const override;
+ virtual status_t config_nb(
+ const std::vector<C2Param* const> ¶ms,
+ std::vector<std::unique_ptr<C2SettingResult>>* const failures) override;
+ virtual status_t commit_sm(
+ const std::vector<C2Param* const> ¶ms,
+ std::vector<std::unique_ptr<C2SettingResult>>* const failures) override;
+ virtual status_t createTunnel_sm(node_id targetComponent) override;
+ virtual status_t releaseTunnel_sm(node_id targetComponent) override;
+ virtual std::shared_ptr<C2ParamReflector> getParamReflector() const override;
+ virtual status_t getSupportedParams(
+ std::vector<std::shared_ptr<C2ParamDescriptor>> * const params) const override;
+ virtual status_t getSupportedValues(
+ const std::vector<const C2ParamField> &fields,
+ std::vector<C2FieldSupportedValues>* const values) const override;
+
+private:
+ class ParamReflector;
+
+ const C2String mName;
+ const node_id mId;
+
+ C2ComponentDomainInfo mDomainInfo;
+ // TODO: config desc
+ std::unique_ptr<C2PortMimeConfig::input> mInputPortMime;
+ C2PortStreamCountConfig::input mInputStreamCount;
+ std::unique_ptr<C2PortMimeConfig::output> mOutputPortMime;
+ C2PortStreamCountConfig::output mOutputStreamCount;
+ // TODO: C2StreamMimeConfig mInputStreamMime;
+ // TODO: C2StreamMimeConfig mOutputStreamMime;
+ C2StreamFormatConfig::input mInputStreamFormat;
+ C2StreamFormatConfig::output mOutputStreamFormat;
+ C2VideoSizeStreamInfo::output mVideoSize;
+ C2MaxVideoSizeHintPortSetting::input mMaxVideoSizeHint;
+ C2AvcProfileInfo::input mProfile;
+ C2AvcLevelInfo::input mLevel;
+ C2BlockSizeInfo::output mBlockSize;
+ C2AlignmentInfo::output mAlignment;
+ C2FrameRateInfo::output mFrameRate;
+ C2BlocksPerSecondInfo::output mBlocksPerSecond;
+
+ std::shared_ptr<C2ParamReflector> mParamReflector;
+
+ std::unordered_map<uint32_t, C2Param *> mParams;
+ // C2ParamField is LessThanComparable
+ std::map<C2ParamField, SupportedValuesWithFields> mSupportedValues;
+ std::unordered_map<
+ uint32_t, std::function<std::unique_ptr<C2SettingResult>(C2Param *)>> mFieldVerifiers;
+ std::vector<std::shared_ptr<C2ParamDescriptor>> mParamDescs;
+
+ void updateSupportedValues();
+};
+
+class C2SoftAvcDec
+ : public C2Component,
+ public std::enable_shared_from_this<C2SoftAvcDec> {
+public:
+ C2SoftAvcDec(
+ const char *name, node_id id, const std::shared_ptr<C2ComponentListener> &listener);
+ virtual ~C2SoftAvcDec();
+
+ // From C2Component
+ virtual status_t queue_nb(std::list<std::unique_ptr<C2Work>>* const items) override;
+ virtual status_t announce_nb(const std::vector<C2WorkOutline> &items) override;
+ virtual status_t flush_sm(
+ bool flushThrough, std::list<std::unique_ptr<C2Work>>* const flushedWork) override;
+ virtual status_t drain_nb(bool drainThrough) override;
+ virtual status_t start() override;
+ virtual status_t stop() override;
+ virtual void reset() override;
+ virtual void release() override;
+ virtual std::shared_ptr<C2ComponentInterface> intf() override;
+
+private:
+ class QueueProcessThread;
+
+ Mutex mColorAspectsLock;
+ // color aspects passed from the framework.
+ ColorAspects mDefaultColorAspects;
+ // color aspects parsed from the bitstream.
+ ColorAspects mBitstreamColorAspects;
+ // final color aspects after combining the above two aspects.
+ ColorAspects mFinalColorAspects;
+ bool mUpdateColorAspects;
+
+ bool colorAspectsDiffer(const ColorAspects &a, const ColorAspects &b);
+
+ // This functions takes two color aspects and updates the mFinalColorAspects
+ // based on |preferredAspects|.
+ void updateFinalColorAspects(
+ const ColorAspects &otherAspects, const ColorAspects &preferredAspects);
+
+ // This function will update the mFinalColorAspects based on codec preference.
+ status_t handleColorAspectsChange();
+
+ // Number of input and output buffers
+ enum {
+ kNumBuffers = 8
+ };
+
+ using IndexType = decltype(C2WorkOrdinalStruct().frame_index);
+
+ const std::shared_ptr<C2SoftAvcDecIntf> mIntf;
+ const std::shared_ptr<C2ComponentListener> mListener;
+
+ std::mutex mQueueLock;
+ std::condition_variable mQueueCond;
+ std::list<std::unique_ptr<C2Work>> mQueue;
+
+ std::mutex mPendingLock;
+ std::unordered_map<IndexType, std::unique_ptr<C2Work>> mPendingWork;
+
+ std::unique_ptr<QueueProcessThread> mThread;
+
+ std::shared_ptr<C2GraphicBlock> mAllocatedBlock;
+
+ iv_obj_t *mCodecCtx; // Codec context
+
+ size_t mNumCores; // Number of cores to be uesd by the codec
+
+ struct timeval mTimeStart; // Time at the start of decode()
+ struct timeval mTimeEnd; // Time at the end of decode()
+
+ // Internal buffer to be used to flush out the buffers from decoder
+ uint8_t *mFlushOutBuffer;
+
+#ifdef FILE_DUMP_ENABLE
+ char mInFile[200];
+#endif /* FILE_DUMP_ENABLE */
+
+ int mIvColorFormat; // Ittiam Color format
+
+ bool mIsInFlush; // codec is flush mode
+ bool mReceivedEOS; // EOS is receieved on input port
+
+ // The input stream has changed to a different resolution, which is still supported by the
+ // codec. So the codec is switching to decode the new resolution.
+ bool mChangingResolution;
+ bool mFlushNeeded;
+ bool mSignalledError;
+ int32_t mWidth;
+ int32_t mHeight;
+ int32_t mStride;
+ size_t mInputOffset;
+
+ void processQueue();
+ void process(std::unique_ptr<C2Work> &work);
+
+ status_t initDecoder();
+ status_t deInitDecoder();
+ status_t setFlushMode();
+ status_t setParams(size_t stride);
+ void logVersion();
+ status_t setNumCores();
+ status_t resetDecoder();
+ status_t resetPlugin();
+
+ bool setDecodeArgs(
+ ivd_video_decode_ip_t *ps_dec_ip,
+ ivd_video_decode_op_t *ps_dec_op,
+ C2ReadView *inBuffer,
+ C2GraphicView *outBuffer,
+ uint32_t timeStampIx,
+ size_t inOffset);
+
+ bool getVUIParams();
+
+ DISALLOW_EVIL_CONSTRUCTORS(C2SoftAvcDec);
+};
+
+#ifdef FILE_DUMP_ENABLE
+
+#define INPUT_DUMP_PATH "/sdcard/media/avcd_input"
+#define INPUT_DUMP_EXT "h264"
+
+#define GENERATE_FILE_NAMES() { \
+ GETTIME(&mTimeStart, NULL); \
+ strcpy(mInFile, ""); \
+ sprintf(mInFile, "%s_%ld.%ld.%s", INPUT_DUMP_PATH, \
+ mTimeStart.tv_sec, mTimeStart.tv_usec, \
+ INPUT_DUMP_EXT); \
+}
+
+#define CREATE_DUMP_FILE(m_filename) { \
+ FILE *fp = fopen(m_filename, "wb"); \
+ if (fp != NULL) { \
+ fclose(fp); \
+ } else { \
+ ALOGD("Could not open file %s", m_filename); \
+ } \
+}
+#define DUMP_TO_FILE(m_filename, m_buf, m_size, m_offset)\
+{ \
+ FILE *fp = fopen(m_filename, "ab"); \
+ if (fp != NULL && m_buf != NULL && m_offset == 0) { \
+ int i; \
+ i = fwrite(m_buf, 1, m_size, fp); \
+ ALOGD("fwrite ret %d to write %d", i, m_size); \
+ if (i != (int) m_size) { \
+ ALOGD("Error in fwrite, returned %d", i); \
+ perror("Error in write to file"); \
+ } \
+ } else if (fp == NULL) { \
+ ALOGD("Could not write to file %s", m_filename);\
+ } \
+ if (fp) { \
+ fclose(fp); \
+ } \
+}
+#else /* FILE_DUMP_ENABLE */
+#define INPUT_DUMP_PATH
+#define INPUT_DUMP_EXT
+#define OUTPUT_DUMP_PATH
+#define OUTPUT_DUMP_EXT
+#define GENERATE_FILE_NAMES()
+#define CREATE_DUMP_FILE(m_filename)
+#define DUMP_TO_FILE(m_filename, m_buf, m_size, m_offset)
+#endif /* FILE_DUMP_ENABLE */
+
+} // namespace android
+
+#endif // C2_SOFT_H264_DEC_H_
diff --git a/media/libstagefright/codecs/cmds/Android.bp b/media/libstagefright/codecs/cmds/Android.bp
new file mode 100644
index 0000000..e44e53c
--- /dev/null
+++ b/media/libstagefright/codecs/cmds/Android.bp
@@ -0,0 +1,50 @@
+cc_binary {
+ name: "codec2",
+
+ srcs: [
+ "codec2.cpp",
+ ],
+
+ include_dirs: [
+ "frameworks/av/media/libstagefright/codec2/include",
+ "frameworks/av/media/libstagefright/codec2/vndk/include",
+ ],
+
+ shared_libs: [
+ "android.hardware.graphics.allocator@2.0",
+ "android.hardware.graphics.mapper@2.0",
+ "libbinder",
+ "libcutils",
+ "libgui",
+ "libhidlbase",
+ "libion",
+ "liblog",
+ "libstagefright",
+ "libstagefright_codec2",
+ "libstagefright_foundation",
+ "libstagefright_soft_c2avcdec",
+ "libui",
+ "libutils",
+ ],
+
+ static_libs: [
+ "libstagefright_codec2_vndk",
+ ],
+
+ cflags: [
+ "-Werror",
+ "-Wall",
+ "-std=c++14",
+ ],
+
+// sanitize: {
+// cfi: true,
+// misc_undefined: [
+// "unsigned-integer-overflow",
+// "signed-integer-overflow",
+// ],
+// diag: {
+// cfi: true,
+// },
+// },
+}
diff --git a/media/libstagefright/codecs/cmds/codec2.cpp b/media/libstagefright/codecs/cmds/codec2.cpp
new file mode 100644
index 0000000..ad4da9b
--- /dev/null
+++ b/media/libstagefright/codecs/cmds/codec2.cpp
@@ -0,0 +1,464 @@
+/*
+ * 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.
+ */
+
+#include <inttypes.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <thread>
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "codec2"
+#include <media/stagefright/foundation/ADebug.h>
+
+#include <binder/IServiceManager.h>
+#include <binder/ProcessState.h>
+#include <media/ICrypto.h>
+#include <media/IMediaHTTPService.h>
+#include <media/stagefright/foundation/ABuffer.h>
+#include <media/stagefright/foundation/ALooper.h>
+#include <media/stagefright/foundation/AMessage.h>
+#include <media/stagefright/foundation/AUtils.h>
+#include <media/stagefright/DataSource.h>
+#include <media/stagefright/MediaDefs.h>
+#include <media/stagefright/MediaErrors.h>
+#include <media/stagefright/MediaExtractor.h>
+#include <media/stagefright/MediaSource.h>
+#include <media/stagefright/MetaData.h>
+#include <media/stagefright/Utils.h>
+
+#include <gui/GLConsumer.h>
+#include <gui/IProducerListener.h>
+#include <gui/Surface.h>
+#include <gui/SurfaceComposerClient.h>
+
+#include <util/C2ParamUtils.h>
+#include <C2Buffer.h>
+#include <C2BufferPriv.h>
+#include <C2Component.h>
+#include <C2Work.h>
+
+#include "../avcdec/C2SoftAvcDec.h"
+
+using namespace android;
+using namespace std::chrono_literals;
+
+namespace {
+
+class LinearBuffer : public C2Buffer {
+public:
+ explicit LinearBuffer(const std::shared_ptr<C2LinearBlock> &block)
+ : C2Buffer({ block->share(block->offset(), block->size(), ::android::C2Fence()) }) {}
+};
+
+class Listener;
+
+class SimplePlayer {
+public:
+ SimplePlayer();
+ ~SimplePlayer();
+
+ void onWorkDone(std::weak_ptr<C2Component> component,
+ std::vector<std::unique_ptr<C2Work>> workItems);
+ void onTripped(std::weak_ptr<C2Component> component,
+ std::vector<std::shared_ptr<C2SettingResult>> settingResult);
+ void onError(std::weak_ptr<C2Component> component, uint32_t errorCode);
+
+ void play(const sp<IMediaSource> &source);
+
+private:
+ typedef std::unique_lock<std::mutex> ULock;
+
+ std::shared_ptr<Listener> mListener;
+ std::shared_ptr<C2Component> mComponent;
+
+ sp<IProducerListener> mProducerListener;
+
+ std::shared_ptr<C2Allocator> mAllocIon;
+ std::shared_ptr<C2Allocator> mAllocGralloc;
+ std::shared_ptr<C2BlockAllocator> mLinearAlloc;
+ std::shared_ptr<C2BlockAllocator> mGraphicAlloc;
+
+ std::mutex mQueueLock;
+ std::condition_variable mQueueCondition;
+ std::list<std::unique_ptr<C2Work>> mWorkQueue;
+
+ std::mutex mProcessedLock;
+ std::condition_variable mProcessedCondition;
+ std::list<std::unique_ptr<C2Work>> mProcessedWork;
+
+ sp<Surface> mSurface;
+ sp<SurfaceComposerClient> mComposerClient;
+ sp<SurfaceControl> mControl;
+};
+
+class Listener : public C2ComponentListener {
+public:
+ explicit Listener(SimplePlayer *thiz) : mThis(thiz) {}
+ virtual ~Listener() = default;
+
+ virtual void onWorkDone(std::weak_ptr<C2Component> component,
+ std::vector<std::unique_ptr<C2Work>> workItems) override {
+ mThis->onWorkDone(component, std::move(workItems));
+ }
+
+ virtual void onTripped(std::weak_ptr<C2Component> component,
+ std::vector<std::shared_ptr<C2SettingResult>> settingResult) override {
+ mThis->onTripped(component, settingResult);
+ }
+
+ virtual void onError(std::weak_ptr<C2Component> component,
+ uint32_t errorCode) override {
+ mThis->onError(component, errorCode);
+ }
+
+private:
+ SimplePlayer * const mThis;
+};
+
+
+SimplePlayer::SimplePlayer()
+ : mListener(new Listener(this)),
+ mProducerListener(new DummyProducerListener),
+ mAllocIon(new C2AllocatorIon),
+ mAllocGralloc(new C2AllocatorGralloc),
+ mLinearAlloc(new C2DefaultBlockAllocator(mAllocIon)),
+ mGraphicAlloc(new C2DefaultGraphicBlockAllocator(mAllocGralloc)),
+ mComposerClient(new SurfaceComposerClient) {
+ CHECK_EQ(mComposerClient->initCheck(), (status_t)OK);
+
+ mControl = mComposerClient->createSurface(
+ String8("A Surface"),
+ 1280,
+ 800,
+ HAL_PIXEL_FORMAT_YV12);
+ //PIXEL_FORMAT_RGB_565);
+
+ CHECK(mControl != NULL);
+ CHECK(mControl->isValid());
+
+ SurfaceComposerClient::openGlobalTransaction();
+ CHECK_EQ(mControl->setLayer(INT_MAX), (status_t)OK);
+ CHECK_EQ(mControl->show(), (status_t)OK);
+ SurfaceComposerClient::closeGlobalTransaction();
+
+ mSurface = mControl->getSurface();
+ CHECK(mSurface != NULL);
+ mSurface->connect(NATIVE_WINDOW_API_CPU, mProducerListener);
+}
+
+SimplePlayer::~SimplePlayer() {
+ mComposerClient->dispose();
+}
+
+void SimplePlayer::onWorkDone(
+ std::weak_ptr<C2Component> component, std::vector<std::unique_ptr<C2Work>> workItems) {
+ (void) component;
+ ULock l(mProcessedLock);
+ for (auto & item : workItems) {
+ mProcessedWork.push_back(std::move(item));
+ }
+ mProcessedCondition.notify_all();
+}
+
+void SimplePlayer::onTripped(
+ std::weak_ptr<C2Component> component,
+ std::vector<std::shared_ptr<C2SettingResult>> settingResult) {
+ (void) component;
+ (void) settingResult;
+ // TODO
+}
+
+void SimplePlayer::onError(std::weak_ptr<C2Component> component, uint32_t errorCode) {
+ (void) component;
+ (void) errorCode;
+ // TODO
+}
+
+void SimplePlayer::play(const sp<IMediaSource> &source) {
+ sp<AMessage> format;
+ (void) convertMetaDataToMessage(source->getFormat(), &format);
+
+ sp<ABuffer> csd0, csd1;
+ format->findBuffer("csd-0", &csd0);
+ format->findBuffer("csd-1", &csd1);
+
+ status_t err = source->start();
+
+ if (err != OK) {
+ fprintf(stderr, "source returned error %d (0x%08x)\n", err, err);
+ return;
+ }
+
+ std::shared_ptr<C2Component> component(std::make_shared<C2SoftAvcDec>("avc", 0, mListener));
+ component->start();
+
+ for (int i = 0; i < 8; ++i) {
+ mWorkQueue.emplace_back(new C2Work);
+ }
+
+ std::atomic_bool running(true);
+ std::thread surfaceThread([this, &running]() {
+ const sp<IGraphicBufferProducer> &igbp = mSurface->getIGraphicBufferProducer();
+ while (running) {
+ std::unique_ptr<C2Work> work;
+ {
+ ULock l(mProcessedLock);
+ if (mProcessedWork.empty()) {
+ mProcessedCondition.wait_for(l, 100ms);
+ if (mProcessedWork.empty()) {
+ continue;
+ }
+ }
+ work.swap(mProcessedWork.front());
+ mProcessedWork.pop_front();
+ }
+ int slot;
+ sp<Fence> fence;
+ const std::shared_ptr<C2Buffer> &output = work->worklets.front()->output.buffers[0];
+ const C2ConstGraphicBlock &block = output->data().graphicBlocks().front();
+ sp<GraphicBuffer> buffer(new GraphicBuffer(
+ block.handle(),
+ GraphicBuffer::CLONE_HANDLE,
+ block.width(),
+ block.height(),
+ HAL_PIXEL_FORMAT_YV12,
+ 1,
+ (uint64_t)GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN,
+ block.width()));
+
+ status_t err = igbp->attachBuffer(&slot, buffer);
+
+ IGraphicBufferProducer::QueueBufferInput qbi(
+ work->worklets.front()->output.ordinal.timestamp * 1000ll,
+ false,
+ HAL_DATASPACE_UNKNOWN,
+ Rect(block.width(), block.height()),
+ NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW,
+ 0,
+ Fence::NO_FENCE,
+ 0);
+ IGraphicBufferProducer::QueueBufferOutput qbo;
+ err = igbp->queueBuffer(slot, qbi, &qbo);
+
+ work->input.buffers.clear();
+ work->worklets.clear();
+
+ ULock l(mQueueLock);
+ mWorkQueue.push_back(std::move(work));
+ mQueueCondition.notify_all();
+ }
+ });
+
+ long numFrames = 0;
+ mLinearAlloc.reset(new C2DefaultBlockAllocator(mAllocIon));
+
+ for (;;) {
+ size_t size = 0u;
+ void *data = nullptr;
+ int64_t timestamp = 0u;
+ MediaBuffer *buffer = nullptr;
+ sp<ABuffer> csd;
+ if (csd0 != nullptr) {
+ csd = csd0;
+ csd0 = nullptr;
+ } else if (csd1 != nullptr) {
+ csd = csd1;
+ csd1 = nullptr;
+ } else {
+ status_t err = source->read(&buffer);
+ if (err != OK) {
+ CHECK(buffer == NULL);
+
+ if (err == INFO_FORMAT_CHANGED) {
+ continue;
+ }
+
+ break;
+ }
+ sp<MetaData> meta = buffer->meta_data();
+ CHECK(meta->findInt64(kKeyTime, ×tamp));
+
+ size = buffer->size();
+ data = buffer->data();
+ }
+
+ if (csd != nullptr) {
+ size = csd->size();
+ data = csd->data();
+ }
+
+ // Prepare C2Work
+
+ std::unique_ptr<C2Work> work;
+ while (!work) {
+ ULock l(mQueueLock);
+ if (!mWorkQueue.empty()) {
+ work.swap(mWorkQueue.front());
+ mWorkQueue.pop_front();
+ } else {
+ mQueueCondition.wait_for(l, 100ms);
+ }
+ }
+ work->input.flags = (flags_t)0;
+ work->input.ordinal.timestamp = timestamp;
+ work->input.ordinal.frame_index = numFrames;
+
+ std::shared_ptr<C2LinearBlock> block;
+ mLinearAlloc->allocateLinearBlock(
+ size,
+ { C2MemoryUsage::kSoftwareRead, C2MemoryUsage::kSoftwareWrite },
+ &block);
+ C2WriteView view = block->map().get();
+ if (view.error() != C2_OK) {
+ fprintf(stderr, "C2LinearBlock::map() failed : %d", view.error());
+ break;
+ }
+ memcpy(view.base(), data, size);
+
+ work->input.buffers.clear();
+ work->input.buffers.emplace_back(new LinearBuffer(block));
+ work->worklets.clear();
+ work->worklets.emplace_back(new C2Worklet);
+ work->worklets.front()->allocators.emplace_back(mGraphicAlloc);
+
+ std::list<std::unique_ptr<C2Work>> items;
+ items.push_back(std::move(work));
+
+ // DO THE DECODING
+ component->queue_nb(&items);
+
+ if (buffer) {
+ buffer->release();
+ buffer = NULL;
+ }
+
+ ++numFrames;
+ }
+ source->stop();
+ component->release();
+
+ running.store(false);
+ surfaceThread.join();
+ printf("\n");
+}
+
+} // namespace
+
+static void usage(const char *me) {
+ fprintf(stderr, "usage: %s [options] [input_filename]\n", me);
+ fprintf(stderr, " -h(elp)\n");
+}
+
+int main(int argc, char **argv) {
+ android::ProcessState::self()->startThreadPool();
+
+ int res;
+ while ((res = getopt(argc, argv, "h")) >= 0) {
+ switch (res) {
+ case 'h':
+ default:
+ {
+ usage(argv[0]);
+ exit(1);
+ break;
+ }
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (argc < 1) {
+ fprintf(stderr, "No input file specified\n");
+ return 1;
+ }
+
+ status_t err = OK;
+ SimplePlayer player;
+
+ for (int k = 0; k < argc && err == OK; ++k) {
+ const char *filename = argv[k];
+
+ sp<DataSource> dataSource =
+ DataSource::CreateFromURI(NULL /* httpService */, filename);
+
+ if (strncasecmp(filename, "sine:", 5) && dataSource == NULL) {
+ fprintf(stderr, "Unable to create data source.\n");
+ return 1;
+ }
+
+ Vector<sp<IMediaSource> > mediaSources;
+ sp<IMediaSource> mediaSource;
+
+ sp<IMediaExtractor> extractor = MediaExtractor::Create(dataSource);
+
+ if (extractor == NULL) {
+ fprintf(stderr, "could not create extractor.\n");
+ return -1;
+ }
+
+ sp<MetaData> meta = extractor->getMetaData();
+
+ if (meta != NULL) {
+ const char *mime;
+ if (!meta->findCString(kKeyMIMEType, &mime)) {
+ fprintf(stderr, "extractor did not provide MIME type.\n");
+ return -1;
+ }
+ }
+
+ size_t numTracks = extractor->countTracks();
+
+ size_t i;
+ for (i = 0; i < numTracks; ++i) {
+ meta = extractor->getTrackMetaData(
+ i, MediaExtractor::kIncludeExtensiveMetaData);
+
+ if (meta == NULL) {
+ break;
+ }
+ const char *mime;
+ meta->findCString(kKeyMIMEType, &mime);
+
+ // TODO: allowing AVC only for the time being
+ if (!strncasecmp(mime, "video/avc", 9)) {
+ break;
+ }
+
+ meta = NULL;
+ }
+
+ if (meta == NULL) {
+ fprintf(stderr, "No AVC track found.\n");
+ return -1;
+ }
+
+ mediaSource = extractor->getTrack(i);
+ if (mediaSource == nullptr) {
+ fprintf(stderr, "skip NULL track %zu, total tracks %zu.\n", i, numTracks);
+ return -1;
+ }
+
+ player.play(mediaSource);
+ }
+
+ return 0;
+}
diff --git a/media/libstagefright/codecs/flac/dec/SoftFlacDecoder.cpp b/media/libstagefright/codecs/flac/dec/SoftFlacDecoder.cpp
index cff4a33..4ab1ab2 100644
--- a/media/libstagefright/codecs/flac/dec/SoftFlacDecoder.cpp
+++ b/media/libstagefright/codecs/flac/dec/SoftFlacDecoder.cpp
@@ -126,6 +126,29 @@
OMX_INDEXTYPE index, OMX_PTR params) {
ALOGV("internalGetParameter: index(%x)", index);
switch ((OMX_U32)index) {
+ case OMX_IndexParamAudioPortFormat:
+ {
+ OMX_AUDIO_PARAM_PORTFORMATTYPE *formatParams =
+ (OMX_AUDIO_PARAM_PORTFORMATTYPE *)params;
+
+ if (!isValidOMXParam(formatParams)) {
+ return OMX_ErrorBadParameter;
+ }
+
+ if (formatParams->nPortIndex > 1) {
+ return OMX_ErrorUndefined;
+ }
+
+ if (formatParams->nIndex > 0) {
+ return OMX_ErrorNoMore;
+ }
+
+ formatParams->eEncoding =
+ (formatParams->nPortIndex == 0)
+ ? OMX_AUDIO_CodingFLAC : OMX_AUDIO_CodingPCM;
+
+ return OMX_ErrorNone;
+ }
case OMX_IndexParamAudioFlac:
{
OMX_AUDIO_PARAM_FLACTYPE *flacParams =
@@ -219,6 +242,29 @@
return OMX_ErrorNone;
}
+ case OMX_IndexParamAudioPortFormat:
+ {
+ const OMX_AUDIO_PARAM_PORTFORMATTYPE *formatParams =
+ (const OMX_AUDIO_PARAM_PORTFORMATTYPE *)params;
+
+ if (!isValidOMXParam(formatParams)) {
+ return OMX_ErrorBadParameter;
+ }
+
+ if (formatParams->nPortIndex > 1) {
+ return OMX_ErrorUndefined;
+ }
+
+ if ((formatParams->nPortIndex == 0
+ && formatParams->eEncoding != OMX_AUDIO_CodingFLAC)
+ || (formatParams->nPortIndex == 1
+ && formatParams->eEncoding != OMX_AUDIO_CodingPCM)) {
+ return OMX_ErrorUndefined;
+ }
+
+ return OMX_ErrorNone;
+ }
+
case OMX_IndexParamAudioPcm:
{
const OMX_AUDIO_PARAM_PCMMODETYPE *pcmParams =
@@ -256,10 +302,27 @@
while (!inQueue.empty() && !outQueue.empty()) {
BufferInfo *inInfo = *inQueue.begin();
OMX_BUFFERHEADERTYPE *inHeader = inInfo->mHeader;
+ BufferInfo *outInfo = *outQueue.begin();
+ OMX_BUFFERHEADERTYPE *outHeader = outInfo->mHeader;
uint8_t* inBuffer = inHeader->pBuffer + inHeader->nOffset;
uint32_t inBufferLength = inHeader->nFilledLen;
bool endOfInput = (inHeader->nFlags & OMX_BUFFERFLAG_EOS) != 0;
+ if (inHeader->nFilledLen == 0) {
+ if (endOfInput) {
+ outHeader->nFilledLen = 0;
+ outHeader->nFlags = OMX_BUFFERFLAG_EOS;
+ outInfo->mOwnedByUs = false;
+ outQueue.erase(outQueue.begin());
+ notifyFillBufferDone(outHeader);
+ } else {
+ ALOGE("onQueueFilled: emptyInputBuffer received");
+ }
+ inInfo->mOwnedByUs = false;
+ inQueue.erase(inQueue.begin());
+ notifyEmptyBufferDone(inHeader);
+ return;
+ }
if (mInputBufferCount == 0 && !(inHeader->nFlags & OMX_BUFFERFLAG_CODECCONFIG)) {
ALOGE("onQueueFilled: first buffer should have OMX_BUFFERFLAG_CODECCONFIG set");
inHeader->nFlags |= OMX_BUFFERFLAG_CODECCONFIG;
@@ -297,8 +360,6 @@
return;
}
- BufferInfo *outInfo = *outQueue.begin();
- OMX_BUFFERHEADERTYPE *outHeader = outInfo->mHeader;
short *outBuffer =
reinterpret_cast<short *>(outHeader->pBuffer + outHeader->nOffset);
size_t outBufferSize = outHeader->nAllocLen - outHeader->nOffset;
diff --git a/media/libstagefright/codecs/flac/enc/SoftFlacEncoder.cpp b/media/libstagefright/codecs/flac/enc/SoftFlacEncoder.cpp
index caceda9..56d2d69 100644
--- a/media/libstagefright/codecs/flac/enc/SoftFlacEncoder.cpp
+++ b/media/libstagefright/codecs/flac/enc/SoftFlacEncoder.cpp
@@ -154,6 +154,30 @@
ALOGV("SoftFlacEncoder::internalGetParameter(index=0x%x)", index);
switch (index) {
+ case OMX_IndexParamAudioPortFormat:
+ {
+ OMX_AUDIO_PARAM_PORTFORMATTYPE *formatParams =
+ (OMX_AUDIO_PARAM_PORTFORMATTYPE *)params;
+
+ if (!isValidOMXParam(formatParams)) {
+ return OMX_ErrorBadParameter;
+ }
+
+ if (formatParams->nPortIndex > 1) {
+ return OMX_ErrorUndefined;
+ }
+
+ if (formatParams->nIndex > 0) {
+ return OMX_ErrorNoMore;
+ }
+
+ formatParams->eEncoding =
+ (formatParams->nPortIndex == 0)
+ ? OMX_AUDIO_CodingPCM : OMX_AUDIO_CodingFLAC;
+
+ return OMX_ErrorNone;
+ }
+
case OMX_IndexParamAudioPcm:
{
OMX_AUDIO_PARAM_PCMMODETYPE *pcmParams =
@@ -163,7 +187,7 @@
return OMX_ErrorBadParameter;
}
- if (pcmParams->nPortIndex > 1) {
+ if (pcmParams->nPortIndex != 0) {
return OMX_ErrorUndefined;
}
@@ -189,6 +213,10 @@
return OMX_ErrorBadParameter;
}
+ if (flacParams->nPortIndex != 1) {
+ return OMX_ErrorUndefined;
+ }
+
flacParams->nCompressionLevel = mCompressionLevel;
flacParams->nChannels = mNumChannels;
flacParams->nSampleRate = mSampleRate;
@@ -203,6 +231,29 @@
OMX_ERRORTYPE SoftFlacEncoder::internalSetParameter(
OMX_INDEXTYPE index, const OMX_PTR params) {
switch (index) {
+ case OMX_IndexParamAudioPortFormat:
+ {
+ const OMX_AUDIO_PARAM_PORTFORMATTYPE *formatParams =
+ (const OMX_AUDIO_PARAM_PORTFORMATTYPE *)params;
+
+ if (!isValidOMXParam(formatParams)) {
+ return OMX_ErrorBadParameter;
+ }
+
+ if (formatParams->nPortIndex > 1) {
+ return OMX_ErrorUndefined;
+ }
+
+ if ((formatParams->nPortIndex == 0
+ && formatParams->eEncoding != OMX_AUDIO_CodingPCM)
+ || (formatParams->nPortIndex == 1
+ && formatParams->eEncoding != OMX_AUDIO_CodingFLAC)) {
+ return OMX_ErrorUndefined;
+ }
+
+ return OMX_ErrorNone;
+ }
+
case OMX_IndexParamAudioPcm:
{
ALOGV("SoftFlacEncoder::internalSetParameter(OMX_IndexParamAudioPcm)");
@@ -212,7 +263,7 @@
return OMX_ErrorBadParameter;
}
- if (pcmParams->nPortIndex != 0 && pcmParams->nPortIndex != 1) {
+ if (pcmParams->nPortIndex != 0) {
ALOGE("SoftFlacEncoder::internalSetParameter() Error #1");
return OMX_ErrorUndefined;
}
@@ -258,6 +309,10 @@
return OMX_ErrorBadParameter;
}
+ if (flacParams->nPortIndex != 1) {
+ return OMX_ErrorUndefined;
+ }
+
mCompressionLevel = flacParams->nCompressionLevel; // range clamping done inside encoder
return OMX_ErrorNone;
}
diff --git a/media/libstagefright/codecs/on2/h264dec/Android.bp b/media/libstagefright/codecs/on2/h264dec/Android.bp
index 95c2075..6d558b6 100644
--- a/media/libstagefright/codecs/on2/h264dec/Android.bp
+++ b/media/libstagefright/codecs/on2/h264dec/Android.bp
@@ -35,7 +35,7 @@
arm: {
instruction_set: "arm",
- armv7_a_neon: {
+ neon: {
cflags: [
"-DH264DEC_NEON",
"-DH264DEC_OMXDL",
diff --git a/media/libstagefright/codecs/tests/Android.mk b/media/libstagefright/codecs/tests/Android.mk
new file mode 100644
index 0000000..dcb86ba
--- /dev/null
+++ b/media/libstagefright/codecs/tests/Android.mk
@@ -0,0 +1,40 @@
+# 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.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+ C2SoftAvcDec_test.cpp \
+
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE := c2_google_component_test
+
+LOCAL_SHARED_LIBRARIES := \
+ libcutils \
+ libmedia \
+ libstagefright_codec2 \
+ libstagefright_soft_c2avcdec \
+ liblog \
+
+LOCAL_C_INCLUDES := \
+ frameworks/av/media/libstagefright/codec2/include \
+ frameworks/av/media/libstagefright/codec2/vndk/include \
+ frameworks/av/media/libstagefright/codecs/avcdec \
+
+LOCAL_CFLAGS += -Werror -Wall -std=c++14
+LOCAL_CLANG := true
+
+include $(BUILD_NATIVE_TEST)
diff --git a/media/libstagefright/codecs/tests/C2SoftAvcDec_test.cpp b/media/libstagefright/codecs/tests/C2SoftAvcDec_test.cpp
new file mode 100644
index 0000000..e59c03e
--- /dev/null
+++ b/media/libstagefright/codecs/tests/C2SoftAvcDec_test.cpp
@@ -0,0 +1,185 @@
+/*
+ * 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.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "C2SoftAvcDec_test"
+#include <utils/Log.h>
+
+#include <gtest/gtest.h>
+
+#include <media/MediaDefs.h>
+
+#include "C2SoftAvcDec.h"
+
+namespace android {
+
+namespace {
+
+template <class T>
+std::unique_ptr<T> alloc_unique_cstr(const char *cstr) {
+ std::unique_ptr<T> ptr = T::alloc_unique(strlen(cstr) + 1);
+ strcpy(ptr->m.mValue, cstr);
+ return ptr;
+}
+
+} // namespace
+
+
+class C2SoftAvcDecTest : public ::testing::Test {
+public:
+ C2SoftAvcDecTest() : mIntf(new C2SoftAvcDecIntf("dummy", 0u)) {}
+ ~C2SoftAvcDecTest() = default;
+
+ template <typename T>
+ void testReadOnlyParam(const T *expected, const T *invalid);
+
+ template <typename T>
+ void testReadOnlyParamOnStack(const T *expected, const T *invalid);
+
+ template <typename T>
+ void testReadOnlyParamOnHeap(const T *expected, const T *invalid);
+
+ template <typename T>
+ void testReadOnlyFlexParam(
+ const std::unique_ptr<T> &expected, const std::unique_ptr<T> &invalid);
+
+protected:
+ std::shared_ptr<C2SoftAvcDecIntf> mIntf;
+};
+
+template <typename T>
+void C2SoftAvcDecTest::testReadOnlyParam(const T *expected, const T *invalid) {
+ testReadOnlyParamOnStack(expected, invalid);
+ testReadOnlyParamOnHeap(expected, invalid);
+}
+
+template <typename T>
+void C2SoftAvcDecTest::testReadOnlyParamOnStack(const T *expected, const T *invalid) {
+ T param;
+ ASSERT_EQ(C2_OK, mIntf->query_nb({¶m}, {}, nullptr));
+ ASSERT_EQ(*expected, param);
+
+ std::vector<C2Param * const> params{ (C2Param * const)invalid };
+ std::vector<std::unique_ptr<C2SettingResult>> failures;
+ ASSERT_EQ(C2_BAD_VALUE, mIntf->config_nb(params, &failures));
+
+ // The param must not change after failed config.
+ ASSERT_EQ(C2_OK, mIntf->query_nb({¶m}, {}, nullptr));
+ ASSERT_EQ(*expected, param);
+}
+
+template <typename T>
+void C2SoftAvcDecTest::testReadOnlyParamOnHeap(const T *expected, const T *invalid) {
+ std::vector<std::unique_ptr<C2Param>> heapParams;
+
+ uint32_t index = expected->type();
+ if (expected->forStream()) {
+ index |= ((expected->stream() << 17) & 0x01FE0000) | 0x02000000;
+ }
+
+ ASSERT_EQ(C2_OK, mIntf->query_nb({}, {index}, &heapParams));
+ ASSERT_EQ(1u, heapParams.size());
+ ASSERT_EQ(*expected, *heapParams[0]);
+
+ std::vector<C2Param * const> params{ (C2Param * const)invalid };
+ std::vector<std::unique_ptr<C2SettingResult>> failures;
+ ASSERT_EQ(C2_BAD_VALUE, mIntf->config_nb(params, &failures));
+
+ // The param must not change after failed config.
+ heapParams.clear();
+ ASSERT_EQ(C2_OK, mIntf->query_nb({}, {index}, &heapParams));
+ ASSERT_EQ(1u, heapParams.size());
+ ASSERT_EQ(*expected, *heapParams[0]);
+}
+
+template <typename T>
+void C2SoftAvcDecTest::testReadOnlyFlexParam(
+ const std::unique_ptr<T> &expected, const std::unique_ptr<T> &invalid) {
+ std::vector<std::unique_ptr<C2Param>> heapParams;
+
+ uint32_t index = expected->type();
+ if (expected->forStream()) {
+ index |= ((expected->stream() << 17) & 0x01FE0000) | 0x02000000;
+ }
+
+ ASSERT_EQ(C2_OK, mIntf->query_nb({}, {index}, &heapParams));
+ ASSERT_EQ(1u, heapParams.size());
+ ASSERT_EQ(*expected, *heapParams[0]);
+
+ std::vector<C2Param * const> params{ invalid.get() };
+ std::vector<std::unique_ptr<C2SettingResult>> failures;
+ ASSERT_EQ(C2_BAD_VALUE, mIntf->config_nb(params, &failures));
+
+ // The param must not change after failed config.
+ heapParams.clear();
+ ASSERT_EQ(C2_OK, mIntf->query_nb({}, {index}, &heapParams));
+ ASSERT_EQ(1u, heapParams.size());
+ ASSERT_EQ(*expected, *heapParams[0]);
+}
+
+
+TEST_F(C2SoftAvcDecTest, TestNameAndId) {
+ EXPECT_STREQ("dummy", mIntf->getName().c_str());
+ EXPECT_EQ(0u, mIntf->getId());
+}
+
+TEST_F(C2SoftAvcDecTest, TestDomainInfo) {
+ C2ComponentDomainInfo expected(C2DomainVideo);
+ C2ComponentDomainInfo invalid(C2DomainAudio);
+ testReadOnlyParam(&expected, &invalid);
+}
+
+TEST_F(C2SoftAvcDecTest, TestInputStreamCount) {
+ C2PortStreamCountConfig::input expected(1);
+ C2PortStreamCountConfig::input invalid(100);
+ testReadOnlyParam(&expected, &invalid);
+}
+
+TEST_F(C2SoftAvcDecTest, TestOutputStreamCount) {
+ C2PortStreamCountConfig::output expected(1);
+ C2PortStreamCountConfig::output invalid(100);
+ testReadOnlyParam(&expected, &invalid);
+}
+
+TEST_F(C2SoftAvcDecTest, TestInputPortMime) {
+ std::unique_ptr<C2PortMimeConfig::input> expected(
+ alloc_unique_cstr<C2PortMimeConfig::input>(MEDIA_MIMETYPE_VIDEO_AVC));
+ std::unique_ptr<C2PortMimeConfig::input> invalid(
+ alloc_unique_cstr<C2PortMimeConfig::input>(MEDIA_MIMETYPE_VIDEO_RAW));
+ testReadOnlyFlexParam(expected, invalid);
+}
+
+TEST_F(C2SoftAvcDecTest, TestOutputPortMime) {
+ std::unique_ptr<C2PortMimeConfig::output> expected(
+ alloc_unique_cstr<C2PortMimeConfig::output>(MEDIA_MIMETYPE_VIDEO_RAW));
+ std::unique_ptr<C2PortMimeConfig::output> invalid(
+ alloc_unique_cstr<C2PortMimeConfig::output>(MEDIA_MIMETYPE_VIDEO_AVC));
+ testReadOnlyFlexParam(expected, invalid);
+}
+
+TEST_F(C2SoftAvcDecTest, TestInputStreamFormat) {
+ C2StreamFormatConfig::input expected(0u, C2FormatCompressed);
+ C2StreamFormatConfig::input invalid(0u, C2FormatVideo);
+ testReadOnlyParam(&expected, &invalid);
+}
+
+TEST_F(C2SoftAvcDecTest, TestOutputStreamFormat) {
+ C2StreamFormatConfig::output expected(0u, C2FormatVideo);
+ C2StreamFormatConfig::output invalid(0u, C2FormatCompressed);
+ testReadOnlyParam(&expected, &invalid);
+}
+
+} // namespace android
diff --git a/media/libstagefright/data/media_codecs_google_video.xml b/media/libstagefright/data/media_codecs_google_video.xml
index a127843..829f403 100644
--- a/media/libstagefright/data/media_codecs_google_video.xml
+++ b/media/libstagefright/data/media_codecs_google_video.xml
@@ -35,10 +35,10 @@
</MediaCodec>
<MediaCodec name="OMX.google.h264.decoder" type="video/avc">
<!-- profiles and levels: ProfileHigh : Level52 -->
- <Limit name="size" min="2x2" max="4096x4096" />
+ <Limit name="size" min="2x2" max="4080x4080" />
<Limit name="alignment" value="2x2" />
<Limit name="block-size" value="16x16" />
- <Limit name="block-count" range="1-32768" /> <!-- max 4096x2048 -->
+ <Limit name="block-count" range="1-32768" /> <!-- max 4096x2048 equivalent -->
<Limit name="blocks-per-second" range="1-1966080" />
<Limit name="bitrate" range="1-48000000" />
<Feature name="adaptive-playback" />
diff --git a/media/libstagefright/include/AACExtractor.h b/media/libstagefright/include/AACExtractor.h
index bd4c41c..91e2eed 100644
--- a/media/libstagefright/include/AACExtractor.h
+++ b/media/libstagefright/include/AACExtractor.h
@@ -32,7 +32,7 @@
AACExtractor(const sp<DataSource> &source, const sp<AMessage> &meta);
virtual size_t countTracks();
- virtual sp<IMediaSource> getTrack(size_t index);
+ virtual sp<MediaSource> getTrack(size_t index);
virtual sp<MetaData> getTrackMetaData(size_t index, uint32_t flags);
virtual sp<MetaData> getMetaData();
diff --git a/media/libstagefright/include/AMRExtractor.h b/media/libstagefright/include/AMRExtractor.h
index 8abcb12..06cd387 100644
--- a/media/libstagefright/include/AMRExtractor.h
+++ b/media/libstagefright/include/AMRExtractor.h
@@ -32,7 +32,7 @@
explicit AMRExtractor(const sp<DataSource> &source);
virtual size_t countTracks();
- virtual sp<IMediaSource> getTrack(size_t index);
+ virtual sp<MediaSource> getTrack(size_t index);
virtual sp<MetaData> getTrackMetaData(size_t index, uint32_t flags);
virtual sp<MetaData> getMetaData();
diff --git a/media/libstagefright/include/FLACExtractor.h b/media/libstagefright/include/FLACExtractor.h
index 51bc139..e945cf6 100644
--- a/media/libstagefright/include/FLACExtractor.h
+++ b/media/libstagefright/include/FLACExtractor.h
@@ -32,7 +32,7 @@
explicit FLACExtractor(const sp<DataSource> &source);
virtual size_t countTracks();
- virtual sp<IMediaSource> getTrack(size_t index);
+ virtual sp<MediaSource> getTrack(size_t index);
virtual sp<MetaData> getTrackMetaData(size_t index, uint32_t flags);
virtual sp<MetaData> getMetaData();
diff --git a/media/libstagefright/include/MP3Extractor.h b/media/libstagefright/include/MP3Extractor.h
index 2fd04f2..2b16c24 100644
--- a/media/libstagefright/include/MP3Extractor.h
+++ b/media/libstagefright/include/MP3Extractor.h
@@ -34,7 +34,7 @@
MP3Extractor(const sp<DataSource> &source, const sp<AMessage> &meta);
virtual size_t countTracks();
- virtual sp<IMediaSource> getTrack(size_t index);
+ virtual sp<MediaSource> getTrack(size_t index);
virtual sp<MetaData> getTrackMetaData(size_t index, uint32_t flags);
virtual sp<MetaData> getMetaData();
diff --git a/media/libstagefright/include/MPEG2PSExtractor.h b/media/libstagefright/include/MPEG2PSExtractor.h
index f5471b3..75e1346 100644
--- a/media/libstagefright/include/MPEG2PSExtractor.h
+++ b/media/libstagefright/include/MPEG2PSExtractor.h
@@ -34,7 +34,7 @@
explicit MPEG2PSExtractor(const sp<DataSource> &source);
virtual size_t countTracks();
- virtual sp<IMediaSource> getTrack(size_t index);
+ virtual sp<MediaSource> getTrack(size_t index);
virtual sp<MetaData> getTrackMetaData(size_t index, uint32_t flags);
virtual sp<MetaData> getMetaData();
diff --git a/media/libstagefright/include/MPEG2TSExtractor.h b/media/libstagefright/include/MPEG2TSExtractor.h
index ac93b5e..1702157 100644
--- a/media/libstagefright/include/MPEG2TSExtractor.h
+++ b/media/libstagefright/include/MPEG2TSExtractor.h
@@ -40,7 +40,7 @@
explicit MPEG2TSExtractor(const sp<DataSource> &source);
virtual size_t countTracks();
- virtual sp<IMediaSource> getTrack(size_t index);
+ virtual sp<MediaSource> getTrack(size_t index);
virtual sp<MetaData> getTrackMetaData(size_t index, uint32_t flags);
virtual sp<MetaData> getMetaData();
diff --git a/media/libstagefright/include/MPEG4Extractor.h b/media/libstagefright/include/MPEG4Extractor.h
index 4a4c538..bd404d2 100644
--- a/media/libstagefright/include/MPEG4Extractor.h
+++ b/media/libstagefright/include/MPEG4Extractor.h
@@ -56,12 +56,13 @@
explicit MPEG4Extractor(const sp<DataSource> &source);
virtual size_t countTracks();
- virtual sp<IMediaSource> getTrack(size_t index);
+ virtual sp<MediaSource> getTrack(size_t index);
virtual sp<MetaData> getTrackMetaData(size_t index, uint32_t flags);
virtual sp<MetaData> getMetaData();
virtual uint32_t flags() const;
virtual const char * name() { return "MPEG4Extractor"; }
+ virtual void release();
// for DRM
virtual char* getDrmTrackInfo(size_t trackID, int *len);
@@ -85,6 +86,9 @@
sp<SampleTable> sampleTable;
bool includes_expensive_metadata;
bool skipTrack;
+ bool has_elst;
+ int64_t elst_media_time;
+ uint64_t elst_segment_duration;
};
Vector<SidxEntry> mSidxEntries;
diff --git a/media/libstagefright/include/MidiExtractor.h b/media/libstagefright/include/MidiExtractor.h
index 94d2d08..87a4a02 100644
--- a/media/libstagefright/include/MidiExtractor.h
+++ b/media/libstagefright/include/MidiExtractor.h
@@ -56,7 +56,7 @@
explicit MidiExtractor(const sp<DataSource> &source);
virtual size_t countTracks();
- virtual sp<IMediaSource> getTrack(size_t index);
+ virtual sp<MediaSource> getTrack(size_t index);
virtual sp<MetaData> getTrackMetaData(size_t index, uint32_t flags);
virtual sp<MetaData> getMetaData();
diff --git a/media/libstagefright/include/OggExtractor.h b/media/libstagefright/include/OggExtractor.h
index 55aafed..c01850f 100644
--- a/media/libstagefright/include/OggExtractor.h
+++ b/media/libstagefright/include/OggExtractor.h
@@ -34,7 +34,7 @@
explicit OggExtractor(const sp<DataSource> &source);
virtual size_t countTracks();
- virtual sp<IMediaSource> getTrack(size_t index);
+ virtual sp<MediaSource> getTrack(size_t index);
virtual sp<MetaData> getTrackMetaData(size_t index, uint32_t flags);
virtual sp<MetaData> getMetaData();
diff --git a/media/libstagefright/include/WAVExtractor.h b/media/libstagefright/include/WAVExtractor.h
index 12ad441..dbcbe86 100644
--- a/media/libstagefright/include/WAVExtractor.h
+++ b/media/libstagefright/include/WAVExtractor.h
@@ -33,7 +33,7 @@
explicit WAVExtractor(const sp<DataSource> &source);
virtual size_t countTracks();
- virtual sp<IMediaSource> getTrack(size_t index);
+ virtual sp<MediaSource> getTrack(size_t index);
virtual sp<MetaData> getTrackMetaData(size_t index, uint32_t flags);
virtual sp<MetaData> getMetaData();
diff --git a/media/libstagefright/include/media/stagefright/AACWriter.h b/media/libstagefright/include/media/stagefright/AACWriter.h
index a1f63d7..aa60a19 100644
--- a/media/libstagefright/include/media/stagefright/AACWriter.h
+++ b/media/libstagefright/include/media/stagefright/AACWriter.h
@@ -31,7 +31,7 @@
status_t initCheck() const;
- virtual status_t addSource(const sp<IMediaSource> &source);
+ virtual status_t addSource(const sp<MediaSource> &source);
virtual bool reachedEOS();
virtual status_t start(MetaData *params = NULL);
virtual status_t stop() { return reset(); }
@@ -48,7 +48,7 @@
int mFd;
status_t mInitCheck;
- sp<IMediaSource> mSource;
+ sp<MediaSource> mSource;
bool mStarted;
volatile bool mPaused;
volatile bool mResumed;
diff --git a/media/libstagefright/include/media/stagefright/ACodec.h b/media/libstagefright/include/media/stagefright/ACodec.h
index b9f48c4..424246d 100644
--- a/media/libstagefright/include/media/stagefright/ACodec.h
+++ b/media/libstagefright/include/media/stagefright/ACodec.h
@@ -20,6 +20,7 @@
#include <stdint.h>
#include <android/native_window.h>
#include <media/hardware/MetadataBufferType.h>
+#include <media/MediaCodecInfo.h>
#include <media/IOMX.h>
#include <media/stagefright/foundation/AHierarchicalStateMachine.h>
#include <media/stagefright/CodecBase.h>
@@ -30,6 +31,8 @@
#include <OMX_Audio.h>
#include <hardware/gralloc.h>
#include <nativebase/nativebase.h>
+#include <android/hidl/allocator/1.0/IAllocator.h>
+#include <android/hidl/memory/1.0/IMemory.h>
#define TRACK_BUFFER_TIMING 0
@@ -42,20 +45,6 @@
struct DescribeColorFormat2Params;
struct DataConverter;
-// Treble shared memory
-namespace hidl {
-namespace allocator {
-namespace V1_0 {
-struct IAllocator;
-} // V1_0
-} // allocator
-namespace memory {
-namespace V1_0 {
-struct IMemory;
-} // V1_0
-} // memory
-} // hidl
-
typedef hidl::allocator::V1_0::IAllocator TAllocator;
typedef hidl::memory::V1_0::IMemory TMemory;
@@ -72,9 +61,10 @@
virtual void initiateStart();
virtual void initiateShutdown(bool keepComponentAllocated = false);
- virtual status_t queryCapabilities(
- const AString &name, const AString &mime, bool isEncoder,
- sp<MediaCodecInfo::Capabilities> *caps);
+ status_t queryCapabilities(
+ const char* owner, const char* name,
+ const char* mime, bool isEncoder,
+ MediaCodecInfo::CapabilitiesWriter* caps);
virtual status_t setSurface(const sp<Surface> &surface);
diff --git a/media/libstagefright/include/media/stagefright/AMRWriter.h b/media/libstagefright/include/media/stagefright/AMRWriter.h
index fbbdf2e..7d2c879 100644
--- a/media/libstagefright/include/media/stagefright/AMRWriter.h
+++ b/media/libstagefright/include/media/stagefright/AMRWriter.h
@@ -20,7 +20,6 @@
#include <stdio.h>
-#include <media/IMediaSource.h>
#include <media/stagefright/MediaWriter.h>
#include <utils/threads.h>
@@ -33,7 +32,7 @@
status_t initCheck() const;
- virtual status_t addSource(const sp<IMediaSource> &source);
+ virtual status_t addSource(const sp<MediaSource> &source);
virtual bool reachedEOS();
virtual status_t start(MetaData *params = NULL);
virtual status_t stop() { return reset(); }
@@ -45,7 +44,7 @@
private:
int mFd;
status_t mInitCheck;
- sp<IMediaSource> mSource;
+ sp<MediaSource> mSource;
bool mStarted;
volatile bool mPaused;
volatile bool mResumed;
diff --git a/media/libstagefright/include/media/stagefright/AudioPlayer.h b/media/libstagefright/include/media/stagefright/AudioPlayer.h
index f7499b6..581ead9 100644
--- a/media/libstagefright/include/media/stagefright/AudioPlayer.h
+++ b/media/libstagefright/include/media/stagefright/AudioPlayer.h
@@ -18,9 +18,9 @@
#define AUDIO_PLAYER_H_
-#include <media/IMediaSource.h>
#include <media/MediaPlayerInterface.h>
#include <media/stagefright/MediaBuffer.h>
+#include <media/stagefright/MediaSource.h>
#include <utils/threads.h>
namespace android {
@@ -50,7 +50,7 @@
virtual ~AudioPlayer();
// Caller retains ownership of "source".
- void setSource(const sp<IMediaSource> &source);
+ void setSource(const sp<MediaSource> &source);
status_t start(bool sourceAlreadyStarted = false);
@@ -66,7 +66,7 @@
status_t getPlaybackRate(AudioPlaybackRate *rate /* nonnull */);
private:
- sp<IMediaSource> mSource;
+ sp<MediaSource> mSource;
sp<AudioTrack> mAudioTrack;
MediaBuffer *mInputBuffer;
diff --git a/media/libstagefright/include/media/stagefright/CallbackMediaSource.h b/media/libstagefright/include/media/stagefright/CallbackMediaSource.h
new file mode 100644
index 0000000..17fca4e
--- /dev/null
+++ b/media/libstagefright/include/media/stagefright/CallbackMediaSource.h
@@ -0,0 +1,46 @@
+/*
+ * 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 CALLBACK_MEDIA_SOURCE_H_
+#define CALLBACK_MEDIA_SOURCE_H_
+
+#include <media/stagefright/MediaSource.h>
+#include <media/stagefright/foundation/ABase.h>
+
+namespace android {
+
+// A stagefright MediaSource that wraps a binder IMediaSource.
+class CallbackMediaSource : public MediaSource {
+public:
+ explicit CallbackMediaSource(const sp<IMediaSource> &source);
+ virtual ~CallbackMediaSource();
+ virtual status_t start(MetaData *params = NULL);
+ virtual status_t stop();
+ virtual sp<MetaData> getFormat();
+ virtual status_t read(
+ MediaBuffer **buffer, const ReadOptions *options = NULL);
+ virtual status_t pause();
+ virtual status_t setBuffers(const Vector<MediaBuffer *> &buffers);
+
+private:
+ sp<IMediaSource> mSource;
+
+ DISALLOW_EVIL_CONSTRUCTORS(CallbackMediaSource);
+};
+
+} // namespace android
+
+#endif // CALLBACK_MEDIA_SOURCE_H_
diff --git a/media/libstagefright/include/media/stagefright/CodecBase.h b/media/libstagefright/include/media/stagefright/CodecBase.h
index 6245ccb..9197f7b 100644
--- a/media/libstagefright/include/media/stagefright/CodecBase.h
+++ b/media/libstagefright/include/media/stagefright/CodecBase.h
@@ -213,10 +213,6 @@
// require an explicit message handler
virtual void onMessageReceived(const sp<AMessage> &msg) = 0;
- virtual status_t queryCapabilities(
- const AString& /*name*/, const AString& /*mime*/, bool /*isEncoder*/,
- sp<MediaCodecInfo::Capabilities>* /*caps*/ /* nonnull */) { return INVALID_OPERATION; }
-
virtual status_t setSurface(const sp<Surface>& /*surface*/) { return INVALID_OPERATION; }
virtual void signalFlush() = 0;
diff --git a/media/libstagefright/include/media/stagefright/DataUriSource.h b/media/libstagefright/include/media/stagefright/DataUriSource.h
deleted file mode 100644
index d223c06..0000000
--- a/media/libstagefright/include/media/stagefright/DataUriSource.h
+++ /dev/null
@@ -1,76 +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 DATA_URI_SOURCE_H_
-
-#define DATA_URI_SOURCE_H_
-
-#include <stdio.h>
-
-#include <media/stagefright/DataSource.h>
-#include <media/stagefright/MediaErrors.h>
-#include <media/stagefright/foundation/AString.h>
-
-namespace android {
-
-class DataUriSource : public DataSource {
-public:
- DataUriSource(const char *uri);
-
- virtual status_t initCheck() const {
- return mInited;
- }
-
- virtual ssize_t readAt(off64_t offset, void *data, size_t size);
-
- virtual status_t getSize(off64_t *size) {
- if (mInited != OK) {
- return mInited;
- }
-
- *size = mData.size();
- return OK;
- }
-
- virtual String8 getUri() {
- return mDataUri;
- }
-
- virtual String8 getMIMEType() const {
- return mMimeType;
- }
-
-protected:
- virtual ~DataUriSource() {
- // Nothing to delete.
- }
-
-private:
- const String8 mDataUri;
-
- String8 mMimeType;
- // Use AString because individual bytes may not be valid UTF8 chars.
- AString mData;
- status_t mInited;
-
- // Disallow copy and assign.
- DataUriSource(const DataUriSource &);
- DataUriSource &operator=(const DataUriSource &);
-};
-
-} // namespace android
-
-#endif // DATA_URI_SOURCE_H_
diff --git a/media/libstagefright/include/media/stagefright/FrameRenderTracker.h b/media/libstagefright/include/media/stagefright/FrameRenderTracker.h
index 044699c..c14755a 100644
--- a/media/libstagefright/include/media/stagefright/FrameRenderTracker.h
+++ b/media/libstagefright/include/media/stagefright/FrameRenderTracker.h
@@ -23,6 +23,8 @@
#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/foundation/AString.h>
+#include <ui/Fence.h>
+#include <ui/GraphicBuffer.h>
#include <list>
@@ -30,9 +32,6 @@
namespace android {
-class Fence;
-class GraphicBuffer;
-
// Tracks the render information about a frame. Frames go through several states while
// the render information is tracked:
//
diff --git a/media/libstagefright/include/media/stagefright/MPEG2TSWriter.h b/media/libstagefright/include/media/stagefright/MPEG2TSWriter.h
index 4516fb6..3d7960b 100644
--- a/media/libstagefright/include/media/stagefright/MPEG2TSWriter.h
+++ b/media/libstagefright/include/media/stagefright/MPEG2TSWriter.h
@@ -34,7 +34,7 @@
void *cookie,
ssize_t (*write)(void *cookie, const void *data, size_t size));
- virtual status_t addSource(const sp<IMediaSource> &source);
+ virtual status_t addSource(const sp<MediaSource> &source);
virtual status_t start(MetaData *param = NULL);
virtual status_t stop() { return reset(); }
virtual status_t pause();
diff --git a/media/libstagefright/include/media/stagefright/MPEG4Writer.h b/media/libstagefright/include/media/stagefright/MPEG4Writer.h
index 1c7b4a6..eba3b32 100644
--- a/media/libstagefright/include/media/stagefright/MPEG4Writer.h
+++ b/media/libstagefright/include/media/stagefright/MPEG4Writer.h
@@ -20,7 +20,6 @@
#include <stdio.h>
-#include <media/IMediaSource.h>
#include <media/stagefright/MediaWriter.h>
#include <utils/List.h>
#include <utils/threads.h>
@@ -40,7 +39,7 @@
// Limitations
// No more than one video and/or one audio source can be added, but
// multiple metadata sources can be added.
- virtual status_t addSource(const sp<IMediaSource> &source);
+ virtual status_t addSource(const sp<MediaSource> &source);
// Returns INVALID_OPERATION if there is no source or track.
virtual status_t start(MetaData *param = NULL);
diff --git a/media/libstagefright/include/media/stagefright/MediaClock.h b/media/libstagefright/include/media/stagefright/MediaClock.h
index dd1a809..7511913 100644
--- a/media/libstagefright/include/media/stagefright/MediaClock.h
+++ b/media/libstagefright/include/media/stagefright/MediaClock.h
@@ -18,7 +18,8 @@
#define MEDIA_CLOCK_H_
-#include <media/stagefright/foundation/ABase.h>
+#include <list>
+#include <media/stagefright/foundation/AHandler.h>
#include <utils/Mutex.h>
#include <utils/RefBase.h>
@@ -26,8 +27,14 @@
struct AMessage;
-struct MediaClock : public RefBase {
+struct MediaClock : public AHandler {
+ enum {
+ TIMER_REASON_REACHED = 0,
+ TIMER_REASON_RESET = 1,
+ };
+
MediaClock();
+ void init();
void setStartingTimeMedia(int64_t startingTimeMediaUs);
@@ -54,15 +61,38 @@
// The result is saved in |outRealUs|.
status_t getRealTimeFor(int64_t targetMediaUs, int64_t *outRealUs) const;
+ // request to set up a timer. The target time is |mediaTimeUs|, adjusted by
+ // system time of |adjustRealUs|. In other words, the wake up time is
+ // mediaTimeUs + (adjustRealUs / playbackRate)
+ void addTimer(const sp<AMessage> ¬ify, int64_t mediaTimeUs, int64_t adjustRealUs = 0);
+
+ void reset();
+
protected:
virtual ~MediaClock();
+ virtual void onMessageReceived(const sp<AMessage> &msg);
+
private:
+ enum {
+ kWhatTimeIsUp = 'tIsU',
+ };
+
+ struct Timer {
+ Timer(const sp<AMessage> ¬ify, int64_t mediaTimeUs, int64_t adjustRealUs);
+ const sp<AMessage> mNotify;
+ int64_t mMediaTimeUs;
+ int64_t mAdjustRealUs;
+ };
+
status_t getMediaTime_l(
int64_t realUs,
int64_t *outMediaUs,
bool allowPastMaxTime) const;
+ void processTimers_l();
+
+ sp<ALooper> mLooper;
mutable Mutex mLock;
int64_t mAnchorTimeMediaUs;
@@ -72,6 +102,9 @@
float mPlaybackRate;
+ int32_t mGeneration;
+ std::list<Timer> mTimers;
+
DISALLOW_EVIL_CONSTRUCTORS(MediaClock);
};
diff --git a/media/libstagefright/include/media/stagefright/MediaCodec.h b/media/libstagefright/include/media/stagefright/MediaCodec.h
index 209fe12..1030407 100644
--- a/media/libstagefright/include/media/stagefright/MediaCodec.h
+++ b/media/libstagefright/include/media/stagefright/MediaCodec.h
@@ -87,11 +87,6 @@
static sp<PersistentSurface> CreatePersistentInputSurface();
- // utility method to query capabilities
- static status_t QueryCapabilities(
- const AString &name, const AString &mime, bool isEncoder,
- sp<MediaCodecInfo::Capabilities> *caps /* nonnull */);
-
status_t configure(
const sp<AMessage> &format,
const sp<Surface> &nativeWindow,
@@ -319,6 +314,8 @@
SoftwareRenderer *mSoftRenderer;
MediaAnalyticsItem *mAnalyticsItem;
+ void initAnalyticsItem();
+ void flushAnalyticsItem();
sp<AMessage> mOutputFormat;
sp<AMessage> mInputFormat;
diff --git a/media/libstagefright/include/media/stagefright/MediaCodecList.h b/media/libstagefright/include/media/stagefright/MediaCodecList.h
index 430bc16..f2bd496 100644
--- a/media/libstagefright/include/media/stagefright/MediaCodecList.h
+++ b/media/libstagefright/include/media/stagefright/MediaCodecList.h
@@ -21,7 +21,6 @@
#include <media/stagefright/foundation/ABase.h>
#include <media/stagefright/foundation/AString.h>
#include <media/IMediaCodecList.h>
-#include <media/IOMX.h>
#include <media/MediaCodecInfo.h>
#include <sys/types.h>
@@ -36,6 +35,8 @@
struct AMessage;
+struct MediaCodecListBuilderBase;
+
struct MediaCodecList : public BnMediaCodecList {
static sp<IMediaCodecList> getInstance();
@@ -51,7 +52,7 @@
ALOGE("b/24445127");
return NULL;
}
- return mCodecInfos.itemAt(index);
+ return mCodecInfos[index];
}
virtual const sp<AMessage> getGlobalSettings() const;
@@ -62,9 +63,6 @@
// only to be used by getLocalInstance
static void *profilerThreadWrapper(void * /*arg*/);
- // only to be used by MediaPlayerService
- void parseTopLevelXMLFile(const char *path, bool ignore_errors = false);
-
enum Flags {
kPreferSoftwareCodecs = 1,
kHardwareCodecsOnly = 2,
@@ -74,13 +72,11 @@
const char *mime,
bool createEncoder,
uint32_t flags,
- Vector<AString> *matching);
-
- static uint32_t getQuirksFor(const char *mComponentName);
+ Vector<AString> *matchingCodecs,
+ Vector<AString> *owners = nullptr);
static bool isSoftwareCodec(const AString &componentName);
-
private:
class BinderDeathObserver : public IBinder::DeathRecipient {
void binderDied(const wp<IBinder> &the_late_who __unused);
@@ -88,64 +84,86 @@
static sp<BinderDeathObserver> sBinderDeathObserver;
- enum Section {
- SECTION_TOPLEVEL,
- SECTION_SETTINGS,
- SECTION_DECODERS,
- SECTION_DECODER,
- SECTION_DECODER_TYPE,
- SECTION_ENCODERS,
- SECTION_ENCODER,
- SECTION_ENCODER_TYPE,
- SECTION_INCLUDE,
- };
-
static sp<IMediaCodecList> sCodecList;
static sp<IMediaCodecList> sRemoteList;
status_t mInitCheck;
- Section mCurrentSection;
- bool mUpdate;
- Vector<Section> mPastSections;
- int32_t mDepth;
- AString mHrefBase;
sp<AMessage> mGlobalSettings;
- KeyedVector<AString, CodecSettings> mOverrides;
+ std::vector<sp<MediaCodecInfo> > mCodecInfos;
- Vector<sp<MediaCodecInfo> > mCodecInfos;
- sp<MediaCodecInfo> mCurrentInfo;
+ /**
+ * This constructor will call `buildMediaCodecList()` from the given
+ * `MediaCodecListBuilderBase` object.
+ */
+ MediaCodecList(MediaCodecListBuilderBase* builder);
- MediaCodecList();
~MediaCodecList();
status_t initCheck() const;
- void parseXMLFile(const char *path);
- static void StartElementHandlerWrapper(
- void *me, const char *name, const char **attrs);
+ MediaCodecList(const MediaCodecList&) = delete;
+ MediaCodecList& operator=(const MediaCodecList&) = delete;
- static void EndElementHandlerWrapper(void *me, const char *name);
+ friend MediaCodecListWriter;
+};
- void startElementHandler(const char *name, const char **attrs);
- void endElementHandler(const char *name);
+/**
+ * This class is to be used by a `MediaCodecListBuilderBase` instance to add
+ * information to the associated `MediaCodecList` object.
+ */
+struct MediaCodecListWriter {
+ /**
+ * Add a key-value pair to a `MediaCodecList`'s global settings.
+ *
+ * @param key Key.
+ * @param value Value.
+ */
+ void addGlobalSetting(const char* key, const char* value);
+ /**
+ * Create an add a new `MediaCodecInfo` object to a `MediaCodecList`, and
+ * return a `MediaCodecInfoWriter` object associated with the newly added
+ * `MediaCodecInfo`.
+ *
+ * @return The `MediaCodecInfoWriter` object associated with the newly
+ * added `MediaCodecInfo` object.
+ */
+ std::unique_ptr<MediaCodecInfoWriter> addMediaCodecInfo();
+private:
+ /**
+ * The associated `MediaCodecList` object.
+ */
+ MediaCodecList* mList;
- status_t includeXMLFile(const char **attrs);
- status_t addSettingFromAttributes(const char **attrs);
- status_t addMediaCodecFromAttributes(bool encoder, const char **attrs);
- void addMediaCodec(bool encoder, const char *name, const char *type = NULL);
+ /**
+ * Construct this writer object associated with the given `MediaCodecList`
+ * object.
+ *
+ * @param list The "base" `MediaCodecList` object.
+ */
+ MediaCodecListWriter(MediaCodecList* list);
- void setCurrentCodecInfo(bool encoder, const char *name, const char *type);
+ friend MediaCodecList;
+};
- status_t addQuirk(const char **attrs);
- status_t addTypeFromAttributes(const char **attrs);
- status_t addLimit(const char **attrs);
- status_t addFeature(const char **attrs);
- void addType(const char *name);
+/**
+ * This interface is to be used by `MediaCodecList` to fill its members with
+ * appropriate information. `buildMediaCodecList()` will be called from a
+ * `MediaCodecList` object during its construction.
+ */
+struct MediaCodecListBuilderBase {
+ /**
+ * Build the `MediaCodecList` via the given `MediaCodecListWriter` interface.
+ *
+ * @param writer The writer interface.
+ * @return The status of the construction. `NO_ERROR` means success.
+ */
+ virtual status_t buildMediaCodecList(MediaCodecListWriter* writer) = 0;
- status_t initializeCapabilities(const char *type);
-
- DISALLOW_EVIL_CONSTRUCTORS(MediaCodecList);
+ /**
+ * The default destructor does nothing.
+ */
+ virtual ~MediaCodecListBuilderBase();
};
} // namespace android
diff --git a/media/libstagefright/include/media/stagefright/MediaExtractor.h b/media/libstagefright/include/media/stagefright/MediaExtractor.h
index f12160b..f8eb7fd 100644
--- a/media/libstagefright/include/media/stagefright/MediaExtractor.h
+++ b/media/libstagefright/include/media/stagefright/MediaExtractor.h
@@ -19,15 +19,15 @@
#define MEDIA_EXTRACTOR_H_
#include <media/IMediaExtractor.h>
-#include <media/IMediaSource.h>
#include <media/MediaAnalyticsItem.h>
namespace android {
+
class DataSource;
struct MediaSource;
class MetaData;
-class MediaExtractor : public BnMediaExtractor {
+class MediaExtractor : public RefBase {
public:
static sp<IMediaExtractor> Create(
const sp<DataSource> &source, const char *mime = NULL);
@@ -35,7 +35,7 @@
const sp<DataSource> &source, const char *mime = NULL);
virtual size_t countTracks() = 0;
- virtual sp<IMediaSource> getTrack(size_t index) = 0;
+ virtual sp<MediaSource> getTrack(size_t index) = 0;
enum GetTrackMetaDataFlags {
kIncludeExtensiveMetaData = 1
@@ -60,18 +60,23 @@
// CAN_SEEK_BACKWARD | CAN_SEEK_FORWARD | CAN_SEEK | CAN_PAUSE
virtual uint32_t flags() const;
+ // Creates an IMediaExtractor wrapper to this MediaExtractor.
+ virtual sp<IMediaExtractor> asIMediaExtractor();
+
// for DRM
virtual char* getDrmTrackInfo(size_t /*trackID*/, int * /*len*/) {
return NULL;
}
virtual void setUID(uid_t /*uid*/) {
}
- virtual status_t setMediaCas(const HInterfaceToken &/*casToken*/) override {
+ virtual status_t setMediaCas(const HInterfaceToken &/*casToken*/) {
return INVALID_OPERATION;
}
virtual const char * name() { return "<unspecified>"; }
+ virtual void release() {}
+
protected:
MediaExtractor();
virtual ~MediaExtractor();
diff --git a/media/libstagefright/include/media/stagefright/MediaSource.h b/media/libstagefright/include/media/stagefright/MediaSource.h
index 14adb05..7e30e30 100644
--- a/media/libstagefright/include/media/stagefright/MediaSource.h
+++ b/media/libstagefright/include/media/stagefright/MediaSource.h
@@ -22,6 +22,7 @@
#include <media/IMediaSource.h>
#include <media/stagefright/MediaErrors.h>
+#include <media/stagefright/MetaData.h>
#include <utils/RefBase.h>
#include <utils/Vector.h>
@@ -29,8 +30,18 @@
class MediaBuffer;
class MetaData;
+class IMediaSource;
-struct MediaSource : public BnMediaSource {
+struct MediaSource : public virtual RefBase {
+ // TODO: Move ReadOptions implementation from IMediaSource to MediaSource
+ // once this class moves to a separate extractor lib on which both libmedia
+ // and libstagefright rely. For now, alias is added to avoid circular
+ // dependency.
+ using ReadOptions = IMediaSource::ReadOptions;
+
+ // Creates a MediaSource which wraps the given IMediaSource object.
+ static sp<MediaSource> CreateFromIMediaSource(const sp<IMediaSource> &source);
+
MediaSource();
// To be called before any other methods on this object, except
@@ -92,6 +103,9 @@
return ERROR_UNSUPPORTED;
}
+ // Creates an IMediaSource wrapper to this MediaSource.
+ virtual sp<IMediaSource> asIMediaSource();
+
protected:
virtual ~MediaSource();
diff --git a/media/libstagefright/include/media/stagefright/MediaWriter.h b/media/libstagefright/include/media/stagefright/MediaWriter.h
index cd4af4d..80c5358 100644
--- a/media/libstagefright/include/media/stagefright/MediaWriter.h
+++ b/media/libstagefright/include/media/stagefright/MediaWriter.h
@@ -20,7 +20,7 @@
#include <utils/RefBase.h>
#include <media/IMediaRecorderClient.h>
-#include <media/IMediaSource.h>
+#include <media/stagefright/MediaSource.h>
namespace android {
@@ -32,7 +32,7 @@
mMaxFileDurationLimitUs(0) {
}
- virtual status_t addSource(const sp<IMediaSource> &source) = 0;
+ virtual status_t addSource(const sp<MediaSource> &source) = 0;
virtual bool reachedEOS() = 0;
virtual status_t start(MetaData *params = NULL) = 0;
virtual status_t stop() = 0;
diff --git a/media/libstagefright/include/media/stagefright/OMXClient.h b/media/libstagefright/include/media/stagefright/OMXClient.h
index 203a181..2f159b0 100644
--- a/media/libstagefright/include/media/stagefright/OMXClient.h
+++ b/media/libstagefright/include/media/stagefright/OMXClient.h
@@ -28,9 +28,10 @@
status_t connect();
status_t connect(bool* trebleFlag);
+ status_t connect(const char* name, bool* trebleFlag = nullptr);
status_t connectLegacy();
- status_t connectTreble();
+ status_t connectTreble(const char* name = "default");
void disconnect();
sp<IOMX> interface() {
@@ -40,8 +41,8 @@
private:
sp<IOMX> mOMX;
- OMXClient(const OMXClient &);
- OMXClient &operator=(const OMXClient &);
+ OMXClient(const OMXClient &) = delete;
+ OMXClient &operator=(const OMXClient &) = delete;
};
} // namespace android
diff --git a/media/libstagefright/include/media/stagefright/OmxInfoBuilder.h b/media/libstagefright/include/media/stagefright/OmxInfoBuilder.h
new file mode 100644
index 0000000..1b4d873
--- /dev/null
+++ b/media/libstagefright/include/media/stagefright/OmxInfoBuilder.h
@@ -0,0 +1,33 @@
+/*
+ * 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.
+ */
+
+#ifndef OMX_INFO_BUILDER_H_
+#define OMX_INFO_BUILDER_H_
+
+#include <media/stagefright/MediaCodecList.h>
+#include <utils/Errors.h>
+
+namespace android {
+
+class OmxInfoBuilder : public MediaCodecListBuilderBase {
+public:
+ OmxInfoBuilder();
+ status_t buildMediaCodecList(MediaCodecListWriter* writer) override;
+};
+
+} // namespace android
+
+#endif // OMX_INFO_BUILDER_H_
diff --git a/media/libstagefright/include/media/stagefright/RemoteMediaExtractor.h b/media/libstagefright/include/media/stagefright/RemoteMediaExtractor.h
new file mode 100644
index 0000000..96611d1
--- /dev/null
+++ b/media/libstagefright/include/media/stagefright/RemoteMediaExtractor.h
@@ -0,0 +1,53 @@
+/*
+ * 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 REMOTE_MEDIA_EXTRACTOR_H_
+#define REMOTE_MEDIA_EXTRACTOR_H_
+
+#include <media/IMediaExtractor.h>
+#include <media/stagefright/MediaExtractor.h>
+
+namespace android {
+
+// IMediaExtractor wrapper to the MediaExtractor.
+class RemoteMediaExtractor : public BnMediaExtractor {
+public:
+ static sp<IMediaExtractor> wrap(const sp<MediaExtractor> &extractor);
+
+ virtual ~RemoteMediaExtractor();
+ virtual size_t countTracks();
+ virtual sp<IMediaSource> getTrack(size_t index);
+ virtual sp<MetaData> getTrackMetaData(size_t index, uint32_t flags = 0);
+ 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();
+ virtual void release();
+
+private:
+ sp<MediaExtractor> mExtractor;
+
+ explicit RemoteMediaExtractor(const sp<MediaExtractor> &extractor);
+
+ DISALLOW_EVIL_CONSTRUCTORS(RemoteMediaExtractor);
+};
+
+} // namespace android
+
+#endif // REMOTE_MEDIA_EXTRACTOR_H_
diff --git a/media/libstagefright/include/media/stagefright/RemoteMediaSource.h b/media/libstagefright/include/media/stagefright/RemoteMediaSource.h
new file mode 100644
index 0000000..2731114
--- /dev/null
+++ b/media/libstagefright/include/media/stagefright/RemoteMediaSource.h
@@ -0,0 +1,50 @@
+/*
+ * 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 REMOTE_MEDIA_SOURCE_H_
+#define REMOTE_MEDIA_SOURCE_H_
+
+#include <media/IMediaSource.h>
+#include <media/stagefright/MediaSource.h>
+#include <media/stagefright/foundation/ABase.h>
+
+namespace android {
+
+// IMediaSrouce wrapper to the MediaSource.
+class RemoteMediaSource : public BnMediaSource {
+public:
+ static sp<IMediaSource> wrap(const sp<MediaSource> &source);
+ virtual ~RemoteMediaSource();
+ virtual status_t start(MetaData *params = NULL);
+ virtual status_t stop();
+ virtual sp<MetaData> getFormat();
+ virtual status_t read(
+ MediaBuffer **buffer, const ReadOptions *options = NULL);
+ virtual status_t pause();
+ virtual status_t setBuffers(const Vector<MediaBuffer *> &buffers);
+ virtual status_t setStopTimeUs(int64_t stopTimeUs);
+
+private:
+ sp<MediaSource> mSource;
+
+ explicit RemoteMediaSource(const sp<MediaSource> &source);
+
+ DISALLOW_EVIL_CONSTRUCTORS(RemoteMediaSource);
+};
+
+} // namespace android
+
+#endif // REMOTE_MEDIA_SOURCE_H_
diff --git a/media/libstagefright/include/media/stagefright/SimpleDecodingSource.h b/media/libstagefright/include/media/stagefright/SimpleDecodingSource.h
index a000fde..b8d141a 100644
--- a/media/libstagefright/include/media/stagefright/SimpleDecodingSource.h
+++ b/media/libstagefright/include/media/stagefright/SimpleDecodingSource.h
@@ -45,12 +45,12 @@
// does not support secure input or pausing.
// if |desiredCodec| is given, use this specific codec.
static sp<SimpleDecodingSource> Create(
- const sp<IMediaSource> &source, uint32_t flags,
+ const sp<MediaSource> &source, uint32_t flags,
const sp<ANativeWindow> &nativeWindow,
const char *desiredCodec = NULL);
static sp<SimpleDecodingSource> Create(
- const sp<IMediaSource> &source, uint32_t flags = 0);
+ const sp<MediaSource> &source, uint32_t flags = 0);
virtual ~SimpleDecodingSource();
@@ -73,11 +73,11 @@
private:
// Construct this using a codec, source and looper.
SimpleDecodingSource(
- const sp<MediaCodec> &codec, const sp<IMediaSource> &source, const sp<ALooper> &looper,
+ const sp<MediaCodec> &codec, const sp<MediaSource> &source, const sp<ALooper> &looper,
bool usingSurface, bool isVorbis, const sp<AMessage> &format);
sp<MediaCodec> mCodec;
- sp<IMediaSource> mSource;
+ sp<MediaSource> mSource;
sp<ALooper> mLooper;
bool mUsingSurface;
bool mIsVorbis;
diff --git a/media/libstagefright/matroska/MatroskaExtractor.cpp b/media/libstagefright/matroska/MatroskaExtractor.cpp
index 813a257..487603c 100644
--- a/media/libstagefright/matroska/MatroskaExtractor.cpp
+++ b/media/libstagefright/matroska/MatroskaExtractor.cpp
@@ -140,6 +140,7 @@
enum Type {
AVC,
AAC,
+ HEVC,
OTHER
};
@@ -148,7 +149,7 @@
Type mType;
bool mIsAudio;
BlockIterator mBlockIter;
- ssize_t mNALSizeLen; // for type AVC
+ ssize_t mNALSizeLen; // for type AVC or HEVC
List<MediaBuffer *> mPendingFrames;
@@ -244,6 +245,19 @@
} else {
ALOGE("No mNALSizeLen");
}
+ } else if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_HEVC)) {
+ mType = HEVC;
+
+ uint32_t dummy;
+ const uint8_t *hvcc;
+ size_t hvccSize;
+ if (meta->findData(kKeyHVCC, &dummy, (const void **)&hvcc, &hvccSize)
+ && hvccSize >= 22u) {
+ mNALSizeLen = 1 + (hvcc[14+7] & 3);
+ ALOGV("mNALSizeLen = %zu", mNALSizeLen);
+ } else {
+ ALOGE("No mNALSizeLen");
+ }
} else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AAC)) {
mType = AAC;
}
@@ -692,7 +706,7 @@
MediaBuffer *frame = *mPendingFrames.begin();
mPendingFrames.erase(mPendingFrames.begin());
- if (mType != AVC || mNALSizeLen == 0) {
+ if ((mType != AVC && mType != HEVC) || mNALSizeLen == 0) {
if (targetSampleTimeUs >= 0ll) {
frame->meta_data()->setInt64(
kKeyTargetTime, targetSampleTimeUs);
@@ -885,7 +899,7 @@
return mTracks.size();
}
-sp<IMediaSource> MatroskaExtractor::getTrack(size_t index) {
+sp<MediaSource> MatroskaExtractor::getTrack(size_t index) {
if (index >= mTracks.size()) {
return NULL;
}
@@ -1293,6 +1307,14 @@
if (!strcmp("V_MPEG4/ISO/AVC", codecID)) {
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);
+ if (codecPrivateSize > 0) {
+ 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(
diff --git a/media/libstagefright/matroska/MatroskaExtractor.h b/media/libstagefright/matroska/MatroskaExtractor.h
index 19775ce..832463f 100644
--- a/media/libstagefright/matroska/MatroskaExtractor.h
+++ b/media/libstagefright/matroska/MatroskaExtractor.h
@@ -38,7 +38,7 @@
virtual size_t countTracks();
- virtual sp<IMediaSource> getTrack(size_t index);
+ virtual sp<MediaSource> getTrack(size_t index);
virtual sp<MetaData> getTrackMetaData(
size_t index, uint32_t flags);
diff --git a/media/libstagefright/mpeg2ts/MPEG2PSExtractor.cpp b/media/libstagefright/mpeg2ts/MPEG2PSExtractor.cpp
index 078a5f0..0f18fac 100644
--- a/media/libstagefright/mpeg2ts/MPEG2PSExtractor.cpp
+++ b/media/libstagefright/mpeg2ts/MPEG2PSExtractor.cpp
@@ -125,7 +125,7 @@
return mTracks.size();
}
-sp<IMediaSource> MPEG2PSExtractor::getTrack(size_t index) {
+sp<MediaSource> MPEG2PSExtractor::getTrack(size_t index) {
if (index >= mTracks.size()) {
return NULL;
}
diff --git a/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp b/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp
index 9d684e0..b66af7b 100644
--- a/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp
+++ b/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp
@@ -129,7 +129,7 @@
return mSourceImpls.size();
}
-sp<IMediaSource> MPEG2TSExtractor::getTrack(size_t index) {
+sp<MediaSource> MPEG2TSExtractor::getTrack(size_t index) {
if (index >= mSourceImpls.size()) {
return NULL;
}
diff --git a/media/libstagefright/omx/1.0/Omx.cpp b/media/libstagefright/omx/1.0/Omx.cpp
index 8f30a3d..fe50656 100644
--- a/media/libstagefright/omx/1.0/Omx.cpp
+++ b/media/libstagefright/omx/1.0/Omx.cpp
@@ -114,15 +114,23 @@
return Void();
}
instance->setHandle(handle);
- std::vector<AString> quirkVector;
- if (mParser.getQuirks(name.c_str(), &quirkVector) == OK) {
+
+ // Find quirks from mParser
+ const auto& codec = mParser.getCodecMap().find(name.c_str());
+ if (codec == mParser.getCodecMap().cend()) {
+ LOG(WARNING) << "Failed to obtain quirks for omx component "
+ "'" << name.c_str() << "' "
+ "from XML files";
+ } else {
uint32_t quirks = 0;
- for (const AString& quirk : quirkVector) {
+ for (const auto& quirk : codec->second.quirkSet) {
if (quirk == "requires-allocate-on-input-ports") {
- quirks |= kRequiresAllocateBufferOnInputPorts;
+ quirks |= OMXNodeInstance::
+ kRequiresAllocateBufferOnInputPorts;
}
if (quirk == "requires-allocate-on-output-ports") {
- quirks |= kRequiresAllocateBufferOnOutputPorts;
+ quirks |= OMXNodeInstance::
+ kRequiresAllocateBufferOnOutputPorts;
}
}
instance->setQuirks(quirks);
diff --git a/media/libstagefright/omx/1.0/OmxStore.cpp b/media/libstagefright/omx/1.0/OmxStore.cpp
index a82625a..447af6f 100644
--- a/media/libstagefright/omx/1.0/OmxStore.cpp
+++ b/media/libstagefright/omx/1.0/OmxStore.cpp
@@ -21,6 +21,7 @@
#include <media/stagefright/omx/1.0/Conversion.h>
#include <media/stagefright/omx/1.0/OmxStore.h>
+#include <media/stagefright/xmlparser/MediaCodecsXmlParser.h>
namespace android {
namespace hardware {
@@ -29,24 +30,87 @@
namespace V1_0 {
namespace implementation {
-OmxStore::OmxStore() {
+OmxStore::OmxStore(
+ const char* owner,
+ const char* const* searchDirs,
+ const char* mainXmlName,
+ const char* performanceXmlName,
+ const char* profilingResultsXmlPath) {
+ MediaCodecsXmlParser parser(searchDirs,
+ mainXmlName,
+ performanceXmlName,
+ profilingResultsXmlPath);
+ mParsingStatus = toStatus(parser.getParsingStatus());
+
+ const auto& serviceAttributeMap = parser.getServiceAttributeMap();
+ mServiceAttributeList.resize(serviceAttributeMap.size());
+ size_t i = 0;
+ for (const auto& attributePair : serviceAttributeMap) {
+ ServiceAttribute attribute;
+ attribute.key = attributePair.first;
+ attribute.value = attributePair.second;
+ mServiceAttributeList[i] = std::move(attribute);
+ ++i;
+ }
+
+ const auto& roleMap = parser.getRoleMap();
+ mRoleList.resize(roleMap.size());
+ i = 0;
+ for (const auto& rolePair : roleMap) {
+ RoleInfo role;
+ role.role = rolePair.first;
+ role.type = rolePair.second.type;
+ role.isEncoder = rolePair.second.isEncoder;
+ // TODO: Currently, preferPlatformNodes information is not available in
+ // the xml file. Once we have a way to provide this information, it
+ // should be parsed properly.
+ role.preferPlatformNodes = rolePair.first.compare(0, 5, "audio") == 0;
+ hidl_vec<NodeInfo>& nodeList = role.nodes;
+ nodeList.resize(rolePair.second.nodeList.size());
+ size_t j = 0;
+ for (const auto& nodePair : rolePair.second.nodeList) {
+ NodeInfo node;
+ node.name = nodePair.second.name;
+ node.owner = owner;
+ hidl_vec<NodeAttribute>& attributeList = node.attributes;
+ attributeList.resize(nodePair.second.attributeList.size());
+ size_t k = 0;
+ for (const auto& attributePair : nodePair.second.attributeList) {
+ NodeAttribute attribute;
+ attribute.key = attributePair.first;
+ attribute.value = attributePair.second;
+ attributeList[k] = std::move(attribute);
+ ++k;
+ }
+ nodeList[j] = std::move(node);
+ ++j;
+ }
+ mRoleList[i] = std::move(role);
+ ++i;
+ }
+
+ mPrefix = parser.getCommonPrefix();
}
OmxStore::~OmxStore() {
}
Return<void> OmxStore::listServiceAttributes(listServiceAttributes_cb _hidl_cb) {
- _hidl_cb(toStatus(NO_ERROR), hidl_vec<ServiceAttribute>());
+ if (mParsingStatus == Status::NO_ERROR) {
+ _hidl_cb(Status::NO_ERROR, mServiceAttributeList);
+ } else {
+ _hidl_cb(mParsingStatus, hidl_vec<ServiceAttribute>());
+ }
return Void();
}
Return<void> OmxStore::getNodePrefix(getNodePrefix_cb _hidl_cb) {
- _hidl_cb(hidl_string());
+ _hidl_cb(mPrefix);
return Void();
}
Return<void> OmxStore::listRoles(listRoles_cb _hidl_cb) {
- _hidl_cb(hidl_vec<RoleInfo>());
+ _hidl_cb(mRoleList);
return Void();
}
@@ -54,12 +118,6 @@
return IOmx::tryGetService(omxName);
}
-// Methods from ::android::hidl::base::V1_0::IBase follow.
-
-IOmxStore* HIDL_FETCH_IOmxStore(const char* /* name */) {
- return new OmxStore();
-}
-
} // namespace implementation
} // namespace V1_0
} // namespace omx
diff --git a/media/libstagefright/omx/1.0/WGraphicBufferProducer.cpp b/media/libstagefright/omx/1.0/WGraphicBufferProducer.cpp
index fcf1092..c4499dc 100644
--- a/media/libstagefright/omx/1.0/WGraphicBufferProducer.cpp
+++ b/media/libstagefright/omx/1.0/WGraphicBufferProducer.cpp
@@ -41,7 +41,9 @@
sp<GraphicBuffer> buf;
status_t status = mBase->requestBuffer(slot, &buf);
AnwBuffer anwBuffer;
- wrapAs(&anwBuffer, *buf);
+ if (buf != nullptr) {
+ wrapAs(&anwBuffer, *buf);
+ }
_hidl_cb(static_cast<int32_t>(status), anwBuffer);
return Void();
}
diff --git a/media/libstagefright/omx/Android.bp b/media/libstagefright/omx/Android.bp
index 46f2dd1..bec022a 100644
--- a/media/libstagefright/omx/Android.bp
+++ b/media/libstagefright/omx/Android.bp
@@ -10,6 +10,7 @@
"GraphicBufferSource.cpp",
"BWGraphicBufferSource.cpp",
"OMX.cpp",
+ "OMXStore.cpp",
"OMXMaster.cpp",
"OMXNodeInstance.cpp",
"OMXUtils.cpp",
@@ -32,15 +33,6 @@
"include",
],
- include_dirs: [
- "frameworks/av/include/media/",
- "frameworks/av/media/libstagefright",
- "frameworks/av/media/libstagefright/include",
- "frameworks/native/include", // for media/hardware/MetadataBufferType.h
- "frameworks/native/include/media/hardware",
- "frameworks/native/include/media/openmax",
- ],
-
header_libs: [
"media_plugin_headers",
],
@@ -100,8 +92,12 @@
},
}
-cc_library_static {
+cc_library_shared {
name: "libstagefright_omx_utils",
+ vendor_available: true,
+ vndk: {
+ enabled: true,
+ },
srcs: ["OMXUtils.cpp"],
export_include_dirs: [
"include",
@@ -113,10 +109,11 @@
"media_plugin_headers",
],
shared_libs: [
- "libmedia",
+ "libmedia_omx",
+ "liblog",
],
export_shared_lib_headers: [
- "libmedia",
+ "libmedia_omx",
],
sanitize: {
misc_undefined: [
diff --git a/media/libstagefright/omx/GraphicBufferSource.cpp b/media/libstagefright/omx/GraphicBufferSource.cpp
index 109460d..1917d2a 100644
--- a/media/libstagefright/omx/GraphicBufferSource.cpp
+++ b/media/libstagefright/omx/GraphicBufferSource.cpp
@@ -956,28 +956,40 @@
Mutex::Autolock lock(mMutex);
uint64_t slotMask;
- if (mConsumer->getReleasedBuffers(&slotMask) != NO_ERROR) {
- ALOGW("onBuffersReleased: unable to get released buffer set");
+ uint64_t releaseMask;
+ if (mConsumer->getReleasedBuffers(&releaseMask) != NO_ERROR) {
slotMask = 0xffffffffffffffffULL;
+ ALOGW("onBuffersReleased: unable to get released buffer set");
+ } else {
+ slotMask = releaseMask;
+ ALOGV("onBuffersReleased: 0x%016" PRIx64, slotMask);
}
- ALOGV("onBuffersReleased: 0x%016" PRIx64, slotMask);
-
+ AString unpopulated;
for (int i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) {
if ((slotMask & 0x01) != 0) {
- discardBufferInSlot_l(i);
+ if (!discardBufferInSlot_l(i)) {
+ if (!unpopulated.empty()) {
+ unpopulated.append(", ");
+ }
+ unpopulated.append(i);
+ }
}
slotMask >>= 1;
}
+ if (!unpopulated.empty()) {
+ ALOGW("released unpopulated slots: [%s]", unpopulated.c_str());
+ }
}
-void GraphicBufferSource::discardBufferInSlot_l(GraphicBufferSource::slot_id i) {
+bool GraphicBufferSource::discardBufferInSlot_l(GraphicBufferSource::slot_id i) {
ssize_t bsi = mBufferSlots.indexOfKey(i);
if (bsi < 0) {
- ALOGW("releasing an unpopulated slot: %d", i);
+ return false;
} else {
discardBufferAtSlotIndex_l(bsi);
mBufferSlots.removeItemsAt(bsi);
+ return true;
}
}
diff --git a/media/libstagefright/omx/OMX.cpp b/media/libstagefright/omx/OMX.cpp
index 5388ba0..09c4019 100644
--- a/media/libstagefright/omx/OMX.cpp
+++ b/media/libstagefright/omx/OMX.cpp
@@ -115,15 +115,22 @@
return StatusFromOMXError(err);
}
instance->setHandle(handle);
- std::vector<AString> quirkVector;
- if (mParser.getQuirks(name, &quirkVector) == OK) {
+
+ // Find quirks from mParser
+ const auto& codec = mParser.getCodecMap().find(name);
+ if (codec == mParser.getCodecMap().cend()) {
+ ALOGW("Failed to obtain quirks for omx component '%s' from XML files",
+ name);
+ } else {
uint32_t quirks = 0;
- for (const AString& quirk : quirkVector) {
+ for (const auto& quirk : codec->second.quirkSet) {
if (quirk == "requires-allocate-on-input-ports") {
- quirks |= kRequiresAllocateBufferOnInputPorts;
+ quirks |= OMXNodeInstance::
+ kRequiresAllocateBufferOnInputPorts;
}
if (quirk == "requires-allocate-on-output-ports") {
- quirks |= kRequiresAllocateBufferOnOutputPorts;
+ quirks |= OMXNodeInstance::
+ kRequiresAllocateBufferOnOutputPorts;
}
}
instance->setQuirks(quirks);
diff --git a/media/libstagefright/omx/OMXNodeInstance.cpp b/media/libstagefright/omx/OMXNodeInstance.cpp
index c749454..015a148 100644
--- a/media/libstagefright/omx/OMXNodeInstance.cpp
+++ b/media/libstagefright/omx/OMXNodeInstance.cpp
@@ -372,6 +372,8 @@
mPortMode[1] = IOMX::kPortModePresetByteBuffer;
mSecureBufferType[0] = kSecureBufferTypeUnknown;
mSecureBufferType[1] = kSecureBufferTypeUnknown;
+ mGraphicBufferEnabled[0] = false;
+ mGraphicBufferEnabled[1] = false;
mIsSecure = AString(name).endsWith(".secure");
mLegacyAdaptiveExperiment = ADebug::isExperimentEnabled("legacy-adaptive");
}
@@ -495,6 +497,9 @@
LOG_ALWAYS_FATAL("unknown state %s(%#x).", asString(state), state);
break;
}
+
+ Mutex::Autolock _l(mLock);
+
status_t err = mOwner->freeNode(this);
mDispatcher.clear();
@@ -674,6 +679,11 @@
return BAD_VALUE;
}
+ if (mSailed || mNumPortBuffers[portIndex] > 0) {
+ android_errorWriteLog(0x534e4554, "29422020");
+ return INVALID_OPERATION;
+ }
+
CLOG_CONFIG(setPortMode, "%s(%d), port %d", asString(mode), mode, portIndex);
switch (mode) {
@@ -805,6 +815,12 @@
} else if (mSecureBufferType[portIndex] == kSecureBufferTypeUnknown) {
mSecureBufferType[portIndex] = kSecureBufferTypeOpaque;
}
+ } else {
+ if (err == OMX_ErrorNone) {
+ mGraphicBufferEnabled[portIndex] = enable;
+ } else if (enable) {
+ mGraphicBufferEnabled[portIndex] = false;
+ }
}
} else {
CLOG_ERROR_IF(enable, getExtensionIndex, err, "%s", name);
@@ -1073,6 +1089,12 @@
OMX_ERRORTYPE err = OMX_ErrorNone;
bool isMetadata = mMetadataType[portIndex] != kMetadataBufferTypeInvalid;
+ if (!isMetadata && mGraphicBufferEnabled[portIndex]) {
+ ALOGE("b/62948670");
+ android_errorWriteLog(0x534e4554, "62948670");
+ return INVALID_OPERATION;
+ }
+
size_t paramsSize;
void* paramsPointer;
if (params != NULL && hParams != NULL) {
@@ -1258,6 +1280,13 @@
portIndex, graphicBuffer, buffer);
}
+ if (!mGraphicBufferEnabled[portIndex]) {
+ // Report error if this is not in graphic buffer mode.
+ ALOGE("b/62948670");
+ android_errorWriteLog(0x534e4554, "62948670");
+ return INVALID_OPERATION;
+ }
+
// See if the newer version of the extension is present.
OMX_INDEXTYPE index;
if (OMX_GetExtensionIndex(
diff --git a/media/libstagefright/omx/OMXStore.cpp b/media/libstagefright/omx/OMXStore.cpp
new file mode 100644
index 0000000..345336d
--- /dev/null
+++ b/media/libstagefright/omx/OMXStore.cpp
@@ -0,0 +1,119 @@
+/*
+ * 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 "OMXStore"
+#include <utils/Log.h>
+
+#include <media/stagefright/omx/OMXUtils.h>
+#include <media/stagefright/omx/OMX.h>
+#include <media/stagefright/omx/OMXStore.h>
+#include <media/stagefright/xmlparser/MediaCodecsXmlParser.h>
+
+#include <map>
+#include <string>
+
+namespace android {
+
+namespace {
+ struct RoleProperties {
+ std::string type;
+ bool isEncoder;
+ bool preferPlatformNodes;
+ std::multimap<size_t, IOMXStore::NodeInfo> nodeList;
+ };
+} // Unnamed namespace
+
+OMXStore::OMXStore(
+ const char* owner,
+ const char* const* searchDirs,
+ const char* mainXmlName,
+ const char* performanceXmlName,
+ const char* profilingResultsXmlPath) {
+ MediaCodecsXmlParser parser(
+ searchDirs,
+ mainXmlName,
+ performanceXmlName,
+ profilingResultsXmlPath);
+ mParsingStatus = parser.getParsingStatus();
+
+ const auto& serviceAttributeMap = parser.getServiceAttributeMap();
+ mServiceAttributeList.reserve(serviceAttributeMap.size());
+ for (const auto& attributePair : serviceAttributeMap) {
+ Attribute attribute;
+ attribute.key = attributePair.first;
+ attribute.value = attributePair.second;
+ mServiceAttributeList.push_back(std::move(attribute));
+ }
+
+ const auto& roleMap = parser.getRoleMap();
+ mRoleList.reserve(roleMap.size());
+ for (const auto& rolePair : roleMap) {
+ RoleInfo role;
+ role.role = rolePair.first;
+ role.type = rolePair.second.type;
+ role.isEncoder = rolePair.second.isEncoder;
+ // TODO: Currently, preferPlatformNodes information is not available in
+ // the xml file. Once we have a way to provide this information, it
+ // should be parsed properly.
+ role.preferPlatformNodes = rolePair.first.compare(0, 5, "audio") == 0;
+ std::vector<NodeInfo>& nodeList = role.nodes;
+ nodeList.reserve(rolePair.second.nodeList.size());
+ for (const auto& nodePair : rolePair.second.nodeList) {
+ NodeInfo node;
+ node.name = nodePair.second.name;
+ node.owner = owner;
+ std::vector<Attribute>& attributeList = node.attributes;
+ attributeList.reserve(nodePair.second.attributeList.size());
+ for (const auto& attributePair : nodePair.second.attributeList) {
+ Attribute attribute;
+ attribute.key = attributePair.first;
+ attribute.value = attributePair.second;
+ attributeList.push_back(std::move(attribute));
+ }
+ nodeList.push_back(std::move(node));
+ }
+ mRoleList.push_back(std::move(role));
+ }
+
+ mPrefix = parser.getCommonPrefix();
+}
+
+status_t OMXStore::listServiceAttributes(std::vector<Attribute>* attributes) {
+ *attributes = mServiceAttributeList;
+ return mParsingStatus;
+}
+
+status_t OMXStore::getNodePrefix(std::string* prefix) {
+ *prefix = mPrefix;
+ return mParsingStatus;
+}
+
+status_t OMXStore::listRoles(std::vector<RoleInfo>* roleList) {
+ *roleList = mRoleList;
+ return mParsingStatus;
+}
+
+status_t OMXStore::getOmx(const std::string& name, sp<IOMX>* omx) {
+ *omx = new OMX();
+ return NO_ERROR;
+}
+
+OMXStore::~OMXStore() {
+}
+
+} // namespace android
+
diff --git a/media/libstagefright/omx/SimpleSoftOMXComponent.cpp b/media/libstagefright/omx/SimpleSoftOMXComponent.cpp
index 87c2411..1ba5852 100644
--- a/media/libstagefright/omx/SimpleSoftOMXComponent.cpp
+++ b/media/libstagefright/omx/SimpleSoftOMXComponent.cpp
@@ -458,7 +458,12 @@
state = OMX_StateLoaded;
}
- CHECK_EQ((int)mState, (int)mTargetState);
+ if (mState != mTargetState) {
+ ALOGE("State change to state %d requested while still transitioning from state %d to %d",
+ state, mState, mTargetState);
+ notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL);
+ return;
+ }
switch (mState) {
case OMX_StateLoaded:
diff --git a/media/libstagefright/omx/include/media/stagefright/omx/1.0/Conversion.h b/media/libstagefright/omx/include/media/stagefright/omx/1.0/Conversion.h
index f319bdc..8d8a2d9 100644
--- a/media/libstagefright/omx/include/media/stagefright/omx/1.0/Conversion.h
+++ b/media/libstagefright/omx/include/media/stagefright/omx/1.0/Conversion.h
@@ -2064,8 +2064,10 @@
int const* constFds = static_cast<int const*>(baseFds.get());
numFds = baseNumFds;
if (l->unflatten(constBuffer, size, constFds, numFds) != NO_ERROR) {
- native_handle_close(nh);
- native_handle_delete(nh);
+ if (nh != nullptr) {
+ native_handle_close(nh);
+ native_handle_delete(nh);
+ }
return false;
}
diff --git a/media/libstagefright/omx/include/media/stagefright/omx/1.0/OmxStore.h b/media/libstagefright/omx/include/media/stagefright/omx/1.0/OmxStore.h
index f377f5a..006d2d9 100644
--- a/media/libstagefright/omx/include/media/stagefright/omx/1.0/OmxStore.h
+++ b/media/libstagefright/omx/include/media/stagefright/omx/1.0/OmxStore.h
@@ -21,6 +21,7 @@
#include <hidl/Status.h>
#include <android/hardware/media/omx/1.0/IOmxStore.h>
+#include <media/stagefright/xmlparser/MediaCodecsXmlParser.h>
namespace android {
namespace hardware {
@@ -41,17 +42,31 @@
using ::android::wp;
struct OmxStore : public IOmxStore {
- OmxStore();
+ OmxStore(
+ const char* owner = "default",
+ const char* const* searchDirs
+ = MediaCodecsXmlParser::defaultSearchDirs,
+ const char* mainXmlName
+ = MediaCodecsXmlParser::defaultMainXmlName,
+ const char* performanceXmlName
+ = MediaCodecsXmlParser::defaultPerformanceXmlName,
+ const char* profilingResultsXmlPath
+ = MediaCodecsXmlParser::defaultProfilingResultsXmlPath);
+
virtual ~OmxStore();
- // Methods from IOmx
+ // Methods from IOmxStore
Return<void> listServiceAttributes(listServiceAttributes_cb) override;
Return<void> getNodePrefix(getNodePrefix_cb) override;
Return<void> listRoles(listRoles_cb) override;
Return<sp<IOmx>> getOmx(hidl_string const&) override;
-};
-extern "C" IOmxStore* HIDL_FETCH_IOmxStore(const char* name);
+protected:
+ Status mParsingStatus;
+ hidl_string mPrefix;
+ hidl_vec<ServiceAttribute> mServiceAttributeList;
+ hidl_vec<RoleInfo> mRoleList;
+};
} // namespace implementation
} // namespace V1_0
diff --git a/media/libstagefright/omx/include/media/stagefright/omx/GraphicBufferSource.h b/media/libstagefright/omx/include/media/stagefright/omx/GraphicBufferSource.h
index 29b51a8..84fee6f 100644
--- a/media/libstagefright/omx/include/media/stagefright/omx/GraphicBufferSource.h
+++ b/media/libstagefright/omx/include/media/stagefright/omx/GraphicBufferSource.h
@@ -22,7 +22,7 @@
#include <gui/BufferQueue.h>
#include <utils/RefBase.h>
-#include <VideoAPI.h>
+#include <media/hardware/VideoAPI.h>
#include <media/IOMX.h>
#include <media/OMXFenceParcelable.h>
#include <media/stagefright/foundation/ABase.h>
@@ -264,8 +264,8 @@
void onBufferAcquired_l(const VideoBuffer &buffer);
// marks the buffer at the slot no longer cached, and accounts for the outstanding
- // acquire count
- void discardBufferInSlot_l(slot_id i);
+ // acquire count. Returns true if the slot was populated; otherwise, false.
+ bool discardBufferInSlot_l(slot_id i);
// marks the buffer at the slot index no longer cached, and accounts for the outstanding
// acquire count
diff --git a/media/libstagefright/omx/include/media/stagefright/omx/OMXMaster.h b/media/libstagefright/omx/include/media/stagefright/omx/OMXMaster.h
index 3f9c0ca..897f287 100644
--- a/media/libstagefright/omx/include/media/stagefright/omx/OMXMaster.h
+++ b/media/libstagefright/omx/include/media/stagefright/omx/OMXMaster.h
@@ -18,7 +18,7 @@
#define OMX_MASTER_H_
-#include <OMXPluginBase.h>
+#include <media/hardware/OMXPluginBase.h>
#include <utils/threads.h>
#include <utils/KeyedVector.h>
diff --git a/media/libstagefright/omx/include/media/stagefright/omx/OMXNodeInstance.h b/media/libstagefright/omx/include/media/stagefright/omx/OMXNodeInstance.h
index 8e08d15..1065ca5 100644
--- a/media/libstagefright/omx/include/media/stagefright/omx/OMXNodeInstance.h
+++ b/media/libstagefright/omx/include/media/stagefright/omx/OMXNodeInstance.h
@@ -92,6 +92,15 @@
status_t getExtensionIndex(
const char *parameterName, OMX_INDEXTYPE *index);
+ // Quirk still supported, even though deprecated
+ enum Quirks {
+ kRequiresAllocateBufferOnInputPorts = 1,
+ kRequiresAllocateBufferOnOutputPorts = 2,
+
+ kQuirksMask = kRequiresAllocateBufferOnInputPorts
+ | kRequiresAllocateBufferOnOutputPorts,
+ };
+
status_t setQuirks(OMX_U32 quirks);
bool isSecure() const {
@@ -143,7 +152,7 @@
bool mLegacyAdaptiveExperiment;
IOMX::PortMode mPortMode[2];
- // metadata and secure buffer type tracking
+ // metadata and secure buffer types and graphic buffer mode tracking
MetadataBufferType mMetadataType[2];
enum SecureBufferType {
kSecureBufferTypeUnknown,
@@ -151,6 +160,7 @@
kSecureBufferTypeNativeHandle,
};
SecureBufferType mSecureBufferType[2];
+ bool mGraphicBufferEnabled[2];
// Following are OMX parameters managed by us (instead of the component)
// OMX_IndexParamMaxFrameDurationForBitrateControl
diff --git a/media/libstagefright/omx/include/media/stagefright/omx/OMXStore.h b/media/libstagefright/omx/include/media/stagefright/omx/OMXStore.h
new file mode 100644
index 0000000..e00d713
--- /dev/null
+++ b/media/libstagefright/omx/include/media/stagefright/omx/OMXStore.h
@@ -0,0 +1,62 @@
+/*
+ * 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 ANDROID_OMXSTORE_H_
+#define ANDROID_OMXSTORE_H_
+
+#include <media/IOMXStore.h>
+#include <media/IOMX.h>
+#include <media/stagefright/xmlparser/MediaCodecsXmlParser.h>
+
+#include <vector>
+#include <string>
+
+namespace android {
+
+class OMXStore : public BnOMXStore {
+public:
+ OMXStore(
+ const char* owner = "default",
+ const char* const* searchDirs
+ = MediaCodecsXmlParser::defaultSearchDirs,
+ const char* mainXmlName
+ = MediaCodecsXmlParser::defaultMainXmlName,
+ const char* performanceXmlName
+ = MediaCodecsXmlParser::defaultPerformanceXmlName,
+ const char* profilingResultsXmlPath
+ = MediaCodecsXmlParser::defaultProfilingResultsXmlPath);
+
+ status_t listServiceAttributes(
+ std::vector<Attribute>* attributes) override;
+
+ status_t getNodePrefix(std::string* prefix) override;
+
+ status_t listRoles(std::vector<RoleInfo>* roleList) override;
+
+ status_t getOmx(const std::string& name, sp<IOMX>* omx) override;
+
+ ~OMXStore() override;
+
+protected:
+ status_t mParsingStatus;
+ std::string mPrefix;
+ std::vector<Attribute> mServiceAttributeList;
+ std::vector<RoleInfo> mRoleList;
+};
+
+} // namespace android
+
+#endif // ANDROID_OMXSTORE_H_
diff --git a/media/libstagefright/omx/include/media/stagefright/omx/SoftOMXPlugin.h b/media/libstagefright/omx/include/media/stagefright/omx/SoftOMXPlugin.h
index c89cd87..8ec717e 100644
--- a/media/libstagefright/omx/include/media/stagefright/omx/SoftOMXPlugin.h
+++ b/media/libstagefright/omx/include/media/stagefright/omx/SoftOMXPlugin.h
@@ -19,7 +19,7 @@
#define SOFT_OMX_PLUGIN_H_
#include <media/stagefright/foundation/ABase.h>
-#include <OMXPluginBase.h>
+#include <media/hardware/OMXPluginBase.h>
namespace android {
diff --git a/media/libstagefright/omx/tests/OMXHarness.cpp b/media/libstagefright/omx/tests/OMXHarness.cpp
index 3266439..4b624f2 100644
--- a/media/libstagefright/omx/tests/OMXHarness.cpp
+++ b/media/libstagefright/omx/tests/OMXHarness.cpp
@@ -543,7 +543,7 @@
return NULL;
}
-static sp<IMediaSource> CreateSourceForMime(const char *mime) {
+static sp<MediaSource> CreateSourceForMime(const char *mime) {
const char *url = GetURLForMime(mime);
if (url == NULL) {
@@ -564,7 +564,7 @@
CHECK(meta->findCString(kKeyMIMEType, &trackMime));
if (!strcasecmp(mime, trackMime)) {
- return extractor->getTrack(i);
+ return MediaSource::CreateFromIMediaSource(extractor->getTrack(i));
}
}
@@ -610,7 +610,7 @@
return OK;
}
- sp<IMediaSource> source = CreateSourceForMime(mime);
+ sp<MediaSource> source = CreateSourceForMime(mime);
if (source == NULL) {
printf(" * Unable to open test content for type '%s', "
@@ -620,14 +620,14 @@
return OK;
}
- sp<IMediaSource> seekSource = CreateSourceForMime(mime);
+ sp<MediaSource> seekSource = CreateSourceForMime(mime);
if (source == NULL || seekSource == NULL) {
return UNKNOWN_ERROR;
}
CHECK_EQ(seekSource->start(), (status_t)OK);
- sp<IMediaSource> codec = SimpleDecodingSource::Create(
+ sp<MediaSource> codec = SimpleDecodingSource::Create(
source, 0 /* flags */, NULL /* nativeWindow */, componentName);
CHECK(codec != NULL);
diff --git a/media/libstagefright/rtsp/ARTPWriter.cpp b/media/libstagefright/rtsp/ARTPWriter.cpp
index 1f6b6f7..56c4aa6 100644
--- a/media/libstagefright/rtsp/ARTPWriter.cpp
+++ b/media/libstagefright/rtsp/ARTPWriter.cpp
@@ -104,7 +104,7 @@
mFd = -1;
}
-status_t ARTPWriter::addSource(const sp<IMediaSource> &source) {
+status_t ARTPWriter::addSource(const sp<MediaSource> &source) {
mSource = source;
return OK;
}
diff --git a/media/libstagefright/rtsp/ARTPWriter.h b/media/libstagefright/rtsp/ARTPWriter.h
index 3c7042e..92a64f2 100644
--- a/media/libstagefright/rtsp/ARTPWriter.h
+++ b/media/libstagefright/rtsp/ARTPWriter.h
@@ -37,7 +37,7 @@
struct ARTPWriter : public MediaWriter {
explicit ARTPWriter(int fd);
- virtual status_t addSource(const sp<IMediaSource> &source);
+ virtual status_t addSource(const sp<MediaSource> &source);
virtual bool reachedEOS();
virtual status_t start(MetaData *params);
virtual status_t stop();
@@ -72,7 +72,7 @@
int mRTCPFd;
#endif
- sp<IMediaSource> mSource;
+ sp<MediaSource> mSource;
sp<ALooper> mLooper;
sp<AHandlerReflector<ARTPWriter> > mReflector;
diff --git a/media/libstagefright/tests/MediaCodecListOverrides_test.cpp b/media/libstagefright/tests/MediaCodecListOverrides_test.cpp
index 2599608..0c22a42 100644
--- a/media/libstagefright/tests/MediaCodecListOverrides_test.cpp
+++ b/media/libstagefright/tests/MediaCodecListOverrides_test.cpp
@@ -26,6 +26,8 @@
#include <media/stagefright/foundation/AMessage.h>
#include <media/stagefright/MediaCodecList.h>
+#include <vector>
+
namespace android {
static const char kTestOverridesStr[] =
@@ -117,7 +119,7 @@
// TODO: the codec component never returns OMX_EventCmdComplete in unit test.
TEST_F(MediaCodecListOverridesTest, DISABLED_profileCodecs) {
sp<IMediaCodecList> list = MediaCodecList::getInstance();
- Vector<sp<MediaCodecInfo>> infos;
+ std::vector<sp<MediaCodecInfo>> infos;
for (size_t i = 0; i < list->countCodecs(); ++i) {
infos.push_back(list->getCodecInfo(i));
}
diff --git a/media/libstagefright/webm/WebmFrameThread.cpp b/media/libstagefright/webm/WebmFrameThread.cpp
index 71bfbc9..420890b 100644
--- a/media/libstagefright/webm/WebmFrameThread.cpp
+++ b/media/libstagefright/webm/WebmFrameThread.cpp
@@ -252,7 +252,7 @@
}
WebmFrameMediaSourceThread::WebmFrameMediaSourceThread(
- const sp<IMediaSource>& source,
+ const sp<MediaSource>& source,
int type,
LinkedBlockingQueue<const sp<WebmFrame> >& sink,
uint64_t timeCodeScale,
diff --git a/media/libstagefright/webm/WebmFrameThread.h b/media/libstagefright/webm/WebmFrameThread.h
index 528984f..d65d9b7 100644
--- a/media/libstagefright/webm/WebmFrameThread.h
+++ b/media/libstagefright/webm/WebmFrameThread.h
@@ -123,7 +123,7 @@
class WebmFrameMediaSourceThread: public WebmFrameSourceThread {
public:
WebmFrameMediaSourceThread(
- const sp<IMediaSource>& source,
+ const sp<MediaSource>& source,
int type,
LinkedBlockingQueue<const sp<WebmFrame> >& sink,
uint64_t timeCodeScale,
@@ -142,7 +142,7 @@
}
private:
- const sp<IMediaSource> mSource;
+ const sp<MediaSource> mSource;
const uint64_t mTimeCodeScale;
uint64_t mStartTimeUs;
diff --git a/media/libstagefright/webm/WebmWriter.cpp b/media/libstagefright/webm/WebmWriter.cpp
index d6c6930..4d73eb8 100644
--- a/media/libstagefright/webm/WebmWriter.cpp
+++ b/media/libstagefright/webm/WebmWriter.cpp
@@ -360,7 +360,7 @@
return err;
}
-status_t WebmWriter::addSource(const sp<IMediaSource> &source) {
+status_t WebmWriter::addSource(const sp<MediaSource> &source) {
Mutex::Autolock l(mLock);
if (mStarted) {
ALOGE("Attempt to add source AFTER recording is started");
diff --git a/media/libstagefright/webm/WebmWriter.h b/media/libstagefright/webm/WebmWriter.h
index 9f3b19f..ed5bc4c 100644
--- a/media/libstagefright/webm/WebmWriter.h
+++ b/media/libstagefright/webm/WebmWriter.h
@@ -40,7 +40,7 @@
~WebmWriter() { reset(); }
- virtual status_t addSource(const sp<IMediaSource> &source);
+ virtual status_t addSource(const sp<MediaSource> &source);
virtual status_t start(MetaData *param = NULL);
virtual status_t stop();
virtual status_t pause();
@@ -85,7 +85,7 @@
const char *mName;
sp<WebmElement> (*mMakeTrack)(const sp<MetaData>&);
- sp<IMediaSource> mSource;
+ sp<MediaSource> mSource;
sp<WebmElement> mTrackEntry;
sp<WebmFrameSourceThread> mThread;
LinkedBlockingQueue<const sp<WebmFrame> > mSink;
diff --git a/media/libstagefright/wifi-display/Android.bp b/media/libstagefright/wifi-display/Android.bp
deleted file mode 100644
index fb08c5b..0000000
--- a/media/libstagefright/wifi-display/Android.bp
+++ /dev/null
@@ -1,51 +0,0 @@
-cc_library_shared {
- name: "libstagefright_wfd",
-
- srcs: [
- "MediaSender.cpp",
- "Parameters.cpp",
- "rtp/RTPSender.cpp",
- "source/Converter.cpp",
- "source/MediaPuller.cpp",
- "source/PlaybackSession.cpp",
- "source/RepeaterSource.cpp",
- "source/TSPacketizer.cpp",
- "source/WifiDisplaySource.cpp",
- "VideoFormats.cpp",
- ],
-
- include_dirs: [
- "frameworks/av/media/libstagefright",
- "frameworks/native/include/media/openmax",
- "frameworks/native/include/media/hardware",
- "frameworks/av/media/libstagefright/mpeg2ts",
- ],
-
- shared_libs: [
- "libbinder",
- "libcutils",
- "liblog",
- "libmedia",
- "libstagefright",
- "libstagefright_foundation",
- "libui",
- "libgui",
- "libutils",
- ],
-
- cflags: [
- "-Wno-multichar",
- "-Werror",
- "-Wall",
- ],
-
- sanitize: {
- misc_undefined: [
- "signed-integer-overflow",
- ],
- cfi: true,
- diag: {
- cfi: true,
- },
- },
-}
diff --git a/media/libstagefright/wifi-display/MediaSender.cpp b/media/libstagefright/wifi-display/MediaSender.cpp
deleted file mode 100644
index cc412f5..0000000
--- a/media/libstagefright/wifi-display/MediaSender.cpp
+++ /dev/null
@@ -1,519 +0,0 @@
-/*
- * Copyright 2013, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-//#define LOG_NDEBUG 0
-#define LOG_TAG "MediaSender"
-#include <utils/Log.h>
-
-#include "MediaSender.h"
-
-#include "rtp/RTPSender.h"
-#include "source/TSPacketizer.h"
-
-#include "include/avc_utils.h"
-
-#include <media/IHDCP.h>
-#include <media/stagefright/MediaBuffer.h>
-#include <media/stagefright/foundation/ABuffer.h>
-#include <media/stagefright/foundation/ADebug.h>
-#include <media/stagefright/foundation/AMessage.h>
-#include <media/stagefright/foundation/ANetworkSession.h>
-#include <ui/GraphicBuffer.h>
-
-namespace android {
-
-MediaSender::MediaSender(
- const sp<ANetworkSession> &netSession,
- const sp<AMessage> ¬ify)
- : mNetSession(netSession),
- mNotify(notify),
- mMode(MODE_UNDEFINED),
- mGeneration(0),
- mPrevTimeUs(-1ll),
- mInitDoneCount(0),
- mLogFile(NULL) {
- // mLogFile = fopen("/data/misc/log.ts", "wb");
-}
-
-MediaSender::~MediaSender() {
- if (mLogFile != NULL) {
- fclose(mLogFile);
- mLogFile = NULL;
- }
-}
-
-status_t MediaSender::setHDCP(const sp<IHDCP> &hdcp) {
- if (mMode != MODE_UNDEFINED) {
- return INVALID_OPERATION;
- }
-
- mHDCP = hdcp;
-
- return OK;
-}
-
-ssize_t MediaSender::addTrack(const sp<AMessage> &format, uint32_t flags) {
- if (mMode != MODE_UNDEFINED) {
- return INVALID_OPERATION;
- }
-
- TrackInfo info;
- info.mFormat = format;
- info.mFlags = flags;
- info.mPacketizerTrackIndex = -1;
-
- AString mime;
- CHECK(format->findString("mime", &mime));
- info.mIsAudio = !strncasecmp("audio/", mime.c_str(), 6);
-
- size_t index = mTrackInfos.size();
- mTrackInfos.push_back(info);
-
- return index;
-}
-
-status_t MediaSender::initAsync(
- ssize_t trackIndex,
- const char *remoteHost,
- int32_t remoteRTPPort,
- RTPSender::TransportMode rtpMode,
- int32_t remoteRTCPPort,
- RTPSender::TransportMode rtcpMode,
- int32_t *localRTPPort) {
- if (trackIndex < 0) {
- if (mMode != MODE_UNDEFINED) {
- return INVALID_OPERATION;
- }
-
- uint32_t flags = 0;
- if (mHDCP != NULL) {
- // XXX Determine proper HDCP version.
- flags |= TSPacketizer::EMIT_HDCP20_DESCRIPTOR;
- }
- mTSPacketizer = new TSPacketizer(flags);
-
- status_t err = OK;
- for (size_t i = 0; i < mTrackInfos.size(); ++i) {
- TrackInfo *info = &mTrackInfos.editItemAt(i);
-
- ssize_t packetizerTrackIndex =
- mTSPacketizer->addTrack(info->mFormat);
-
- if (packetizerTrackIndex < 0) {
- err = packetizerTrackIndex;
- break;
- }
-
- info->mPacketizerTrackIndex = packetizerTrackIndex;
- }
-
- if (err == OK) {
- sp<AMessage> notify = new AMessage(kWhatSenderNotify, this);
- notify->setInt32("generation", mGeneration);
- mTSSender = new RTPSender(mNetSession, notify);
- looper()->registerHandler(mTSSender);
-
- err = mTSSender->initAsync(
- remoteHost,
- remoteRTPPort,
- rtpMode,
- remoteRTCPPort,
- rtcpMode,
- localRTPPort);
-
- if (err != OK) {
- looper()->unregisterHandler(mTSSender->id());
- mTSSender.clear();
- }
- }
-
- if (err != OK) {
- for (size_t i = 0; i < mTrackInfos.size(); ++i) {
- TrackInfo *info = &mTrackInfos.editItemAt(i);
- info->mPacketizerTrackIndex = -1;
- }
-
- mTSPacketizer.clear();
- return err;
- }
-
- mMode = MODE_TRANSPORT_STREAM;
- mInitDoneCount = 1;
-
- return OK;
- }
-
- if (mMode == MODE_TRANSPORT_STREAM) {
- return INVALID_OPERATION;
- }
-
- if ((size_t)trackIndex >= mTrackInfos.size()) {
- return -ERANGE;
- }
-
- TrackInfo *info = &mTrackInfos.editItemAt(trackIndex);
-
- if (info->mSender != NULL) {
- return INVALID_OPERATION;
- }
-
- sp<AMessage> notify = new AMessage(kWhatSenderNotify, this);
- notify->setInt32("generation", mGeneration);
- notify->setSize("trackIndex", trackIndex);
-
- info->mSender = new RTPSender(mNetSession, notify);
- looper()->registerHandler(info->mSender);
-
- status_t err = info->mSender->initAsync(
- remoteHost,
- remoteRTPPort,
- rtpMode,
- remoteRTCPPort,
- rtcpMode,
- localRTPPort);
-
- if (err != OK) {
- looper()->unregisterHandler(info->mSender->id());
- info->mSender.clear();
-
- return err;
- }
-
- if (mMode == MODE_UNDEFINED) {
- mInitDoneCount = mTrackInfos.size();
- }
-
- mMode = MODE_ELEMENTARY_STREAMS;
-
- return OK;
-}
-
-status_t MediaSender::queueAccessUnit(
- size_t trackIndex, const sp<ABuffer> &accessUnit) {
- if (mMode == MODE_UNDEFINED) {
- return INVALID_OPERATION;
- }
-
- if (trackIndex >= mTrackInfos.size()) {
- return -ERANGE;
- }
-
- if (mMode == MODE_TRANSPORT_STREAM) {
- TrackInfo *info = &mTrackInfos.editItemAt(trackIndex);
- info->mAccessUnits.push_back(accessUnit);
-
- mTSPacketizer->extractCSDIfNecessary(info->mPacketizerTrackIndex);
-
- for (;;) {
- ssize_t minTrackIndex = -1;
- int64_t minTimeUs = -1ll;
-
- for (size_t i = 0; i < mTrackInfos.size(); ++i) {
- const TrackInfo &info = mTrackInfos.itemAt(i);
-
- if (info.mAccessUnits.empty()) {
- minTrackIndex = -1;
- minTimeUs = -1ll;
- break;
- }
-
- int64_t timeUs;
- const sp<ABuffer> &accessUnit = *info.mAccessUnits.begin();
- CHECK(accessUnit->meta()->findInt64("timeUs", &timeUs));
-
- if (minTrackIndex < 0 || timeUs < minTimeUs) {
- minTrackIndex = i;
- minTimeUs = timeUs;
- }
- }
-
- if (minTrackIndex < 0) {
- return OK;
- }
-
- TrackInfo *info = &mTrackInfos.editItemAt(minTrackIndex);
- sp<ABuffer> accessUnit = *info->mAccessUnits.begin();
- info->mAccessUnits.erase(info->mAccessUnits.begin());
-
- sp<ABuffer> tsPackets;
- status_t err = packetizeAccessUnit(
- minTrackIndex, accessUnit, &tsPackets);
-
- if (err == OK) {
- if (mLogFile != NULL) {
- fwrite(tsPackets->data(), 1, tsPackets->size(), mLogFile);
- }
-
- int64_t timeUs;
- CHECK(accessUnit->meta()->findInt64("timeUs", &timeUs));
- tsPackets->meta()->setInt64("timeUs", timeUs);
-
- err = mTSSender->queueBuffer(
- tsPackets,
- 33 /* packetType */,
- RTPSender::PACKETIZATION_TRANSPORT_STREAM);
- }
-
- if (err != OK) {
- return err;
- }
- }
- }
-
- TrackInfo *info = &mTrackInfos.editItemAt(trackIndex);
-
- return info->mSender->queueBuffer(
- accessUnit,
- info->mIsAudio ? 96 : 97 /* packetType */,
- info->mIsAudio
- ? RTPSender::PACKETIZATION_AAC : RTPSender::PACKETIZATION_H264);
-}
-
-void MediaSender::onMessageReceived(const sp<AMessage> &msg) {
- switch (msg->what()) {
- case kWhatSenderNotify:
- {
- int32_t generation;
- CHECK(msg->findInt32("generation", &generation));
- if (generation != mGeneration) {
- break;
- }
-
- onSenderNotify(msg);
- break;
- }
-
- default:
- TRESPASS();
- }
-}
-
-void MediaSender::onSenderNotify(const sp<AMessage> &msg) {
- int32_t what;
- CHECK(msg->findInt32("what", &what));
-
- switch (what) {
- case RTPSender::kWhatInitDone:
- {
- --mInitDoneCount;
-
- int32_t err;
- CHECK(msg->findInt32("err", &err));
-
- if (err != OK) {
- notifyInitDone(err);
- ++mGeneration;
- break;
- }
-
- if (mInitDoneCount == 0) {
- notifyInitDone(OK);
- }
- break;
- }
-
- case RTPSender::kWhatError:
- {
- int32_t err;
- CHECK(msg->findInt32("err", &err));
-
- notifyError(err);
- break;
- }
-
- case kWhatNetworkStall:
- {
- size_t numBytesQueued;
- CHECK(msg->findSize("numBytesQueued", &numBytesQueued));
-
- notifyNetworkStall(numBytesQueued);
- break;
- }
-
- case kWhatInformSender:
- {
- int64_t avgLatencyUs;
- CHECK(msg->findInt64("avgLatencyUs", &avgLatencyUs));
-
- int64_t maxLatencyUs;
- CHECK(msg->findInt64("maxLatencyUs", &maxLatencyUs));
-
- sp<AMessage> notify = mNotify->dup();
- notify->setInt32("what", kWhatInformSender);
- notify->setInt64("avgLatencyUs", avgLatencyUs);
- notify->setInt64("maxLatencyUs", maxLatencyUs);
- notify->post();
- break;
- }
-
- default:
- TRESPASS();
- }
-}
-
-void MediaSender::notifyInitDone(status_t err) {
- sp<AMessage> notify = mNotify->dup();
- notify->setInt32("what", kWhatInitDone);
- notify->setInt32("err", err);
- notify->post();
-}
-
-void MediaSender::notifyError(status_t err) {
- sp<AMessage> notify = mNotify->dup();
- notify->setInt32("what", kWhatError);
- notify->setInt32("err", err);
- notify->post();
-}
-
-void MediaSender::notifyNetworkStall(size_t numBytesQueued) {
- sp<AMessage> notify = mNotify->dup();
- notify->setInt32("what", kWhatNetworkStall);
- notify->setSize("numBytesQueued", numBytesQueued);
- notify->post();
-}
-
-status_t MediaSender::packetizeAccessUnit(
- size_t trackIndex,
- sp<ABuffer> accessUnit,
- sp<ABuffer> *tsPackets) {
- const TrackInfo &info = mTrackInfos.itemAt(trackIndex);
-
- uint32_t flags = 0;
-
- bool isHDCPEncrypted = false;
- uint64_t inputCTR;
- uint8_t HDCP_private_data[16];
-
- bool manuallyPrependSPSPPS =
- !info.mIsAudio
- && (info.mFlags & FLAG_MANUALLY_PREPEND_SPS_PPS)
- && IsIDR(accessUnit);
-
- if (mHDCP != NULL && !info.mIsAudio) {
- isHDCPEncrypted = true;
-
- if (manuallyPrependSPSPPS) {
- accessUnit = mTSPacketizer->prependCSD(
- info.mPacketizerTrackIndex, accessUnit);
- }
-
- status_t err;
- native_handle_t* handle;
- if (accessUnit->meta()->findPointer("handle", (void**)&handle)
- && handle != NULL) {
- int32_t rangeLength, rangeOffset;
- sp<AMessage> notify;
- CHECK(accessUnit->meta()->findInt32("rangeOffset", &rangeOffset));
- CHECK(accessUnit->meta()->findInt32("rangeLength", &rangeLength));
- CHECK(accessUnit->meta()->findMessage("notify", ¬ify)
- && notify != NULL);
- CHECK_GE((int32_t)accessUnit->size(), rangeLength);
-
- sp<GraphicBuffer> grbuf(new GraphicBuffer(
- rangeOffset + rangeLength /* width */, 1 /* height */,
- HAL_PIXEL_FORMAT_Y8, 1 /* layerCount */,
- GRALLOC_USAGE_HW_VIDEO_ENCODER,
- rangeOffset + rangeLength /* stride */, handle,
- false /* keepOwnership */));
-
- err = mHDCP->encryptNative(
- grbuf, rangeOffset, rangeLength,
- trackIndex /* streamCTR */,
- &inputCTR,
- accessUnit->data());
- notify->post();
- } else {
- err = mHDCP->encrypt(
- accessUnit->data(), accessUnit->size(),
- trackIndex /* streamCTR */,
- &inputCTR,
- accessUnit->data());
- }
-
- if (err != OK) {
- ALOGE("Failed to HDCP-encrypt media data (err %d)",
- err);
-
- return err;
- }
-
- HDCP_private_data[0] = 0x00;
-
- HDCP_private_data[1] =
- (((trackIndex >> 30) & 3) << 1) | 1;
-
- HDCP_private_data[2] = (trackIndex >> 22) & 0xff;
-
- HDCP_private_data[3] =
- (((trackIndex >> 15) & 0x7f) << 1) | 1;
-
- HDCP_private_data[4] = (trackIndex >> 7) & 0xff;
-
- HDCP_private_data[5] =
- ((trackIndex & 0x7f) << 1) | 1;
-
- HDCP_private_data[6] = 0x00;
-
- HDCP_private_data[7] =
- (((inputCTR >> 60) & 0x0f) << 1) | 1;
-
- HDCP_private_data[8] = (inputCTR >> 52) & 0xff;
-
- HDCP_private_data[9] =
- (((inputCTR >> 45) & 0x7f) << 1) | 1;
-
- HDCP_private_data[10] = (inputCTR >> 37) & 0xff;
-
- HDCP_private_data[11] =
- (((inputCTR >> 30) & 0x7f) << 1) | 1;
-
- HDCP_private_data[12] = (inputCTR >> 22) & 0xff;
-
- HDCP_private_data[13] =
- (((inputCTR >> 15) & 0x7f) << 1) | 1;
-
- HDCP_private_data[14] = (inputCTR >> 7) & 0xff;
-
- HDCP_private_data[15] =
- ((inputCTR & 0x7f) << 1) | 1;
-
- flags |= TSPacketizer::IS_ENCRYPTED;
- } else if (manuallyPrependSPSPPS) {
- flags |= TSPacketizer::PREPEND_SPS_PPS_TO_IDR_FRAMES;
- }
-
- int64_t timeUs = ALooper::GetNowUs();
- if (mPrevTimeUs < 0ll || mPrevTimeUs + 100000ll <= timeUs) {
- flags |= TSPacketizer::EMIT_PCR;
- flags |= TSPacketizer::EMIT_PAT_AND_PMT;
-
- mPrevTimeUs = timeUs;
- }
-
- mTSPacketizer->packetize(
- info.mPacketizerTrackIndex,
- accessUnit,
- tsPackets,
- flags,
- !isHDCPEncrypted ? NULL : HDCP_private_data,
- !isHDCPEncrypted ? 0 : sizeof(HDCP_private_data),
- info.mIsAudio ? 2 : 0 /* numStuffingBytes */);
-
- return OK;
-}
-
-} // namespace android
-
diff --git a/media/libstagefright/wifi-display/MediaSender.h b/media/libstagefright/wifi-display/MediaSender.h
deleted file mode 100644
index 04538ea..0000000
--- a/media/libstagefright/wifi-display/MediaSender.h
+++ /dev/null
@@ -1,132 +0,0 @@
-/*
- * Copyright 2013, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef MEDIA_SENDER_H_
-
-#define MEDIA_SENDER_H_
-
-#include "rtp/RTPSender.h"
-
-#include <media/stagefright/foundation/ABase.h>
-#include <media/stagefright/foundation/AHandler.h>
-#include <utils/Errors.h>
-#include <utils/Vector.h>
-
-namespace android {
-
-struct ABuffer;
-struct ANetworkSession;
-struct AMessage;
-struct IHDCP;
-struct TSPacketizer;
-
-// This class facilitates sending of data from one or more media tracks
-// through one or more RTP channels, either providing a 1:1 mapping from
-// track to RTP channel or muxing all tracks into a single RTP channel and
-// using transport stream encapsulation.
-// Optionally the (video) data is encrypted using the provided hdcp object.
-struct MediaSender : public AHandler {
- enum {
- kWhatInitDone,
- kWhatError,
- kWhatNetworkStall,
- kWhatInformSender,
- };
-
- MediaSender(
- const sp<ANetworkSession> &netSession,
- const sp<AMessage> ¬ify);
-
- status_t setHDCP(const sp<IHDCP> &hdcp);
-
- enum FlagBits {
- FLAG_MANUALLY_PREPEND_SPS_PPS = 1,
- };
- ssize_t addTrack(const sp<AMessage> &format, uint32_t flags);
-
- // If trackIndex == -1, initialize for transport stream muxing.
- status_t initAsync(
- ssize_t trackIndex,
- const char *remoteHost,
- int32_t remoteRTPPort,
- RTPSender::TransportMode rtpMode,
- int32_t remoteRTCPPort,
- RTPSender::TransportMode rtcpMode,
- int32_t *localRTPPort);
-
- status_t queueAccessUnit(
- size_t trackIndex, const sp<ABuffer> &accessUnit);
-
-protected:
- virtual void onMessageReceived(const sp<AMessage> &msg);
- virtual ~MediaSender();
-
-private:
- enum {
- kWhatSenderNotify,
- };
-
- enum Mode {
- MODE_UNDEFINED,
- MODE_TRANSPORT_STREAM,
- MODE_ELEMENTARY_STREAMS,
- };
-
- struct TrackInfo {
- sp<AMessage> mFormat;
- uint32_t mFlags;
- sp<RTPSender> mSender;
- List<sp<ABuffer> > mAccessUnits;
- ssize_t mPacketizerTrackIndex;
- bool mIsAudio;
- };
-
- sp<ANetworkSession> mNetSession;
- sp<AMessage> mNotify;
-
- sp<IHDCP> mHDCP;
-
- Mode mMode;
- int32_t mGeneration;
-
- Vector<TrackInfo> mTrackInfos;
-
- sp<TSPacketizer> mTSPacketizer;
- sp<RTPSender> mTSSender;
- int64_t mPrevTimeUs;
-
- size_t mInitDoneCount;
-
- FILE *mLogFile;
-
- void onSenderNotify(const sp<AMessage> &msg);
-
- void notifyInitDone(status_t err);
- void notifyError(status_t err);
- void notifyNetworkStall(size_t numBytesQueued);
-
- status_t packetizeAccessUnit(
- size_t trackIndex,
- sp<ABuffer> accessUnit,
- sp<ABuffer> *tsPackets);
-
- DISALLOW_EVIL_CONSTRUCTORS(MediaSender);
-};
-
-} // namespace android
-
-#endif // MEDIA_SENDER_H_
-
diff --git a/media/libstagefright/wifi-display/Parameters.cpp b/media/libstagefright/wifi-display/Parameters.cpp
deleted file mode 100644
index d2a61ea..0000000
--- a/media/libstagefright/wifi-display/Parameters.cpp
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * Copyright 2012, 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 "Parameters.h"
-
-#include <media/stagefright/MediaErrors.h>
-
-namespace android {
-
-// static
-sp<Parameters> Parameters::Parse(const char *data, size_t size) {
- sp<Parameters> params = new Parameters;
- status_t err = params->parse(data, size);
-
- if (err != OK) {
- return NULL;
- }
-
- return params;
-}
-
-Parameters::Parameters() {}
-
-Parameters::~Parameters() {}
-
-status_t Parameters::parse(const char *data, size_t size) {
- size_t i = 0;
- while (i < size) {
- size_t nameStart = i;
- while (i < size && data[i] != ':') {
- ++i;
- }
-
- if (i == size || i == nameStart) {
- return ERROR_MALFORMED;
- }
-
- AString name(&data[nameStart], i - nameStart);
- name.trim();
- name.tolower();
-
- ++i;
-
- size_t valueStart = i;
-
- while (i + 1 < size && (data[i] != '\r' || data[i + 1] != '\n')) {
- ++i;
- }
-
- AString value(&data[valueStart], i - valueStart);
- value.trim();
-
- mDict.add(name, value);
-
- while (i + 1 < size && data[i] == '\r' && data[i + 1] == '\n') {
- i += 2;
- }
- }
-
- return OK;
-}
-
-bool Parameters::findParameter(const char *name, AString *value) const {
- AString key = name;
- key.tolower();
-
- ssize_t index = mDict.indexOfKey(key);
-
- if (index < 0) {
- value->clear();
-
- return false;
- }
-
- *value = mDict.valueAt(index);
- return true;
-}
-
-} // namespace android
diff --git a/media/libstagefright/wifi-display/Parameters.h b/media/libstagefright/wifi-display/Parameters.h
deleted file mode 100644
index a5e787e..0000000
--- a/media/libstagefright/wifi-display/Parameters.h
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright 2012, 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 <media/stagefright/foundation/ABase.h>
-#include <media/stagefright/foundation/AString.h>
-#include <utils/KeyedVector.h>
-#include <utils/RefBase.h>
-
-namespace android {
-
-struct Parameters : public RefBase {
- static sp<Parameters> Parse(const char *data, size_t size);
-
- bool findParameter(const char *name, AString *value) const;
-
-protected:
- virtual ~Parameters();
-
-private:
- KeyedVector<AString, AString> mDict;
-
- Parameters();
- status_t parse(const char *data, size_t size);
-
- DISALLOW_EVIL_CONSTRUCTORS(Parameters);
-};
-
-} // namespace android
diff --git a/media/libstagefright/wifi-display/VideoFormats.cpp b/media/libstagefright/wifi-display/VideoFormats.cpp
deleted file mode 100644
index dbc511c..0000000
--- a/media/libstagefright/wifi-display/VideoFormats.cpp
+++ /dev/null
@@ -1,550 +0,0 @@
-/*
- * Copyright 2013, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-//#define LOG_NDEBUG 0
-#define LOG_TAG "VideoFormats"
-#include <utils/Log.h>
-
-#include "VideoFormats.h"
-
-#include <media/stagefright/foundation/ADebug.h>
-
-namespace android {
-
-// static
-const VideoFormats::config_t VideoFormats::mResolutionTable[][32] = {
- {
- // CEA Resolutions
- { 640, 480, 60, false, 0, 0},
- { 720, 480, 60, false, 0, 0},
- { 720, 480, 60, true, 0, 0},
- { 720, 576, 50, false, 0, 0},
- { 720, 576, 50, true, 0, 0},
- { 1280, 720, 30, false, 0, 0},
- { 1280, 720, 60, false, 0, 0},
- { 1920, 1080, 30, false, 0, 0},
- { 1920, 1080, 60, false, 0, 0},
- { 1920, 1080, 60, true, 0, 0},
- { 1280, 720, 25, false, 0, 0},
- { 1280, 720, 50, false, 0, 0},
- { 1920, 1080, 25, false, 0, 0},
- { 1920, 1080, 50, false, 0, 0},
- { 1920, 1080, 50, true, 0, 0},
- { 1280, 720, 24, false, 0, 0},
- { 1920, 1080, 24, false, 0, 0},
- { 0, 0, 0, false, 0, 0},
- { 0, 0, 0, false, 0, 0},
- { 0, 0, 0, false, 0, 0},
- { 0, 0, 0, false, 0, 0},
- { 0, 0, 0, false, 0, 0},
- { 0, 0, 0, false, 0, 0},
- { 0, 0, 0, false, 0, 0},
- { 0, 0, 0, false, 0, 0},
- { 0, 0, 0, false, 0, 0},
- { 0, 0, 0, false, 0, 0},
- { 0, 0, 0, false, 0, 0},
- { 0, 0, 0, false, 0, 0},
- { 0, 0, 0, false, 0, 0},
- { 0, 0, 0, false, 0, 0},
- { 0, 0, 0, false, 0, 0},
- },
- {
- // VESA Resolutions
- { 800, 600, 30, false, 0, 0},
- { 800, 600, 60, false, 0, 0},
- { 1024, 768, 30, false, 0, 0},
- { 1024, 768, 60, false, 0, 0},
- { 1152, 864, 30, false, 0, 0},
- { 1152, 864, 60, false, 0, 0},
- { 1280, 768, 30, false, 0, 0},
- { 1280, 768, 60, false, 0, 0},
- { 1280, 800, 30, false, 0, 0},
- { 1280, 800, 60, false, 0, 0},
- { 1360, 768, 30, false, 0, 0},
- { 1360, 768, 60, false, 0, 0},
- { 1366, 768, 30, false, 0, 0},
- { 1366, 768, 60, false, 0, 0},
- { 1280, 1024, 30, false, 0, 0},
- { 1280, 1024, 60, false, 0, 0},
- { 1400, 1050, 30, false, 0, 0},
- { 1400, 1050, 60, false, 0, 0},
- { 1440, 900, 30, false, 0, 0},
- { 1440, 900, 60, false, 0, 0},
- { 1600, 900, 30, false, 0, 0},
- { 1600, 900, 60, false, 0, 0},
- { 1600, 1200, 30, false, 0, 0},
- { 1600, 1200, 60, false, 0, 0},
- { 1680, 1024, 30, false, 0, 0},
- { 1680, 1024, 60, false, 0, 0},
- { 1680, 1050, 30, false, 0, 0},
- { 1680, 1050, 60, false, 0, 0},
- { 1920, 1200, 30, false, 0, 0},
- { 1920, 1200, 60, false, 0, 0},
- { 0, 0, 0, false, 0, 0},
- { 0, 0, 0, false, 0, 0},
- },
- {
- // HH Resolutions
- { 800, 480, 30, false, 0, 0},
- { 800, 480, 60, false, 0, 0},
- { 854, 480, 30, false, 0, 0},
- { 854, 480, 60, false, 0, 0},
- { 864, 480, 30, false, 0, 0},
- { 864, 480, 60, false, 0, 0},
- { 640, 360, 30, false, 0, 0},
- { 640, 360, 60, false, 0, 0},
- { 960, 540, 30, false, 0, 0},
- { 960, 540, 60, false, 0, 0},
- { 848, 480, 30, false, 0, 0},
- { 848, 480, 60, false, 0, 0},
- { 0, 0, 0, false, 0, 0},
- { 0, 0, 0, false, 0, 0},
- { 0, 0, 0, false, 0, 0},
- { 0, 0, 0, false, 0, 0},
- { 0, 0, 0, false, 0, 0},
- { 0, 0, 0, false, 0, 0},
- { 0, 0, 0, false, 0, 0},
- { 0, 0, 0, false, 0, 0},
- { 0, 0, 0, false, 0, 0},
- { 0, 0, 0, false, 0, 0},
- { 0, 0, 0, false, 0, 0},
- { 0, 0, 0, false, 0, 0},
- { 0, 0, 0, false, 0, 0},
- { 0, 0, 0, false, 0, 0},
- { 0, 0, 0, false, 0, 0},
- { 0, 0, 0, false, 0, 0},
- { 0, 0, 0, false, 0, 0},
- { 0, 0, 0, false, 0, 0},
- { 0, 0, 0, false, 0, 0},
- { 0, 0, 0, false, 0, 0},
- }
-};
-
-VideoFormats::VideoFormats() {
- memcpy(mConfigs, mResolutionTable, sizeof(mConfigs));
-
- for (size_t i = 0; i < kNumResolutionTypes; ++i) {
- mResolutionEnabled[i] = 0;
- }
-
- setNativeResolution(RESOLUTION_CEA, 0); // default to 640x480 p60
-}
-
-void VideoFormats::setNativeResolution(ResolutionType type, size_t index) {
- CHECK_LT(type, kNumResolutionTypes);
- CHECK(GetConfiguration(type, index, NULL, NULL, NULL, NULL));
-
- mNativeType = type;
- mNativeIndex = index;
-
- setResolutionEnabled(type, index);
-}
-
-void VideoFormats::getNativeResolution(
- ResolutionType *type, size_t *index) const {
- *type = mNativeType;
- *index = mNativeIndex;
-}
-
-void VideoFormats::disableAll() {
- for (size_t i = 0; i < kNumResolutionTypes; ++i) {
- mResolutionEnabled[i] = 0;
- for (size_t j = 0; j < 32; j++) {
- mConfigs[i][j].profile = mConfigs[i][j].level = 0;
- }
- }
-}
-
-void VideoFormats::enableAll() {
- for (size_t i = 0; i < kNumResolutionTypes; ++i) {
- mResolutionEnabled[i] = 0xffffffff;
- for (size_t j = 0; j < 32; j++) {
- mConfigs[i][j].profile = (1ul << PROFILE_CBP);
- mConfigs[i][j].level = (1ul << LEVEL_31);
- }
- }
-}
-
-void VideoFormats::enableResolutionUpto(
- ResolutionType type, size_t index,
- ProfileType profile, LevelType level) {
- size_t width, height, fps, score;
- bool interlaced;
- if (!GetConfiguration(type, index, &width, &height,
- &fps, &interlaced)) {
- ALOGE("Maximum resolution not found!");
- return;
- }
- score = width * height * fps * (!interlaced + 1);
- for (size_t i = 0; i < kNumResolutionTypes; ++i) {
- for (size_t j = 0; j < 32; j++) {
- if (GetConfiguration((ResolutionType)i, j,
- &width, &height, &fps, &interlaced)
- && score >= width * height * fps * (!interlaced + 1)) {
- setResolutionEnabled((ResolutionType)i, j);
- setProfileLevel((ResolutionType)i, j, profile, level);
- }
- }
- }
-}
-
-void VideoFormats::setResolutionEnabled(
- ResolutionType type, size_t index, bool enabled) {
- CHECK_LT(type, kNumResolutionTypes);
- CHECK(GetConfiguration(type, index, NULL, NULL, NULL, NULL));
-
- if (enabled) {
- mResolutionEnabled[type] |= (1ul << index);
- mConfigs[type][index].profile = (1ul << PROFILE_CBP);
- mConfigs[type][index].level = (1ul << LEVEL_31);
- } else {
- mResolutionEnabled[type] &= ~(1ul << index);
- mConfigs[type][index].profile = 0;
- mConfigs[type][index].level = 0;
- }
-}
-
-void VideoFormats::setProfileLevel(
- ResolutionType type, size_t index,
- ProfileType profile, LevelType level) {
- CHECK_LT(type, kNumResolutionTypes);
- CHECK(GetConfiguration(type, index, NULL, NULL, NULL, NULL));
-
- mConfigs[type][index].profile = (1ul << profile);
- mConfigs[type][index].level = (1ul << level);
-}
-
-void VideoFormats::getProfileLevel(
- ResolutionType type, size_t index,
- ProfileType *profile, LevelType *level) const{
- CHECK_LT(type, kNumResolutionTypes);
- CHECK(GetConfiguration(type, index, NULL, NULL, NULL, NULL));
-
- int i, bestProfile = -1, bestLevel = -1;
-
- for (i = 0; i < kNumProfileTypes; ++i) {
- if (mConfigs[type][index].profile & (1ul << i)) {
- bestProfile = i;
- }
- }
-
- for (i = 0; i < kNumLevelTypes; ++i) {
- if (mConfigs[type][index].level & (1ul << i)) {
- bestLevel = i;
- }
- }
-
- if (bestProfile == -1 || bestLevel == -1) {
- ALOGE("Profile or level not set for resolution type %d, index %zu",
- type, index);
- bestProfile = PROFILE_CBP;
- bestLevel = LEVEL_31;
- }
-
- *profile = (ProfileType) bestProfile;
- *level = (LevelType) bestLevel;
-}
-
-bool VideoFormats::isResolutionEnabled(
- ResolutionType type, size_t index) const {
- CHECK_LT(type, kNumResolutionTypes);
- CHECK(GetConfiguration(type, index, NULL, NULL, NULL, NULL));
-
- return mResolutionEnabled[type] & (1ul << index);
-}
-
-// static
-bool VideoFormats::GetConfiguration(
- ResolutionType type,
- size_t index,
- size_t *width, size_t *height, size_t *framesPerSecond,
- bool *interlaced) {
- CHECK_LT(type, kNumResolutionTypes);
-
- if (index >= 32) {
- return false;
- }
-
- const config_t *config = &mResolutionTable[type][index];
-
- if (config->width == 0) {
- return false;
- }
-
- if (width) {
- *width = config->width;
- }
-
- if (height) {
- *height = config->height;
- }
-
- if (framesPerSecond) {
- *framesPerSecond = config->framesPerSecond;
- }
-
- if (interlaced) {
- *interlaced = config->interlaced;
- }
-
- return true;
-}
-
-bool VideoFormats::parseH264Codec(const char *spec) {
- unsigned profile, level, res[3];
-
- if (sscanf(
- spec,
- "%02x %02x %08X %08X %08X",
- &profile,
- &level,
- &res[0],
- &res[1],
- &res[2]) != 5) {
- return false;
- }
-
- for (size_t i = 0; i < kNumResolutionTypes; ++i) {
- for (size_t j = 0; j < 32; ++j) {
- if (res[i] & (1ul << j)){
- mResolutionEnabled[i] |= (1ul << j);
- if (profile > mConfigs[i][j].profile) {
- // prefer higher profile (even if level is lower)
- mConfigs[i][j].profile = profile;
- mConfigs[i][j].level = level;
- } else if (profile == mConfigs[i][j].profile &&
- level > mConfigs[i][j].level) {
- mConfigs[i][j].level = level;
- }
- }
- }
- }
-
- return true;
-}
-
-// static
-bool VideoFormats::GetProfileLevel(
- ProfileType profile, LevelType level, unsigned *profileIdc,
- unsigned *levelIdc, unsigned *constraintSet) {
- CHECK_LT(profile, kNumProfileTypes);
- CHECK_LT(level, kNumLevelTypes);
-
- static const unsigned kProfileIDC[kNumProfileTypes] = {
- 66, // PROFILE_CBP
- 100, // PROFILE_CHP
- };
-
- static const unsigned kLevelIDC[kNumLevelTypes] = {
- 31, // LEVEL_31
- 32, // LEVEL_32
- 40, // LEVEL_40
- 41, // LEVEL_41
- 42, // LEVEL_42
- };
-
- static const unsigned kConstraintSet[kNumProfileTypes] = {
- 0xc0, // PROFILE_CBP
- 0x0c, // PROFILE_CHP
- };
-
- if (profileIdc) {
- *profileIdc = kProfileIDC[profile];
- }
-
- if (levelIdc) {
- *levelIdc = kLevelIDC[level];
- }
-
- if (constraintSet) {
- *constraintSet = kConstraintSet[profile];
- }
-
- return true;
-}
-
-bool VideoFormats::parseFormatSpec(const char *spec) {
- CHECK_EQ(kNumResolutionTypes, 3);
-
- disableAll();
-
- unsigned native, dummy;
- size_t size = strlen(spec);
- size_t offset = 0;
-
- if (sscanf(spec, "%02x %02x ", &native, &dummy) != 2) {
- return false;
- }
-
- offset += 6; // skip native and preferred-display-mode-supported
- CHECK_LE(offset + 58, size);
- while (offset < size) {
- parseH264Codec(spec + offset);
- offset += 60; // skip H.264-codec + ", "
- }
-
- mNativeIndex = native >> 3;
- mNativeType = (ResolutionType)(native & 7);
-
- bool success;
- if (mNativeType >= kNumResolutionTypes) {
- success = false;
- } else {
- success = GetConfiguration(
- mNativeType, mNativeIndex, NULL, NULL, NULL, NULL);
- }
-
- if (!success) {
- ALOGW("sink advertised an illegal native resolution, fortunately "
- "this value is ignored for the time being...");
- }
-
- return true;
-}
-
-AString VideoFormats::getFormatSpec(bool forM4Message) const {
- CHECK_EQ(kNumResolutionTypes, 3);
-
- // wfd_video_formats:
- // 1 byte "native"
- // 1 byte "preferred-display-mode-supported" 0 or 1
- // one or more avc codec structures
- // 1 byte profile
- // 1 byte level
- // 4 byte CEA mask
- // 4 byte VESA mask
- // 4 byte HH mask
- // 1 byte latency
- // 2 byte min-slice-slice
- // 2 byte slice-enc-params
- // 1 byte framerate-control-support
- // max-hres (none or 2 byte)
- // max-vres (none or 2 byte)
-
- return AStringPrintf(
- "%02x 00 %02x %02x %08x %08x %08x 00 0000 0000 00 none none",
- forM4Message ? 0x00 : ((mNativeIndex << 3) | mNativeType),
- mConfigs[mNativeType][mNativeIndex].profile,
- mConfigs[mNativeType][mNativeIndex].level,
- mResolutionEnabled[0],
- mResolutionEnabled[1],
- mResolutionEnabled[2]);
-}
-
-// static
-bool VideoFormats::PickBestFormat(
- const VideoFormats &sinkSupported,
- const VideoFormats &sourceSupported,
- ResolutionType *chosenType,
- size_t *chosenIndex,
- ProfileType *chosenProfile,
- LevelType *chosenLevel) {
-#if 0
- // Support for the native format is a great idea, the spec includes
- // these features, but nobody supports it and the tests don't validate it.
-
- ResolutionType nativeType;
- size_t nativeIndex;
- sinkSupported.getNativeResolution(&nativeType, &nativeIndex);
- if (sinkSupported.isResolutionEnabled(nativeType, nativeIndex)) {
- if (sourceSupported.isResolutionEnabled(nativeType, nativeIndex)) {
- ALOGI("Choosing sink's native resolution");
- *chosenType = nativeType;
- *chosenIndex = nativeIndex;
- return true;
- }
- } else {
- ALOGW("Sink advertised native resolution that it doesn't "
- "actually support... ignoring");
- }
-
- sourceSupported.getNativeResolution(&nativeType, &nativeIndex);
- if (sourceSupported.isResolutionEnabled(nativeType, nativeIndex)) {
- if (sinkSupported.isResolutionEnabled(nativeType, nativeIndex)) {
- ALOGI("Choosing source's native resolution");
- *chosenType = nativeType;
- *chosenIndex = nativeIndex;
- return true;
- }
- } else {
- ALOGW("Source advertised native resolution that it doesn't "
- "actually support... ignoring");
- }
-#endif
-
- bool first = true;
- uint32_t bestScore = 0;
- size_t bestType = 0;
- size_t bestIndex = 0;
- for (size_t i = 0; i < kNumResolutionTypes; ++i) {
- for (size_t j = 0; j < 32; ++j) {
- size_t width, height, framesPerSecond;
- bool interlaced;
- if (!GetConfiguration(
- (ResolutionType)i,
- j,
- &width, &height, &framesPerSecond, &interlaced)) {
- break;
- }
-
- if (!sinkSupported.isResolutionEnabled((ResolutionType)i, j)
- || !sourceSupported.isResolutionEnabled(
- (ResolutionType)i, j)) {
- continue;
- }
-
- ALOGV("type %zu, index %zu, %zu x %zu %c%zu supported",
- i, j, width, height, interlaced ? 'i' : 'p', framesPerSecond);
-
- uint32_t score = width * height * framesPerSecond;
- if (!interlaced) {
- score *= 2;
- }
-
- if (first || score > bestScore) {
- bestScore = score;
- bestType = i;
- bestIndex = j;
-
- first = false;
- }
- }
- }
-
- if (first) {
- return false;
- }
-
- *chosenType = (ResolutionType)bestType;
- *chosenIndex = bestIndex;
-
- // Pick the best profile/level supported by both sink and source.
- ProfileType srcProfile, sinkProfile;
- LevelType srcLevel, sinkLevel;
- sourceSupported.getProfileLevel(
- (ResolutionType)bestType, bestIndex,
- &srcProfile, &srcLevel);
- sinkSupported.getProfileLevel(
- (ResolutionType)bestType, bestIndex,
- &sinkProfile, &sinkLevel);
- *chosenProfile = srcProfile < sinkProfile ? srcProfile : sinkProfile;
- *chosenLevel = srcLevel < sinkLevel ? srcLevel : sinkLevel;
-
- return true;
-}
-
-} // namespace android
-
diff --git a/media/libstagefright/wifi-display/VideoFormats.h b/media/libstagefright/wifi-display/VideoFormats.h
deleted file mode 100644
index fd38fd1..0000000
--- a/media/libstagefright/wifi-display/VideoFormats.h
+++ /dev/null
@@ -1,125 +0,0 @@
-/*
- * Copyright 2013, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef VIDEO_FORMATS_H_
-
-#define VIDEO_FORMATS_H_
-
-#include <media/stagefright/foundation/ABase.h>
-
-#include <stdint.h>
-
-namespace android {
-
-struct AString;
-
-// This class encapsulates that video resolution capabilities of a wfd source
-// or sink as outlined in the wfd specs. Currently three sets of resolutions
-// are specified, each of which supports up to 32 resolutions.
-// In addition to its capabilities each sink/source also publishes its
-// "native" resolution, presumably one that is preferred among all others
-// because it wouldn't require any scaling and directly corresponds to the
-// display capabilities/pixels.
-struct VideoFormats {
- VideoFormats();
-
- struct config_t {
- size_t width, height, framesPerSecond;
- bool interlaced;
- unsigned char profile, level;
- };
-
- enum ProfileType {
- PROFILE_CBP = 0,
- PROFILE_CHP,
- kNumProfileTypes,
- };
-
- enum LevelType {
- LEVEL_31 = 0,
- LEVEL_32,
- LEVEL_40,
- LEVEL_41,
- LEVEL_42,
- kNumLevelTypes,
- };
-
- enum ResolutionType {
- RESOLUTION_CEA,
- RESOLUTION_VESA,
- RESOLUTION_HH,
- kNumResolutionTypes,
- };
-
- void setNativeResolution(ResolutionType type, size_t index);
- void getNativeResolution(ResolutionType *type, size_t *index) const;
-
- void disableAll();
- void enableAll();
- void enableResolutionUpto(
- ResolutionType type, size_t index,
- ProfileType profile, LevelType level);
-
- void setResolutionEnabled(
- ResolutionType type, size_t index, bool enabled = true);
-
- bool isResolutionEnabled(ResolutionType type, size_t index) const;
-
- void setProfileLevel(
- ResolutionType type, size_t index,
- ProfileType profile, LevelType level);
-
- void getProfileLevel(
- ResolutionType type, size_t index,
- ProfileType *profile, LevelType *level) const;
-
- static bool GetConfiguration(
- ResolutionType type, size_t index,
- size_t *width, size_t *height, size_t *framesPerSecond,
- bool *interlaced);
-
- static bool GetProfileLevel(
- ProfileType profile, LevelType level,
- unsigned *profileIdc, unsigned *levelIdc,
- unsigned *constraintSet);
-
- bool parseFormatSpec(const char *spec);
- AString getFormatSpec(bool forM4Message = false) const;
-
- static bool PickBestFormat(
- const VideoFormats &sinkSupported,
- const VideoFormats &sourceSupported,
- ResolutionType *chosenType,
- size_t *chosenIndex,
- ProfileType *chosenProfile,
- LevelType *chosenLevel);
-
-private:
- bool parseH264Codec(const char *spec);
- ResolutionType mNativeType;
- size_t mNativeIndex;
-
- uint32_t mResolutionEnabled[kNumResolutionTypes];
- static const config_t mResolutionTable[kNumResolutionTypes][32];
- config_t mConfigs[kNumResolutionTypes][32];
-
- DISALLOW_EVIL_CONSTRUCTORS(VideoFormats);
-};
-
-} // namespace android
-
-#endif // VIDEO_FORMATS_H_
-
diff --git a/media/libstagefright/wifi-display/rtp/RTPBase.h b/media/libstagefright/wifi-display/rtp/RTPBase.h
deleted file mode 100644
index 194f1ee..0000000
--- a/media/libstagefright/wifi-display/rtp/RTPBase.h
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright 2013, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef RTP_BASE_H_
-
-#define RTP_BASE_H_
-
-namespace android {
-
-struct RTPBase {
- enum PacketizationMode {
- PACKETIZATION_TRANSPORT_STREAM,
- PACKETIZATION_H264,
- PACKETIZATION_AAC,
- PACKETIZATION_NONE,
- };
-
- enum TransportMode {
- TRANSPORT_UNDEFINED,
- TRANSPORT_NONE,
- TRANSPORT_UDP,
- TRANSPORT_TCP,
- TRANSPORT_TCP_INTERLEAVED,
- };
-
- // Really UDP _payload_ size
- const unsigned int kMaxUDPPacketSize = 1472; // 1472 good, 1473 bad on Android@Home
-
- static int32_t PickRandomRTPPort();
-};
-
-} // namespace android
-
-#endif // RTP_BASE_H_
-
-
diff --git a/media/libstagefright/wifi-display/rtp/RTPSender.cpp b/media/libstagefright/wifi-display/rtp/RTPSender.cpp
deleted file mode 100644
index ca9fdd2..0000000
--- a/media/libstagefright/wifi-display/rtp/RTPSender.cpp
+++ /dev/null
@@ -1,808 +0,0 @@
-/*
- * Copyright 2013, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-//#define LOG_NDEBUG 0
-#define LOG_TAG "RTPSender"
-#include <utils/Log.h>
-
-#include "RTPSender.h"
-
-#include <media/stagefright/foundation/ABuffer.h>
-#include <media/stagefright/foundation/ADebug.h>
-#include <media/stagefright/foundation/AMessage.h>
-#include <media/stagefright/foundation/ANetworkSession.h>
-#include <media/stagefright/foundation/hexdump.h>
-#include <media/stagefright/MediaErrors.h>
-#include <media/stagefright/Utils.h>
-
-#include "include/avc_utils.h"
-
-namespace android {
-
-RTPSender::RTPSender(
- const sp<ANetworkSession> &netSession,
- const sp<AMessage> ¬ify)
- : mNetSession(netSession),
- mNotify(notify),
- mRTPMode(TRANSPORT_UNDEFINED),
- mRTCPMode(TRANSPORT_UNDEFINED),
- mRTPSessionID(0),
- mRTCPSessionID(0),
- mRTPConnected(false),
- mRTCPConnected(false),
- mLastNTPTime(0),
- mLastRTPTime(0),
- mNumRTPSent(0),
- mNumRTPOctetsSent(0),
- mNumSRsSent(0),
- mRTPSeqNo(0),
- mHistorySize(0) {
-}
-
-RTPSender::~RTPSender() {
- if (mRTCPSessionID != 0) {
- mNetSession->destroySession(mRTCPSessionID);
- mRTCPSessionID = 0;
- }
-
- if (mRTPSessionID != 0) {
- mNetSession->destroySession(mRTPSessionID);
- mRTPSessionID = 0;
- }
-}
-
-// static
-int32_t RTPBase::PickRandomRTPPort() {
- // Pick an even integer in range [1024, 65534)
-
- static const size_t kRange = (65534 - 1024) / 2;
-
- return (int32_t)(((float)(kRange + 1) * rand()) / RAND_MAX) * 2 + 1024;
-}
-
-status_t RTPSender::initAsync(
- const char *remoteHost,
- int32_t remoteRTPPort,
- TransportMode rtpMode,
- int32_t remoteRTCPPort,
- TransportMode rtcpMode,
- int32_t *outLocalRTPPort) {
- if (mRTPMode != TRANSPORT_UNDEFINED
- || rtpMode == TRANSPORT_UNDEFINED
- || rtpMode == TRANSPORT_NONE
- || rtcpMode == TRANSPORT_UNDEFINED) {
- return INVALID_OPERATION;
- }
-
- CHECK_NE(rtpMode, TRANSPORT_TCP_INTERLEAVED);
- CHECK_NE(rtcpMode, TRANSPORT_TCP_INTERLEAVED);
-
- if ((rtcpMode == TRANSPORT_NONE && remoteRTCPPort >= 0)
- || (rtcpMode != TRANSPORT_NONE && remoteRTCPPort < 0)) {
- return INVALID_OPERATION;
- }
-
- sp<AMessage> rtpNotify = new AMessage(kWhatRTPNotify, this);
-
- sp<AMessage> rtcpNotify;
- if (remoteRTCPPort >= 0) {
- rtcpNotify = new AMessage(kWhatRTCPNotify, this);
- }
-
- CHECK_EQ(mRTPSessionID, 0);
- CHECK_EQ(mRTCPSessionID, 0);
-
- int32_t localRTPPort;
-
- for (;;) {
- localRTPPort = PickRandomRTPPort();
-
- status_t err;
- if (rtpMode == TRANSPORT_UDP) {
- err = mNetSession->createUDPSession(
- localRTPPort,
- remoteHost,
- remoteRTPPort,
- rtpNotify,
- &mRTPSessionID);
- } else {
- CHECK_EQ(rtpMode, TRANSPORT_TCP);
- err = mNetSession->createTCPDatagramSession(
- localRTPPort,
- remoteHost,
- remoteRTPPort,
- rtpNotify,
- &mRTPSessionID);
- }
-
- if (err != OK) {
- continue;
- }
-
- if (remoteRTCPPort < 0) {
- break;
- }
-
- if (rtcpMode == TRANSPORT_UDP) {
- err = mNetSession->createUDPSession(
- localRTPPort + 1,
- remoteHost,
- remoteRTCPPort,
- rtcpNotify,
- &mRTCPSessionID);
- } else {
- CHECK_EQ(rtcpMode, TRANSPORT_TCP);
- err = mNetSession->createTCPDatagramSession(
- localRTPPort + 1,
- remoteHost,
- remoteRTCPPort,
- rtcpNotify,
- &mRTCPSessionID);
- }
-
- if (err == OK) {
- break;
- }
-
- mNetSession->destroySession(mRTPSessionID);
- mRTPSessionID = 0;
- }
-
- if (rtpMode == TRANSPORT_UDP) {
- mRTPConnected = true;
- }
-
- if (rtcpMode == TRANSPORT_UDP) {
- mRTCPConnected = true;
- }
-
- mRTPMode = rtpMode;
- mRTCPMode = rtcpMode;
- *outLocalRTPPort = localRTPPort;
-
- if (mRTPMode == TRANSPORT_UDP
- && (mRTCPMode == TRANSPORT_UDP || mRTCPMode == TRANSPORT_NONE)) {
- notifyInitDone(OK);
- }
-
- return OK;
-}
-
-status_t RTPSender::queueBuffer(
- const sp<ABuffer> &buffer, uint8_t packetType, PacketizationMode mode) {
- status_t err;
-
- switch (mode) {
- case PACKETIZATION_NONE:
- err = queueRawPacket(buffer, packetType);
- break;
-
- case PACKETIZATION_TRANSPORT_STREAM:
- err = queueTSPackets(buffer, packetType);
- break;
-
- case PACKETIZATION_H264:
- err = queueAVCBuffer(buffer, packetType);
- break;
-
- default:
- TRESPASS();
- }
-
- return err;
-}
-
-status_t RTPSender::queueRawPacket(
- const sp<ABuffer> &packet, uint8_t packetType) {
- CHECK_LE(packet->size(), kMaxUDPPacketSize - 12);
-
- int64_t timeUs;
- CHECK(packet->meta()->findInt64("timeUs", &timeUs));
-
- sp<ABuffer> udpPacket = new ABuffer(12 + packet->size());
-
- udpPacket->setInt32Data(mRTPSeqNo);
-
- uint8_t *rtp = udpPacket->data();
- rtp[0] = 0x80;
- rtp[1] = packetType;
-
- rtp[2] = (mRTPSeqNo >> 8) & 0xff;
- rtp[3] = mRTPSeqNo & 0xff;
- ++mRTPSeqNo;
-
- uint32_t rtpTime = (timeUs * 9) / 100ll;
-
- rtp[4] = rtpTime >> 24;
- rtp[5] = (rtpTime >> 16) & 0xff;
- rtp[6] = (rtpTime >> 8) & 0xff;
- rtp[7] = rtpTime & 0xff;
-
- rtp[8] = kSourceID >> 24;
- rtp[9] = (kSourceID >> 16) & 0xff;
- rtp[10] = (kSourceID >> 8) & 0xff;
- rtp[11] = kSourceID & 0xff;
-
- memcpy(&rtp[12], packet->data(), packet->size());
-
- return sendRTPPacket(
- udpPacket,
- true /* storeInHistory */,
- true /* timeValid */,
- ALooper::GetNowUs());
-}
-
-status_t RTPSender::queueTSPackets(
- const sp<ABuffer> &tsPackets, uint8_t packetType) {
- CHECK_EQ(0u, tsPackets->size() % 188);
-
- int64_t timeUs;
- CHECK(tsPackets->meta()->findInt64("timeUs", &timeUs));
-
- size_t srcOffset = 0;
- while (srcOffset < tsPackets->size()) {
- sp<ABuffer> udpPacket =
- new ABuffer(12 + kMaxNumTSPacketsPerRTPPacket * 188);
-
- udpPacket->setInt32Data(mRTPSeqNo);
-
- uint8_t *rtp = udpPacket->data();
- rtp[0] = 0x80;
- rtp[1] = packetType;
-
- rtp[2] = (mRTPSeqNo >> 8) & 0xff;
- rtp[3] = mRTPSeqNo & 0xff;
- ++mRTPSeqNo;
-
- int64_t nowUs = ALooper::GetNowUs();
- uint32_t rtpTime = (nowUs * 9) / 100ll;
-
- rtp[4] = rtpTime >> 24;
- rtp[5] = (rtpTime >> 16) & 0xff;
- rtp[6] = (rtpTime >> 8) & 0xff;
- rtp[7] = rtpTime & 0xff;
-
- rtp[8] = kSourceID >> 24;
- rtp[9] = (kSourceID >> 16) & 0xff;
- rtp[10] = (kSourceID >> 8) & 0xff;
- rtp[11] = kSourceID & 0xff;
-
- size_t numTSPackets = (tsPackets->size() - srcOffset) / 188;
- if (numTSPackets > kMaxNumTSPacketsPerRTPPacket) {
- numTSPackets = kMaxNumTSPacketsPerRTPPacket;
- }
-
- memcpy(&rtp[12], tsPackets->data() + srcOffset, numTSPackets * 188);
-
- udpPacket->setRange(0, 12 + numTSPackets * 188);
-
- srcOffset += numTSPackets * 188;
- bool isLastPacket = (srcOffset == tsPackets->size());
-
- status_t err = sendRTPPacket(
- udpPacket,
- true /* storeInHistory */,
- isLastPacket /* timeValid */,
- timeUs);
-
- if (err != OK) {
- return err;
- }
- }
-
- return OK;
-}
-
-status_t RTPSender::queueAVCBuffer(
- const sp<ABuffer> &accessUnit, uint8_t packetType) {
- int64_t timeUs;
- CHECK(accessUnit->meta()->findInt64("timeUs", &timeUs));
-
- uint32_t rtpTime = (timeUs * 9 / 100ll);
-
- List<sp<ABuffer> > packets;
-
- sp<ABuffer> out = new ABuffer(kMaxUDPPacketSize);
- size_t outBytesUsed = 12; // Placeholder for RTP header.
-
- const uint8_t *data = accessUnit->data();
- size_t size = accessUnit->size();
- const uint8_t *nalStart;
- size_t nalSize;
- while (getNextNALUnit(
- &data, &size, &nalStart, &nalSize,
- true /* startCodeFollows */) == OK) {
- size_t bytesNeeded = nalSize + 2;
- if (outBytesUsed == 12) {
- ++bytesNeeded;
- }
-
- if (outBytesUsed + bytesNeeded > out->capacity()) {
- bool emitSingleNALPacket = false;
-
- if (outBytesUsed == 12
- && outBytesUsed + nalSize <= out->capacity()) {
- // We haven't emitted anything into the current packet yet and
- // this NAL unit fits into a single-NAL-unit-packet while
- // it wouldn't have fit as part of a STAP-A packet.
-
- memcpy(out->data() + outBytesUsed, nalStart, nalSize);
- outBytesUsed += nalSize;
-
- emitSingleNALPacket = true;
- }
-
- if (outBytesUsed > 12) {
- out->setRange(0, outBytesUsed);
- packets.push_back(out);
- out = new ABuffer(kMaxUDPPacketSize);
- outBytesUsed = 12; // Placeholder for RTP header
- }
-
- if (emitSingleNALPacket) {
- continue;
- }
- }
-
- if (outBytesUsed + bytesNeeded <= out->capacity()) {
- uint8_t *dst = out->data() + outBytesUsed;
-
- if (outBytesUsed == 12) {
- *dst++ = 24; // STAP-A header
- }
-
- *dst++ = (nalSize >> 8) & 0xff;
- *dst++ = nalSize & 0xff;
- memcpy(dst, nalStart, nalSize);
-
- outBytesUsed += bytesNeeded;
- continue;
- }
-
- // This single NAL unit does not fit into a single RTP packet,
- // we need to emit an FU-A.
-
- CHECK_EQ(outBytesUsed, 12u);
-
- uint8_t nalType = nalStart[0] & 0x1f;
- uint8_t nri = (nalStart[0] >> 5) & 3;
-
- size_t srcOffset = 1;
- while (srcOffset < nalSize) {
- size_t copy = out->capacity() - outBytesUsed - 2;
- if (copy > nalSize - srcOffset) {
- copy = nalSize - srcOffset;
- }
-
- uint8_t *dst = out->data() + outBytesUsed;
- dst[0] = (nri << 5) | 28;
-
- dst[1] = nalType;
-
- if (srcOffset == 1) {
- dst[1] |= 0x80;
- }
-
- if (srcOffset + copy == nalSize) {
- dst[1] |= 0x40;
- }
-
- memcpy(&dst[2], nalStart + srcOffset, copy);
- srcOffset += copy;
-
- out->setRange(0, outBytesUsed + copy + 2);
-
- packets.push_back(out);
- out = new ABuffer(kMaxUDPPacketSize);
- outBytesUsed = 12; // Placeholder for RTP header
- }
- }
-
- if (outBytesUsed > 12) {
- out->setRange(0, outBytesUsed);
- packets.push_back(out);
- }
-
- while (!packets.empty()) {
- sp<ABuffer> out = *packets.begin();
- packets.erase(packets.begin());
-
- out->setInt32Data(mRTPSeqNo);
-
- bool last = packets.empty();
-
- uint8_t *dst = out->data();
-
- dst[0] = 0x80;
-
- dst[1] = packetType;
- if (last) {
- dst[1] |= 1 << 7; // M-bit
- }
-
- dst[2] = (mRTPSeqNo >> 8) & 0xff;
- dst[3] = mRTPSeqNo & 0xff;
- ++mRTPSeqNo;
-
- dst[4] = rtpTime >> 24;
- dst[5] = (rtpTime >> 16) & 0xff;
- dst[6] = (rtpTime >> 8) & 0xff;
- dst[7] = rtpTime & 0xff;
- dst[8] = kSourceID >> 24;
- dst[9] = (kSourceID >> 16) & 0xff;
- dst[10] = (kSourceID >> 8) & 0xff;
- dst[11] = kSourceID & 0xff;
-
- status_t err = sendRTPPacket(out, true /* storeInHistory */);
-
- if (err != OK) {
- return err;
- }
- }
-
- return OK;
-}
-
-status_t RTPSender::sendRTPPacket(
- const sp<ABuffer> &buffer, bool storeInHistory,
- bool timeValid, int64_t timeUs) {
- CHECK(mRTPConnected);
-
- status_t err = mNetSession->sendRequest(
- mRTPSessionID, buffer->data(), buffer->size(),
- timeValid, timeUs);
-
- if (err != OK) {
- return err;
- }
-
- mLastNTPTime = GetNowNTP();
- mLastRTPTime = U32_AT(buffer->data() + 4);
-
- ++mNumRTPSent;
- mNumRTPOctetsSent += buffer->size() - 12;
-
- if (storeInHistory) {
- if (mHistorySize == kMaxHistorySize) {
- mHistory.erase(mHistory.begin());
- } else {
- ++mHistorySize;
- }
- mHistory.push_back(buffer);
- }
-
- return OK;
-}
-
-// static
-uint64_t RTPSender::GetNowNTP() {
- struct timeval tv;
- gettimeofday(&tv, NULL /* timezone */);
-
- uint64_t nowUs = tv.tv_sec * 1000000ll + tv.tv_usec;
-
- nowUs += ((70ll * 365 + 17) * 24) * 60 * 60 * 1000000ll;
-
- uint64_t hi = nowUs / 1000000ll;
- uint64_t lo = ((1ll << 32) * (nowUs % 1000000ll)) / 1000000ll;
-
- return (hi << 32) | lo;
-}
-
-void RTPSender::onMessageReceived(const sp<AMessage> &msg) {
- switch (msg->what()) {
- case kWhatRTPNotify:
- case kWhatRTCPNotify:
- onNetNotify(msg->what() == kWhatRTPNotify, msg);
- break;
-
- default:
- TRESPASS();
- }
-}
-
-void RTPSender::onNetNotify(bool isRTP, const sp<AMessage> &msg) {
- int32_t reason;
- CHECK(msg->findInt32("reason", &reason));
-
- switch (reason) {
- case ANetworkSession::kWhatError:
- {
- int32_t sessionID;
- CHECK(msg->findInt32("sessionID", &sessionID));
-
- int32_t err;
- CHECK(msg->findInt32("err", &err));
-
- int32_t errorOccuredDuringSend;
- CHECK(msg->findInt32("send", &errorOccuredDuringSend));
-
- AString detail;
- CHECK(msg->findString("detail", &detail));
-
- ALOGE("An error occurred during %s in session %d "
- "(%d, '%s' (%s)).",
- errorOccuredDuringSend ? "send" : "receive",
- sessionID,
- err,
- detail.c_str(),
- strerror(-err));
-
- mNetSession->destroySession(sessionID);
-
- if (sessionID == mRTPSessionID) {
- mRTPSessionID = 0;
- } else if (sessionID == mRTCPSessionID) {
- mRTCPSessionID = 0;
- }
-
- if (!mRTPConnected
- || (mRTPMode != TRANSPORT_NONE && !mRTCPConnected)) {
- // We haven't completed initialization, attach the error
- // to the notification instead.
- notifyInitDone(err);
- break;
- }
-
- notifyError(err);
- break;
- }
-
- case ANetworkSession::kWhatDatagram:
- {
- sp<ABuffer> data;
- CHECK(msg->findBuffer("data", &data));
-
- if (isRTP) {
- ALOGW("Huh? Received data on RTP connection...");
- } else {
- onRTCPData(data);
- }
- break;
- }
-
- case ANetworkSession::kWhatConnected:
- {
- int32_t sessionID;
- CHECK(msg->findInt32("sessionID", &sessionID));
-
- if (isRTP) {
- CHECK_EQ(mRTPMode, TRANSPORT_TCP);
- CHECK_EQ(sessionID, mRTPSessionID);
- mRTPConnected = true;
- } else {
- CHECK_EQ(mRTCPMode, TRANSPORT_TCP);
- CHECK_EQ(sessionID, mRTCPSessionID);
- mRTCPConnected = true;
- }
-
- if (mRTPConnected
- && (mRTCPMode == TRANSPORT_NONE || mRTCPConnected)) {
- notifyInitDone(OK);
- }
- break;
- }
-
- case ANetworkSession::kWhatNetworkStall:
- {
- size_t numBytesQueued;
- CHECK(msg->findSize("numBytesQueued", &numBytesQueued));
-
- notifyNetworkStall(numBytesQueued);
- break;
- }
-
- default:
- TRESPASS();
- }
-}
-
-status_t RTPSender::onRTCPData(const sp<ABuffer> &buffer) {
- const uint8_t *data = buffer->data();
- size_t size = buffer->size();
-
- while (size > 0) {
- if (size < 8) {
- // Too short to be a valid RTCP header
- return ERROR_MALFORMED;
- }
-
- if ((data[0] >> 6) != 2) {
- // Unsupported version.
- return ERROR_UNSUPPORTED;
- }
-
- if (data[0] & 0x20) {
- // Padding present.
-
- size_t paddingLength = data[size - 1];
-
- if (paddingLength + 12 > size) {
- // If we removed this much padding we'd end up with something
- // that's too short to be a valid RTP header.
- return ERROR_MALFORMED;
- }
-
- size -= paddingLength;
- }
-
- size_t headerLength = 4 * (data[2] << 8 | data[3]) + 4;
-
- if (size < headerLength) {
- // Only received a partial packet?
- return ERROR_MALFORMED;
- }
-
- switch (data[1]) {
- case 200:
- case 201: // RR
- parseReceiverReport(data, headerLength);
- break;
-
- case 202: // SDES
- case 203:
- break;
-
- case 204: // APP
- parseAPP(data, headerLength);
- break;
-
- case 205: // TSFB (transport layer specific feedback)
- parseTSFB(data, headerLength);
- break;
-
- case 206: // PSFB (payload specific feedback)
- // hexdump(data, headerLength);
- break;
-
- default:
- {
- ALOGW("Unknown RTCP packet type %u of size %zu",
- (unsigned)data[1], headerLength);
- break;
- }
- }
-
- data += headerLength;
- size -= headerLength;
- }
-
- return OK;
-}
-
-status_t RTPSender::parseReceiverReport(
- const uint8_t *data, size_t /* size */) {
- float fractionLost = data[12] / 256.0f;
-
- ALOGI("lost %.2f %% of packets during report interval.",
- 100.0f * fractionLost);
-
- return OK;
-}
-
-status_t RTPSender::parseTSFB(const uint8_t *data, size_t size) {
- if ((data[0] & 0x1f) != 1) {
- return ERROR_UNSUPPORTED; // We only support NACK for now.
- }
-
- uint32_t srcId = U32_AT(&data[8]);
- if (srcId != kSourceID) {
- return ERROR_MALFORMED;
- }
-
- for (size_t i = 12; i < size; i += 4) {
- uint16_t seqNo = U16_AT(&data[i]);
- uint16_t blp = U16_AT(&data[i + 2]);
-
- List<sp<ABuffer> >::iterator it = mHistory.begin();
- bool foundSeqNo = false;
- while (it != mHistory.end()) {
- const sp<ABuffer> &buffer = *it;
-
- uint16_t bufferSeqNo = buffer->int32Data() & 0xffff;
-
- bool retransmit = false;
- if (bufferSeqNo == seqNo) {
- retransmit = true;
- } else if (blp != 0) {
- for (size_t i = 0; i < 16; ++i) {
- if ((blp & (1 << i))
- && (bufferSeqNo == ((seqNo + i + 1) & 0xffff))) {
- blp &= ~(1 << i);
- retransmit = true;
- }
- }
- }
-
- if (retransmit) {
- ALOGV("retransmitting seqNo %d", bufferSeqNo);
-
- CHECK_EQ((status_t)OK,
- sendRTPPacket(buffer, false /* storeInHistory */));
-
- if (bufferSeqNo == seqNo) {
- foundSeqNo = true;
- }
-
- if (foundSeqNo && blp == 0) {
- break;
- }
- }
-
- ++it;
- }
-
- if (!foundSeqNo || blp != 0) {
- ALOGI("Some sequence numbers were no longer available for "
- "retransmission (seqNo = %d, foundSeqNo = %d, blp = 0x%04x)",
- seqNo, foundSeqNo, blp);
-
- if (!mHistory.empty()) {
- int32_t earliest = (*mHistory.begin())->int32Data() & 0xffff;
- int32_t latest = (*--mHistory.end())->int32Data() & 0xffff;
-
- ALOGI("have seq numbers from %d - %d", earliest, latest);
- }
- }
- }
-
- return OK;
-}
-
-status_t RTPSender::parseAPP(const uint8_t *data, size_t size) {
- static const size_t late_offset = 8;
- static const char late_string[] = "late";
- static const size_t avgLatencyUs_offset = late_offset + sizeof(late_string) - 1;
- static const size_t maxLatencyUs_offset = avgLatencyUs_offset + sizeof(int64_t);
-
- if ((size >= (maxLatencyUs_offset + sizeof(int64_t)))
- && !memcmp(late_string, &data[late_offset], sizeof(late_string) - 1)) {
- int64_t avgLatencyUs = (int64_t)U64_AT(&data[avgLatencyUs_offset]);
- int64_t maxLatencyUs = (int64_t)U64_AT(&data[maxLatencyUs_offset]);
-
- sp<AMessage> notify = mNotify->dup();
- notify->setInt32("what", kWhatInformSender);
- notify->setInt64("avgLatencyUs", avgLatencyUs);
- notify->setInt64("maxLatencyUs", maxLatencyUs);
- notify->post();
- }
-
- return OK;
-}
-
-void RTPSender::notifyInitDone(status_t err) {
- sp<AMessage> notify = mNotify->dup();
- notify->setInt32("what", kWhatInitDone);
- notify->setInt32("err", err);
- notify->post();
-}
-
-void RTPSender::notifyError(status_t err) {
- sp<AMessage> notify = mNotify->dup();
- notify->setInt32("what", kWhatError);
- notify->setInt32("err", err);
- notify->post();
-}
-
-void RTPSender::notifyNetworkStall(size_t numBytesQueued) {
- sp<AMessage> notify = mNotify->dup();
- notify->setInt32("what", kWhatNetworkStall);
- notify->setSize("numBytesQueued", numBytesQueued);
- notify->post();
-}
-
-} // namespace android
-
diff --git a/media/libstagefright/wifi-display/rtp/RTPSender.h b/media/libstagefright/wifi-display/rtp/RTPSender.h
deleted file mode 100644
index bedfd01..0000000
--- a/media/libstagefright/wifi-display/rtp/RTPSender.h
+++ /dev/null
@@ -1,119 +0,0 @@
-/*
- * Copyright 2013, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef RTP_SENDER_H_
-
-#define RTP_SENDER_H_
-
-#include "RTPBase.h"
-
-#include <media/stagefright/foundation/AHandler.h>
-
-namespace android {
-
-struct ABuffer;
-struct ANetworkSession;
-
-// An object of this class facilitates sending of media data over an RTP
-// channel. The channel is established over a UDP or TCP connection depending
-// on which "TransportMode" was chosen. In addition different RTP packetization
-// schemes are supported such as "Transport Stream Packets over RTP",
-// or "AVC/H.264 encapsulation as specified in RFC 3984 (non-interleaved mode)"
-struct RTPSender : public RTPBase, public AHandler {
- enum {
- kWhatInitDone,
- kWhatError,
- kWhatNetworkStall,
- kWhatInformSender,
- };
- RTPSender(
- const sp<ANetworkSession> &netSession,
- const sp<AMessage> ¬ify);
-
- status_t initAsync(
- const char *remoteHost,
- int32_t remoteRTPPort,
- TransportMode rtpMode,
- int32_t remoteRTCPPort,
- TransportMode rtcpMode,
- int32_t *outLocalRTPPort);
-
- status_t queueBuffer(
- const sp<ABuffer> &buffer,
- uint8_t packetType,
- PacketizationMode mode);
-
-protected:
- virtual ~RTPSender();
- virtual void onMessageReceived(const sp<AMessage> &msg);
-
-private:
- enum {
- kWhatRTPNotify,
- kWhatRTCPNotify,
- };
-
- const unsigned int kMaxNumTSPacketsPerRTPPacket = (kMaxUDPPacketSize - 12) / 188;
- const unsigned int kMaxHistorySize = 1024;
- const unsigned int kSourceID = 0xdeadbeef;
-
- sp<ANetworkSession> mNetSession;
- sp<AMessage> mNotify;
- TransportMode mRTPMode;
- TransportMode mRTCPMode;
- int32_t mRTPSessionID;
- int32_t mRTCPSessionID;
- bool mRTPConnected;
- bool mRTCPConnected;
-
- uint64_t mLastNTPTime;
- uint32_t mLastRTPTime;
- uint32_t mNumRTPSent;
- uint32_t mNumRTPOctetsSent;
- uint32_t mNumSRsSent;
-
- uint32_t mRTPSeqNo;
-
- List<sp<ABuffer> > mHistory;
- size_t mHistorySize;
-
- static uint64_t GetNowNTP();
-
- status_t queueRawPacket(const sp<ABuffer> &tsPackets, uint8_t packetType);
- status_t queueTSPackets(const sp<ABuffer> &tsPackets, uint8_t packetType);
- status_t queueAVCBuffer(const sp<ABuffer> &accessUnit, uint8_t packetType);
-
- status_t sendRTPPacket(
- const sp<ABuffer> &packet, bool storeInHistory,
- bool timeValid = false, int64_t timeUs = -1ll);
-
- void onNetNotify(bool isRTP, const sp<AMessage> &msg);
-
- status_t onRTCPData(const sp<ABuffer> &data);
- status_t parseReceiverReport(const uint8_t *data, size_t size);
- status_t parseTSFB(const uint8_t *data, size_t size);
- status_t parseAPP(const uint8_t *data, size_t size);
-
- void notifyInitDone(status_t err);
- void notifyError(status_t err);
- void notifyNetworkStall(size_t numBytesQueued);
-
- DISALLOW_EVIL_CONSTRUCTORS(RTPSender);
-};
-
-} // namespace android
-
-#endif // RTP_SENDER_H_
diff --git a/media/libstagefright/wifi-display/source/Converter.cpp b/media/libstagefright/wifi-display/source/Converter.cpp
deleted file mode 100644
index 273af18..0000000
--- a/media/libstagefright/wifi-display/source/Converter.cpp
+++ /dev/null
@@ -1,821 +0,0 @@
-/*
- * Copyright 2012, 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 "Converter"
-#include <utils/Log.h>
-
-#include "Converter.h"
-
-#include "MediaPuller.h"
-#include "include/avc_utils.h"
-
-#include <cutils/properties.h>
-#include <gui/Surface.h>
-#include <media/ICrypto.h>
-#include <media/MediaCodecBuffer.h>
-#include <media/stagefright/foundation/ABuffer.h>
-#include <media/stagefright/foundation/ADebug.h>
-#include <media/stagefright/foundation/AMessage.h>
-#include <media/stagefright/MediaBuffer.h>
-#include <media/stagefright/MediaCodec.h>
-#include <media/stagefright/MediaDefs.h>
-#include <media/stagefright/MediaErrors.h>
-
-#include <arpa/inet.h>
-
-#include <OMX_Video.h>
-
-namespace android {
-
-Converter::Converter(
- const sp<AMessage> ¬ify,
- const sp<ALooper> &codecLooper,
- const sp<AMessage> &outputFormat,
- uint32_t flags)
- : mNotify(notify),
- mCodecLooper(codecLooper),
- mOutputFormat(outputFormat),
- mFlags(flags),
- mIsVideo(false),
- mIsH264(false),
- mIsPCMAudio(false),
- mNeedToManuallyPrependSPSPPS(false),
- mDoMoreWorkPending(false)
-#if ENABLE_SILENCE_DETECTION
- ,mFirstSilentFrameUs(-1ll)
- ,mInSilentMode(false)
-#endif
- ,mPrevVideoBitrate(-1)
- ,mNumFramesToDrop(0)
- ,mEncodingSuspended(false)
- {
- AString mime;
- CHECK(mOutputFormat->findString("mime", &mime));
-
- if (!strncasecmp("video/", mime.c_str(), 6)) {
- mIsVideo = true;
-
- mIsH264 = !strcasecmp(mime.c_str(), MEDIA_MIMETYPE_VIDEO_AVC);
- } else if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_RAW, mime.c_str())) {
- mIsPCMAudio = true;
- }
-}
-
-void Converter::releaseEncoder() {
- if (mEncoder == NULL) {
- return;
- }
-
- mEncoder->release();
- mEncoder.clear();
-
- mInputBufferQueue.clear();
- mEncoderInputBuffers.clear();
- mEncoderOutputBuffers.clear();
-}
-
-Converter::~Converter() {
- CHECK(mEncoder == NULL);
-}
-
-void Converter::shutdownAsync() {
- ALOGV("shutdown");
- (new AMessage(kWhatShutdown, this))->post();
-}
-
-status_t Converter::init() {
- status_t err = initEncoder();
-
- if (err != OK) {
- releaseEncoder();
- }
-
- return err;
-}
-
-sp<IGraphicBufferProducer> Converter::getGraphicBufferProducer() {
- CHECK(mFlags & FLAG_USE_SURFACE_INPUT);
- return mGraphicBufferProducer;
-}
-
-size_t Converter::getInputBufferCount() const {
- return mEncoderInputBuffers.size();
-}
-
-sp<AMessage> Converter::getOutputFormat() const {
- return mOutputFormat;
-}
-
-bool Converter::needToManuallyPrependSPSPPS() const {
- return mNeedToManuallyPrependSPSPPS;
-}
-
-// static
-int32_t Converter::GetInt32Property(
- const char *propName, int32_t defaultValue) {
- char val[PROPERTY_VALUE_MAX];
- if (property_get(propName, val, NULL)) {
- char *end;
- unsigned long x = strtoul(val, &end, 10);
-
- if (*end == '\0' && end > val && x > 0) {
- return x;
- }
- }
-
- return defaultValue;
-}
-
-status_t Converter::initEncoder() {
- AString outputMIME;
- CHECK(mOutputFormat->findString("mime", &outputMIME));
-
- bool isAudio = !strncasecmp(outputMIME.c_str(), "audio/", 6);
-
- if (!mIsPCMAudio) {
- mEncoder = MediaCodec::CreateByType(
- mCodecLooper, outputMIME.c_str(), true /* encoder */);
-
- if (mEncoder == NULL) {
- return ERROR_UNSUPPORTED;
- }
- }
-
- if (mIsPCMAudio) {
- return OK;
- }
-
- int32_t audioBitrate = GetInt32Property("media.wfd.audio-bitrate", 128000);
- int32_t videoBitrate = GetInt32Property("media.wfd.video-bitrate", 5000000);
- mPrevVideoBitrate = videoBitrate;
-
- ALOGI("using audio bitrate of %d bps, video bitrate of %d bps",
- audioBitrate, videoBitrate);
-
- if (isAudio) {
- mOutputFormat->setInt32("bitrate", audioBitrate);
- } else {
- mOutputFormat->setInt32("bitrate", videoBitrate);
- mOutputFormat->setInt32("bitrate-mode", OMX_Video_ControlRateConstant);
- mOutputFormat->setInt32("frame-rate", 30);
- mOutputFormat->setInt32("i-frame-interval", 15); // Iframes every 15 secs
-
- // Configure encoder to use intra macroblock refresh mode
- mOutputFormat->setInt32("intra-refresh-mode", OMX_VIDEO_IntraRefreshCyclic);
-
- int width, height, mbs;
- if (!mOutputFormat->findInt32("width", &width)
- || !mOutputFormat->findInt32("height", &height)) {
- return ERROR_UNSUPPORTED;
- }
-
- // Update macroblocks in a cyclic fashion with 10% of all MBs within
- // frame gets updated at one time. It takes about 10 frames to
- // completely update a whole video frame. If the frame rate is 30,
- // it takes about 333 ms in the best case (if next frame is not an IDR)
- // to recover from a lost/corrupted packet.
- mbs = (((width + 15) / 16) * ((height + 15) / 16) * 10) / 100;
- mOutputFormat->setInt32("intra-refresh-CIR-mbs", mbs);
- }
-
- ALOGV("output format is '%s'", mOutputFormat->debugString(0).c_str());
-
- mNeedToManuallyPrependSPSPPS = false;
-
- status_t err = NO_INIT;
-
- if (!isAudio) {
- sp<AMessage> tmp = mOutputFormat->dup();
- tmp->setInt32("prepend-sps-pps-to-idr-frames", 1);
-
- err = mEncoder->configure(
- tmp,
- NULL /* nativeWindow */,
- NULL /* crypto */,
- MediaCodec::CONFIGURE_FLAG_ENCODE);
-
- if (err == OK) {
- // Encoder supported prepending SPS/PPS, we don't need to emulate
- // it.
- mOutputFormat = tmp;
- } else {
- mNeedToManuallyPrependSPSPPS = true;
-
- ALOGI("We going to manually prepend SPS and PPS to IDR frames.");
- }
- }
-
- if (err != OK) {
- // We'll get here for audio or if we failed to configure the encoder
- // to automatically prepend SPS/PPS in the case of video.
-
- err = mEncoder->configure(
- mOutputFormat,
- NULL /* nativeWindow */,
- NULL /* crypto */,
- MediaCodec::CONFIGURE_FLAG_ENCODE);
- }
-
- if (err != OK) {
- return err;
- }
-
- if (mFlags & FLAG_USE_SURFACE_INPUT) {
- CHECK(mIsVideo);
-
- err = mEncoder->createInputSurface(&mGraphicBufferProducer);
-
- if (err != OK) {
- return err;
- }
- }
-
- err = mEncoder->start();
-
- if (err != OK) {
- return err;
- }
-
- err = mEncoder->getInputBuffers(&mEncoderInputBuffers);
-
- if (err != OK) {
- return err;
- }
-
- err = mEncoder->getOutputBuffers(&mEncoderOutputBuffers);
-
- if (err != OK) {
- return err;
- }
-
- if (mFlags & FLAG_USE_SURFACE_INPUT) {
- scheduleDoMoreWork();
- }
-
- return OK;
-}
-
-void Converter::notifyError(status_t err) {
- sp<AMessage> notify = mNotify->dup();
- notify->setInt32("what", kWhatError);
- notify->setInt32("err", err);
- notify->post();
-}
-
-// static
-bool Converter::IsSilence(const sp<ABuffer> &accessUnit) {
- const uint8_t *ptr = accessUnit->data();
- const uint8_t *end = ptr + accessUnit->size();
- while (ptr < end) {
- if (*ptr != 0) {
- return false;
- }
- ++ptr;
- }
-
- return true;
-}
-
-void Converter::onMessageReceived(const sp<AMessage> &msg) {
- switch (msg->what()) {
- case kWhatMediaPullerNotify:
- {
- int32_t what;
- CHECK(msg->findInt32("what", &what));
-
- if (!mIsPCMAudio && mEncoder == NULL) {
- ALOGV("got msg '%s' after encoder shutdown.",
- msg->debugString().c_str());
-
- if (what == MediaPuller::kWhatAccessUnit) {
- sp<ABuffer> accessUnit;
- CHECK(msg->findBuffer("accessUnit", &accessUnit));
-
- accessUnit->setMediaBufferBase(NULL);
- }
- break;
- }
-
- if (what == MediaPuller::kWhatEOS) {
- mInputBufferQueue.push_back(NULL);
-
- feedEncoderInputBuffers();
-
- scheduleDoMoreWork();
- } else {
- CHECK_EQ(what, MediaPuller::kWhatAccessUnit);
-
- sp<ABuffer> accessUnit;
- CHECK(msg->findBuffer("accessUnit", &accessUnit));
-
- if (mNumFramesToDrop > 0 || mEncodingSuspended) {
- if (mNumFramesToDrop > 0) {
- --mNumFramesToDrop;
- ALOGI("dropping frame.");
- }
-
- accessUnit->setMediaBufferBase(NULL);
- break;
- }
-
-#if 0
- MediaBuffer *mbuf =
- (MediaBuffer *)(accessUnit->getMediaBufferBase());
- if (mbuf != NULL) {
- ALOGI("queueing mbuf %p", mbuf);
- mbuf->release();
- }
-#endif
-
-#if ENABLE_SILENCE_DETECTION
- if (!mIsVideo) {
- if (IsSilence(accessUnit)) {
- if (mInSilentMode) {
- break;
- }
-
- int64_t nowUs = ALooper::GetNowUs();
-
- if (mFirstSilentFrameUs < 0ll) {
- mFirstSilentFrameUs = nowUs;
- } else if (nowUs >= mFirstSilentFrameUs + 10000000ll) {
- mInSilentMode = true;
- ALOGI("audio in silent mode now.");
- break;
- }
- } else {
- if (mInSilentMode) {
- ALOGI("audio no longer in silent mode.");
- }
- mInSilentMode = false;
- mFirstSilentFrameUs = -1ll;
- }
- }
-#endif
-
- mInputBufferQueue.push_back(accessUnit);
-
- feedEncoderInputBuffers();
-
- scheduleDoMoreWork();
- }
- break;
- }
-
- case kWhatEncoderActivity:
- {
-#if 0
- int64_t whenUs;
- if (msg->findInt64("whenUs", &whenUs)) {
- int64_t nowUs = ALooper::GetNowUs();
- ALOGI("[%s] kWhatEncoderActivity after %lld us",
- mIsVideo ? "video" : "audio", nowUs - whenUs);
- }
-#endif
-
- mDoMoreWorkPending = false;
-
- if (mEncoder == NULL) {
- break;
- }
-
- status_t err = doMoreWork();
-
- if (err != OK) {
- notifyError(err);
- } else {
- scheduleDoMoreWork();
- }
- break;
- }
-
- case kWhatRequestIDRFrame:
- {
- if (mEncoder == NULL) {
- break;
- }
-
- if (mIsVideo) {
- ALOGV("requesting IDR frame");
- mEncoder->requestIDRFrame();
- }
- break;
- }
-
- case kWhatShutdown:
- {
- ALOGI("shutting down %s encoder", mIsVideo ? "video" : "audio");
-
- releaseEncoder();
-
- AString mime;
- CHECK(mOutputFormat->findString("mime", &mime));
- ALOGI("encoder (%s) shut down.", mime.c_str());
-
- sp<AMessage> notify = mNotify->dup();
- notify->setInt32("what", kWhatShutdownCompleted);
- notify->post();
- break;
- }
-
- case kWhatDropAFrame:
- {
- ++mNumFramesToDrop;
- break;
- }
-
- case kWhatReleaseOutputBuffer:
- {
- if (mEncoder != NULL) {
- size_t bufferIndex;
- CHECK(msg->findInt32("bufferIndex", (int32_t*)&bufferIndex));
- CHECK(bufferIndex < mEncoderOutputBuffers.size());
- mEncoder->releaseOutputBuffer(bufferIndex);
- }
- break;
- }
-
- case kWhatSuspendEncoding:
- {
- int32_t suspend;
- CHECK(msg->findInt32("suspend", &suspend));
-
- mEncodingSuspended = suspend;
-
- if (mFlags & FLAG_USE_SURFACE_INPUT) {
- sp<AMessage> params = new AMessage;
- params->setInt32("drop-input-frames",suspend);
- mEncoder->setParameters(params);
- }
- break;
- }
-
- default:
- TRESPASS();
- }
-}
-
-void Converter::scheduleDoMoreWork() {
- if (mIsPCMAudio) {
- // There's no encoder involved in this case.
- return;
- }
-
- if (mDoMoreWorkPending) {
- return;
- }
-
- mDoMoreWorkPending = true;
-
-#if 1
- if (mEncoderActivityNotify == NULL) {
- mEncoderActivityNotify = new AMessage(kWhatEncoderActivity, this);
- }
- mEncoder->requestActivityNotification(mEncoderActivityNotify->dup());
-#else
- sp<AMessage> notify = new AMessage(kWhatEncoderActivity, this);
- notify->setInt64("whenUs", ALooper::GetNowUs());
- mEncoder->requestActivityNotification(notify);
-#endif
-}
-
-status_t Converter::feedRawAudioInputBuffers() {
- // Split incoming PCM audio into buffers of 6 AUs of 80 audio frames each
- // and add a 4 byte header according to the wifi display specs.
-
- while (!mInputBufferQueue.empty()) {
- sp<ABuffer> buffer = *mInputBufferQueue.begin();
- mInputBufferQueue.erase(mInputBufferQueue.begin());
-
- int16_t *ptr = (int16_t *)buffer->data();
- int16_t *stop = (int16_t *)(buffer->data() + buffer->size());
- while (ptr < stop) {
- *ptr = htons(*ptr);
- ++ptr;
- }
-
- static const size_t kFrameSize = 2 * sizeof(int16_t); // stereo
- static const size_t kFramesPerAU = 80;
- static const size_t kNumAUsPerPESPacket = 6;
-
- if (mPartialAudioAU != NULL) {
- size_t bytesMissingForFullAU =
- kNumAUsPerPESPacket * kFramesPerAU * kFrameSize
- - mPartialAudioAU->size() + 4;
-
- size_t copy = buffer->size();
- if(copy > bytesMissingForFullAU) {
- copy = bytesMissingForFullAU;
- }
-
- memcpy(mPartialAudioAU->data() + mPartialAudioAU->size(),
- buffer->data(),
- copy);
-
- mPartialAudioAU->setRange(0, mPartialAudioAU->size() + copy);
-
- buffer->setRange(buffer->offset() + copy, buffer->size() - copy);
-
- int64_t timeUs;
- CHECK(buffer->meta()->findInt64("timeUs", &timeUs));
-
- int64_t copyUs = (int64_t)((copy / kFrameSize) * 1E6 / 48000.0);
- timeUs += copyUs;
- buffer->meta()->setInt64("timeUs", timeUs);
-
- if (bytesMissingForFullAU == copy) {
- sp<AMessage> notify = mNotify->dup();
- notify->setInt32("what", kWhatAccessUnit);
- notify->setBuffer("accessUnit", mPartialAudioAU);
- notify->post();
-
- mPartialAudioAU.clear();
- }
- }
-
- while (buffer->size() > 0) {
- sp<ABuffer> partialAudioAU =
- new ABuffer(
- 4
- + kNumAUsPerPESPacket * kFrameSize * kFramesPerAU);
-
- uint8_t *ptr = partialAudioAU->data();
- ptr[0] = 0xa0; // 10100000b
- ptr[1] = kNumAUsPerPESPacket;
- ptr[2] = 0; // reserved, audio _emphasis_flag = 0
-
- static const unsigned kQuantizationWordLength = 0; // 16-bit
- static const unsigned kAudioSamplingFrequency = 2; // 48Khz
- static const unsigned kNumberOfAudioChannels = 1; // stereo
-
- ptr[3] = (kQuantizationWordLength << 6)
- | (kAudioSamplingFrequency << 3)
- | kNumberOfAudioChannels;
-
- size_t copy = buffer->size();
- if (copy > partialAudioAU->size() - 4) {
- copy = partialAudioAU->size() - 4;
- }
-
- memcpy(&ptr[4], buffer->data(), copy);
-
- partialAudioAU->setRange(0, 4 + copy);
- buffer->setRange(buffer->offset() + copy, buffer->size() - copy);
-
- int64_t timeUs;
- CHECK(buffer->meta()->findInt64("timeUs", &timeUs));
-
- partialAudioAU->meta()->setInt64("timeUs", timeUs);
-
- int64_t copyUs = (int64_t)((copy / kFrameSize) * 1E6 / 48000.0);
- timeUs += copyUs;
- buffer->meta()->setInt64("timeUs", timeUs);
-
- if (copy == partialAudioAU->capacity() - 4) {
- sp<AMessage> notify = mNotify->dup();
- notify->setInt32("what", kWhatAccessUnit);
- notify->setBuffer("accessUnit", partialAudioAU);
- notify->post();
-
- partialAudioAU.clear();
- continue;
- }
-
- mPartialAudioAU = partialAudioAU;
- }
- }
-
- return OK;
-}
-
-status_t Converter::feedEncoderInputBuffers() {
- if (mIsPCMAudio) {
- return feedRawAudioInputBuffers();
- }
-
- while (!mInputBufferQueue.empty()
- && !mAvailEncoderInputIndices.empty()) {
- sp<ABuffer> buffer = *mInputBufferQueue.begin();
- mInputBufferQueue.erase(mInputBufferQueue.begin());
-
- size_t bufferIndex = *mAvailEncoderInputIndices.begin();
- mAvailEncoderInputIndices.erase(mAvailEncoderInputIndices.begin());
-
- int64_t timeUs = 0ll;
- uint32_t flags = 0;
-
- if (buffer != NULL) {
- CHECK(buffer->meta()->findInt64("timeUs", &timeUs));
-
- memcpy(mEncoderInputBuffers.itemAt(bufferIndex)->data(),
- buffer->data(),
- buffer->size());
-
- MediaBuffer *mediaBuffer =
- (MediaBuffer *)(buffer->getMediaBufferBase());
- if (mediaBuffer != NULL) {
- mEncoderInputBuffers.itemAt(bufferIndex)->setMediaBufferBase(
- mediaBuffer);
-
- buffer->setMediaBufferBase(NULL);
- }
- } else {
- flags = MediaCodec::BUFFER_FLAG_EOS;
- }
-
- status_t err = mEncoder->queueInputBuffer(
- bufferIndex, 0, (buffer == NULL) ? 0 : buffer->size(),
- timeUs, flags);
-
- if (err != OK) {
- return err;
- }
- }
-
- return OK;
-}
-
-sp<ABuffer> Converter::prependCSD(const sp<ABuffer> &accessUnit) const {
- CHECK(mCSD0 != NULL);
-
- sp<ABuffer> dup = new ABuffer(accessUnit->size() + mCSD0->size());
- memcpy(dup->data(), mCSD0->data(), mCSD0->size());
- memcpy(dup->data() + mCSD0->size(), accessUnit->data(), accessUnit->size());
-
- int64_t timeUs;
- CHECK(accessUnit->meta()->findInt64("timeUs", &timeUs));
-
- dup->meta()->setInt64("timeUs", timeUs);
-
- return dup;
-}
-
-status_t Converter::doMoreWork() {
- status_t err;
-
- if (!(mFlags & FLAG_USE_SURFACE_INPUT)) {
- for (;;) {
- size_t bufferIndex;
- err = mEncoder->dequeueInputBuffer(&bufferIndex);
-
- if (err != OK) {
- break;
- }
-
- mAvailEncoderInputIndices.push_back(bufferIndex);
- }
-
- feedEncoderInputBuffers();
- }
-
- for (;;) {
- size_t bufferIndex;
- size_t offset;
- size_t size;
- int64_t timeUs;
- uint32_t flags;
- native_handle_t* handle = NULL;
- err = mEncoder->dequeueOutputBuffer(
- &bufferIndex, &offset, &size, &timeUs, &flags);
-
- if (err != OK) {
- if (err == INFO_FORMAT_CHANGED) {
- continue;
- } else if (err == INFO_OUTPUT_BUFFERS_CHANGED) {
- mEncoder->getOutputBuffers(&mEncoderOutputBuffers);
- continue;
- }
-
- if (err == -EAGAIN) {
- err = OK;
- }
- break;
- }
-
- if (flags & MediaCodec::BUFFER_FLAG_EOS) {
- sp<AMessage> notify = mNotify->dup();
- notify->setInt32("what", kWhatEOS);
- notify->post();
- } else {
-#if 0
- if (mIsVideo) {
- int32_t videoBitrate = GetInt32Property(
- "media.wfd.video-bitrate", 5000000);
-
- setVideoBitrate(videoBitrate);
- }
-#endif
-
- sp<ABuffer> buffer;
- sp<MediaCodecBuffer> outbuf = mEncoderOutputBuffers.itemAt(bufferIndex);
-
- if (outbuf->meta()->findPointer("handle", (void**)&handle) &&
- handle != NULL) {
- int32_t rangeLength, rangeOffset;
- CHECK(outbuf->meta()->findInt32("rangeOffset", &rangeOffset));
- CHECK(outbuf->meta()->findInt32("rangeLength", &rangeLength));
- outbuf->meta()->setPointer("handle", NULL);
-
- // MediaSender will post the following message when HDCP
- // is done, to release the output buffer back to encoder.
- sp<AMessage> notify(new AMessage(kWhatReleaseOutputBuffer, this));
- notify->setInt32("bufferIndex", bufferIndex);
-
- buffer = new ABuffer(
- rangeLength > (int32_t)size ? rangeLength : size);
- buffer->meta()->setPointer("handle", handle);
- buffer->meta()->setInt32("rangeOffset", rangeOffset);
- buffer->meta()->setInt32("rangeLength", rangeLength);
- buffer->meta()->setMessage("notify", notify);
- } else {
- buffer = new ABuffer(size);
- }
-
- buffer->meta()->setInt64("timeUs", timeUs);
-
- ALOGV("[%s] time %lld us (%.2f secs)",
- mIsVideo ? "video" : "audio", (long long)timeUs, timeUs / 1E6);
-
- memcpy(buffer->data(), outbuf->base() + offset, size);
-
- if (flags & MediaCodec::BUFFER_FLAG_CODECCONFIG) {
- if (!handle) {
- if (mIsH264) {
- mCSD0 = buffer;
- }
- mOutputFormat->setBuffer("csd-0", buffer);
- }
- } else {
- if (mNeedToManuallyPrependSPSPPS
- && mIsH264
- && (mFlags & FLAG_PREPEND_CSD_IF_NECESSARY)
- && IsIDR(buffer)) {
- buffer = prependCSD(buffer);
- }
-
- sp<AMessage> notify = mNotify->dup();
- notify->setInt32("what", kWhatAccessUnit);
- notify->setBuffer("accessUnit", buffer);
- notify->post();
- }
- }
-
- if (!handle) {
- mEncoder->releaseOutputBuffer(bufferIndex);
- }
-
- if (flags & MediaCodec::BUFFER_FLAG_EOS) {
- break;
- }
- }
-
- return err;
-}
-
-void Converter::requestIDRFrame() {
- (new AMessage(kWhatRequestIDRFrame, this))->post();
-}
-
-void Converter::dropAFrame() {
- // Unsupported in surface input mode.
- CHECK(!(mFlags & FLAG_USE_SURFACE_INPUT));
-
- (new AMessage(kWhatDropAFrame, this))->post();
-}
-
-void Converter::suspendEncoding(bool suspend) {
- sp<AMessage> msg = new AMessage(kWhatSuspendEncoding, this);
- msg->setInt32("suspend", suspend);
- msg->post();
-}
-
-int32_t Converter::getVideoBitrate() const {
- return mPrevVideoBitrate;
-}
-
-void Converter::setVideoBitrate(int32_t bitRate) {
- if (mIsVideo && mEncoder != NULL && bitRate != mPrevVideoBitrate) {
- sp<AMessage> params = new AMessage;
- params->setInt32("video-bitrate", bitRate);
-
- mEncoder->setParameters(params);
-
- mPrevVideoBitrate = bitRate;
- }
-}
-
-} // namespace android
diff --git a/media/libstagefright/wifi-display/source/Converter.h b/media/libstagefright/wifi-display/source/Converter.h
deleted file mode 100644
index ad95ab5..0000000
--- a/media/libstagefright/wifi-display/source/Converter.h
+++ /dev/null
@@ -1,157 +0,0 @@
-/*
- * Copyright 2012, 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 CONVERTER_H_
-
-#define CONVERTER_H_
-
-#include <media/stagefright/foundation/AHandler.h>
-
-namespace android {
-
-struct ABuffer;
-class IGraphicBufferProducer;
-struct MediaCodec;
-class MediaCodecBuffer;
-
-#define ENABLE_SILENCE_DETECTION 0
-
-// Utility class that receives media access units and converts them into
-// media access unit of a different format.
-// Right now this'll convert raw video into H.264 and raw audio into AAC.
-struct Converter : public AHandler {
- enum {
- kWhatAccessUnit,
- kWhatEOS,
- kWhatError,
- kWhatShutdownCompleted,
- };
-
- enum FlagBits {
- FLAG_USE_SURFACE_INPUT = 1,
- FLAG_PREPEND_CSD_IF_NECESSARY = 2,
- };
- Converter(const sp<AMessage> ¬ify,
- const sp<ALooper> &codecLooper,
- const sp<AMessage> &outputFormat,
- uint32_t flags = 0);
-
- status_t init();
-
- sp<IGraphicBufferProducer> getGraphicBufferProducer();
-
- size_t getInputBufferCount() const;
-
- sp<AMessage> getOutputFormat() const;
- bool needToManuallyPrependSPSPPS() const;
-
- void feedAccessUnit(const sp<ABuffer> &accessUnit);
- void signalEOS();
-
- void requestIDRFrame();
-
- void dropAFrame();
- void suspendEncoding(bool suspend);
-
- void shutdownAsync();
-
- int32_t getVideoBitrate() const;
- void setVideoBitrate(int32_t bitrate);
-
- static int32_t GetInt32Property(const char *propName, int32_t defaultValue);
-
- enum {
- // MUST not conflict with private enums below.
- kWhatMediaPullerNotify = 'pulN',
- };
-
-protected:
- virtual ~Converter();
- virtual void onMessageReceived(const sp<AMessage> &msg);
-
-private:
- enum {
- kWhatDoMoreWork,
- kWhatRequestIDRFrame,
- kWhatSuspendEncoding,
- kWhatShutdown,
- kWhatEncoderActivity,
- kWhatDropAFrame,
- kWhatReleaseOutputBuffer,
- };
-
- sp<AMessage> mNotify;
- sp<ALooper> mCodecLooper;
- sp<AMessage> mOutputFormat;
- uint32_t mFlags;
- bool mIsVideo;
- bool mIsH264;
- bool mIsPCMAudio;
- bool mNeedToManuallyPrependSPSPPS;
-
- sp<MediaCodec> mEncoder;
- sp<AMessage> mEncoderActivityNotify;
-
- sp<IGraphicBufferProducer> mGraphicBufferProducer;
-
- Vector<sp<MediaCodecBuffer> > mEncoderInputBuffers;
- Vector<sp<MediaCodecBuffer> > mEncoderOutputBuffers;
-
- List<size_t> mAvailEncoderInputIndices;
-
- List<sp<ABuffer> > mInputBufferQueue;
-
- sp<ABuffer> mCSD0;
-
- bool mDoMoreWorkPending;
-
-#if ENABLE_SILENCE_DETECTION
- int64_t mFirstSilentFrameUs;
- bool mInSilentMode;
-#endif
-
- sp<ABuffer> mPartialAudioAU;
-
- int32_t mPrevVideoBitrate;
-
- int32_t mNumFramesToDrop;
- bool mEncodingSuspended;
-
- status_t initEncoder();
- void releaseEncoder();
-
- status_t feedEncoderInputBuffers();
-
- void scheduleDoMoreWork();
- status_t doMoreWork();
-
- void notifyError(status_t err);
-
- // Packetizes raw PCM audio data available in mInputBufferQueue
- // into a format suitable for transport stream inclusion and
- // notifies the observer.
- status_t feedRawAudioInputBuffers();
-
- static bool IsSilence(const sp<ABuffer> &accessUnit);
-
- sp<ABuffer> prependCSD(const sp<ABuffer> &accessUnit) const;
-
- DISALLOW_EVIL_CONSTRUCTORS(Converter);
-};
-
-} // namespace android
-
-#endif // CONVERTER_H_
diff --git a/media/libstagefright/wifi-display/source/MediaPuller.cpp b/media/libstagefright/wifi-display/source/MediaPuller.cpp
deleted file mode 100644
index ce07a4e..0000000
--- a/media/libstagefright/wifi-display/source/MediaPuller.cpp
+++ /dev/null
@@ -1,224 +0,0 @@
-/*
- * Copyright 2012, 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 "MediaPuller"
-#include <utils/Log.h>
-
-#include "MediaPuller.h"
-
-#include <media/stagefright/foundation/ABuffer.h>
-#include <media/stagefright/foundation/ADebug.h>
-#include <media/stagefright/foundation/AMessage.h>
-#include <media/stagefright/MediaBuffer.h>
-#include <media/stagefright/MediaSource.h>
-#include <media/stagefright/MetaData.h>
-
-namespace android {
-
-MediaPuller::MediaPuller(
- const sp<MediaSource> &source, const sp<AMessage> ¬ify)
- : mSource(source),
- mNotify(notify),
- mPullGeneration(0),
- mIsAudio(false),
- mPaused(false) {
- sp<MetaData> meta = source->getFormat();
- const char *mime;
- CHECK(meta->findCString(kKeyMIMEType, &mime));
-
- mIsAudio = !strncasecmp(mime, "audio/", 6);
-}
-
-MediaPuller::~MediaPuller() {
-}
-
-status_t MediaPuller::postSynchronouslyAndReturnError(
- const sp<AMessage> &msg) {
- sp<AMessage> response;
- status_t err = msg->postAndAwaitResponse(&response);
-
- if (err != OK) {
- return err;
- }
-
- if (!response->findInt32("err", &err)) {
- err = OK;
- }
-
- return err;
-}
-
-status_t MediaPuller::start() {
- return postSynchronouslyAndReturnError(new AMessage(kWhatStart, this));
-}
-
-void MediaPuller::stopAsync(const sp<AMessage> ¬ify) {
- sp<AMessage> msg = new AMessage(kWhatStop, this);
- msg->setMessage("notify", notify);
- msg->post();
-}
-
-void MediaPuller::pause() {
- (new AMessage(kWhatPause, this))->post();
-}
-
-void MediaPuller::resume() {
- (new AMessage(kWhatResume, this))->post();
-}
-
-void MediaPuller::onMessageReceived(const sp<AMessage> &msg) {
- switch (msg->what()) {
- case kWhatStart:
- {
- status_t err;
- if (mIsAudio) {
- // This atrocity causes AudioSource to deliver absolute
- // systemTime() based timestamps (off by 1 us).
- sp<MetaData> params = new MetaData;
- params->setInt64(kKeyTime, 1ll);
- err = mSource->start(params.get());
- } else {
- err = mSource->start();
- if (err != OK) {
- ALOGE("source failed to start w/ err %d", err);
- }
- }
-
- if (err == OK) {
- schedulePull();
- }
-
- sp<AMessage> response = new AMessage;
- response->setInt32("err", err);
-
- sp<AReplyToken> replyID;
- CHECK(msg->senderAwaitsResponse(&replyID));
- response->postReply(replyID);
- break;
- }
-
- case kWhatStop:
- {
- sp<MetaData> meta = mSource->getFormat();
- const char *tmp;
- CHECK(meta->findCString(kKeyMIMEType, &tmp));
- AString mime = tmp;
-
- ALOGI("MediaPuller(%s) stopping.", mime.c_str());
- mSource->stop();
- ALOGI("MediaPuller(%s) stopped.", mime.c_str());
- ++mPullGeneration;
-
- sp<AMessage> notify;
- CHECK(msg->findMessage("notify", ¬ify));
- notify->post();
- break;
- }
-
- case kWhatPull:
- {
- int32_t generation;
- CHECK(msg->findInt32("generation", &generation));
-
- if (generation != mPullGeneration) {
- break;
- }
-
- MediaBuffer *mbuf;
- status_t err = mSource->read(&mbuf);
-
- if (mPaused) {
- if (err == OK) {
- mbuf->release();
- mbuf = NULL;
- }
-
- schedulePull();
- break;
- }
-
- if (err != OK) {
- if (err == ERROR_END_OF_STREAM) {
- ALOGI("stream ended.");
- } else {
- ALOGE("error %d reading stream.", err);
- }
-
- sp<AMessage> notify = mNotify->dup();
- notify->setInt32("what", kWhatEOS);
- notify->post();
- } else {
- int64_t timeUs;
- CHECK(mbuf->meta_data()->findInt64(kKeyTime, &timeUs));
-
- sp<ABuffer> accessUnit = new ABuffer(mbuf->range_length());
-
- memcpy(accessUnit->data(),
- (const uint8_t *)mbuf->data() + mbuf->range_offset(),
- mbuf->range_length());
-
- accessUnit->meta()->setInt64("timeUs", timeUs);
-
- if (mIsAudio) {
- mbuf->release();
- mbuf = NULL;
- } else {
- // video encoder will release MediaBuffer when done
- // with underlying data.
- accessUnit->setMediaBufferBase(mbuf);
- }
-
- sp<AMessage> notify = mNotify->dup();
-
- notify->setInt32("what", kWhatAccessUnit);
- notify->setBuffer("accessUnit", accessUnit);
- notify->post();
-
- if (mbuf != NULL) {
- ALOGV("posted mbuf %p", mbuf);
- }
-
- schedulePull();
- }
- break;
- }
-
- case kWhatPause:
- {
- mPaused = true;
- break;
- }
-
- case kWhatResume:
- {
- mPaused = false;
- break;
- }
-
- default:
- TRESPASS();
- }
-}
-
-void MediaPuller::schedulePull() {
- sp<AMessage> msg = new AMessage(kWhatPull, this);
- msg->setInt32("generation", mPullGeneration);
- msg->post();
-}
-
-} // namespace android
-
diff --git a/media/libstagefright/wifi-display/source/MediaPuller.h b/media/libstagefright/wifi-display/source/MediaPuller.h
deleted file mode 100644
index 1291bb3..0000000
--- a/media/libstagefright/wifi-display/source/MediaPuller.h
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Copyright 2012, 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_PULLER_H_
-
-#define MEDIA_PULLER_H_
-
-#include <media/stagefright/foundation/AHandler.h>
-
-namespace android {
-
-struct MediaSource;
-
-struct MediaPuller : public AHandler {
- enum {
- kWhatEOS,
- kWhatAccessUnit
- };
-
- MediaPuller(const sp<MediaSource> &source, const sp<AMessage> ¬ify);
-
- status_t start();
- void stopAsync(const sp<AMessage> ¬ify);
-
- void pause();
- void resume();
-
-protected:
- virtual void onMessageReceived(const sp<AMessage> &msg);
- virtual ~MediaPuller();
-
-private:
- enum {
- kWhatStart,
- kWhatStop,
- kWhatPull,
- kWhatPause,
- kWhatResume,
- };
-
- sp<MediaSource> mSource;
- sp<AMessage> mNotify;
- int32_t mPullGeneration;
- bool mIsAudio;
- bool mPaused;
-
- status_t postSynchronouslyAndReturnError(const sp<AMessage> &msg);
- void schedulePull();
-
- DISALLOW_EVIL_CONSTRUCTORS(MediaPuller);
-};
-
-} // namespace android
-
-#endif // MEDIA_PULLER_H_
diff --git a/media/libstagefright/wifi-display/source/PlaybackSession.cpp b/media/libstagefright/wifi-display/source/PlaybackSession.cpp
deleted file mode 100644
index f1ecca0..0000000
--- a/media/libstagefright/wifi-display/source/PlaybackSession.cpp
+++ /dev/null
@@ -1,1112 +0,0 @@
-/*
- * Copyright 2012, 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 "PlaybackSession"
-#include <utils/Log.h>
-
-#include "PlaybackSession.h"
-
-#include "Converter.h"
-#include "MediaPuller.h"
-#include "RepeaterSource.h"
-#include "include/avc_utils.h"
-#include "WifiDisplaySource.h"
-
-#include <binder/IServiceManager.h>
-#include <cutils/properties.h>
-#include <media/IHDCP.h>
-#include <media/IMediaHTTPService.h>
-#include <media/stagefright/foundation/ABitReader.h>
-#include <media/stagefright/foundation/ABuffer.h>
-#include <media/stagefright/foundation/ADebug.h>
-#include <media/stagefright/foundation/AMessage.h>
-#include <media/stagefright/foundation/hexdump.h>
-#include <media/stagefright/AudioSource.h>
-#include <media/stagefright/MediaDefs.h>
-#include <media/stagefright/MediaErrors.h>
-#include <media/stagefright/MediaSource.h>
-#include <media/stagefright/MetaData.h>
-#include <media/stagefright/NuMediaExtractor.h>
-#include <media/stagefright/SurfaceMediaSource.h>
-#include <media/stagefright/Utils.h>
-
-#include <OMX_IVCommon.h>
-
-namespace android {
-
-struct WifiDisplaySource::PlaybackSession::Track : public AHandler {
- enum {
- kWhatStopped,
- };
-
- Track(const sp<AMessage> ¬ify,
- const sp<ALooper> &pullLooper,
- const sp<ALooper> &codecLooper,
- const sp<MediaPuller> &mediaPuller,
- const sp<Converter> &converter);
-
- Track(const sp<AMessage> ¬ify, const sp<AMessage> &format);
-
- void setRepeaterSource(const sp<RepeaterSource> &source);
-
- sp<AMessage> getFormat();
- bool isAudio() const;
-
- const sp<Converter> &converter() const;
- const sp<RepeaterSource> &repeaterSource() const;
-
- ssize_t mediaSenderTrackIndex() const;
- void setMediaSenderTrackIndex(size_t index);
-
- status_t start();
- void stopAsync();
-
- void pause();
- void resume();
-
- void queueAccessUnit(const sp<ABuffer> &accessUnit);
- sp<ABuffer> dequeueAccessUnit();
-
- bool hasOutputBuffer(int64_t *timeUs) const;
- void queueOutputBuffer(const sp<ABuffer> &accessUnit);
- sp<ABuffer> dequeueOutputBuffer();
-
-#if SUSPEND_VIDEO_IF_IDLE
- bool isSuspended() const;
-#endif
-
- size_t countQueuedOutputBuffers() const {
- return mQueuedOutputBuffers.size();
- }
-
- void requestIDRFrame();
-
-protected:
- virtual void onMessageReceived(const sp<AMessage> &msg);
- virtual ~Track();
-
-private:
- enum {
- kWhatMediaPullerStopped,
- };
-
- sp<AMessage> mNotify;
- sp<ALooper> mPullLooper;
- sp<ALooper> mCodecLooper;
- sp<MediaPuller> mMediaPuller;
- sp<Converter> mConverter;
- sp<AMessage> mFormat;
- bool mStarted;
- ssize_t mMediaSenderTrackIndex;
- bool mIsAudio;
- List<sp<ABuffer> > mQueuedAccessUnits;
- sp<RepeaterSource> mRepeaterSource;
- List<sp<ABuffer> > mQueuedOutputBuffers;
- int64_t mLastOutputBufferQueuedTimeUs;
-
- static bool IsAudioFormat(const sp<AMessage> &format);
-
- DISALLOW_EVIL_CONSTRUCTORS(Track);
-};
-
-WifiDisplaySource::PlaybackSession::Track::Track(
- const sp<AMessage> ¬ify,
- const sp<ALooper> &pullLooper,
- const sp<ALooper> &codecLooper,
- const sp<MediaPuller> &mediaPuller,
- const sp<Converter> &converter)
- : mNotify(notify),
- mPullLooper(pullLooper),
- mCodecLooper(codecLooper),
- mMediaPuller(mediaPuller),
- mConverter(converter),
- mStarted(false),
- mIsAudio(IsAudioFormat(mConverter->getOutputFormat())),
- mLastOutputBufferQueuedTimeUs(-1ll) {
-}
-
-WifiDisplaySource::PlaybackSession::Track::Track(
- const sp<AMessage> ¬ify, const sp<AMessage> &format)
- : mNotify(notify),
- mFormat(format),
- mStarted(false),
- mIsAudio(IsAudioFormat(format)),
- mLastOutputBufferQueuedTimeUs(-1ll) {
-}
-
-WifiDisplaySource::PlaybackSession::Track::~Track() {
- CHECK(!mStarted);
-}
-
-// static
-bool WifiDisplaySource::PlaybackSession::Track::IsAudioFormat(
- const sp<AMessage> &format) {
- AString mime;
- CHECK(format->findString("mime", &mime));
-
- return !strncasecmp(mime.c_str(), "audio/", 6);
-}
-
-sp<AMessage> WifiDisplaySource::PlaybackSession::Track::getFormat() {
- return mFormat != NULL ? mFormat : mConverter->getOutputFormat();
-}
-
-bool WifiDisplaySource::PlaybackSession::Track::isAudio() const {
- return mIsAudio;
-}
-
-const sp<Converter> &WifiDisplaySource::PlaybackSession::Track::converter() const {
- return mConverter;
-}
-
-const sp<RepeaterSource> &
-WifiDisplaySource::PlaybackSession::Track::repeaterSource() const {
- return mRepeaterSource;
-}
-
-ssize_t WifiDisplaySource::PlaybackSession::Track::mediaSenderTrackIndex() const {
- CHECK_GE(mMediaSenderTrackIndex, 0);
- return mMediaSenderTrackIndex;
-}
-
-void WifiDisplaySource::PlaybackSession::Track::setMediaSenderTrackIndex(
- size_t index) {
- mMediaSenderTrackIndex = index;
-}
-
-status_t WifiDisplaySource::PlaybackSession::Track::start() {
- ALOGV("Track::start isAudio=%d", mIsAudio);
-
- CHECK(!mStarted);
-
- status_t err = OK;
-
- if (mMediaPuller != NULL) {
- err = mMediaPuller->start();
- }
-
- if (err == OK) {
- mStarted = true;
- }
-
- return err;
-}
-
-void WifiDisplaySource::PlaybackSession::Track::stopAsync() {
- ALOGV("Track::stopAsync isAudio=%d", mIsAudio);
-
- if (mConverter != NULL) {
- mConverter->shutdownAsync();
- }
-
- sp<AMessage> msg = new AMessage(kWhatMediaPullerStopped, this);
-
- if (mStarted && mMediaPuller != NULL) {
- if (mRepeaterSource != NULL) {
- // Let's unblock MediaPuller's MediaSource::read().
- mRepeaterSource->wakeUp();
- }
-
- mMediaPuller->stopAsync(msg);
- } else {
- mStarted = false;
- msg->post();
- }
-}
-
-void WifiDisplaySource::PlaybackSession::Track::pause() {
- mMediaPuller->pause();
-}
-
-void WifiDisplaySource::PlaybackSession::Track::resume() {
- mMediaPuller->resume();
-}
-
-void WifiDisplaySource::PlaybackSession::Track::onMessageReceived(
- const sp<AMessage> &msg) {
- switch (msg->what()) {
- case kWhatMediaPullerStopped:
- {
- mConverter.clear();
-
- mStarted = false;
-
- sp<AMessage> notify = mNotify->dup();
- notify->setInt32("what", kWhatStopped);
- notify->post();
-
- ALOGI("kWhatStopped %s posted", mIsAudio ? "audio" : "video");
- break;
- }
-
- default:
- TRESPASS();
- }
-}
-
-void WifiDisplaySource::PlaybackSession::Track::queueAccessUnit(
- const sp<ABuffer> &accessUnit) {
- mQueuedAccessUnits.push_back(accessUnit);
-}
-
-sp<ABuffer> WifiDisplaySource::PlaybackSession::Track::dequeueAccessUnit() {
- if (mQueuedAccessUnits.empty()) {
- return NULL;
- }
-
- sp<ABuffer> accessUnit = *mQueuedAccessUnits.begin();
- CHECK(accessUnit != NULL);
-
- mQueuedAccessUnits.erase(mQueuedAccessUnits.begin());
-
- return accessUnit;
-}
-
-void WifiDisplaySource::PlaybackSession::Track::setRepeaterSource(
- const sp<RepeaterSource> &source) {
- mRepeaterSource = source;
-}
-
-void WifiDisplaySource::PlaybackSession::Track::requestIDRFrame() {
- if (mIsAudio) {
- return;
- }
-
- if (mRepeaterSource != NULL) {
- mRepeaterSource->wakeUp();
- }
-
- mConverter->requestIDRFrame();
-}
-
-bool WifiDisplaySource::PlaybackSession::Track::hasOutputBuffer(
- int64_t *timeUs) const {
- *timeUs = 0ll;
-
- if (mQueuedOutputBuffers.empty()) {
- return false;
- }
-
- const sp<ABuffer> &outputBuffer = *mQueuedOutputBuffers.begin();
-
- CHECK(outputBuffer->meta()->findInt64("timeUs", timeUs));
-
- return true;
-}
-
-void WifiDisplaySource::PlaybackSession::Track::queueOutputBuffer(
- const sp<ABuffer> &accessUnit) {
- mQueuedOutputBuffers.push_back(accessUnit);
- mLastOutputBufferQueuedTimeUs = ALooper::GetNowUs();
-}
-
-sp<ABuffer> WifiDisplaySource::PlaybackSession::Track::dequeueOutputBuffer() {
- CHECK(!mQueuedOutputBuffers.empty());
-
- sp<ABuffer> outputBuffer = *mQueuedOutputBuffers.begin();
- mQueuedOutputBuffers.erase(mQueuedOutputBuffers.begin());
-
- return outputBuffer;
-}
-
-#if SUSPEND_VIDEO_IF_IDLE
-bool WifiDisplaySource::PlaybackSession::Track::isSuspended() const {
- if (!mQueuedOutputBuffers.empty()) {
- return false;
- }
-
- if (mLastOutputBufferQueuedTimeUs < 0ll) {
- // We've never seen an output buffer queued, but tracks start
- // out live, not suspended.
- return false;
- }
-
- // If we've not seen new output data for 60ms or more, we consider
- // this track suspended for the time being.
- return (ALooper::GetNowUs() - mLastOutputBufferQueuedTimeUs) > 60000ll;
-}
-#endif
-
-////////////////////////////////////////////////////////////////////////////////
-
-WifiDisplaySource::PlaybackSession::PlaybackSession(
- const String16 &opPackageName,
- const sp<ANetworkSession> &netSession,
- const sp<AMessage> ¬ify,
- const in_addr &interfaceAddr,
- const sp<IHDCP> &hdcp,
- const char *path)
- : mOpPackageName(opPackageName),
- mNetSession(netSession),
- mNotify(notify),
- mInterfaceAddr(interfaceAddr),
- mHDCP(hdcp),
- mLocalRTPPort(-1),
- mWeAreDead(false),
- mPaused(false),
- mLastLifesignUs(),
- mVideoTrackIndex(-1),
- mPrevTimeUs(-1ll),
- mPullExtractorPending(false),
- mPullExtractorGeneration(0),
- mFirstSampleTimeRealUs(-1ll),
- mFirstSampleTimeUs(-1ll) {
- if (path != NULL) {
- mMediaPath.setTo(path);
- }
-}
-
-status_t WifiDisplaySource::PlaybackSession::init(
- const char *clientIP,
- int32_t clientRtp,
- RTPSender::TransportMode rtpMode,
- int32_t clientRtcp,
- RTPSender::TransportMode rtcpMode,
- bool enableAudio,
- bool usePCMAudio,
- bool enableVideo,
- VideoFormats::ResolutionType videoResolutionType,
- size_t videoResolutionIndex,
- VideoFormats::ProfileType videoProfileType,
- VideoFormats::LevelType videoLevelType) {
- sp<AMessage> notify = new AMessage(kWhatMediaSenderNotify, this);
- mMediaSender = new MediaSender(mNetSession, notify);
- looper()->registerHandler(mMediaSender);
-
- mMediaSender->setHDCP(mHDCP);
-
- status_t err = setupPacketizer(
- enableAudio,
- usePCMAudio,
- enableVideo,
- videoResolutionType,
- videoResolutionIndex,
- videoProfileType,
- videoLevelType);
-
- if (err == OK) {
- err = mMediaSender->initAsync(
- -1 /* trackIndex */,
- clientIP,
- clientRtp,
- rtpMode,
- clientRtcp,
- rtcpMode,
- &mLocalRTPPort);
- }
-
- if (err != OK) {
- mLocalRTPPort = -1;
-
- looper()->unregisterHandler(mMediaSender->id());
- mMediaSender.clear();
-
- return err;
- }
-
- updateLiveness();
-
- return OK;
-}
-
-WifiDisplaySource::PlaybackSession::~PlaybackSession() {
-}
-
-int32_t WifiDisplaySource::PlaybackSession::getRTPPort() const {
- return mLocalRTPPort;
-}
-
-int64_t WifiDisplaySource::PlaybackSession::getLastLifesignUs() const {
- return mLastLifesignUs;
-}
-
-void WifiDisplaySource::PlaybackSession::updateLiveness() {
- mLastLifesignUs = ALooper::GetNowUs();
-}
-
-status_t WifiDisplaySource::PlaybackSession::play() {
- updateLiveness();
-
- (new AMessage(kWhatResume, this))->post();
-
- return OK;
-}
-
-status_t WifiDisplaySource::PlaybackSession::onMediaSenderInitialized() {
- for (size_t i = 0; i < mTracks.size(); ++i) {
- CHECK_EQ((status_t)OK, mTracks.editValueAt(i)->start());
- }
-
- sp<AMessage> notify = mNotify->dup();
- notify->setInt32("what", kWhatSessionEstablished);
- notify->post();
-
- return OK;
-}
-
-status_t WifiDisplaySource::PlaybackSession::pause() {
- updateLiveness();
-
- (new AMessage(kWhatPause, this))->post();
-
- return OK;
-}
-
-void WifiDisplaySource::PlaybackSession::destroyAsync() {
- ALOGI("destroyAsync");
-
- for (size_t i = 0; i < mTracks.size(); ++i) {
- mTracks.valueAt(i)->stopAsync();
- }
-}
-
-void WifiDisplaySource::PlaybackSession::onMessageReceived(
- const sp<AMessage> &msg) {
- switch (msg->what()) {
- case kWhatConverterNotify:
- {
- if (mWeAreDead) {
- ALOGV("dropping msg '%s' because we're dead",
- msg->debugString().c_str());
-
- break;
- }
-
- int32_t what;
- CHECK(msg->findInt32("what", &what));
-
- size_t trackIndex;
- CHECK(msg->findSize("trackIndex", &trackIndex));
-
- if (what == Converter::kWhatAccessUnit) {
- sp<ABuffer> accessUnit;
- CHECK(msg->findBuffer("accessUnit", &accessUnit));
-
- const sp<Track> &track = mTracks.valueFor(trackIndex);
-
- status_t err = mMediaSender->queueAccessUnit(
- track->mediaSenderTrackIndex(),
- accessUnit);
-
- if (err != OK) {
- notifySessionDead();
- }
- break;
- } else if (what == Converter::kWhatEOS) {
- CHECK_EQ(what, Converter::kWhatEOS);
-
- ALOGI("output EOS on track %zu", trackIndex);
-
- ssize_t index = mTracks.indexOfKey(trackIndex);
- CHECK_GE(index, 0);
-
- const sp<Converter> &converter =
- mTracks.valueAt(index)->converter();
- looper()->unregisterHandler(converter->id());
-
- mTracks.removeItemsAt(index);
-
- if (mTracks.isEmpty()) {
- ALOGI("Reached EOS");
- }
- } else if (what != Converter::kWhatShutdownCompleted) {
- CHECK_EQ(what, Converter::kWhatError);
-
- status_t err;
- CHECK(msg->findInt32("err", &err));
-
- ALOGE("converter signaled error %d", err);
-
- notifySessionDead();
- }
- break;
- }
-
- case kWhatMediaSenderNotify:
- {
- int32_t what;
- CHECK(msg->findInt32("what", &what));
-
- if (what == MediaSender::kWhatInitDone) {
- status_t err;
- CHECK(msg->findInt32("err", &err));
-
- if (err == OK) {
- onMediaSenderInitialized();
- } else {
- notifySessionDead();
- }
- } else if (what == MediaSender::kWhatError) {
- notifySessionDead();
- } else if (what == MediaSender::kWhatNetworkStall) {
- size_t numBytesQueued;
- CHECK(msg->findSize("numBytesQueued", &numBytesQueued));
-
- if (mVideoTrackIndex >= 0) {
- const sp<Track> &videoTrack =
- mTracks.valueFor(mVideoTrackIndex);
-
- sp<Converter> converter = videoTrack->converter();
- if (converter != NULL) {
- converter->dropAFrame();
- }
- }
- } else if (what == MediaSender::kWhatInformSender) {
- onSinkFeedback(msg);
- } else {
- TRESPASS();
- }
- break;
- }
-
- case kWhatTrackNotify:
- {
- int32_t what;
- CHECK(msg->findInt32("what", &what));
-
- size_t trackIndex;
- CHECK(msg->findSize("trackIndex", &trackIndex));
-
- if (what == Track::kWhatStopped) {
- ALOGI("Track %zu stopped", trackIndex);
-
- sp<Track> track = mTracks.valueFor(trackIndex);
- looper()->unregisterHandler(track->id());
- mTracks.removeItem(trackIndex);
- track.clear();
-
- if (!mTracks.isEmpty()) {
- ALOGI("not all tracks are stopped yet");
- break;
- }
-
- looper()->unregisterHandler(mMediaSender->id());
- mMediaSender.clear();
-
- sp<AMessage> notify = mNotify->dup();
- notify->setInt32("what", kWhatSessionDestroyed);
- notify->post();
- }
- break;
- }
-
- case kWhatPause:
- {
- if (mExtractor != NULL) {
- ++mPullExtractorGeneration;
- mFirstSampleTimeRealUs = -1ll;
- mFirstSampleTimeUs = -1ll;
- }
-
- if (mPaused) {
- break;
- }
-
- for (size_t i = 0; i < mTracks.size(); ++i) {
- mTracks.editValueAt(i)->pause();
- }
-
- mPaused = true;
- break;
- }
-
- case kWhatResume:
- {
- if (mExtractor != NULL) {
- schedulePullExtractor();
- }
-
- if (!mPaused) {
- break;
- }
-
- for (size_t i = 0; i < mTracks.size(); ++i) {
- mTracks.editValueAt(i)->resume();
- }
-
- mPaused = false;
- break;
- }
-
- case kWhatPullExtractorSample:
- {
- int32_t generation;
- CHECK(msg->findInt32("generation", &generation));
-
- if (generation != mPullExtractorGeneration) {
- break;
- }
-
- mPullExtractorPending = false;
-
- onPullExtractor();
- break;
- }
-
- default:
- TRESPASS();
- }
-}
-
-void WifiDisplaySource::PlaybackSession::onSinkFeedback(const sp<AMessage> &msg) {
- int64_t avgLatencyUs;
- CHECK(msg->findInt64("avgLatencyUs", &avgLatencyUs));
-
- int64_t maxLatencyUs;
- CHECK(msg->findInt64("maxLatencyUs", &maxLatencyUs));
-
- ALOGI("sink reports avg. latency of %lld ms (max %lld ms)",
- avgLatencyUs / 1000ll,
- maxLatencyUs / 1000ll);
-
- if (mVideoTrackIndex >= 0) {
- const sp<Track> &videoTrack = mTracks.valueFor(mVideoTrackIndex);
- sp<Converter> converter = videoTrack->converter();
-
- if (converter != NULL) {
- int32_t videoBitrate =
- Converter::GetInt32Property("media.wfd.video-bitrate", -1);
-
- char val[PROPERTY_VALUE_MAX];
- if (videoBitrate < 0
- && property_get("media.wfd.video-bitrate", val, NULL)
- && !strcasecmp("adaptive", val)) {
- videoBitrate = converter->getVideoBitrate();
-
- if (avgLatencyUs > 300000ll) {
- videoBitrate *= 0.6;
- } else if (avgLatencyUs < 100000ll) {
- videoBitrate *= 1.1;
- }
- }
-
- if (videoBitrate > 0) {
- if (videoBitrate < 500000) {
- videoBitrate = 500000;
- } else if (videoBitrate > 10000000) {
- videoBitrate = 10000000;
- }
-
- if (videoBitrate != converter->getVideoBitrate()) {
- ALOGI("setting video bitrate to %d bps", videoBitrate);
-
- converter->setVideoBitrate(videoBitrate);
- }
- }
- }
-
- sp<RepeaterSource> repeaterSource = videoTrack->repeaterSource();
- if (repeaterSource != NULL) {
- double rateHz =
- Converter::GetInt32Property(
- "media.wfd.video-framerate", -1);
-
- char val[PROPERTY_VALUE_MAX];
- if (rateHz < 0.0
- && property_get("media.wfd.video-framerate", val, NULL)
- && !strcasecmp("adaptive", val)) {
- rateHz = repeaterSource->getFrameRate();
-
- if (avgLatencyUs > 300000ll) {
- rateHz *= 0.9;
- } else if (avgLatencyUs < 200000ll) {
- rateHz *= 1.1;
- }
- }
-
- if (rateHz > 0) {
- if (rateHz < 5.0) {
- rateHz = 5.0;
- } else if (rateHz > 30.0) {
- rateHz = 30.0;
- }
-
- if (rateHz != repeaterSource->getFrameRate()) {
- ALOGI("setting frame rate to %.2f Hz", rateHz);
-
- repeaterSource->setFrameRate(rateHz);
- }
- }
- }
- }
-}
-
-status_t WifiDisplaySource::PlaybackSession::setupMediaPacketizer(
- bool enableAudio, bool enableVideo) {
- mExtractor = new NuMediaExtractor;
-
- status_t err = mExtractor->setDataSource(
- NULL /* httpService */, mMediaPath.c_str());
-
- if (err != OK) {
- return err;
- }
-
- size_t n = mExtractor->countTracks();
- bool haveAudio = false;
- bool haveVideo = false;
- for (size_t i = 0; i < n; ++i) {
- sp<AMessage> format;
- err = mExtractor->getTrackFormat(i, &format);
-
- if (err != OK) {
- continue;
- }
-
- AString mime;
- CHECK(format->findString("mime", &mime));
-
- bool isAudio = !strncasecmp(mime.c_str(), "audio/", 6);
- bool isVideo = !strncasecmp(mime.c_str(), "video/", 6);
-
- if (isAudio && enableAudio && !haveAudio) {
- haveAudio = true;
- } else if (isVideo && enableVideo && !haveVideo) {
- haveVideo = true;
- } else {
- continue;
- }
-
- err = mExtractor->selectTrack(i);
-
- size_t trackIndex = mTracks.size();
-
- sp<AMessage> notify = new AMessage(kWhatTrackNotify, this);
- notify->setSize("trackIndex", trackIndex);
-
- sp<Track> track = new Track(notify, format);
- looper()->registerHandler(track);
-
- mTracks.add(trackIndex, track);
-
- mExtractorTrackToInternalTrack.add(i, trackIndex);
-
- if (isVideo) {
- mVideoTrackIndex = trackIndex;
- }
-
- uint32_t flags = MediaSender::FLAG_MANUALLY_PREPEND_SPS_PPS;
-
- ssize_t mediaSenderTrackIndex =
- mMediaSender->addTrack(format, flags);
- CHECK_GE(mediaSenderTrackIndex, 0);
-
- track->setMediaSenderTrackIndex(mediaSenderTrackIndex);
-
- if ((haveAudio || !enableAudio) && (haveVideo || !enableVideo)) {
- break;
- }
- }
-
- return OK;
-}
-
-void WifiDisplaySource::PlaybackSession::schedulePullExtractor() {
- if (mPullExtractorPending) {
- return;
- }
-
- int64_t delayUs = 1000000; // default delay is 1 sec
- int64_t sampleTimeUs;
- status_t err = mExtractor->getSampleTime(&sampleTimeUs);
-
- if (err == OK) {
- int64_t nowUs = ALooper::GetNowUs();
-
- if (mFirstSampleTimeRealUs < 0ll) {
- mFirstSampleTimeRealUs = nowUs;
- mFirstSampleTimeUs = sampleTimeUs;
- }
-
- int64_t whenUs = sampleTimeUs - mFirstSampleTimeUs + mFirstSampleTimeRealUs;
- delayUs = whenUs - nowUs;
- } else {
- ALOGW("could not get sample time (%d)", err);
- }
-
- sp<AMessage> msg = new AMessage(kWhatPullExtractorSample, this);
- msg->setInt32("generation", mPullExtractorGeneration);
- msg->post(delayUs);
-
- mPullExtractorPending = true;
-}
-
-void WifiDisplaySource::PlaybackSession::onPullExtractor() {
- sp<ABuffer> accessUnit = new ABuffer(1024 * 1024);
- status_t err = mExtractor->readSampleData(accessUnit);
- if (err != OK) {
- // EOS.
- return;
- }
-
- int64_t timeUs;
- CHECK_EQ((status_t)OK, mExtractor->getSampleTime(&timeUs));
-
- accessUnit->meta()->setInt64(
- "timeUs", mFirstSampleTimeRealUs + timeUs - mFirstSampleTimeUs);
-
- size_t trackIndex;
- CHECK_EQ((status_t)OK, mExtractor->getSampleTrackIndex(&trackIndex));
-
- sp<AMessage> msg = new AMessage(kWhatConverterNotify, this);
-
- msg->setSize(
- "trackIndex", mExtractorTrackToInternalTrack.valueFor(trackIndex));
-
- msg->setInt32("what", Converter::kWhatAccessUnit);
- msg->setBuffer("accessUnit", accessUnit);
- msg->post();
-
- mExtractor->advance();
-
- schedulePullExtractor();
-}
-
-status_t WifiDisplaySource::PlaybackSession::setupPacketizer(
- bool enableAudio,
- bool usePCMAudio,
- bool enableVideo,
- VideoFormats::ResolutionType videoResolutionType,
- size_t videoResolutionIndex,
- VideoFormats::ProfileType videoProfileType,
- VideoFormats::LevelType videoLevelType) {
- CHECK(enableAudio || enableVideo);
-
- if (!mMediaPath.empty()) {
- return setupMediaPacketizer(enableAudio, enableVideo);
- }
-
- if (enableVideo) {
- status_t err = addVideoSource(
- videoResolutionType, videoResolutionIndex, videoProfileType,
- videoLevelType);
-
- if (err != OK) {
- return err;
- }
- }
-
- if (!enableAudio) {
- return OK;
- }
-
- return addAudioSource(usePCMAudio);
-}
-
-status_t WifiDisplaySource::PlaybackSession::addSource(
- bool isVideo, const sp<MediaSource> &source, bool isRepeaterSource,
- bool usePCMAudio, unsigned profileIdc, unsigned levelIdc,
- unsigned constraintSet, size_t *numInputBuffers) {
- CHECK(!usePCMAudio || !isVideo);
- CHECK(!isRepeaterSource || isVideo);
- CHECK(!profileIdc || isVideo);
- CHECK(!levelIdc || isVideo);
- CHECK(!constraintSet || isVideo);
-
- sp<ALooper> pullLooper = new ALooper;
- pullLooper->setName("pull_looper");
-
- pullLooper->start(
- false /* runOnCallingThread */,
- false /* canCallJava */,
- PRIORITY_AUDIO);
-
- sp<ALooper> codecLooper = new ALooper;
- codecLooper->setName("codec_looper");
-
- codecLooper->start(
- false /* runOnCallingThread */,
- false /* canCallJava */,
- PRIORITY_AUDIO);
-
- size_t trackIndex;
-
- sp<AMessage> notify;
-
- trackIndex = mTracks.size();
-
- sp<AMessage> format;
- status_t err = convertMetaDataToMessage(source->getFormat(), &format);
- CHECK_EQ(err, (status_t)OK);
-
- if (isVideo) {
- format->setString("mime", MEDIA_MIMETYPE_VIDEO_AVC);
- format->setInt32(
- "android._input-metadata-buffer-type", kMetadataBufferTypeANWBuffer);
- format->setInt32("android._store-metadata-in-buffers-output", (mHDCP != NULL)
- && (mHDCP->getCaps() & HDCPModule::HDCP_CAPS_ENCRYPT_NATIVE));
- format->setInt32(
- "color-format", OMX_COLOR_FormatAndroidOpaque);
- format->setInt32("profile-idc", profileIdc);
- format->setInt32("level-idc", levelIdc);
- format->setInt32("constraint-set", constraintSet);
- } else {
- if (usePCMAudio) {
- format->setInt32("pcm-encoding", kAudioEncodingPcm16bit);
- format->setString("mime", MEDIA_MIMETYPE_AUDIO_RAW);
- } else {
- format->setString("mime", MEDIA_MIMETYPE_AUDIO_AAC);
- }
- }
-
- notify = new AMessage(kWhatConverterNotify, this);
- notify->setSize("trackIndex", trackIndex);
-
- sp<Converter> converter = new Converter(notify, codecLooper, format);
-
- looper()->registerHandler(converter);
-
- err = converter->init();
- if (err != OK) {
- ALOGE("%s converter returned err %d", isVideo ? "video" : "audio", err);
-
- looper()->unregisterHandler(converter->id());
- return err;
- }
-
- notify = new AMessage(Converter::kWhatMediaPullerNotify, converter);
- notify->setSize("trackIndex", trackIndex);
-
- sp<MediaPuller> puller = new MediaPuller(source, notify);
- pullLooper->registerHandler(puller);
-
- if (numInputBuffers != NULL) {
- *numInputBuffers = converter->getInputBufferCount();
- }
-
- notify = new AMessage(kWhatTrackNotify, this);
- notify->setSize("trackIndex", trackIndex);
-
- sp<Track> track = new Track(
- notify, pullLooper, codecLooper, puller, converter);
-
- if (isRepeaterSource) {
- track->setRepeaterSource(static_cast<RepeaterSource *>(source.get()));
- }
-
- looper()->registerHandler(track);
-
- mTracks.add(trackIndex, track);
-
- if (isVideo) {
- mVideoTrackIndex = trackIndex;
- }
-
- uint32_t flags = 0;
- if (converter->needToManuallyPrependSPSPPS()) {
- flags |= MediaSender::FLAG_MANUALLY_PREPEND_SPS_PPS;
- }
-
- ssize_t mediaSenderTrackIndex =
- mMediaSender->addTrack(converter->getOutputFormat(), flags);
- CHECK_GE(mediaSenderTrackIndex, 0);
-
- track->setMediaSenderTrackIndex(mediaSenderTrackIndex);
-
- return OK;
-}
-
-status_t WifiDisplaySource::PlaybackSession::addVideoSource(
- VideoFormats::ResolutionType videoResolutionType,
- size_t videoResolutionIndex,
- VideoFormats::ProfileType videoProfileType,
- VideoFormats::LevelType videoLevelType) {
- size_t width, height, framesPerSecond;
- bool interlaced;
- CHECK(VideoFormats::GetConfiguration(
- videoResolutionType,
- videoResolutionIndex,
- &width,
- &height,
- &framesPerSecond,
- &interlaced));
-
- unsigned profileIdc, levelIdc, constraintSet;
- CHECK(VideoFormats::GetProfileLevel(
- videoProfileType,
- videoLevelType,
- &profileIdc,
- &levelIdc,
- &constraintSet));
-
- sp<SurfaceMediaSource> source = new SurfaceMediaSource(width, height);
-
- source->setUseAbsoluteTimestamps();
-
- sp<RepeaterSource> videoSource =
- new RepeaterSource(source, framesPerSecond);
-
- size_t numInputBuffers;
- status_t err = addSource(
- true /* isVideo */, videoSource, true /* isRepeaterSource */,
- false /* usePCMAudio */, profileIdc, levelIdc, constraintSet,
- &numInputBuffers);
-
- if (err != OK) {
- return err;
- }
-
- err = source->setMaxAcquiredBufferCount(numInputBuffers);
- CHECK_EQ(err, (status_t)OK);
-
- mProducer = source->getProducer();
-
- return OK;
-}
-
-status_t WifiDisplaySource::PlaybackSession::addAudioSource(bool usePCMAudio) {
- sp<AudioSource> audioSource = new AudioSource(
- AUDIO_SOURCE_REMOTE_SUBMIX,
- mOpPackageName,
- 48000 /* sampleRate */,
- 2 /* channelCount */);
-
- if (audioSource->initCheck() == OK) {
- return addSource(
- false /* isVideo */, audioSource, false /* isRepeaterSource */,
- usePCMAudio, 0 /* profileIdc */, 0 /* levelIdc */,
- 0 /* constraintSet */, NULL /* numInputBuffers */);
- }
-
- ALOGW("Unable to instantiate audio source");
-
- return OK;
-}
-
-sp<IGraphicBufferProducer> WifiDisplaySource::PlaybackSession::getSurfaceTexture() {
- return mProducer;
-}
-
-void WifiDisplaySource::PlaybackSession::requestIDRFrame() {
- for (size_t i = 0; i < mTracks.size(); ++i) {
- const sp<Track> &track = mTracks.valueAt(i);
-
- track->requestIDRFrame();
- }
-}
-
-void WifiDisplaySource::PlaybackSession::notifySessionDead() {
- // Inform WifiDisplaySource of our premature death (wish).
- sp<AMessage> notify = mNotify->dup();
- notify->setInt32("what", kWhatSessionDead);
- notify->post();
-
- mWeAreDead = true;
-}
-
-} // namespace android
-
diff --git a/media/libstagefright/wifi-display/source/PlaybackSession.h b/media/libstagefright/wifi-display/source/PlaybackSession.h
deleted file mode 100644
index f6673df..0000000
--- a/media/libstagefright/wifi-display/source/PlaybackSession.h
+++ /dev/null
@@ -1,176 +0,0 @@
-/*
- * Copyright 2012, 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 PLAYBACK_SESSION_H_
-
-#define PLAYBACK_SESSION_H_
-
-#include "MediaSender.h"
-#include "VideoFormats.h"
-#include "WifiDisplaySource.h"
-
-#include <utils/String16.h>
-
-namespace android {
-
-struct ABuffer;
-struct IHDCP;
-class IGraphicBufferProducer;
-struct MediaPuller;
-struct MediaSource;
-struct MediaSender;
-struct NuMediaExtractor;
-
-// Encapsulates the state of an RTP/RTCP session in the context of wifi
-// display.
-struct WifiDisplaySource::PlaybackSession : public AHandler {
- PlaybackSession(
- const String16 &opPackageName,
- const sp<ANetworkSession> &netSession,
- const sp<AMessage> ¬ify,
- const struct in_addr &interfaceAddr,
- const sp<IHDCP> &hdcp,
- const char *path = NULL);
-
- status_t init(
- const char *clientIP,
- int32_t clientRtp,
- RTPSender::TransportMode rtpMode,
- int32_t clientRtcp,
- RTPSender::TransportMode rtcpMode,
- bool enableAudio,
- bool usePCMAudio,
- bool enableVideo,
- VideoFormats::ResolutionType videoResolutionType,
- size_t videoResolutionIndex,
- VideoFormats::ProfileType videoProfileType,
- VideoFormats::LevelType videoLevelType);
-
- void destroyAsync();
-
- int32_t getRTPPort() const;
-
- int64_t getLastLifesignUs() const;
- void updateLiveness();
-
- status_t play();
- status_t finishPlay();
- status_t pause();
-
- sp<IGraphicBufferProducer> getSurfaceTexture();
-
- void requestIDRFrame();
-
- enum {
- kWhatSessionDead,
- kWhatBinaryData,
- kWhatSessionEstablished,
- kWhatSessionDestroyed,
- };
-
-protected:
- virtual void onMessageReceived(const sp<AMessage> &msg);
- virtual ~PlaybackSession();
-
-private:
- struct Track;
-
- enum {
- kWhatMediaPullerNotify,
- kWhatConverterNotify,
- kWhatTrackNotify,
- kWhatUpdateSurface,
- kWhatPause,
- kWhatResume,
- kWhatMediaSenderNotify,
- kWhatPullExtractorSample,
- };
-
- String16 mOpPackageName;
-
- sp<ANetworkSession> mNetSession;
- sp<AMessage> mNotify;
- in_addr mInterfaceAddr;
- sp<IHDCP> mHDCP;
- AString mMediaPath;
-
- sp<MediaSender> mMediaSender;
- int32_t mLocalRTPPort;
-
- bool mWeAreDead;
- bool mPaused;
-
- int64_t mLastLifesignUs;
-
- sp<IGraphicBufferProducer> mProducer;
-
- KeyedVector<size_t, sp<Track> > mTracks;
- ssize_t mVideoTrackIndex;
-
- int64_t mPrevTimeUs;
-
- sp<NuMediaExtractor> mExtractor;
- KeyedVector<size_t, size_t> mExtractorTrackToInternalTrack;
- bool mPullExtractorPending;
- int32_t mPullExtractorGeneration;
- int64_t mFirstSampleTimeRealUs;
- int64_t mFirstSampleTimeUs;
-
- status_t setupMediaPacketizer(bool enableAudio, bool enableVideo);
-
- status_t setupPacketizer(
- bool enableAudio,
- bool usePCMAudio,
- bool enableVideo,
- VideoFormats::ResolutionType videoResolutionType,
- size_t videoResolutionIndex,
- VideoFormats::ProfileType videoProfileType,
- VideoFormats::LevelType videoLevelType);
-
- status_t addSource(
- bool isVideo,
- const sp<MediaSource> &source,
- bool isRepeaterSource,
- bool usePCMAudio,
- unsigned profileIdc,
- unsigned levelIdc,
- unsigned contraintSet,
- size_t *numInputBuffers);
-
- status_t addVideoSource(
- VideoFormats::ResolutionType videoResolutionType,
- size_t videoResolutionIndex,
- VideoFormats::ProfileType videoProfileType,
- VideoFormats::LevelType videoLevelType);
-
- status_t addAudioSource(bool usePCMAudio);
-
- status_t onMediaSenderInitialized();
-
- void notifySessionDead();
-
- void schedulePullExtractor();
- void onPullExtractor();
-
- void onSinkFeedback(const sp<AMessage> &msg);
-
- DISALLOW_EVIL_CONSTRUCTORS(PlaybackSession);
-};
-
-} // namespace android
-
-#endif // PLAYBACK_SESSION_H_
-
diff --git a/media/libstagefright/wifi-display/source/RepeaterSource.cpp b/media/libstagefright/wifi-display/source/RepeaterSource.cpp
deleted file mode 100644
index af6b663..0000000
--- a/media/libstagefright/wifi-display/source/RepeaterSource.cpp
+++ /dev/null
@@ -1,219 +0,0 @@
-//#define LOG_NDEBUG 0
-#define LOG_TAG "RepeaterSource"
-#include <utils/Log.h>
-
-#include "RepeaterSource.h"
-
-#include <media/stagefright/foundation/ADebug.h>
-#include <media/stagefright/foundation/ALooper.h>
-#include <media/stagefright/foundation/AMessage.h>
-#include <media/stagefright/MediaBuffer.h>
-#include <media/stagefright/MetaData.h>
-
-namespace android {
-
-RepeaterSource::RepeaterSource(const sp<MediaSource> &source, double rateHz)
- : mStarted(false),
- mSource(source),
- mRateHz(rateHz),
- mBuffer(NULL),
- mResult(OK),
- mLastBufferUpdateUs(-1ll),
- mStartTimeUs(-1ll),
- mFrameCount(0) {
-}
-
-RepeaterSource::~RepeaterSource() {
- CHECK(!mStarted);
-}
-
-double RepeaterSource::getFrameRate() const {
- return mRateHz;
-}
-
-void RepeaterSource::setFrameRate(double rateHz) {
- Mutex::Autolock autoLock(mLock);
-
- if (rateHz == mRateHz) {
- return;
- }
-
- if (mStartTimeUs >= 0ll) {
- int64_t nextTimeUs = mStartTimeUs + (mFrameCount * 1000000ll) / mRateHz;
- mStartTimeUs = nextTimeUs;
- mFrameCount = 0;
- }
- mRateHz = rateHz;
-}
-
-status_t RepeaterSource::start(MetaData *params) {
- CHECK(!mStarted);
-
- status_t err = mSource->start(params);
-
- if (err != OK) {
- return err;
- }
-
- mBuffer = NULL;
- mResult = OK;
- mStartTimeUs = -1ll;
- mFrameCount = 0;
-
- mLooper = new ALooper;
- mLooper->setName("repeater_looper");
- mLooper->start();
-
- mReflector = new AHandlerReflector<RepeaterSource>(this);
- mLooper->registerHandler(mReflector);
-
- postRead();
-
- mStarted = true;
-
- return OK;
-}
-
-status_t RepeaterSource::stop() {
- CHECK(mStarted);
-
- ALOGV("stopping");
-
- status_t err = mSource->stop();
-
- if (mLooper != NULL) {
- mLooper->stop();
- mLooper.clear();
-
- mReflector.clear();
- }
-
- if (mBuffer != NULL) {
- ALOGV("releasing mbuf %p", mBuffer);
- mBuffer->release();
- mBuffer = NULL;
- }
-
-
- ALOGV("stopped");
-
- mStarted = false;
-
- return err;
-}
-
-sp<MetaData> RepeaterSource::getFormat() {
- return mSource->getFormat();
-}
-
-status_t RepeaterSource::read(
- MediaBuffer **buffer, const ReadOptions *options) {
- int64_t seekTimeUs;
- ReadOptions::SeekMode seekMode;
- CHECK(options == NULL || !options->getSeekTo(&seekTimeUs, &seekMode));
-
- for (;;) {
- int64_t bufferTimeUs = -1ll;
-
- if (mStartTimeUs < 0ll) {
- Mutex::Autolock autoLock(mLock);
- while ((mLastBufferUpdateUs < 0ll || mBuffer == NULL)
- && mResult == OK) {
- mCondition.wait(mLock);
- }
-
- ALOGV("now resuming.");
- mStartTimeUs = ALooper::GetNowUs();
- bufferTimeUs = mStartTimeUs;
- } else {
- bufferTimeUs = mStartTimeUs + (mFrameCount * 1000000ll) / mRateHz;
-
- int64_t nowUs = ALooper::GetNowUs();
- int64_t delayUs = bufferTimeUs - nowUs;
-
- if (delayUs > 0ll) {
- usleep(delayUs);
- }
- }
-
- bool stale = false;
-
- {
- Mutex::Autolock autoLock(mLock);
- if (mResult != OK) {
- CHECK(mBuffer == NULL);
- return mResult;
- }
-
-#if SUSPEND_VIDEO_IF_IDLE
- int64_t nowUs = ALooper::GetNowUs();
- if (nowUs - mLastBufferUpdateUs > 1000000ll) {
- mLastBufferUpdateUs = -1ll;
- stale = true;
- } else
-#endif
- {
- mBuffer->add_ref();
- *buffer = mBuffer;
- (*buffer)->meta_data()->setInt64(kKeyTime, bufferTimeUs);
- ++mFrameCount;
- }
- }
-
- if (!stale) {
- break;
- }
-
- mStartTimeUs = -1ll;
- mFrameCount = 0;
- ALOGV("now dormant");
- }
-
- return OK;
-}
-
-void RepeaterSource::postRead() {
- (new AMessage(kWhatRead, mReflector))->post();
-}
-
-void RepeaterSource::onMessageReceived(const sp<AMessage> &msg) {
- switch (msg->what()) {
- case kWhatRead:
- {
- MediaBuffer *buffer;
- status_t err = mSource->read(&buffer);
-
- ALOGV("read mbuf %p", buffer);
-
- Mutex::Autolock autoLock(mLock);
- if (mBuffer != NULL) {
- mBuffer->release();
- mBuffer = NULL;
- }
- mBuffer = buffer;
- mResult = err;
- mLastBufferUpdateUs = ALooper::GetNowUs();
-
- mCondition.broadcast();
-
- if (err == OK) {
- postRead();
- }
- break;
- }
-
- default:
- TRESPASS();
- }
-}
-
-void RepeaterSource::wakeUp() {
- ALOGV("wakeUp");
- Mutex::Autolock autoLock(mLock);
- if (mLastBufferUpdateUs < 0ll && mBuffer != NULL) {
- mLastBufferUpdateUs = ALooper::GetNowUs();
- mCondition.broadcast();
- }
-}
-
-} // namespace android
diff --git a/media/libstagefright/wifi-display/source/RepeaterSource.h b/media/libstagefright/wifi-display/source/RepeaterSource.h
deleted file mode 100644
index 8d414fd..0000000
--- a/media/libstagefright/wifi-display/source/RepeaterSource.h
+++ /dev/null
@@ -1,67 +0,0 @@
-#ifndef REPEATER_SOURCE_H_
-
-#define REPEATER_SOURCE_H_
-
-#include <media/stagefright/foundation/ABase.h>
-#include <media/stagefright/foundation/AHandlerReflector.h>
-#include <media/stagefright/MediaSource.h>
-
-#define SUSPEND_VIDEO_IF_IDLE 0
-
-namespace android {
-
-// This MediaSource delivers frames at a constant rate by repeating buffers
-// if necessary.
-struct RepeaterSource : public MediaSource {
- RepeaterSource(const sp<MediaSource> &source, double rateHz);
-
- virtual status_t start(MetaData *params);
- virtual status_t stop();
- virtual sp<MetaData> getFormat();
-
- virtual status_t read(
- MediaBuffer **buffer, const ReadOptions *options);
-
- void onMessageReceived(const sp<AMessage> &msg);
-
- // If RepeaterSource is currently dormant, because SurfaceFlinger didn't
- // send updates in a while, this is its wakeup call.
- void wakeUp();
-
- double getFrameRate() const;
- void setFrameRate(double rateHz);
-
-protected:
- virtual ~RepeaterSource();
-
-private:
- enum {
- kWhatRead,
- };
-
- Mutex mLock;
- Condition mCondition;
-
- bool mStarted;
-
- sp<MediaSource> mSource;
- double mRateHz;
-
- sp<ALooper> mLooper;
- sp<AHandlerReflector<RepeaterSource> > mReflector;
-
- MediaBuffer *mBuffer;
- status_t mResult;
- int64_t mLastBufferUpdateUs;
-
- int64_t mStartTimeUs;
- int32_t mFrameCount;
-
- void postRead();
-
- DISALLOW_EVIL_CONSTRUCTORS(RepeaterSource);
-};
-
-} // namespace android
-
-#endif // REPEATER_SOURCE_H_
diff --git a/media/libstagefright/wifi-display/source/TSPacketizer.cpp b/media/libstagefright/wifi-display/source/TSPacketizer.cpp
deleted file mode 100644
index 865ba94..0000000
--- a/media/libstagefright/wifi-display/source/TSPacketizer.cpp
+++ /dev/null
@@ -1,1055 +0,0 @@
-/*
- * Copyright 2012, 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 "TSPacketizer"
-#include <utils/Log.h>
-
-#include "TSPacketizer.h"
-#include "include/avc_utils.h"
-
-#include <media/stagefright/foundation/ABuffer.h>
-#include <media/stagefright/foundation/ADebug.h>
-#include <media/stagefright/foundation/AMessage.h>
-#include <media/stagefright/foundation/hexdump.h>
-#include <media/stagefright/MediaDefs.h>
-#include <media/stagefright/MediaErrors.h>
-
-#include <arpa/inet.h>
-
-namespace android {
-
-struct TSPacketizer::Track : public RefBase {
- Track(const sp<AMessage> &format,
- unsigned PID, unsigned streamType, unsigned streamID);
-
- unsigned PID() const;
- unsigned streamType() const;
- unsigned streamID() const;
-
- // Returns the previous value.
- unsigned incrementContinuityCounter();
-
- bool isAudio() const;
- bool isVideo() const;
-
- bool isH264() const;
- bool isAAC() const;
- bool lacksADTSHeader() const;
- bool isPCMAudio() const;
-
- sp<ABuffer> prependCSD(const sp<ABuffer> &accessUnit) const;
- sp<ABuffer> prependADTSHeader(const sp<ABuffer> &accessUnit) const;
-
- size_t countDescriptors() const;
- sp<ABuffer> descriptorAt(size_t index) const;
-
- void finalize();
- void extractCSDIfNecessary();
-
-protected:
- virtual ~Track();
-
-private:
- sp<AMessage> mFormat;
-
- unsigned mPID;
- unsigned mStreamType;
- unsigned mStreamID;
- unsigned mContinuityCounter;
-
- AString mMIME;
- Vector<sp<ABuffer> > mCSD;
-
- Vector<sp<ABuffer> > mDescriptors;
-
- bool mAudioLacksATDSHeaders;
- bool mFinalized;
- bool mExtractedCSD;
-
- DISALLOW_EVIL_CONSTRUCTORS(Track);
-};
-
-TSPacketizer::Track::Track(
- const sp<AMessage> &format,
- unsigned PID, unsigned streamType, unsigned streamID)
- : mFormat(format),
- mPID(PID),
- mStreamType(streamType),
- mStreamID(streamID),
- mContinuityCounter(0),
- mAudioLacksATDSHeaders(false),
- mFinalized(false),
- mExtractedCSD(false) {
- CHECK(format->findString("mime", &mMIME));
-}
-
-void TSPacketizer::Track::extractCSDIfNecessary() {
- if (mExtractedCSD) {
- return;
- }
-
- if (!strcasecmp(mMIME.c_str(), MEDIA_MIMETYPE_VIDEO_AVC)
- || !strcasecmp(mMIME.c_str(), MEDIA_MIMETYPE_AUDIO_AAC)) {
- for (size_t i = 0;; ++i) {
- sp<ABuffer> csd;
- if (!mFormat->findBuffer(AStringPrintf("csd-%d", i).c_str(), &csd)) {
- break;
- }
-
- mCSD.push(csd);
- }
-
- if (!strcasecmp(mMIME.c_str(), MEDIA_MIMETYPE_AUDIO_AAC)) {
- int32_t isADTS;
- if (!mFormat->findInt32("is-adts", &isADTS) || isADTS == 0) {
- mAudioLacksATDSHeaders = true;
- }
- }
- }
-
- mExtractedCSD = true;
-}
-
-TSPacketizer::Track::~Track() {
-}
-
-unsigned TSPacketizer::Track::PID() const {
- return mPID;
-}
-
-unsigned TSPacketizer::Track::streamType() const {
- return mStreamType;
-}
-
-unsigned TSPacketizer::Track::streamID() const {
- return mStreamID;
-}
-
-unsigned TSPacketizer::Track::incrementContinuityCounter() {
- unsigned prevCounter = mContinuityCounter;
-
- if (++mContinuityCounter == 16) {
- mContinuityCounter = 0;
- }
-
- return prevCounter;
-}
-
-bool TSPacketizer::Track::isAudio() const {
- return !strncasecmp("audio/", mMIME.c_str(), 6);
-}
-
-bool TSPacketizer::Track::isVideo() const {
- return !strncasecmp("video/", mMIME.c_str(), 6);
-}
-
-bool TSPacketizer::Track::isH264() const {
- return !strcasecmp(mMIME.c_str(), MEDIA_MIMETYPE_VIDEO_AVC);
-}
-
-bool TSPacketizer::Track::isAAC() const {
- return !strcasecmp(mMIME.c_str(), MEDIA_MIMETYPE_AUDIO_AAC);
-}
-
-bool TSPacketizer::Track::isPCMAudio() const {
- return !strcasecmp(mMIME.c_str(), MEDIA_MIMETYPE_AUDIO_RAW);
-}
-
-bool TSPacketizer::Track::lacksADTSHeader() const {
- return mAudioLacksATDSHeaders;
-}
-
-sp<ABuffer> TSPacketizer::Track::prependCSD(
- const sp<ABuffer> &accessUnit) const {
- size_t size = 0;
- for (size_t i = 0; i < mCSD.size(); ++i) {
- size += mCSD.itemAt(i)->size();
- }
-
- sp<ABuffer> dup = new ABuffer(accessUnit->size() + size);
- size_t offset = 0;
- for (size_t i = 0; i < mCSD.size(); ++i) {
- const sp<ABuffer> &csd = mCSD.itemAt(i);
-
- memcpy(dup->data() + offset, csd->data(), csd->size());
- offset += csd->size();
- }
-
- memcpy(dup->data() + offset, accessUnit->data(), accessUnit->size());
-
- return dup;
-}
-
-sp<ABuffer> TSPacketizer::Track::prependADTSHeader(
- const sp<ABuffer> &accessUnit) const {
- CHECK_EQ(mCSD.size(), 1u);
-
- const uint8_t *codec_specific_data = mCSD.itemAt(0)->data();
-
- const uint32_t aac_frame_length = accessUnit->size() + 7;
-
- sp<ABuffer> dup = new ABuffer(aac_frame_length);
-
- unsigned profile = (codec_specific_data[0] >> 3) - 1;
-
- unsigned sampling_freq_index =
- ((codec_specific_data[0] & 7) << 1)
- | (codec_specific_data[1] >> 7);
-
- unsigned channel_configuration =
- (codec_specific_data[1] >> 3) & 0x0f;
-
- uint8_t *ptr = dup->data();
-
- *ptr++ = 0xff;
- *ptr++ = 0xf9; // b11111001, ID=1(MPEG-2), layer=0, protection_absent=1
-
- *ptr++ =
- profile << 6
- | sampling_freq_index << 2
- | ((channel_configuration >> 2) & 1); // private_bit=0
-
- // original_copy=0, home=0, copyright_id_bit=0, copyright_id_start=0
- *ptr++ =
- (channel_configuration & 3) << 6
- | aac_frame_length >> 11;
- *ptr++ = (aac_frame_length >> 3) & 0xff;
- *ptr++ = (aac_frame_length & 7) << 5;
-
- // adts_buffer_fullness=0, number_of_raw_data_blocks_in_frame=0
- *ptr++ = 0;
-
- memcpy(ptr, accessUnit->data(), accessUnit->size());
-
- return dup;
-}
-
-size_t TSPacketizer::Track::countDescriptors() const {
- return mDescriptors.size();
-}
-
-sp<ABuffer> TSPacketizer::Track::descriptorAt(size_t index) const {
- CHECK_LT(index, mDescriptors.size());
- return mDescriptors.itemAt(index);
-}
-
-void TSPacketizer::Track::finalize() {
- if (mFinalized) {
- return;
- }
-
- if (isH264()) {
- {
- // AVC video descriptor (40)
-
- sp<ABuffer> descriptor = new ABuffer(6);
- uint8_t *data = descriptor->data();
- data[0] = 40; // descriptor_tag
- data[1] = 4; // descriptor_length
-
- if (mCSD.size() > 0) {
- CHECK_GE(mCSD.size(), 1u);
- const sp<ABuffer> &sps = mCSD.itemAt(0);
- CHECK(!memcmp("\x00\x00\x00\x01", sps->data(), 4));
- CHECK_GE(sps->size(), 7u);
- // profile_idc, constraint_set*, level_idc
- memcpy(&data[2], sps->data() + 4, 3);
- } else {
- int32_t profileIdc, levelIdc, constraintSet;
- CHECK(mFormat->findInt32("profile-idc", &profileIdc));
- CHECK(mFormat->findInt32("level-idc", &levelIdc));
- CHECK(mFormat->findInt32("constraint-set", &constraintSet));
- CHECK_GE(profileIdc, 0);
- CHECK_GE(levelIdc, 0);
- data[2] = profileIdc; // profile_idc
- data[3] = constraintSet; // constraint_set*
- data[4] = levelIdc; // level_idc
- }
-
- // AVC_still_present=0, AVC_24_hour_picture_flag=0, reserved
- data[5] = 0x3f;
-
- mDescriptors.push_back(descriptor);
- }
-
- {
- // AVC timing and HRD descriptor (42)
-
- sp<ABuffer> descriptor = new ABuffer(4);
- uint8_t *data = descriptor->data();
- data[0] = 42; // descriptor_tag
- data[1] = 2; // descriptor_length
-
- // hrd_management_valid_flag = 0
- // reserved = 111111b
- // picture_and_timing_info_present = 0
-
- data[2] = 0x7e;
-
- // fixed_frame_rate_flag = 0
- // temporal_poc_flag = 0
- // picture_to_display_conversion_flag = 0
- // reserved = 11111b
- data[3] = 0x1f;
-
- mDescriptors.push_back(descriptor);
- }
- } else if (isPCMAudio()) {
- // LPCM audio stream descriptor (0x83)
-
- int32_t channelCount;
- CHECK(mFormat->findInt32("channel-count", &channelCount));
- CHECK_EQ(channelCount, 2);
-
- int32_t sampleRate;
- CHECK(mFormat->findInt32("sample-rate", &sampleRate));
- CHECK(sampleRate == 44100 || sampleRate == 48000);
-
- sp<ABuffer> descriptor = new ABuffer(4);
- uint8_t *data = descriptor->data();
- data[0] = 0x83; // descriptor_tag
- data[1] = 2; // descriptor_length
-
- unsigned sampling_frequency = (sampleRate == 44100) ? 1 : 2;
-
- data[2] = (sampling_frequency << 5)
- | (3 /* reserved */ << 1)
- | 0 /* emphasis_flag */;
-
- data[3] =
- (1 /* number_of_channels = stereo */ << 5)
- | 0xf /* reserved */;
-
- mDescriptors.push_back(descriptor);
- }
-
- mFinalized = true;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-
-TSPacketizer::TSPacketizer(uint32_t flags)
- : mFlags(flags),
- mPATContinuityCounter(0),
- mPMTContinuityCounter(0) {
- initCrcTable();
-
- if (flags & (EMIT_HDCP20_DESCRIPTOR | EMIT_HDCP21_DESCRIPTOR)) {
- int32_t hdcpVersion;
- if (flags & EMIT_HDCP20_DESCRIPTOR) {
- CHECK(!(flags & EMIT_HDCP21_DESCRIPTOR));
- hdcpVersion = 0x20;
- } else {
- CHECK(!(flags & EMIT_HDCP20_DESCRIPTOR));
-
- // HDCP2.0 _and_ HDCP 2.1 specs say to set the version
- // inside the HDCP descriptor to 0x20!!!
- hdcpVersion = 0x20;
- }
-
- // HDCP descriptor
- sp<ABuffer> descriptor = new ABuffer(7);
- uint8_t *data = descriptor->data();
- data[0] = 0x05; // descriptor_tag
- data[1] = 5; // descriptor_length
- data[2] = 'H';
- data[3] = 'D';
- data[4] = 'C';
- data[5] = 'P';
- data[6] = hdcpVersion;
-
- mProgramInfoDescriptors.push_back(descriptor);
- }
-}
-
-TSPacketizer::~TSPacketizer() {
-}
-
-ssize_t TSPacketizer::addTrack(const sp<AMessage> &format) {
- AString mime;
- CHECK(format->findString("mime", &mime));
-
- unsigned PIDStart;
- bool isVideo = !strncasecmp("video/", mime.c_str(), 6);
- bool isAudio = !strncasecmp("audio/", mime.c_str(), 6);
-
- if (isVideo) {
- PIDStart = 0x1011;
- } else if (isAudio) {
- PIDStart = 0x1100;
- } else {
- return ERROR_UNSUPPORTED;
- }
-
- unsigned streamType;
- unsigned streamIDStart;
- unsigned streamIDStop;
-
- if (!strcasecmp(mime.c_str(), MEDIA_MIMETYPE_VIDEO_AVC)) {
- streamType = 0x1b;
- streamIDStart = 0xe0;
- streamIDStop = 0xef;
- } else if (!strcasecmp(mime.c_str(), MEDIA_MIMETYPE_AUDIO_AAC)) {
- streamType = 0x0f;
- streamIDStart = 0xc0;
- streamIDStop = 0xdf;
- } else if (!strcasecmp(mime.c_str(), MEDIA_MIMETYPE_AUDIO_RAW)) {
- streamType = 0x83;
- streamIDStart = 0xbd;
- streamIDStop = 0xbd;
- } else {
- return ERROR_UNSUPPORTED;
- }
-
- size_t numTracksOfThisType = 0;
- unsigned PID = PIDStart;
-
- for (size_t i = 0; i < mTracks.size(); ++i) {
- const sp<Track> &track = mTracks.itemAt(i);
-
- if (track->streamType() == streamType) {
- ++numTracksOfThisType;
- }
-
- if ((isAudio && track->isAudio()) || (isVideo && track->isVideo())) {
- ++PID;
- }
- }
-
- unsigned streamID = streamIDStart + numTracksOfThisType;
- if (streamID > streamIDStop) {
- return -ERANGE;
- }
-
- sp<Track> track = new Track(format, PID, streamType, streamID);
- return mTracks.add(track);
-}
-
-status_t TSPacketizer::extractCSDIfNecessary(size_t trackIndex) {
- if (trackIndex >= mTracks.size()) {
- return -ERANGE;
- }
-
- const sp<Track> &track = mTracks.itemAt(trackIndex);
- track->extractCSDIfNecessary();
-
- return OK;
-}
-
-status_t TSPacketizer::packetize(
- size_t trackIndex,
- const sp<ABuffer> &_accessUnit,
- sp<ABuffer> *packets,
- uint32_t flags,
- const uint8_t *PES_private_data, size_t PES_private_data_len,
- size_t numStuffingBytes) {
- sp<ABuffer> accessUnit = _accessUnit;
-
- int64_t timeUs;
- CHECK(accessUnit->meta()->findInt64("timeUs", &timeUs));
-
- packets->clear();
-
- if (trackIndex >= mTracks.size()) {
- return -ERANGE;
- }
-
- const sp<Track> &track = mTracks.itemAt(trackIndex);
-
- if (track->isH264() && (flags & PREPEND_SPS_PPS_TO_IDR_FRAMES)
- && IsIDR(accessUnit)) {
- // prepend codec specific data, i.e. SPS and PPS.
- accessUnit = track->prependCSD(accessUnit);
- } else if (track->isAAC() && track->lacksADTSHeader()) {
- CHECK(!(flags & IS_ENCRYPTED));
- accessUnit = track->prependADTSHeader(accessUnit);
- }
-
- // 0x47
- // transport_error_indicator = b0
- // payload_unit_start_indicator = b1
- // transport_priority = b0
- // PID
- // transport_scrambling_control = b00
- // adaptation_field_control = b??
- // continuity_counter = b????
- // -- payload follows
- // packet_startcode_prefix = 0x000001
- // stream_id
- // PES_packet_length = 0x????
- // reserved = b10
- // PES_scrambling_control = b00
- // PES_priority = b0
- // data_alignment_indicator = b1
- // copyright = b0
- // original_or_copy = b0
- // PTS_DTS_flags = b10 (PTS only)
- // ESCR_flag = b0
- // ES_rate_flag = b0
- // DSM_trick_mode_flag = b0
- // additional_copy_info_flag = b0
- // PES_CRC_flag = b0
- // PES_extension_flag = b0
- // PES_header_data_length = 0x05
- // reserved = b0010 (PTS)
- // PTS[32..30] = b???
- // reserved = b1
- // PTS[29..15] = b??? ???? ???? ???? (15 bits)
- // reserved = b1
- // PTS[14..0] = b??? ???? ???? ???? (15 bits)
- // reserved = b1
- // the first fragment of "buffer" follows
-
- // Each transport packet (except for the last one contributing to the PES
- // payload) must contain a multiple of 16 bytes of payload per HDCP spec.
- bool alignPayload =
- (mFlags & (EMIT_HDCP20_DESCRIPTOR | EMIT_HDCP21_DESCRIPTOR));
-
- /*
- a) The very first PES transport stream packet contains
-
- 4 bytes of TS header
- ... padding
- 14 bytes of static PES header
- PES_private_data_len + 1 bytes (only if PES_private_data_len > 0)
- numStuffingBytes bytes
-
- followed by the payload
-
- b) Subsequent PES transport stream packets contain
-
- 4 bytes of TS header
- ... padding
-
- followed by the payload
- */
-
- size_t PES_packet_length = accessUnit->size() + 8 + numStuffingBytes;
- if (PES_private_data_len > 0) {
- PES_packet_length += PES_private_data_len + 1;
- }
-
- size_t numTSPackets = 1;
-
- {
- // Make sure the PES header fits into a single TS packet:
- size_t PES_header_size = 14 + numStuffingBytes;
- if (PES_private_data_len > 0) {
- PES_header_size += PES_private_data_len + 1;
- }
-
- CHECK_LE(PES_header_size, 188u - 4u);
-
- size_t sizeAvailableForPayload = 188 - 4 - PES_header_size;
- size_t numBytesOfPayload = accessUnit->size();
-
- if (numBytesOfPayload > sizeAvailableForPayload) {
- numBytesOfPayload = sizeAvailableForPayload;
-
- if (alignPayload && numBytesOfPayload > 16) {
- numBytesOfPayload -= (numBytesOfPayload % 16);
- }
- }
-
- size_t numPaddingBytes = sizeAvailableForPayload - numBytesOfPayload;
- ALOGV("packet 1 contains %zd padding bytes and %zd bytes of payload",
- numPaddingBytes, numBytesOfPayload);
-
- size_t numBytesOfPayloadRemaining = accessUnit->size() - numBytesOfPayload;
-
-#if 0
- // The following hopefully illustrates the logic that led to the
- // more efficient computation in the #else block...
-
- while (numBytesOfPayloadRemaining > 0) {
- size_t sizeAvailableForPayload = 188 - 4;
-
- size_t numBytesOfPayload = numBytesOfPayloadRemaining;
-
- if (numBytesOfPayload > sizeAvailableForPayload) {
- numBytesOfPayload = sizeAvailableForPayload;
-
- if (alignPayload && numBytesOfPayload > 16) {
- numBytesOfPayload -= (numBytesOfPayload % 16);
- }
- }
-
- size_t numPaddingBytes = sizeAvailableForPayload - numBytesOfPayload;
- ALOGI("packet %zd contains %zd padding bytes and %zd bytes of payload",
- numTSPackets + 1, numPaddingBytes, numBytesOfPayload);
-
- numBytesOfPayloadRemaining -= numBytesOfPayload;
- ++numTSPackets;
- }
-#else
- // This is how many bytes of payload each subsequent TS packet
- // can contain at most.
- sizeAvailableForPayload = 188 - 4;
- size_t sizeAvailableForAlignedPayload = sizeAvailableForPayload;
- if (alignPayload) {
- // We're only going to use a subset of the available space
- // since we need to make each fragment a multiple of 16 in size.
- sizeAvailableForAlignedPayload -=
- (sizeAvailableForAlignedPayload % 16);
- }
-
- size_t numFullTSPackets =
- numBytesOfPayloadRemaining / sizeAvailableForAlignedPayload;
-
- numTSPackets += numFullTSPackets;
-
- numBytesOfPayloadRemaining -=
- numFullTSPackets * sizeAvailableForAlignedPayload;
-
- // numBytesOfPayloadRemaining < sizeAvailableForAlignedPayload
- if (numFullTSPackets == 0 && numBytesOfPayloadRemaining > 0) {
- // There wasn't enough payload left to form a full aligned payload,
- // the last packet doesn't have to be aligned.
- ++numTSPackets;
- } else if (numFullTSPackets > 0
- && numBytesOfPayloadRemaining
- + sizeAvailableForAlignedPayload > sizeAvailableForPayload) {
- // The last packet emitted had a full aligned payload and together
- // with the bytes remaining does exceed the unaligned payload
- // size, so we need another packet.
- ++numTSPackets;
- }
-#endif
- }
-
- if (flags & EMIT_PAT_AND_PMT) {
- numTSPackets += 2;
- }
-
- if (flags & EMIT_PCR) {
- ++numTSPackets;
- }
-
- sp<ABuffer> buffer = new ABuffer(numTSPackets * 188);
- uint8_t *packetDataStart = buffer->data();
-
- if (flags & EMIT_PAT_AND_PMT) {
- // Program Association Table (PAT):
- // 0x47
- // transport_error_indicator = b0
- // payload_unit_start_indicator = b1
- // transport_priority = b0
- // PID = b0000000000000 (13 bits)
- // transport_scrambling_control = b00
- // adaptation_field_control = b01 (no adaptation field, payload only)
- // continuity_counter = b????
- // skip = 0x00
- // --- payload follows
- // table_id = 0x00
- // section_syntax_indicator = b1
- // must_be_zero = b0
- // reserved = b11
- // section_length = 0x00d
- // transport_stream_id = 0x0000
- // reserved = b11
- // version_number = b00001
- // current_next_indicator = b1
- // section_number = 0x00
- // last_section_number = 0x00
- // one program follows:
- // program_number = 0x0001
- // reserved = b111
- // program_map_PID = kPID_PMT (13 bits!)
- // CRC = 0x????????
-
- if (++mPATContinuityCounter == 16) {
- mPATContinuityCounter = 0;
- }
-
- uint8_t *ptr = packetDataStart;
- *ptr++ = 0x47;
- *ptr++ = 0x40;
- *ptr++ = 0x00;
- *ptr++ = 0x10 | mPATContinuityCounter;
- *ptr++ = 0x00;
-
- uint8_t *crcDataStart = ptr;
- *ptr++ = 0x00;
- *ptr++ = 0xb0;
- *ptr++ = 0x0d;
- *ptr++ = 0x00;
- *ptr++ = 0x00;
- *ptr++ = 0xc3;
- *ptr++ = 0x00;
- *ptr++ = 0x00;
- *ptr++ = 0x00;
- *ptr++ = 0x01;
- *ptr++ = 0xe0 | (kPID_PMT >> 8);
- *ptr++ = kPID_PMT & 0xff;
-
- CHECK_EQ(ptr - crcDataStart, 12);
- uint32_t crc = htonl(crc32(crcDataStart, ptr - crcDataStart));
- memcpy(ptr, &crc, 4);
- ptr += 4;
-
- size_t sizeLeft = packetDataStart + 188 - ptr;
- memset(ptr, 0xff, sizeLeft);
-
- packetDataStart += 188;
-
- // Program Map (PMT):
- // 0x47
- // transport_error_indicator = b0
- // payload_unit_start_indicator = b1
- // transport_priority = b0
- // PID = kPID_PMT (13 bits)
- // transport_scrambling_control = b00
- // adaptation_field_control = b01 (no adaptation field, payload only)
- // continuity_counter = b????
- // skip = 0x00
- // -- payload follows
- // table_id = 0x02
- // section_syntax_indicator = b1
- // must_be_zero = b0
- // reserved = b11
- // section_length = 0x???
- // program_number = 0x0001
- // reserved = b11
- // version_number = b00001
- // current_next_indicator = b1
- // section_number = 0x00
- // last_section_number = 0x00
- // reserved = b111
- // PCR_PID = kPCR_PID (13 bits)
- // reserved = b1111
- // program_info_length = 0x???
- // program_info_descriptors follow
- // one or more elementary stream descriptions follow:
- // stream_type = 0x??
- // reserved = b111
- // elementary_PID = b? ???? ???? ???? (13 bits)
- // reserved = b1111
- // ES_info_length = 0x000
- // CRC = 0x????????
-
- if (++mPMTContinuityCounter == 16) {
- mPMTContinuityCounter = 0;
- }
-
- ptr = packetDataStart;
- *ptr++ = 0x47;
- *ptr++ = 0x40 | (kPID_PMT >> 8);
- *ptr++ = kPID_PMT & 0xff;
- *ptr++ = 0x10 | mPMTContinuityCounter;
- *ptr++ = 0x00;
-
- crcDataStart = ptr;
- *ptr++ = 0x02;
-
- *ptr++ = 0x00; // section_length to be filled in below.
- *ptr++ = 0x00;
-
- *ptr++ = 0x00;
- *ptr++ = 0x01;
- *ptr++ = 0xc3;
- *ptr++ = 0x00;
- *ptr++ = 0x00;
- *ptr++ = 0xe0 | (kPID_PCR >> 8);
- *ptr++ = kPID_PCR & 0xff;
-
- size_t program_info_length = 0;
- for (size_t i = 0; i < mProgramInfoDescriptors.size(); ++i) {
- program_info_length += mProgramInfoDescriptors.itemAt(i)->size();
- }
-
- CHECK_LT(program_info_length, 0x400u);
- *ptr++ = 0xf0 | (program_info_length >> 8);
- *ptr++ = (program_info_length & 0xff);
-
- for (size_t i = 0; i < mProgramInfoDescriptors.size(); ++i) {
- const sp<ABuffer> &desc = mProgramInfoDescriptors.itemAt(i);
- memcpy(ptr, desc->data(), desc->size());
- ptr += desc->size();
- }
-
- for (size_t i = 0; i < mTracks.size(); ++i) {
- const sp<Track> &track = mTracks.itemAt(i);
-
- // Make sure all the decriptors have been added.
- track->finalize();
-
- *ptr++ = track->streamType();
- *ptr++ = 0xe0 | (track->PID() >> 8);
- *ptr++ = track->PID() & 0xff;
-
- size_t ES_info_length = 0;
- for (size_t i = 0; i < track->countDescriptors(); ++i) {
- ES_info_length += track->descriptorAt(i)->size();
- }
- CHECK_LE(ES_info_length, 0xfffu);
-
- *ptr++ = 0xf0 | (ES_info_length >> 8);
- *ptr++ = (ES_info_length & 0xff);
-
- for (size_t i = 0; i < track->countDescriptors(); ++i) {
- const sp<ABuffer> &descriptor = track->descriptorAt(i);
- memcpy(ptr, descriptor->data(), descriptor->size());
- ptr += descriptor->size();
- }
- }
-
- size_t section_length = ptr - (crcDataStart + 3) + 4 /* CRC */;
-
- crcDataStart[1] = 0xb0 | (section_length >> 8);
- crcDataStart[2] = section_length & 0xff;
-
- crc = htonl(crc32(crcDataStart, ptr - crcDataStart));
- memcpy(ptr, &crc, 4);
- ptr += 4;
-
- sizeLeft = packetDataStart + 188 - ptr;
- memset(ptr, 0xff, sizeLeft);
-
- packetDataStart += 188;
- }
-
- if (flags & EMIT_PCR) {
- // PCR stream
- // 0x47
- // transport_error_indicator = b0
- // payload_unit_start_indicator = b1
- // transport_priority = b0
- // PID = kPCR_PID (13 bits)
- // transport_scrambling_control = b00
- // adaptation_field_control = b10 (adaptation field only, no payload)
- // continuity_counter = b0000 (does not increment)
- // adaptation_field_length = 183
- // discontinuity_indicator = b0
- // random_access_indicator = b0
- // elementary_stream_priority_indicator = b0
- // PCR_flag = b1
- // OPCR_flag = b0
- // splicing_point_flag = b0
- // transport_private_data_flag = b0
- // adaptation_field_extension_flag = b0
- // program_clock_reference_base = b?????????????????????????????????
- // reserved = b111111
- // program_clock_reference_extension = b?????????
-
- int64_t nowUs = ALooper::GetNowUs();
-
- uint64_t PCR = nowUs * 27; // PCR based on a 27MHz clock
- uint64_t PCR_base = PCR / 300;
- uint32_t PCR_ext = PCR % 300;
-
- uint8_t *ptr = packetDataStart;
- *ptr++ = 0x47;
- *ptr++ = 0x40 | (kPID_PCR >> 8);
- *ptr++ = kPID_PCR & 0xff;
- *ptr++ = 0x20;
- *ptr++ = 0xb7; // adaptation_field_length
- *ptr++ = 0x10;
- *ptr++ = (PCR_base >> 25) & 0xff;
- *ptr++ = (PCR_base >> 17) & 0xff;
- *ptr++ = (PCR_base >> 9) & 0xff;
- *ptr++ = ((PCR_base & 1) << 7) | 0x7e | ((PCR_ext >> 8) & 1);
- *ptr++ = (PCR_ext & 0xff);
-
- size_t sizeLeft = packetDataStart + 188 - ptr;
- memset(ptr, 0xff, sizeLeft);
-
- packetDataStart += 188;
- }
-
- uint64_t PTS = (timeUs * 9ll) / 100ll;
-
- if (PES_packet_length >= 65536) {
- // This really should only happen for video.
- CHECK(track->isVideo());
-
- // It's valid to set this to 0 for video according to the specs.
- PES_packet_length = 0;
- }
-
- size_t sizeAvailableForPayload = 188 - 4 - 14 - numStuffingBytes;
- if (PES_private_data_len > 0) {
- sizeAvailableForPayload -= PES_private_data_len + 1;
- }
-
- size_t copy = accessUnit->size();
-
- if (copy > sizeAvailableForPayload) {
- copy = sizeAvailableForPayload;
-
- if (alignPayload && copy > 16) {
- copy -= (copy % 16);
- }
- }
-
- size_t numPaddingBytes = sizeAvailableForPayload - copy;
-
- uint8_t *ptr = packetDataStart;
- *ptr++ = 0x47;
- *ptr++ = 0x40 | (track->PID() >> 8);
- *ptr++ = track->PID() & 0xff;
-
- *ptr++ = (numPaddingBytes > 0 ? 0x30 : 0x10)
- | track->incrementContinuityCounter();
-
- if (numPaddingBytes > 0) {
- *ptr++ = numPaddingBytes - 1;
- if (numPaddingBytes >= 2) {
- *ptr++ = 0x00;
- memset(ptr, 0xff, numPaddingBytes - 2);
- ptr += numPaddingBytes - 2;
- }
- }
-
- *ptr++ = 0x00;
- *ptr++ = 0x00;
- *ptr++ = 0x01;
- *ptr++ = track->streamID();
- *ptr++ = PES_packet_length >> 8;
- *ptr++ = PES_packet_length & 0xff;
- *ptr++ = 0x84;
- *ptr++ = (PES_private_data_len > 0) ? 0x81 : 0x80;
-
- size_t headerLength = 0x05 + numStuffingBytes;
- if (PES_private_data_len > 0) {
- headerLength += 1 + PES_private_data_len;
- }
-
- *ptr++ = headerLength;
-
- *ptr++ = 0x20 | (((PTS >> 30) & 7) << 1) | 1;
- *ptr++ = (PTS >> 22) & 0xff;
- *ptr++ = (((PTS >> 15) & 0x7f) << 1) | 1;
- *ptr++ = (PTS >> 7) & 0xff;
- *ptr++ = ((PTS & 0x7f) << 1) | 1;
-
- if (PES_private_data_len > 0) {
- *ptr++ = 0x8e; // PES_private_data_flag, reserved.
- memcpy(ptr, PES_private_data, PES_private_data_len);
- ptr += PES_private_data_len;
- }
-
- for (size_t i = 0; i < numStuffingBytes; ++i) {
- *ptr++ = 0xff;
- }
-
- memcpy(ptr, accessUnit->data(), copy);
- ptr += copy;
-
- CHECK_EQ(ptr, packetDataStart + 188);
- packetDataStart += 188;
-
- size_t offset = copy;
- while (offset < accessUnit->size()) {
- // for subsequent fragments of "buffer":
- // 0x47
- // transport_error_indicator = b0
- // payload_unit_start_indicator = b0
- // transport_priority = b0
- // PID = b0 0001 1110 ???? (13 bits) [0x1e0 + 1 + sourceIndex]
- // transport_scrambling_control = b00
- // adaptation_field_control = b??
- // continuity_counter = b????
- // the fragment of "buffer" follows.
-
- size_t sizeAvailableForPayload = 188 - 4;
-
- size_t copy = accessUnit->size() - offset;
-
- if (copy > sizeAvailableForPayload) {
- copy = sizeAvailableForPayload;
-
- if (alignPayload && copy > 16) {
- copy -= (copy % 16);
- }
- }
-
- size_t numPaddingBytes = sizeAvailableForPayload - copy;
-
- uint8_t *ptr = packetDataStart;
- *ptr++ = 0x47;
- *ptr++ = 0x00 | (track->PID() >> 8);
- *ptr++ = track->PID() & 0xff;
-
- *ptr++ = (numPaddingBytes > 0 ? 0x30 : 0x10)
- | track->incrementContinuityCounter();
-
- if (numPaddingBytes > 0) {
- *ptr++ = numPaddingBytes - 1;
- if (numPaddingBytes >= 2) {
- *ptr++ = 0x00;
- memset(ptr, 0xff, numPaddingBytes - 2);
- ptr += numPaddingBytes - 2;
- }
- }
-
- memcpy(ptr, accessUnit->data() + offset, copy);
- ptr += copy;
- CHECK_EQ(ptr, packetDataStart + 188);
-
- offset += copy;
- packetDataStart += 188;
- }
-
- CHECK(packetDataStart == buffer->data() + buffer->capacity());
-
- *packets = buffer;
-
- return OK;
-}
-
-void TSPacketizer::initCrcTable() {
- uint32_t poly = 0x04C11DB7;
-
- for (int i = 0; i < 256; i++) {
- uint32_t crc = i << 24;
- for (int j = 0; j < 8; j++) {
- crc = (crc << 1) ^ ((crc & 0x80000000) ? (poly) : 0);
- }
- mCrcTable[i] = crc;
- }
-}
-
-uint32_t TSPacketizer::crc32(const uint8_t *start, size_t size) const {
- uint32_t crc = 0xFFFFFFFF;
- const uint8_t *p;
-
- for (p = start; p < start + size; ++p) {
- crc = (crc << 8) ^ mCrcTable[((crc >> 24) ^ *p) & 0xFF];
- }
-
- return crc;
-}
-
-sp<ABuffer> TSPacketizer::prependCSD(
- size_t trackIndex, const sp<ABuffer> &accessUnit) const {
- CHECK_LT(trackIndex, mTracks.size());
-
- const sp<Track> &track = mTracks.itemAt(trackIndex);
- CHECK(track->isH264() && IsIDR(accessUnit));
-
- int64_t timeUs;
- CHECK(accessUnit->meta()->findInt64("timeUs", &timeUs));
-
- sp<ABuffer> accessUnit2 = track->prependCSD(accessUnit);
-
- accessUnit2->meta()->setInt64("timeUs", timeUs);
-
- return accessUnit2;
-}
-
-} // namespace android
-
diff --git a/media/libstagefright/wifi-display/source/TSPacketizer.h b/media/libstagefright/wifi-display/source/TSPacketizer.h
deleted file mode 100644
index 0dcb179..0000000
--- a/media/libstagefright/wifi-display/source/TSPacketizer.h
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- * Copyright 2012, 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 TS_PACKETIZER_H_
-
-#define TS_PACKETIZER_H_
-
-#include <media/stagefright/foundation/ABase.h>
-#include <utils/Errors.h>
-#include <utils/RefBase.h>
-#include <utils/Vector.h>
-
-namespace android {
-
-struct ABuffer;
-struct AMessage;
-
-// Forms the packets of a transport stream given access units.
-// Emits metadata tables (PAT and PMT) and timestamp stream (PCR) based
-// on flags.
-struct TSPacketizer : public RefBase {
- enum {
- EMIT_HDCP20_DESCRIPTOR = 1,
- EMIT_HDCP21_DESCRIPTOR = 2,
- };
- explicit TSPacketizer(uint32_t flags);
-
- // Returns trackIndex or error.
- ssize_t addTrack(const sp<AMessage> &format);
-
- enum {
- EMIT_PAT_AND_PMT = 1,
- EMIT_PCR = 2,
- IS_ENCRYPTED = 4,
- PREPEND_SPS_PPS_TO_IDR_FRAMES = 8,
- };
- status_t packetize(
- size_t trackIndex, const sp<ABuffer> &accessUnit,
- sp<ABuffer> *packets,
- uint32_t flags,
- const uint8_t *PES_private_data, size_t PES_private_data_len,
- size_t numStuffingBytes = 0);
-
- status_t extractCSDIfNecessary(size_t trackIndex);
-
- // XXX to be removed once encoder config option takes care of this for
- // encrypted mode.
- sp<ABuffer> prependCSD(
- size_t trackIndex, const sp<ABuffer> &accessUnit) const;
-
-protected:
- virtual ~TSPacketizer();
-
-private:
- enum {
- kPID_PMT = 0x100,
- kPID_PCR = 0x1000,
- };
-
- struct Track;
-
- uint32_t mFlags;
- Vector<sp<Track> > mTracks;
-
- Vector<sp<ABuffer> > mProgramInfoDescriptors;
-
- unsigned mPATContinuityCounter;
- unsigned mPMTContinuityCounter;
-
- uint32_t mCrcTable[256];
-
- void initCrcTable();
- uint32_t crc32(const uint8_t *start, size_t size) const;
-
- DISALLOW_EVIL_CONSTRUCTORS(TSPacketizer);
-};
-
-} // namespace android
-
-#endif // TS_PACKETIZER_H_
-
diff --git a/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp b/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp
deleted file mode 100644
index 4695e5d..0000000
--- a/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp
+++ /dev/null
@@ -1,1737 +0,0 @@
-/*
- * Copyright 2012, 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 "WifiDisplaySource"
-#include <utils/Log.h>
-
-#include "WifiDisplaySource.h"
-#include "PlaybackSession.h"
-#include "Parameters.h"
-#include "rtp/RTPSender.h"
-
-#include <binder/IServiceManager.h>
-#include <gui/IGraphicBufferProducer.h>
-#include <media/IHDCP.h>
-#include <media/IMediaPlayerService.h>
-#include <media/IRemoteDisplayClient.h>
-#include <media/stagefright/foundation/ABuffer.h>
-#include <media/stagefright/foundation/ADebug.h>
-#include <media/stagefright/foundation/AMessage.h>
-#include <media/stagefright/foundation/ParsedMessage.h>
-#include <media/stagefright/MediaErrors.h>
-#include <media/stagefright/Utils.h>
-
-#include <arpa/inet.h>
-#include <cutils/properties.h>
-
-#include <ctype.h>
-
-namespace android {
-
-// static
-const int64_t WifiDisplaySource::kReaperIntervalUs;
-const int64_t WifiDisplaySource::kTeardownTriggerTimeouSecs;
-const int64_t WifiDisplaySource::kPlaybackSessionTimeoutSecs;
-const int64_t WifiDisplaySource::kPlaybackSessionTimeoutUs;
-const AString WifiDisplaySource::sUserAgent = MakeUserAgent();
-
-WifiDisplaySource::WifiDisplaySource(
- const String16 &opPackageName,
- const sp<ANetworkSession> &netSession,
- const sp<IRemoteDisplayClient> &client,
- const char *path)
- : mOpPackageName(opPackageName),
- mState(INITIALIZED),
- mNetSession(netSession),
- mClient(client),
- mSessionID(0),
- mStopReplyID(NULL),
- mChosenRTPPort(-1),
- mUsingPCMAudio(false),
- mClientSessionID(0),
- mReaperPending(false),
- mNextCSeq(1),
- mUsingHDCP(false),
- mIsHDCP2_0(false),
- mHDCPPort(0),
- mHDCPInitializationComplete(false),
- mSetupTriggerDeferred(false),
- mPlaybackSessionEstablished(false) {
- if (path != NULL) {
- mMediaPath.setTo(path);
- }
-
- mSupportedSourceVideoFormats.disableAll();
-
- mSupportedSourceVideoFormats.setNativeResolution(
- VideoFormats::RESOLUTION_CEA, 5); // 1280x720 p30
-
- // Enable all resolutions up to 1280x720p30
- mSupportedSourceVideoFormats.enableResolutionUpto(
- VideoFormats::RESOLUTION_CEA, 5,
- VideoFormats::PROFILE_CHP, // Constrained High Profile
- VideoFormats::LEVEL_32); // Level 3.2
-}
-
-WifiDisplaySource::~WifiDisplaySource() {
-}
-
-static status_t PostAndAwaitResponse(
- const sp<AMessage> &msg, sp<AMessage> *response) {
- status_t err = msg->postAndAwaitResponse(response);
-
- if (err != OK) {
- return err;
- }
-
- if (response == NULL || !(*response)->findInt32("err", &err)) {
- err = OK;
- }
-
- return err;
-}
-
-status_t WifiDisplaySource::start(const char *iface) {
- CHECK_EQ(mState, INITIALIZED);
-
- sp<AMessage> msg = new AMessage(kWhatStart, this);
- msg->setString("iface", iface);
-
- sp<AMessage> response;
- return PostAndAwaitResponse(msg, &response);
-}
-
-status_t WifiDisplaySource::stop() {
- sp<AMessage> msg = new AMessage(kWhatStop, this);
-
- sp<AMessage> response;
- return PostAndAwaitResponse(msg, &response);
-}
-
-status_t WifiDisplaySource::pause() {
- sp<AMessage> msg = new AMessage(kWhatPause, this);
-
- sp<AMessage> response;
- return PostAndAwaitResponse(msg, &response);
-}
-
-status_t WifiDisplaySource::resume() {
- sp<AMessage> msg = new AMessage(kWhatResume, this);
-
- sp<AMessage> response;
- return PostAndAwaitResponse(msg, &response);
-}
-
-void WifiDisplaySource::onMessageReceived(const sp<AMessage> &msg) {
- switch (msg->what()) {
- case kWhatStart:
- {
- sp<AReplyToken> replyID;
- CHECK(msg->senderAwaitsResponse(&replyID));
-
- AString iface;
- CHECK(msg->findString("iface", &iface));
-
- status_t err = OK;
-
- ssize_t colonPos = iface.find(":");
-
- unsigned long port;
-
- if (colonPos >= 0) {
- const char *s = iface.c_str() + colonPos + 1;
-
- char *end;
- port = strtoul(s, &end, 10);
-
- if (end == s || *end != '\0' || port > 65535) {
- err = -EINVAL;
- } else {
- iface.erase(colonPos, iface.size() - colonPos);
- }
- } else {
- port = kWifiDisplayDefaultPort;
- }
-
- if (err == OK) {
- if (inet_aton(iface.c_str(), &mInterfaceAddr) != 0) {
- sp<AMessage> notify = new AMessage(kWhatRTSPNotify, this);
-
- err = mNetSession->createRTSPServer(
- mInterfaceAddr, port, notify, &mSessionID);
- } else {
- err = -EINVAL;
- }
- }
-
- mState = AWAITING_CLIENT_CONNECTION;
-
- sp<AMessage> response = new AMessage;
- response->setInt32("err", err);
- response->postReply(replyID);
- break;
- }
-
- case kWhatRTSPNotify:
- {
- int32_t reason;
- CHECK(msg->findInt32("reason", &reason));
-
- switch (reason) {
- case ANetworkSession::kWhatError:
- {
- int32_t sessionID;
- CHECK(msg->findInt32("sessionID", &sessionID));
-
- int32_t err;
- CHECK(msg->findInt32("err", &err));
-
- AString detail;
- CHECK(msg->findString("detail", &detail));
-
- ALOGE("An error occurred in session %d (%d, '%s/%s').",
- sessionID,
- err,
- detail.c_str(),
- strerror(-err));
-
- mNetSession->destroySession(sessionID);
-
- if (sessionID == mClientSessionID) {
- mClientSessionID = 0;
-
- mClient->onDisplayError(
- IRemoteDisplayClient::kDisplayErrorUnknown);
- }
- break;
- }
-
- case ANetworkSession::kWhatClientConnected:
- {
- int32_t sessionID;
- CHECK(msg->findInt32("sessionID", &sessionID));
-
- if (mClientSessionID > 0) {
- ALOGW("A client tried to connect, but we already "
- "have one.");
-
- mNetSession->destroySession(sessionID);
- break;
- }
-
- CHECK_EQ(mState, AWAITING_CLIENT_CONNECTION);
-
- CHECK(msg->findString("client-ip", &mClientInfo.mRemoteIP));
- CHECK(msg->findString("server-ip", &mClientInfo.mLocalIP));
-
- if (mClientInfo.mRemoteIP == mClientInfo.mLocalIP) {
- // Disallow connections from the local interface
- // for security reasons.
- mNetSession->destroySession(sessionID);
- break;
- }
-
- CHECK(msg->findInt32(
- "server-port", &mClientInfo.mLocalPort));
- mClientInfo.mPlaybackSessionID = -1;
-
- mClientSessionID = sessionID;
-
- ALOGI("We now have a client (%d) connected.", sessionID);
-
- mState = AWAITING_CLIENT_SETUP;
-
- status_t err = sendM1(sessionID);
- CHECK_EQ(err, (status_t)OK);
- break;
- }
-
- case ANetworkSession::kWhatData:
- {
- status_t err = onReceiveClientData(msg);
-
- if (err != OK) {
- mClient->onDisplayError(
- IRemoteDisplayClient::kDisplayErrorUnknown);
- }
-
-#if 0
- // testing only.
- char val[PROPERTY_VALUE_MAX];
- if (property_get("media.wfd.trigger", val, NULL)) {
- if (!strcasecmp(val, "pause") && mState == PLAYING) {
- mState = PLAYING_TO_PAUSED;
- sendTrigger(mClientSessionID, TRIGGER_PAUSE);
- } else if (!strcasecmp(val, "play")
- && mState == PAUSED) {
- mState = PAUSED_TO_PLAYING;
- sendTrigger(mClientSessionID, TRIGGER_PLAY);
- }
- }
-#endif
- break;
- }
-
- case ANetworkSession::kWhatNetworkStall:
- {
- break;
- }
-
- default:
- TRESPASS();
- }
- break;
- }
-
- case kWhatStop:
- {
- CHECK(msg->senderAwaitsResponse(&mStopReplyID));
-
- CHECK_LT(mState, AWAITING_CLIENT_TEARDOWN);
-
- if (mState >= AWAITING_CLIENT_PLAY) {
- // We have a session, i.e. a previous SETUP succeeded.
-
- status_t err = sendTrigger(
- mClientSessionID, TRIGGER_TEARDOWN);
-
- if (err == OK) {
- mState = AWAITING_CLIENT_TEARDOWN;
-
- (new AMessage(kWhatTeardownTriggerTimedOut, this))->post(
- kTeardownTriggerTimeouSecs * 1000000ll);
-
- break;
- }
-
- // fall through.
- }
-
- finishStop();
- break;
- }
-
- case kWhatPause:
- {
- sp<AReplyToken> replyID;
- CHECK(msg->senderAwaitsResponse(&replyID));
-
- status_t err = OK;
-
- if (mState != PLAYING) {
- err = INVALID_OPERATION;
- } else {
- mState = PLAYING_TO_PAUSED;
- sendTrigger(mClientSessionID, TRIGGER_PAUSE);
- }
-
- sp<AMessage> response = new AMessage;
- response->setInt32("err", err);
- response->postReply(replyID);
- break;
- }
-
- case kWhatResume:
- {
- sp<AReplyToken> replyID;
- CHECK(msg->senderAwaitsResponse(&replyID));
-
- status_t err = OK;
-
- if (mState != PAUSED) {
- err = INVALID_OPERATION;
- } else {
- mState = PAUSED_TO_PLAYING;
- sendTrigger(mClientSessionID, TRIGGER_PLAY);
- }
-
- sp<AMessage> response = new AMessage;
- response->setInt32("err", err);
- response->postReply(replyID);
- break;
- }
-
- case kWhatReapDeadClients:
- {
- mReaperPending = false;
-
- if (mClientSessionID == 0
- || mClientInfo.mPlaybackSession == NULL) {
- break;
- }
-
- if (mClientInfo.mPlaybackSession->getLastLifesignUs()
- + kPlaybackSessionTimeoutUs < ALooper::GetNowUs()) {
- ALOGI("playback session timed out, reaping.");
-
- mNetSession->destroySession(mClientSessionID);
- mClientSessionID = 0;
-
- mClient->onDisplayError(
- IRemoteDisplayClient::kDisplayErrorUnknown);
- } else {
- scheduleReaper();
- }
- break;
- }
-
- case kWhatPlaybackSessionNotify:
- {
- int32_t playbackSessionID;
- CHECK(msg->findInt32("playbackSessionID", &playbackSessionID));
-
- int32_t what;
- CHECK(msg->findInt32("what", &what));
-
- if (what == PlaybackSession::kWhatSessionDead) {
- ALOGI("playback session wants to quit.");
-
- mClient->onDisplayError(
- IRemoteDisplayClient::kDisplayErrorUnknown);
- } else if (what == PlaybackSession::kWhatSessionEstablished) {
- mPlaybackSessionEstablished = true;
-
- if (mClient != NULL) {
- if (!mSinkSupportsVideo) {
- mClient->onDisplayConnected(
- NULL, // SurfaceTexture
- 0, // width,
- 0, // height,
- mUsingHDCP
- ? IRemoteDisplayClient::kDisplayFlagSecure
- : 0,
- 0);
- } else {
- size_t width, height;
-
- CHECK(VideoFormats::GetConfiguration(
- mChosenVideoResolutionType,
- mChosenVideoResolutionIndex,
- &width,
- &height,
- NULL /* framesPerSecond */,
- NULL /* interlaced */));
-
- mClient->onDisplayConnected(
- mClientInfo.mPlaybackSession
- ->getSurfaceTexture(),
- width,
- height,
- mUsingHDCP
- ? IRemoteDisplayClient::kDisplayFlagSecure
- : 0,
- playbackSessionID);
- }
- }
-
- finishPlay();
-
- if (mState == ABOUT_TO_PLAY) {
- mState = PLAYING;
- }
- } else if (what == PlaybackSession::kWhatSessionDestroyed) {
- disconnectClient2();
- } else {
- CHECK_EQ(what, PlaybackSession::kWhatBinaryData);
-
- int32_t channel;
- CHECK(msg->findInt32("channel", &channel));
-
- sp<ABuffer> data;
- CHECK(msg->findBuffer("data", &data));
-
- CHECK_LE(channel, 0xff);
- CHECK_LE(data->size(), 0xffffu);
-
- int32_t sessionID;
- CHECK(msg->findInt32("sessionID", &sessionID));
-
- char header[4];
- header[0] = '$';
- header[1] = channel;
- header[2] = data->size() >> 8;
- header[3] = data->size() & 0xff;
-
- mNetSession->sendRequest(
- sessionID, header, sizeof(header));
-
- mNetSession->sendRequest(
- sessionID, data->data(), data->size());
- }
- break;
- }
-
- case kWhatKeepAlive:
- {
- int32_t sessionID;
- CHECK(msg->findInt32("sessionID", &sessionID));
-
- if (mClientSessionID != sessionID) {
- // Obsolete event, client is already gone.
- break;
- }
-
- sendM16(sessionID);
- break;
- }
-
- case kWhatTeardownTriggerTimedOut:
- {
- if (mState == AWAITING_CLIENT_TEARDOWN) {
- ALOGI("TEARDOWN trigger timed out, forcing disconnection.");
-
- CHECK(mStopReplyID != NULL);
- finishStop();
- break;
- }
- break;
- }
-
- case kWhatHDCPNotify:
- {
- int32_t msgCode, ext1, ext2;
- CHECK(msg->findInt32("msg", &msgCode));
- CHECK(msg->findInt32("ext1", &ext1));
- CHECK(msg->findInt32("ext2", &ext2));
-
- ALOGI("Saw HDCP notification code %d, ext1 %d, ext2 %d",
- msgCode, ext1, ext2);
-
- switch (msgCode) {
- case HDCPModule::HDCP_INITIALIZATION_COMPLETE:
- {
- mHDCPInitializationComplete = true;
-
- if (mSetupTriggerDeferred) {
- mSetupTriggerDeferred = false;
-
- sendTrigger(mClientSessionID, TRIGGER_SETUP);
- }
- break;
- }
-
- case HDCPModule::HDCP_SHUTDOWN_COMPLETE:
- case HDCPModule::HDCP_SHUTDOWN_FAILED:
- {
- // Ugly hack to make sure that the call to
- // HDCPObserver::notify is completely handled before
- // we clear the HDCP instance and unload the shared
- // library :(
- (new AMessage(kWhatFinishStop2, this))->post(300000ll);
- break;
- }
-
- default:
- {
- ALOGE("HDCP failure, shutting down.");
-
- mClient->onDisplayError(
- IRemoteDisplayClient::kDisplayErrorUnknown);
- break;
- }
- }
- break;
- }
-
- case kWhatFinishStop2:
- {
- finishStop2();
- break;
- }
-
- default:
- TRESPASS();
- }
-}
-
-void WifiDisplaySource::registerResponseHandler(
- int32_t sessionID, int32_t cseq, HandleRTSPResponseFunc func) {
- ResponseID id;
- id.mSessionID = sessionID;
- id.mCSeq = cseq;
- mResponseHandlers.add(id, func);
-}
-
-status_t WifiDisplaySource::sendM1(int32_t sessionID) {
- AString request = "OPTIONS * RTSP/1.0\r\n";
- AppendCommonResponse(&request, mNextCSeq);
-
- request.append(
- "Require: org.wfa.wfd1.0\r\n"
- "\r\n");
-
- status_t err =
- mNetSession->sendRequest(sessionID, request.c_str(), request.size());
-
- if (err != OK) {
- return err;
- }
-
- registerResponseHandler(
- sessionID, mNextCSeq, &WifiDisplaySource::onReceiveM1Response);
-
- ++mNextCSeq;
-
- return OK;
-}
-
-status_t WifiDisplaySource::sendM3(int32_t sessionID) {
- AString body =
- "wfd_content_protection\r\n"
- "wfd_video_formats\r\n"
- "wfd_audio_codecs\r\n"
- "wfd_client_rtp_ports\r\n";
-
- AString request = "GET_PARAMETER rtsp://localhost/wfd1.0 RTSP/1.0\r\n";
- AppendCommonResponse(&request, mNextCSeq);
-
- request.append("Content-Type: text/parameters\r\n");
- request.append(AStringPrintf("Content-Length: %d\r\n", body.size()));
- request.append("\r\n");
- request.append(body);
-
- status_t err =
- mNetSession->sendRequest(sessionID, request.c_str(), request.size());
-
- if (err != OK) {
- return err;
- }
-
- registerResponseHandler(
- sessionID, mNextCSeq, &WifiDisplaySource::onReceiveM3Response);
-
- ++mNextCSeq;
-
- return OK;
-}
-
-status_t WifiDisplaySource::sendM4(int32_t sessionID) {
- CHECK_EQ(sessionID, mClientSessionID);
-
- AString body;
-
- if (mSinkSupportsVideo) {
- body.append("wfd_video_formats: ");
-
- VideoFormats chosenVideoFormat;
- chosenVideoFormat.disableAll();
- chosenVideoFormat.setNativeResolution(
- mChosenVideoResolutionType, mChosenVideoResolutionIndex);
- chosenVideoFormat.setProfileLevel(
- mChosenVideoResolutionType, mChosenVideoResolutionIndex,
- mChosenVideoProfile, mChosenVideoLevel);
-
- body.append(chosenVideoFormat.getFormatSpec(true /* forM4Message */));
- body.append("\r\n");
- }
-
- if (mSinkSupportsAudio) {
- body.append(
- AStringPrintf("wfd_audio_codecs: %s\r\n",
- (mUsingPCMAudio
- ? "LPCM 00000002 00" // 2 ch PCM 48kHz
- : "AAC 00000001 00"))); // 2 ch AAC 48kHz
- }
-
- body.append(
- AStringPrintf(
- "wfd_presentation_URL: rtsp://%s/wfd1.0/streamid=0 none\r\n",
- mClientInfo.mLocalIP.c_str()));
-
- body.append(
- AStringPrintf(
- "wfd_client_rtp_ports: %s\r\n", mWfdClientRtpPorts.c_str()));
-
- AString request = "SET_PARAMETER rtsp://localhost/wfd1.0 RTSP/1.0\r\n";
- AppendCommonResponse(&request, mNextCSeq);
-
- request.append("Content-Type: text/parameters\r\n");
- request.append(AStringPrintf("Content-Length: %d\r\n", body.size()));
- request.append("\r\n");
- request.append(body);
-
- status_t err =
- mNetSession->sendRequest(sessionID, request.c_str(), request.size());
-
- if (err != OK) {
- return err;
- }
-
- registerResponseHandler(
- sessionID, mNextCSeq, &WifiDisplaySource::onReceiveM4Response);
-
- ++mNextCSeq;
-
- return OK;
-}
-
-status_t WifiDisplaySource::sendTrigger(
- int32_t sessionID, TriggerType triggerType) {
- AString body = "wfd_trigger_method: ";
- switch (triggerType) {
- case TRIGGER_SETUP:
- body.append("SETUP");
- break;
- case TRIGGER_TEARDOWN:
- ALOGI("Sending TEARDOWN trigger.");
- body.append("TEARDOWN");
- break;
- case TRIGGER_PAUSE:
- body.append("PAUSE");
- break;
- case TRIGGER_PLAY:
- body.append("PLAY");
- break;
- default:
- TRESPASS();
- }
-
- body.append("\r\n");
-
- AString request = "SET_PARAMETER rtsp://localhost/wfd1.0 RTSP/1.0\r\n";
- AppendCommonResponse(&request, mNextCSeq);
-
- request.append("Content-Type: text/parameters\r\n");
- request.append(AStringPrintf("Content-Length: %d\r\n", body.size()));
- request.append("\r\n");
- request.append(body);
-
- status_t err =
- mNetSession->sendRequest(sessionID, request.c_str(), request.size());
-
- if (err != OK) {
- return err;
- }
-
- registerResponseHandler(
- sessionID, mNextCSeq, &WifiDisplaySource::onReceiveM5Response);
-
- ++mNextCSeq;
-
- return OK;
-}
-
-status_t WifiDisplaySource::sendM16(int32_t sessionID) {
- AString request = "GET_PARAMETER rtsp://localhost/wfd1.0 RTSP/1.0\r\n";
- AppendCommonResponse(&request, mNextCSeq);
-
- CHECK_EQ(sessionID, mClientSessionID);
- request.append(
- AStringPrintf("Session: %d\r\n", mClientInfo.mPlaybackSessionID));
- request.append("\r\n"); // Empty body
-
- status_t err =
- mNetSession->sendRequest(sessionID, request.c_str(), request.size());
-
- if (err != OK) {
- return err;
- }
-
- registerResponseHandler(
- sessionID, mNextCSeq, &WifiDisplaySource::onReceiveM16Response);
-
- ++mNextCSeq;
-
- scheduleKeepAlive(sessionID);
-
- return OK;
-}
-
-status_t WifiDisplaySource::onReceiveM1Response(
- int32_t /* sessionID */, const sp<ParsedMessage> &msg) {
- int32_t statusCode;
- if (!msg->getStatusCode(&statusCode)) {
- return ERROR_MALFORMED;
- }
-
- if (statusCode != 200) {
- return ERROR_UNSUPPORTED;
- }
-
- return OK;
-}
-
-// sink_audio_list := ("LPCM"|"AAC"|"AC3" HEXDIGIT*8 HEXDIGIT*2)
-// (", " sink_audio_list)*
-static void GetAudioModes(const char *s, const char *prefix, uint32_t *modes) {
- *modes = 0;
-
- size_t prefixLen = strlen(prefix);
-
- while (*s != '0') {
- if (!strncmp(s, prefix, prefixLen) && s[prefixLen] == ' ') {
- unsigned latency;
- if (sscanf(&s[prefixLen + 1], "%08x %02x", modes, &latency) != 2) {
- *modes = 0;
- }
-
- return;
- }
-
- const char *commaPos = strchr(s, ',');
- if (commaPos != NULL) {
- s = commaPos + 1;
-
- while (isspace(*s)) {
- ++s;
- }
- } else {
- break;
- }
- }
-}
-
-status_t WifiDisplaySource::onReceiveM3Response(
- int32_t sessionID, const sp<ParsedMessage> &msg) {
- int32_t statusCode;
- if (!msg->getStatusCode(&statusCode)) {
- return ERROR_MALFORMED;
- }
-
- if (statusCode != 200) {
- return ERROR_UNSUPPORTED;
- }
-
- sp<Parameters> params =
- Parameters::Parse(msg->getContent(), strlen(msg->getContent()));
-
- if (params == NULL) {
- return ERROR_MALFORMED;
- }
-
- AString value;
- if (!params->findParameter("wfd_client_rtp_ports", &value)) {
- ALOGE("Sink doesn't report its choice of wfd_client_rtp_ports.");
- return ERROR_MALFORMED;
- }
-
- unsigned port0 = 0, port1 = 0;
- if (sscanf(value.c_str(),
- "RTP/AVP/UDP;unicast %u %u mode=play",
- &port0,
- &port1) == 2
- || sscanf(value.c_str(),
- "RTP/AVP/TCP;unicast %u %u mode=play",
- &port0,
- &port1) == 2) {
- if (port0 == 0 || port0 > 65535 || port1 != 0) {
- ALOGE("Sink chose its wfd_client_rtp_ports poorly (%s)",
- value.c_str());
-
- return ERROR_MALFORMED;
- }
- } else if (strcmp(value.c_str(), "RTP/AVP/TCP;interleaved mode=play")) {
- ALOGE("Unsupported value for wfd_client_rtp_ports (%s)",
- value.c_str());
-
- return ERROR_UNSUPPORTED;
- }
-
- mWfdClientRtpPorts = value;
- mChosenRTPPort = port0;
-
- if (!params->findParameter("wfd_video_formats", &value)) {
- ALOGE("Sink doesn't report its choice of wfd_video_formats.");
- return ERROR_MALFORMED;
- }
-
- mSinkSupportsVideo = false;
-
- if (!(value == "none")) {
- mSinkSupportsVideo = true;
- if (!mSupportedSinkVideoFormats.parseFormatSpec(value.c_str())) {
- ALOGE("Failed to parse sink provided wfd_video_formats (%s)",
- value.c_str());
-
- return ERROR_MALFORMED;
- }
-
- if (!VideoFormats::PickBestFormat(
- mSupportedSinkVideoFormats,
- mSupportedSourceVideoFormats,
- &mChosenVideoResolutionType,
- &mChosenVideoResolutionIndex,
- &mChosenVideoProfile,
- &mChosenVideoLevel)) {
- ALOGE("Sink and source share no commonly supported video "
- "formats.");
-
- return ERROR_UNSUPPORTED;
- }
-
- size_t width, height, framesPerSecond;
- bool interlaced;
- CHECK(VideoFormats::GetConfiguration(
- mChosenVideoResolutionType,
- mChosenVideoResolutionIndex,
- &width,
- &height,
- &framesPerSecond,
- &interlaced));
-
- ALOGI("Picked video resolution %zu x %zu %c%zu",
- width, height, interlaced ? 'i' : 'p', framesPerSecond);
-
- ALOGI("Picked AVC profile %d, level %d",
- mChosenVideoProfile, mChosenVideoLevel);
- } else {
- ALOGI("Sink doesn't support video at all.");
- }
-
- if (!params->findParameter("wfd_audio_codecs", &value)) {
- ALOGE("Sink doesn't report its choice of wfd_audio_codecs.");
- return ERROR_MALFORMED;
- }
-
- mSinkSupportsAudio = false;
-
- if (!(value == "none")) {
- mSinkSupportsAudio = true;
-
- uint32_t modes;
- GetAudioModes(value.c_str(), "AAC", &modes);
-
- bool supportsAAC = (modes & 1) != 0; // AAC 2ch 48kHz
-
- GetAudioModes(value.c_str(), "LPCM", &modes);
-
- bool supportsPCM = (modes & 2) != 0; // LPCM 2ch 48kHz
-
- if (supportsPCM
- && property_get_bool("media.wfd.use-pcm-audio", false)) {
- ALOGI("Using PCM audio.");
- mUsingPCMAudio = true;
- } else if (supportsAAC) {
- ALOGI("Using AAC audio.");
- mUsingPCMAudio = false;
- } else if (supportsPCM) {
- ALOGI("Using PCM audio.");
- mUsingPCMAudio = true;
- } else {
- ALOGI("Sink doesn't support an audio format we do.");
- return ERROR_UNSUPPORTED;
- }
- } else {
- ALOGI("Sink doesn't support audio at all.");
- }
-
- if (!mSinkSupportsVideo && !mSinkSupportsAudio) {
- ALOGE("Sink supports neither video nor audio...");
- return ERROR_UNSUPPORTED;
- }
-
- mUsingHDCP = false;
- if (!params->findParameter("wfd_content_protection", &value)) {
- ALOGI("Sink doesn't appear to support content protection.");
- } else if (value == "none") {
- ALOGI("Sink does not support content protection.");
- } else {
- mUsingHDCP = true;
-
- bool isHDCP2_0 = false;
- if (value.startsWith("HDCP2.0 ")) {
- isHDCP2_0 = true;
- } else if (!value.startsWith("HDCP2.1 ")) {
- ALOGE("malformed wfd_content_protection: '%s'", value.c_str());
-
- return ERROR_MALFORMED;
- }
-
- int32_t hdcpPort;
- if (!ParsedMessage::GetInt32Attribute(
- value.c_str() + 8, "port", &hdcpPort)
- || hdcpPort < 1 || hdcpPort > 65535) {
- return ERROR_MALFORMED;
- }
-
- mIsHDCP2_0 = isHDCP2_0;
- mHDCPPort = hdcpPort;
-
- status_t err = makeHDCP();
- if (err != OK) {
- ALOGE("Unable to instantiate HDCP component. "
- "Not using HDCP after all.");
-
- mUsingHDCP = false;
- }
- }
-
- return sendM4(sessionID);
-}
-
-status_t WifiDisplaySource::onReceiveM4Response(
- int32_t sessionID, const sp<ParsedMessage> &msg) {
- int32_t statusCode;
- if (!msg->getStatusCode(&statusCode)) {
- return ERROR_MALFORMED;
- }
-
- if (statusCode != 200) {
- return ERROR_UNSUPPORTED;
- }
-
- if (mUsingHDCP && !mHDCPInitializationComplete) {
- ALOGI("Deferring SETUP trigger until HDCP initialization completes.");
-
- mSetupTriggerDeferred = true;
- return OK;
- }
-
- return sendTrigger(sessionID, TRIGGER_SETUP);
-}
-
-status_t WifiDisplaySource::onReceiveM5Response(
- int32_t /* sessionID */, const sp<ParsedMessage> &msg) {
- int32_t statusCode;
- if (!msg->getStatusCode(&statusCode)) {
- return ERROR_MALFORMED;
- }
-
- if (statusCode != 200) {
- return ERROR_UNSUPPORTED;
- }
-
- return OK;
-}
-
-status_t WifiDisplaySource::onReceiveM16Response(
- int32_t sessionID, const sp<ParsedMessage> & /* msg */) {
- // If only the response was required to include a "Session:" header...
-
- CHECK_EQ(sessionID, mClientSessionID);
-
- if (mClientInfo.mPlaybackSession != NULL) {
- mClientInfo.mPlaybackSession->updateLiveness();
- }
-
- return OK;
-}
-
-void WifiDisplaySource::scheduleReaper() {
- if (mReaperPending) {
- return;
- }
-
- mReaperPending = true;
- (new AMessage(kWhatReapDeadClients, this))->post(kReaperIntervalUs);
-}
-
-void WifiDisplaySource::scheduleKeepAlive(int32_t sessionID) {
- // We need to send updates at least 5 secs before the timeout is set to
- // expire, make sure the timeout is greater than 5 secs to begin with.
- CHECK_GT(kPlaybackSessionTimeoutUs, 5000000ll);
-
- sp<AMessage> msg = new AMessage(kWhatKeepAlive, this);
- msg->setInt32("sessionID", sessionID);
- msg->post(kPlaybackSessionTimeoutUs - 5000000ll);
-}
-
-status_t WifiDisplaySource::onReceiveClientData(const sp<AMessage> &msg) {
- int32_t sessionID;
- CHECK(msg->findInt32("sessionID", &sessionID));
-
- sp<RefBase> obj;
- CHECK(msg->findObject("data", &obj));
-
- sp<ParsedMessage> data =
- static_cast<ParsedMessage *>(obj.get());
-
- ALOGV("session %d received '%s'",
- sessionID, data->debugString().c_str());
-
- AString method;
- AString uri;
- data->getRequestField(0, &method);
-
- int32_t cseq;
- if (!data->findInt32("cseq", &cseq)) {
- sendErrorResponse(sessionID, "400 Bad Request", -1 /* cseq */);
- return ERROR_MALFORMED;
- }
-
- if (method.startsWith("RTSP/")) {
- // This is a response.
-
- ResponseID id;
- id.mSessionID = sessionID;
- id.mCSeq = cseq;
-
- ssize_t index = mResponseHandlers.indexOfKey(id);
-
- if (index < 0) {
- ALOGW("Received unsolicited server response, cseq %d", cseq);
- return ERROR_MALFORMED;
- }
-
- HandleRTSPResponseFunc func = mResponseHandlers.valueAt(index);
- mResponseHandlers.removeItemsAt(index);
-
- status_t err = (this->*func)(sessionID, data);
-
- if (err != OK) {
- ALOGW("Response handler for session %d, cseq %d returned "
- "err %d (%s)",
- sessionID, cseq, err, strerror(-err));
-
- return err;
- }
-
- return OK;
- }
-
- AString version;
- data->getRequestField(2, &version);
- if (!(version == AString("RTSP/1.0"))) {
- sendErrorResponse(sessionID, "505 RTSP Version not supported", cseq);
- return ERROR_UNSUPPORTED;
- }
-
- status_t err;
- if (method == "OPTIONS") {
- err = onOptionsRequest(sessionID, cseq, data);
- } else if (method == "SETUP") {
- err = onSetupRequest(sessionID, cseq, data);
- } else if (method == "PLAY") {
- err = onPlayRequest(sessionID, cseq, data);
- } else if (method == "PAUSE") {
- err = onPauseRequest(sessionID, cseq, data);
- } else if (method == "TEARDOWN") {
- err = onTeardownRequest(sessionID, cseq, data);
- } else if (method == "GET_PARAMETER") {
- err = onGetParameterRequest(sessionID, cseq, data);
- } else if (method == "SET_PARAMETER") {
- err = onSetParameterRequest(sessionID, cseq, data);
- } else {
- sendErrorResponse(sessionID, "405 Method Not Allowed", cseq);
-
- err = ERROR_UNSUPPORTED;
- }
-
- return err;
-}
-
-status_t WifiDisplaySource::onOptionsRequest(
- int32_t sessionID,
- int32_t cseq,
- const sp<ParsedMessage> &data) {
- int32_t playbackSessionID;
- sp<PlaybackSession> playbackSession =
- findPlaybackSession(data, &playbackSessionID);
-
- if (playbackSession != NULL) {
- playbackSession->updateLiveness();
- }
-
- AString response = "RTSP/1.0 200 OK\r\n";
- AppendCommonResponse(&response, cseq);
-
- response.append(
- "Public: org.wfa.wfd1.0, SETUP, TEARDOWN, PLAY, PAUSE, "
- "GET_PARAMETER, SET_PARAMETER\r\n");
-
- response.append("\r\n");
-
- status_t err = mNetSession->sendRequest(sessionID, response.c_str());
-
- if (err == OK) {
- err = sendM3(sessionID);
- }
-
- return err;
-}
-
-status_t WifiDisplaySource::onSetupRequest(
- int32_t sessionID,
- int32_t cseq,
- const sp<ParsedMessage> &data) {
- CHECK_EQ(sessionID, mClientSessionID);
- if (mClientInfo.mPlaybackSessionID != -1) {
- // We only support a single playback session per client.
- // This is due to the reversed keep-alive design in the wfd specs...
- sendErrorResponse(sessionID, "400 Bad Request", cseq);
- return ERROR_MALFORMED;
- }
-
- AString transport;
- if (!data->findString("transport", &transport)) {
- sendErrorResponse(sessionID, "400 Bad Request", cseq);
- return ERROR_MALFORMED;
- }
-
- RTPSender::TransportMode rtpMode = RTPSender::TRANSPORT_UDP;
-
- int clientRtp, clientRtcp;
- if (transport.startsWith("RTP/AVP/TCP;")) {
- AString interleaved;
- if (ParsedMessage::GetAttribute(
- transport.c_str(), "interleaved", &interleaved)
- && sscanf(interleaved.c_str(), "%d-%d",
- &clientRtp, &clientRtcp) == 2) {
- rtpMode = RTPSender::TRANSPORT_TCP_INTERLEAVED;
- } else {
- bool badRequest = false;
-
- AString clientPort;
- if (!ParsedMessage::GetAttribute(
- transport.c_str(), "client_port", &clientPort)) {
- badRequest = true;
- } else if (sscanf(clientPort.c_str(), "%d-%d",
- &clientRtp, &clientRtcp) == 2) {
- } else if (sscanf(clientPort.c_str(), "%d", &clientRtp) == 1) {
- // No RTCP.
- clientRtcp = -1;
- } else {
- badRequest = true;
- }
-
- if (badRequest) {
- sendErrorResponse(sessionID, "400 Bad Request", cseq);
- return ERROR_MALFORMED;
- }
-
- rtpMode = RTPSender::TRANSPORT_TCP;
- }
- } else if (transport.startsWith("RTP/AVP;unicast;")
- || transport.startsWith("RTP/AVP/UDP;unicast;")) {
- bool badRequest = false;
-
- AString clientPort;
- if (!ParsedMessage::GetAttribute(
- transport.c_str(), "client_port", &clientPort)) {
- badRequest = true;
- } else if (sscanf(clientPort.c_str(), "%d-%d",
- &clientRtp, &clientRtcp) == 2) {
- } else if (sscanf(clientPort.c_str(), "%d", &clientRtp) == 1) {
- // No RTCP.
- clientRtcp = -1;
- } else {
- badRequest = true;
- }
-
- if (badRequest) {
- sendErrorResponse(sessionID, "400 Bad Request", cseq);
- return ERROR_MALFORMED;
- }
-#if 1
- // The older LG dongles doesn't specify client_port=xxx apparently.
- } else if (transport == "RTP/AVP/UDP;unicast") {
- clientRtp = 19000;
- clientRtcp = -1;
-#endif
- } else {
- sendErrorResponse(sessionID, "461 Unsupported Transport", cseq);
- return ERROR_UNSUPPORTED;
- }
-
- int32_t playbackSessionID = makeUniquePlaybackSessionID();
-
- sp<AMessage> notify = new AMessage(kWhatPlaybackSessionNotify, this);
- notify->setInt32("playbackSessionID", playbackSessionID);
- notify->setInt32("sessionID", sessionID);
-
- sp<PlaybackSession> playbackSession =
- new PlaybackSession(
- mOpPackageName, mNetSession, notify, mInterfaceAddr, mHDCP, mMediaPath.c_str());
-
- looper()->registerHandler(playbackSession);
-
- AString uri;
- data->getRequestField(1, &uri);
-
- if (strncasecmp("rtsp://", uri.c_str(), 7)) {
- sendErrorResponse(sessionID, "400 Bad Request", cseq);
- return ERROR_MALFORMED;
- }
-
- if (!(uri.startsWith("rtsp://") && uri.endsWith("/wfd1.0/streamid=0"))) {
- sendErrorResponse(sessionID, "404 Not found", cseq);
- return ERROR_MALFORMED;
- }
-
- RTPSender::TransportMode rtcpMode = RTPSender::TRANSPORT_UDP;
- if (clientRtcp < 0) {
- rtcpMode = RTPSender::TRANSPORT_NONE;
- }
-
- status_t err = playbackSession->init(
- mClientInfo.mRemoteIP.c_str(),
- clientRtp,
- rtpMode,
- clientRtcp,
- rtcpMode,
- mSinkSupportsAudio,
- mUsingPCMAudio,
- mSinkSupportsVideo,
- mChosenVideoResolutionType,
- mChosenVideoResolutionIndex,
- mChosenVideoProfile,
- mChosenVideoLevel);
-
- if (err != OK) {
- looper()->unregisterHandler(playbackSession->id());
- playbackSession.clear();
- }
-
- switch (err) {
- case OK:
- break;
- case -ENOENT:
- sendErrorResponse(sessionID, "404 Not Found", cseq);
- return err;
- default:
- sendErrorResponse(sessionID, "403 Forbidden", cseq);
- return err;
- }
-
- mClientInfo.mPlaybackSessionID = playbackSessionID;
- mClientInfo.mPlaybackSession = playbackSession;
-
- AString response = "RTSP/1.0 200 OK\r\n";
- AppendCommonResponse(&response, cseq, playbackSessionID);
-
- if (rtpMode == RTPSender::TRANSPORT_TCP_INTERLEAVED) {
- response.append(
- AStringPrintf(
- "Transport: RTP/AVP/TCP;interleaved=%d-%d;",
- clientRtp, clientRtcp));
- } else {
- int32_t serverRtp = playbackSession->getRTPPort();
-
- AString transportString = "UDP";
- if (rtpMode == RTPSender::TRANSPORT_TCP) {
- transportString = "TCP";
- }
-
- if (clientRtcp >= 0) {
- response.append(
- AStringPrintf(
- "Transport: RTP/AVP/%s;unicast;client_port=%d-%d;"
- "server_port=%d-%d\r\n",
- transportString.c_str(),
- clientRtp, clientRtcp, serverRtp, serverRtp + 1));
- } else {
- response.append(
- AStringPrintf(
- "Transport: RTP/AVP/%s;unicast;client_port=%d;"
- "server_port=%d\r\n",
- transportString.c_str(),
- clientRtp, serverRtp));
- }
- }
-
- response.append("\r\n");
-
- err = mNetSession->sendRequest(sessionID, response.c_str());
-
- if (err != OK) {
- return err;
- }
-
- mState = AWAITING_CLIENT_PLAY;
-
- scheduleReaper();
- scheduleKeepAlive(sessionID);
-
- return OK;
-}
-
-status_t WifiDisplaySource::onPlayRequest(
- int32_t sessionID,
- int32_t cseq,
- const sp<ParsedMessage> &data) {
- int32_t playbackSessionID;
- sp<PlaybackSession> playbackSession =
- findPlaybackSession(data, &playbackSessionID);
-
- if (playbackSession == NULL) {
- sendErrorResponse(sessionID, "454 Session Not Found", cseq);
- return ERROR_MALFORMED;
- }
-
- if (mState != AWAITING_CLIENT_PLAY
- && mState != PAUSED_TO_PLAYING
- && mState != PAUSED) {
- ALOGW("Received PLAY request but we're in state %d", mState);
-
- sendErrorResponse(
- sessionID, "455 Method Not Valid in This State", cseq);
-
- return INVALID_OPERATION;
- }
-
- ALOGI("Received PLAY request.");
- if (mPlaybackSessionEstablished) {
- finishPlay();
- } else {
- ALOGI("deferring PLAY request until session established.");
- }
-
- AString response = "RTSP/1.0 200 OK\r\n";
- AppendCommonResponse(&response, cseq, playbackSessionID);
- response.append("Range: npt=now-\r\n");
- response.append("\r\n");
-
- status_t err = mNetSession->sendRequest(sessionID, response.c_str());
-
- if (err != OK) {
- return err;
- }
-
- if (mState == PAUSED_TO_PLAYING || mPlaybackSessionEstablished) {
- mState = PLAYING;
- return OK;
- }
-
- CHECK_EQ(mState, AWAITING_CLIENT_PLAY);
- mState = ABOUT_TO_PLAY;
-
- return OK;
-}
-
-void WifiDisplaySource::finishPlay() {
- const sp<PlaybackSession> &playbackSession =
- mClientInfo.mPlaybackSession;
-
- status_t err = playbackSession->play();
- CHECK_EQ(err, (status_t)OK);
-}
-
-status_t WifiDisplaySource::onPauseRequest(
- int32_t sessionID,
- int32_t cseq,
- const sp<ParsedMessage> &data) {
- int32_t playbackSessionID;
- sp<PlaybackSession> playbackSession =
- findPlaybackSession(data, &playbackSessionID);
-
- if (playbackSession == NULL) {
- sendErrorResponse(sessionID, "454 Session Not Found", cseq);
- return ERROR_MALFORMED;
- }
-
- ALOGI("Received PAUSE request.");
-
- if (mState != PLAYING_TO_PAUSED && mState != PLAYING) {
- return INVALID_OPERATION;
- }
-
- status_t err = playbackSession->pause();
- CHECK_EQ(err, (status_t)OK);
-
- AString response = "RTSP/1.0 200 OK\r\n";
- AppendCommonResponse(&response, cseq, playbackSessionID);
- response.append("\r\n");
-
- err = mNetSession->sendRequest(sessionID, response.c_str());
-
- if (err != OK) {
- return err;
- }
-
- mState = PAUSED;
-
- return err;
-}
-
-status_t WifiDisplaySource::onTeardownRequest(
- int32_t sessionID,
- int32_t cseq,
- const sp<ParsedMessage> &data) {
- ALOGI("Received TEARDOWN request.");
-
- int32_t playbackSessionID;
- sp<PlaybackSession> playbackSession =
- findPlaybackSession(data, &playbackSessionID);
-
- if (playbackSession == NULL) {
- sendErrorResponse(sessionID, "454 Session Not Found", cseq);
- return ERROR_MALFORMED;
- }
-
- AString response = "RTSP/1.0 200 OK\r\n";
- AppendCommonResponse(&response, cseq, playbackSessionID);
- response.append("Connection: close\r\n");
- response.append("\r\n");
-
- mNetSession->sendRequest(sessionID, response.c_str());
-
- if (mState == AWAITING_CLIENT_TEARDOWN) {
- CHECK(mStopReplyID != NULL);
- finishStop();
- } else {
- mClient->onDisplayError(IRemoteDisplayClient::kDisplayErrorUnknown);
- }
-
- return OK;
-}
-
-void WifiDisplaySource::finishStop() {
- ALOGV("finishStop");
-
- mState = STOPPING;
-
- disconnectClientAsync();
-}
-
-void WifiDisplaySource::finishStopAfterDisconnectingClient() {
- ALOGV("finishStopAfterDisconnectingClient");
-
- if (mHDCP != NULL) {
- ALOGI("Initiating HDCP shutdown.");
- mHDCP->shutdownAsync();
- return;
- }
-
- finishStop2();
-}
-
-void WifiDisplaySource::finishStop2() {
- ALOGV("finishStop2");
-
- if (mHDCP != NULL) {
- mHDCP->setObserver(NULL);
- mHDCPObserver.clear();
- mHDCP.clear();
- }
-
- if (mSessionID != 0) {
- mNetSession->destroySession(mSessionID);
- mSessionID = 0;
- }
-
- ALOGI("We're stopped.");
- mState = STOPPED;
-
- status_t err = OK;
-
- sp<AMessage> response = new AMessage;
- response->setInt32("err", err);
- response->postReply(mStopReplyID);
-}
-
-status_t WifiDisplaySource::onGetParameterRequest(
- int32_t sessionID,
- int32_t cseq,
- const sp<ParsedMessage> &data) {
- int32_t playbackSessionID;
- sp<PlaybackSession> playbackSession =
- findPlaybackSession(data, &playbackSessionID);
-
- if (playbackSession == NULL) {
- sendErrorResponse(sessionID, "454 Session Not Found", cseq);
- return ERROR_MALFORMED;
- }
-
- playbackSession->updateLiveness();
-
- AString response = "RTSP/1.0 200 OK\r\n";
- AppendCommonResponse(&response, cseq, playbackSessionID);
- response.append("\r\n");
-
- status_t err = mNetSession->sendRequest(sessionID, response.c_str());
- return err;
-}
-
-status_t WifiDisplaySource::onSetParameterRequest(
- int32_t sessionID,
- int32_t cseq,
- const sp<ParsedMessage> &data) {
- int32_t playbackSessionID;
- sp<PlaybackSession> playbackSession =
- findPlaybackSession(data, &playbackSessionID);
-
- if (playbackSession == NULL) {
- sendErrorResponse(sessionID, "454 Session Not Found", cseq);
- return ERROR_MALFORMED;
- }
-
- if (strstr(data->getContent(), "wfd_idr_request\r\n")) {
- playbackSession->requestIDRFrame();
- }
-
- playbackSession->updateLiveness();
-
- AString response = "RTSP/1.0 200 OK\r\n";
- AppendCommonResponse(&response, cseq, playbackSessionID);
- response.append("\r\n");
-
- status_t err = mNetSession->sendRequest(sessionID, response.c_str());
- return err;
-}
-
-// static
-void WifiDisplaySource::AppendCommonResponse(
- AString *response, int32_t cseq, int32_t playbackSessionID) {
- time_t now = time(NULL);
- struct tm *now2 = gmtime(&now);
- char buf[128];
- strftime(buf, sizeof(buf), "%a, %d %b %Y %H:%M:%S %z", now2);
-
- response->append("Date: ");
- response->append(buf);
- response->append("\r\n");
-
- response->append(AStringPrintf("Server: %s\r\n", sUserAgent.c_str()));
-
- if (cseq >= 0) {
- response->append(AStringPrintf("CSeq: %d\r\n", cseq));
- }
-
- if (playbackSessionID >= 0ll) {
- response->append(
- AStringPrintf(
- "Session: %d;timeout=%lld\r\n",
- playbackSessionID, kPlaybackSessionTimeoutSecs));
- }
-}
-
-void WifiDisplaySource::sendErrorResponse(
- int32_t sessionID,
- const char *errorDetail,
- int32_t cseq) {
- AString response;
- response.append("RTSP/1.0 ");
- response.append(errorDetail);
- response.append("\r\n");
-
- AppendCommonResponse(&response, cseq);
-
- response.append("\r\n");
-
- mNetSession->sendRequest(sessionID, response.c_str());
-}
-
-int32_t WifiDisplaySource::makeUniquePlaybackSessionID() const {
- return rand();
-}
-
-sp<WifiDisplaySource::PlaybackSession> WifiDisplaySource::findPlaybackSession(
- const sp<ParsedMessage> &data, int32_t *playbackSessionID) const {
- if (!data->findInt32("session", playbackSessionID)) {
- // XXX the older dongles do not always include a "Session:" header.
- *playbackSessionID = mClientInfo.mPlaybackSessionID;
- return mClientInfo.mPlaybackSession;
- }
-
- if (*playbackSessionID != mClientInfo.mPlaybackSessionID) {
- return NULL;
- }
-
- return mClientInfo.mPlaybackSession;
-}
-
-void WifiDisplaySource::disconnectClientAsync() {
- ALOGV("disconnectClient");
-
- if (mClientInfo.mPlaybackSession == NULL) {
- disconnectClient2();
- return;
- }
-
- if (mClientInfo.mPlaybackSession != NULL) {
- ALOGV("Destroying PlaybackSession");
- mClientInfo.mPlaybackSession->destroyAsync();
- }
-}
-
-void WifiDisplaySource::disconnectClient2() {
- ALOGV("disconnectClient2");
-
- if (mClientInfo.mPlaybackSession != NULL) {
- looper()->unregisterHandler(mClientInfo.mPlaybackSession->id());
- mClientInfo.mPlaybackSession.clear();
- }
-
- if (mClientSessionID != 0) {
- mNetSession->destroySession(mClientSessionID);
- mClientSessionID = 0;
- }
-
- mClient->onDisplayDisconnected();
-
- finishStopAfterDisconnectingClient();
-}
-
-struct WifiDisplaySource::HDCPObserver : public BnHDCPObserver {
- explicit HDCPObserver(const sp<AMessage> ¬ify);
-
- virtual void notify(
- int msg, int ext1, int ext2, const Parcel *obj);
-
-private:
- sp<AMessage> mNotify;
-
- DISALLOW_EVIL_CONSTRUCTORS(HDCPObserver);
-};
-
-WifiDisplaySource::HDCPObserver::HDCPObserver(
- const sp<AMessage> ¬ify)
- : mNotify(notify) {
-}
-
-void WifiDisplaySource::HDCPObserver::notify(
- int msg, int ext1, int ext2, const Parcel * /* obj */) {
- sp<AMessage> notify = mNotify->dup();
- notify->setInt32("msg", msg);
- notify->setInt32("ext1", ext1);
- notify->setInt32("ext2", ext2);
- notify->post();
-}
-
-status_t WifiDisplaySource::makeHDCP() {
- sp<IServiceManager> sm = defaultServiceManager();
- sp<IBinder> binder = sm->getService(String16("media.player"));
-
- sp<IMediaPlayerService> service =
- interface_cast<IMediaPlayerService>(binder);
-
- CHECK(service != NULL);
-
- mHDCP = service->makeHDCP(true /* createEncryptionModule */);
-
- if (mHDCP == NULL) {
- return ERROR_UNSUPPORTED;
- }
-
- sp<AMessage> notify = new AMessage(kWhatHDCPNotify, this);
- mHDCPObserver = new HDCPObserver(notify);
-
- status_t err = mHDCP->setObserver(mHDCPObserver);
-
- if (err != OK) {
- ALOGE("Failed to set HDCP observer.");
-
- mHDCPObserver.clear();
- mHDCP.clear();
-
- return err;
- }
-
- ALOGI("Initiating HDCP negotiation w/ host %s:%d",
- mClientInfo.mRemoteIP.c_str(), mHDCPPort);
-
- err = mHDCP->initAsync(mClientInfo.mRemoteIP.c_str(), mHDCPPort);
-
- if (err != OK) {
- return err;
- }
-
- return OK;
-}
-
-} // namespace android
-
diff --git a/media/libstagefright/wifi-display/source/WifiDisplaySource.h b/media/libstagefright/wifi-display/source/WifiDisplaySource.h
deleted file mode 100644
index c25a675..0000000
--- a/media/libstagefright/wifi-display/source/WifiDisplaySource.h
+++ /dev/null
@@ -1,278 +0,0 @@
-/*
- * Copyright 2012, 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 WIFI_DISPLAY_SOURCE_H_
-
-#define WIFI_DISPLAY_SOURCE_H_
-
-#include "VideoFormats.h"
-
-#include <media/stagefright/foundation/AHandler.h>
-#include <media/stagefright/foundation/ANetworkSession.h>
-
-#include <netinet/in.h>
-
-#include <utils/String16.h>
-
-namespace android {
-
-struct AReplyToken;
-struct IHDCP;
-class IRemoteDisplayClient;
-struct ParsedMessage;
-
-// Represents the RTSP server acting as a wifi display source.
-// Manages incoming connections, sets up Playback sessions as necessary.
-struct WifiDisplaySource : public AHandler {
- static const unsigned kWifiDisplayDefaultPort = 7236;
-
- WifiDisplaySource(
- const String16 &opPackageName,
- const sp<ANetworkSession> &netSession,
- const sp<IRemoteDisplayClient> &client,
- const char *path = NULL);
-
- status_t start(const char *iface);
- status_t stop();
-
- status_t pause();
- status_t resume();
-
-protected:
- virtual ~WifiDisplaySource();
- virtual void onMessageReceived(const sp<AMessage> &msg);
-
-private:
- struct PlaybackSession;
- struct HDCPObserver;
-
- enum State {
- INITIALIZED,
- AWAITING_CLIENT_CONNECTION,
- AWAITING_CLIENT_SETUP,
- AWAITING_CLIENT_PLAY,
- ABOUT_TO_PLAY,
- PLAYING,
- PLAYING_TO_PAUSED,
- PAUSED,
- PAUSED_TO_PLAYING,
- AWAITING_CLIENT_TEARDOWN,
- STOPPING,
- STOPPED,
- };
-
- enum {
- kWhatStart,
- kWhatRTSPNotify,
- kWhatStop,
- kWhatPause,
- kWhatResume,
- kWhatReapDeadClients,
- kWhatPlaybackSessionNotify,
- kWhatKeepAlive,
- kWhatHDCPNotify,
- kWhatFinishStop2,
- kWhatTeardownTriggerTimedOut,
- };
-
- struct ResponseID {
- int32_t mSessionID;
- int32_t mCSeq;
-
- bool operator<(const ResponseID &other) const {
- return mSessionID < other.mSessionID
- || (mSessionID == other.mSessionID
- && mCSeq < other.mCSeq);
- }
- };
-
- typedef status_t (WifiDisplaySource::*HandleRTSPResponseFunc)(
- int32_t sessionID, const sp<ParsedMessage> &msg);
-
- static const int64_t kReaperIntervalUs = 1000000ll;
-
- // We request that the dongle send us a "TEARDOWN" in order to
- // perform an orderly shutdown. We're willing to wait up to 2 secs
- // for this message to arrive, after that we'll force a disconnect
- // instead.
- static const int64_t kTeardownTriggerTimeouSecs = 2;
-
- static const int64_t kPlaybackSessionTimeoutSecs = 30;
-
- static const int64_t kPlaybackSessionTimeoutUs =
- kPlaybackSessionTimeoutSecs * 1000000ll;
-
- static const AString sUserAgent;
-
- String16 mOpPackageName;
-
- State mState;
- VideoFormats mSupportedSourceVideoFormats;
- sp<ANetworkSession> mNetSession;
- sp<IRemoteDisplayClient> mClient;
- AString mMediaPath;
- struct in_addr mInterfaceAddr;
- int32_t mSessionID;
-
- sp<AReplyToken> mStopReplyID;
-
- AString mWfdClientRtpPorts;
- int32_t mChosenRTPPort; // extracted from "wfd_client_rtp_ports"
-
- bool mSinkSupportsVideo;
- VideoFormats mSupportedSinkVideoFormats;
-
- VideoFormats::ResolutionType mChosenVideoResolutionType;
- size_t mChosenVideoResolutionIndex;
- VideoFormats::ProfileType mChosenVideoProfile;
- VideoFormats::LevelType mChosenVideoLevel;
-
- bool mSinkSupportsAudio;
-
- bool mUsingPCMAudio;
- int32_t mClientSessionID;
-
- struct ClientInfo {
- AString mRemoteIP;
- AString mLocalIP;
- int32_t mLocalPort;
- int32_t mPlaybackSessionID;
- sp<PlaybackSession> mPlaybackSession;
- };
- ClientInfo mClientInfo;
-
- bool mReaperPending;
-
- int32_t mNextCSeq;
-
- KeyedVector<ResponseID, HandleRTSPResponseFunc> mResponseHandlers;
-
- // HDCP specific section >>>>
- bool mUsingHDCP;
- bool mIsHDCP2_0;
- int32_t mHDCPPort;
- sp<IHDCP> mHDCP;
- sp<HDCPObserver> mHDCPObserver;
-
- bool mHDCPInitializationComplete;
- bool mSetupTriggerDeferred;
-
- bool mPlaybackSessionEstablished;
-
- status_t makeHDCP();
- // <<<< HDCP specific section
-
- status_t sendM1(int32_t sessionID);
- status_t sendM3(int32_t sessionID);
- status_t sendM4(int32_t sessionID);
-
- enum TriggerType {
- TRIGGER_SETUP,
- TRIGGER_TEARDOWN,
- TRIGGER_PAUSE,
- TRIGGER_PLAY,
- };
-
- // M5
- status_t sendTrigger(int32_t sessionID, TriggerType triggerType);
-
- status_t sendM16(int32_t sessionID);
-
- status_t onReceiveM1Response(
- int32_t sessionID, const sp<ParsedMessage> &msg);
-
- status_t onReceiveM3Response(
- int32_t sessionID, const sp<ParsedMessage> &msg);
-
- status_t onReceiveM4Response(
- int32_t sessionID, const sp<ParsedMessage> &msg);
-
- status_t onReceiveM5Response(
- int32_t sessionID, const sp<ParsedMessage> &msg);
-
- status_t onReceiveM16Response(
- int32_t sessionID, const sp<ParsedMessage> &msg);
-
- void registerResponseHandler(
- int32_t sessionID, int32_t cseq, HandleRTSPResponseFunc func);
-
- status_t onReceiveClientData(const sp<AMessage> &msg);
-
- status_t onOptionsRequest(
- int32_t sessionID,
- int32_t cseq,
- const sp<ParsedMessage> &data);
-
- status_t onSetupRequest(
- int32_t sessionID,
- int32_t cseq,
- const sp<ParsedMessage> &data);
-
- status_t onPlayRequest(
- int32_t sessionID,
- int32_t cseq,
- const sp<ParsedMessage> &data);
-
- status_t onPauseRequest(
- int32_t sessionID,
- int32_t cseq,
- const sp<ParsedMessage> &data);
-
- status_t onTeardownRequest(
- int32_t sessionID,
- int32_t cseq,
- const sp<ParsedMessage> &data);
-
- status_t onGetParameterRequest(
- int32_t sessionID,
- int32_t cseq,
- const sp<ParsedMessage> &data);
-
- status_t onSetParameterRequest(
- int32_t sessionID,
- int32_t cseq,
- const sp<ParsedMessage> &data);
-
- void sendErrorResponse(
- int32_t sessionID,
- const char *errorDetail,
- int32_t cseq);
-
- static void AppendCommonResponse(
- AString *response, int32_t cseq, int32_t playbackSessionID = -1ll);
-
- void scheduleReaper();
- void scheduleKeepAlive(int32_t sessionID);
-
- int32_t makeUniquePlaybackSessionID() const;
-
- sp<PlaybackSession> findPlaybackSession(
- const sp<ParsedMessage> &data, int32_t *playbackSessionID) const;
-
- void finishStop();
- void disconnectClientAsync();
- void disconnectClient2();
- void finishStopAfterDisconnectingClient();
- void finishStop2();
-
- void finishPlay();
-
- DISALLOW_EVIL_CONSTRUCTORS(WifiDisplaySource);
-};
-
-} // namespace android
-
-#endif // WIFI_DISPLAY_SOURCE_H_
diff --git a/media/libstagefright/xmlparser/Android.bp b/media/libstagefright/xmlparser/Android.bp
index ab893de..3507284 100644
--- a/media/libstagefright/xmlparser/Android.bp
+++ b/media/libstagefright/xmlparser/Android.bp
@@ -9,11 +9,6 @@
"MediaCodecsXmlParser.cpp",
],
- include_dirs: [
- "frameworks/av/media/libstagefright",
- "frameworks/av/include",
- ],
-
export_include_dirs: [
"include",
],
@@ -24,6 +19,7 @@
"liblog",
"libcutils",
"libstagefright_foundation",
+ "libstagefright_omx_utils",
],
cflags: [
@@ -38,6 +34,10 @@
"unsigned-integer-overflow",
"signed-integer-overflow",
],
+ cfi: true,
+ diag: {
+ cfi: true,
+ },
},
}
diff --git a/media/libstagefright/xmlparser/MediaCodecsXmlParser.cpp b/media/libstagefright/xmlparser/MediaCodecsXmlParser.cpp
index 4fdd107..ffd30ea 100644
--- a/media/libstagefright/xmlparser/MediaCodecsXmlParser.cpp
+++ b/media/libstagefright/xmlparser/MediaCodecsXmlParser.cpp
@@ -16,157 +16,190 @@
//#define LOG_NDEBUG 0
#define LOG_TAG "MediaCodecsXmlParser"
-#include <utils/Log.h>
#include <media/stagefright/xmlparser/MediaCodecsXmlParser.h>
-#include <media/MediaCodecInfo.h>
-
-#include <media/stagefright/foundation/ADebug.h>
-#include <media/stagefright/foundation/AMessage.h>
-#include <media/stagefright/foundation/AUtils.h>
+#include <utils/Log.h>
#include <media/stagefright/MediaErrors.h>
-
+#include <media/stagefright/omx/OMXUtils.h>
#include <sys/stat.h>
-
#include <expat.h>
-#include <string>
-#define MEDIA_CODECS_CONFIG_FILE_PATH_MAX_LENGTH 256
+#include <cctype>
+#include <algorithm>
namespace android {
-namespace { // Local variables and functions
+namespace {
-const char *kProfilingResults =
- "/data/misc/media/media_codecs_profiling_results.xml";
-
-// Treblized media codec list will be located in /odm/etc or /vendor/etc.
-const char *kConfigLocationList[] =
- {"/odm/etc", "/vendor/etc", "/etc"};
-constexpr int kConfigLocationListSize =
- (sizeof(kConfigLocationList) / sizeof(kConfigLocationList[0]));
-
-bool findMediaCodecListFileFullPath(
- const char *file_name, std::string *out_path) {
- for (int i = 0; i < kConfigLocationListSize; i++) {
- *out_path = std::string(kConfigLocationList[i]) + "/" + file_name;
- struct stat file_stat;
- if (stat(out_path->c_str(), &file_stat) == 0 &&
- S_ISREG(file_stat.st_mode)) {
+/**
+ * Search for a file in a list of search directories.
+ *
+ * For each string `searchDir` in `searchDirs`, `searchDir/fileName` will be
+ * tested whether it is a valid file name or not. If it is a valid file name,
+ * the concatenated name (`searchDir/fileName`) will be stored in the output
+ * variable `outPath`, and the function will return `true`. Otherwise, the
+ * search continues until the `nullptr` element in `searchDirs` is reached, at
+ * which point the function returns `false`.
+ *
+ * \param[in] searchDirs Null-terminated array of search paths.
+ * \param[in] fileName Name of the file to search.
+ * \param[out] outPath Full path of the file. `outPath` will hold a valid file
+ * name if the return value of this function is `true`.
+ * \return `true` if some element in `searchDirs` combined with `fileName` is a
+ * valid file name; `false` otherwise.
+ */
+bool findFileInDirs(
+ const char* const* searchDirs,
+ const char *fileName,
+ std::string *outPath) {
+ for (; *searchDirs != nullptr; ++searchDirs) {
+ *outPath = std::string(*searchDirs) + "/" + fileName;
+ struct stat fileStat;
+ if (stat(outPath->c_str(), &fileStat) == 0 &&
+ S_ISREG(fileStat.st_mode)) {
return true;
}
}
return false;
}
-// Find TypeInfo by name.
-std::vector<TypeInfo>::iterator findTypeInfo(
- CodecInfo &codecInfo, const AString &typeName) {
- return std::find_if(
- codecInfo.mTypes.begin(), codecInfo.mTypes.end(),
- [typeName](const auto &typeInfo) {
- return typeInfo.mName == typeName;
- });
+bool strnEq(const char* s1, const char* s2, size_t count) {
+ return strncmp(s1, s2, count) == 0;
}
-// Convert a string into a boolean value.
-bool ParseBoolean(const char *s) {
- if (!strcasecmp(s, "true") || !strcasecmp(s, "yes") || !strcasecmp(s, "y")) {
- return true;
- }
- char *end;
- unsigned long res = strtoul(s, &end, 10);
- return *s != '\0' && *end == '\0' && res > 0;
+bool strEq(const char* s1, const char* s2) {
+ return strcmp(s1, s2) == 0;
}
-} // unnamed namespace
+bool striEq(const char* s1, const char* s2) {
+ return strcasecmp(s1, s2) == 0;
+}
-MediaCodecsXmlParser::MediaCodecsXmlParser() :
- mInitCheck(NO_INIT),
- mUpdate(false) {
- std::string config_file_path;
- if (findMediaCodecListFileFullPath(
- "media_codecs.xml", &config_file_path)) {
- parseTopLevelXMLFile(config_file_path.c_str(), false);
+bool strHasPrefix(const char* test, const char* prefix) {
+ return strnEq(test, prefix, strlen(prefix));
+}
+
+bool parseBoolean(const char* s) {
+ return striEq(s, "y") ||
+ striEq(s, "yes") ||
+ striEq(s, "t") ||
+ striEq(s, "true") ||
+ striEq(s, "1");
+}
+
+status_t limitFoundMissingAttr(const char* name, const char *attr, bool found = true) {
+ ALOGE("limit '%s' with %s'%s' attribute", name,
+ (found ? "" : "no "), attr);
+ return -EINVAL;
+}
+
+status_t limitError(const char* name, const char *msg) {
+ ALOGE("limit '%s' %s", name, msg);
+ return -EINVAL;
+}
+
+status_t limitInvalidAttr(const char* name, const char* attr, const char* value) {
+ ALOGE("limit '%s' with invalid '%s' attribute (%s)", name,
+ attr, value);
+ return -EINVAL;
+}
+
+}; // unnamed namespace
+
+constexpr char const* MediaCodecsXmlParser::defaultSearchDirs[];
+constexpr char const* MediaCodecsXmlParser::defaultMainXmlName;
+constexpr char const* MediaCodecsXmlParser::defaultPerformanceXmlName;
+constexpr char const* MediaCodecsXmlParser::defaultProfilingResultsXmlPath;
+
+MediaCodecsXmlParser::MediaCodecsXmlParser(
+ const char* const* searchDirs,
+ const char* mainXmlName,
+ const char* performanceXmlName,
+ const char* profilingResultsXmlPath) :
+ mParsingStatus(NO_INIT),
+ mUpdate(false),
+ mCodecCounter(0) {
+ std::string path;
+ if (findFileInDirs(searchDirs, mainXmlName, &path)) {
+ parseTopLevelXMLFile(path.c_str(), false);
} else {
- mInitCheck = NAME_NOT_FOUND;
+ ALOGE("Cannot find %s", mainXmlName);
+ mParsingStatus = NAME_NOT_FOUND;
}
- if (findMediaCodecListFileFullPath(
- "media_codecs_performance.xml", &config_file_path)) {
- parseTopLevelXMLFile(config_file_path.c_str(), true);
+ if (findFileInDirs(searchDirs, performanceXmlName, &path)) {
+ parseTopLevelXMLFile(path.c_str(), true);
}
- parseTopLevelXMLFile(kProfilingResults, true);
+ if (profilingResultsXmlPath != nullptr) {
+ parseTopLevelXMLFile(profilingResultsXmlPath, true);
+ }
}
-void MediaCodecsXmlParser::parseTopLevelXMLFile(
- const char *codecs_xml, bool ignore_errors) {
+bool MediaCodecsXmlParser::parseTopLevelXMLFile(
+ const char *codecs_xml,
+ bool ignore_errors) {
// get href_base
const char *href_base_end = strrchr(codecs_xml, '/');
- if (href_base_end != NULL) {
- mHrefBase = AString(codecs_xml, href_base_end - codecs_xml + 1);
+ if (href_base_end != nullptr) {
+ mHrefBase = std::string(codecs_xml, href_base_end - codecs_xml + 1);
}
- mInitCheck = OK; // keeping this here for safety
+ mParsingStatus = OK; // keeping this here for safety
mCurrentSection = SECTION_TOPLEVEL;
- mDepth = 0;
parseXMLFile(codecs_xml);
- if (mInitCheck != OK) {
+ if (mParsingStatus != OK) {
+ ALOGW("parseTopLevelXMLFile(%s) failed", codecs_xml);
if (ignore_errors) {
- mInitCheck = OK;
- return;
+ mParsingStatus = OK;
+ return false;
}
- mCodecInfos.clear();
- return;
+ mCodecMap.clear();
+ return false;
}
+ return true;
}
MediaCodecsXmlParser::~MediaCodecsXmlParser() {
}
-status_t MediaCodecsXmlParser::initCheck() const {
- return mInitCheck;
-}
-
void MediaCodecsXmlParser::parseXMLFile(const char *path) {
FILE *file = fopen(path, "r");
- if (file == NULL) {
+ if (file == nullptr) {
ALOGW("unable to open media codecs configuration xml file: %s", path);
- mInitCheck = NAME_NOT_FOUND;
+ mParsingStatus = NAME_NOT_FOUND;
return;
}
- ALOGV("Start parsing %s", path);
- XML_Parser parser = ::XML_ParserCreate(NULL);
- CHECK(parser != NULL);
+ XML_Parser parser = ::XML_ParserCreate(nullptr);
+ LOG_FATAL_IF(parser == nullptr, "XML_MediaCodecsXmlParserCreate() failed.");
::XML_SetUserData(parser, this);
::XML_SetElementHandler(
parser, StartElementHandlerWrapper, EndElementHandlerWrapper);
- const int BUFF_SIZE = 512;
- while (mInitCheck == OK) {
+ static constexpr int BUFF_SIZE = 512;
+ while (mParsingStatus == OK) {
void *buff = ::XML_GetBuffer(parser, BUFF_SIZE);
- if (buff == NULL) {
+ if (buff == nullptr) {
ALOGE("failed in call to XML_GetBuffer()");
- mInitCheck = UNKNOWN_ERROR;
+ mParsingStatus = UNKNOWN_ERROR;
break;
}
int bytes_read = ::fread(buff, 1, BUFF_SIZE, file);
if (bytes_read < 0) {
ALOGE("failed in call to read");
- mInitCheck = ERROR_IO;
+ mParsingStatus = ERROR_IO;
break;
}
XML_Status status = ::XML_ParseBuffer(parser, bytes_read, bytes_read == 0);
if (status != XML_STATUS_OK) {
ALOGE("malformed (%s)", ::XML_ErrorString(::XML_GetErrorCode(parser)));
- mInitCheck = ERROR_MALFORMED;
+ mParsingStatus = ERROR_MALFORMED;
break;
}
@@ -178,30 +211,29 @@
::XML_ParserFree(parser);
fclose(file);
- file = NULL;
+ file = nullptr;
}
// static
void MediaCodecsXmlParser::StartElementHandlerWrapper(
void *me, const char *name, const char **attrs) {
- static_cast<MediaCodecsXmlParser *>(me)->startElementHandler(name, attrs);
+ static_cast<MediaCodecsXmlParser*>(me)->startElementHandler(name, attrs);
}
// static
void MediaCodecsXmlParser::EndElementHandlerWrapper(void *me, const char *name) {
- static_cast<MediaCodecsXmlParser *>(me)->endElementHandler(name);
+ static_cast<MediaCodecsXmlParser*>(me)->endElementHandler(name);
}
status_t MediaCodecsXmlParser::includeXMLFile(const char **attrs) {
- const char *href = NULL;
+ const char *href = nullptr;
size_t i = 0;
- while (attrs[i] != NULL) {
- if (!strcmp(attrs[i], "href")) {
- if (attrs[i + 1] == NULL) {
+ while (attrs[i] != nullptr) {
+ if (strEq(attrs[i], "href")) {
+ if (attrs[++i] == nullptr) {
return -EINVAL;
}
- href = attrs[i + 1];
- ++i;
+ href = attrs[i];
} else {
ALOGE("includeXMLFile: unrecognized attribute: %s", attrs[i]);
return -EINVAL;
@@ -222,44 +254,43 @@
return -EINVAL;
}
- AString filename = href;
- if (!filename.startsWith("media_codecs_") ||
- !filename.endsWith(".xml")) {
+ std::string filename = href;
+ if (filename.compare(0, 13, "media_codecs_") != 0 ||
+ filename.compare(filename.size() - 4, 4, ".xml") != 0) {
ALOGE("invalid include file name: %s", href);
return -EINVAL;
}
- filename.insert(mHrefBase, 0);
+ filename.insert(0, mHrefBase);
parseXMLFile(filename.c_str());
- return mInitCheck;
+ return mParsingStatus;
}
void MediaCodecsXmlParser::startElementHandler(
const char *name, const char **attrs) {
- if (mInitCheck != OK) {
+ if (mParsingStatus != OK) {
return;
}
bool inType = true;
- if (!strcmp(name, "Include")) {
- mInitCheck = includeXMLFile(attrs);
- if (mInitCheck == OK) {
- mPastSections.push(mCurrentSection);
+ if (strEq(name, "Include")) {
+ mParsingStatus = includeXMLFile(attrs);
+ if (mParsingStatus == OK) {
+ mSectionStack.push_back(mCurrentSection);
mCurrentSection = SECTION_INCLUDE;
}
- ++mDepth;
return;
}
switch (mCurrentSection) {
case SECTION_TOPLEVEL:
{
- if (!strcmp(name, "Decoders")) {
+ if (strEq(name, "Decoders")) {
mCurrentSection = SECTION_DECODERS;
- } else if (!strcmp(name, "Encoders")) {
+ } else if (strEq(name, "Encoders")) {
mCurrentSection = SECTION_ENCODERS;
- } else if (!strcmp(name, "Settings")) {
+ } else if (strEq(name, "Settings")) {
mCurrentSection = SECTION_SETTINGS;
}
break;
@@ -267,16 +298,16 @@
case SECTION_SETTINGS:
{
- if (!strcmp(name, "Setting")) {
- mInitCheck = addSettingFromAttributes(attrs);
+ if (strEq(name, "Setting")) {
+ mParsingStatus = addSettingFromAttributes(attrs);
}
break;
}
case SECTION_DECODERS:
{
- if (!strcmp(name, "MediaCodec")) {
- mInitCheck =
+ if (strEq(name, "MediaCodec")) {
+ mParsingStatus =
addMediaCodecFromAttributes(false /* encoder */, attrs);
mCurrentSection = SECTION_DECODER;
@@ -286,8 +317,8 @@
case SECTION_ENCODERS:
{
- if (!strcmp(name, "MediaCodec")) {
- mInitCheck =
+ if (strEq(name, "MediaCodec")) {
+ mParsingStatus =
addMediaCodecFromAttributes(true /* encoder */, attrs);
mCurrentSection = SECTION_ENCODER;
@@ -298,13 +329,14 @@
case SECTION_DECODER:
case SECTION_ENCODER:
{
- if (!strcmp(name, "Quirk")) {
- mInitCheck = addQuirk(attrs);
- } else if (!strcmp(name, "Type")) {
- mInitCheck = addTypeFromAttributes(attrs, (mCurrentSection == SECTION_ENCODER));
+ if (strEq(name, "Quirk")) {
+ mParsingStatus = addQuirk(attrs);
+ } else if (strEq(name, "Type")) {
+ mParsingStatus = addTypeFromAttributes(attrs,
+ (mCurrentSection == SECTION_ENCODER));
mCurrentSection =
- (mCurrentSection == SECTION_DECODER
- ? SECTION_DECODER_TYPE : SECTION_ENCODER_TYPE);
+ (mCurrentSection == SECTION_DECODER ?
+ SECTION_DECODER_TYPE : SECTION_ENCODER_TYPE);
}
}
inType = false;
@@ -314,13 +346,15 @@
case SECTION_ENCODER_TYPE:
{
// ignore limits and features specified outside of type
- bool outside = !inType && mCurrentType == mCodecInfos[mCurrentName].mTypes.end();
- if (outside && (!strcmp(name, "Limit") || !strcmp(name, "Feature"))) {
+ bool outside = !inType &&
+ mCurrentType == mCurrentCodec->second.typeMap.end();
+ if (outside &&
+ (strEq(name, "Limit") || strEq(name, "Feature"))) {
ALOGW("ignoring %s specified outside of a Type", name);
- } else if (!strcmp(name, "Limit")) {
- mInitCheck = addLimit(attrs);
- } else if (!strcmp(name, "Feature")) {
- mInitCheck = addFeature(attrs);
+ } else if (strEq(name, "Limit")) {
+ mParsingStatus = addLimit(attrs);
+ } else if (strEq(name, "Feature")) {
+ mParsingStatus = addFeature(attrs);
}
break;
}
@@ -329,18 +363,17 @@
break;
}
- ++mDepth;
}
void MediaCodecsXmlParser::endElementHandler(const char *name) {
- if (mInitCheck != OK) {
+ if (mParsingStatus != OK) {
return;
}
switch (mCurrentSection) {
case SECTION_SETTINGS:
{
- if (!strcmp(name, "Settings")) {
+ if (strEq(name, "Settings")) {
mCurrentSection = SECTION_TOPLEVEL;
}
break;
@@ -348,7 +381,7 @@
case SECTION_DECODERS:
{
- if (!strcmp(name, "Decoders")) {
+ if (strEq(name, "Decoders")) {
mCurrentSection = SECTION_TOPLEVEL;
}
break;
@@ -356,7 +389,7 @@
case SECTION_ENCODERS:
{
- if (!strcmp(name, "Encoders")) {
+ if (strEq(name, "Encoders")) {
mCurrentSection = SECTION_TOPLEVEL;
}
break;
@@ -365,19 +398,19 @@
case SECTION_DECODER_TYPE:
case SECTION_ENCODER_TYPE:
{
- if (!strcmp(name, "Type")) {
+ if (strEq(name, "Type")) {
mCurrentSection =
- (mCurrentSection == SECTION_DECODER_TYPE
- ? SECTION_DECODER : SECTION_ENCODER);
+ (mCurrentSection == SECTION_DECODER_TYPE ?
+ SECTION_DECODER : SECTION_ENCODER);
- mCurrentType = mCodecInfos[mCurrentName].mTypes.end();
+ mCurrentType = mCurrentCodec->second.typeMap.end();
}
break;
}
case SECTION_DECODER:
{
- if (!strcmp(name, "MediaCodec")) {
+ if (strEq(name, "MediaCodec")) {
mCurrentSection = SECTION_DECODERS;
mCurrentName.clear();
}
@@ -386,7 +419,7 @@
case SECTION_ENCODER:
{
- if (!strcmp(name, "MediaCodec")) {
+ if (strEq(name, "MediaCodec")) {
mCurrentSection = SECTION_ENCODERS;
mCurrentName.clear();
}
@@ -395,9 +428,9 @@
case SECTION_INCLUDE:
{
- if (!strcmp(name, "Include") && mPastSections.size() > 0) {
- mCurrentSection = mPastSections.top();
- mPastSections.pop();
+ if (strEq(name, "Include") && (mSectionStack.size() > 0)) {
+ mCurrentSection = mSectionStack.back();
+ mSectionStack.pop_back();
}
break;
}
@@ -406,253 +439,282 @@
break;
}
- --mDepth;
}
status_t MediaCodecsXmlParser::addSettingFromAttributes(const char **attrs) {
- const char *name = NULL;
- const char *value = NULL;
- const char *update = NULL;
+ const char *name = nullptr;
+ const char *value = nullptr;
+ const char *update = nullptr;
size_t i = 0;
- while (attrs[i] != NULL) {
- if (!strcmp(attrs[i], "name")) {
- if (attrs[i + 1] == NULL) {
+ while (attrs[i] != nullptr) {
+ if (strEq(attrs[i], "name")) {
+ if (attrs[++i] == nullptr) {
ALOGE("addSettingFromAttributes: name is null");
return -EINVAL;
}
- name = attrs[i + 1];
- ++i;
- } else if (!strcmp(attrs[i], "value")) {
- if (attrs[i + 1] == NULL) {
+ name = attrs[i];
+ } else if (strEq(attrs[i], "value")) {
+ if (attrs[++i] == nullptr) {
ALOGE("addSettingFromAttributes: value is null");
return -EINVAL;
}
- value = attrs[i + 1];
- ++i;
- } else if (!strcmp(attrs[i], "update")) {
- if (attrs[i + 1] == NULL) {
+ value = attrs[i];
+ } else if (strEq(attrs[i], "update")) {
+ if (attrs[++i] == nullptr) {
ALOGE("addSettingFromAttributes: update is null");
return -EINVAL;
}
- update = attrs[i + 1];
- ++i;
+ update = attrs[i];
} else {
ALOGE("addSettingFromAttributes: unrecognized attribute: %s", attrs[i]);
return -EINVAL;
}
-
++i;
}
- if (name == NULL || value == NULL) {
+ if (name == nullptr || value == nullptr) {
ALOGE("addSettingFromAttributes: name or value unspecified");
return -EINVAL;
}
- mUpdate = (update != NULL) && ParseBoolean(update);
- if (mUpdate != (mGlobalSettings.count(name) > 0)) {
- ALOGE("addSettingFromAttributes: updating non-existing setting");
- return -EINVAL;
+ // Boolean values are converted to "0" or "1".
+ if (strHasPrefix(name, "supports-")) {
+ value = parseBoolean(value) ? "1" : "0";
}
- mGlobalSettings[name] = value;
+
+ mUpdate = (update != nullptr) && parseBoolean(update);
+ auto attribute = mServiceAttributeMap.find(name);
+ if (attribute == mServiceAttributeMap.end()) { // New attribute name
+ if (mUpdate) {
+ ALOGE("addSettingFromAttributes: updating non-existing setting");
+ return -EINVAL;
+ }
+ mServiceAttributeMap.insert(Attribute(name, value));
+ } else { // Existing attribute name
+ if (!mUpdate) {
+ ALOGE("addSettingFromAttributes: adding existing setting");
+ }
+ attribute->second = value;
+ }
return OK;
}
status_t MediaCodecsXmlParser::addMediaCodecFromAttributes(
bool encoder, const char **attrs) {
- const char *name = NULL;
- const char *type = NULL;
- const char *update = NULL;
+ const char *name = nullptr;
+ const char *type = nullptr;
+ const char *update = nullptr;
size_t i = 0;
- while (attrs[i] != NULL) {
- if (!strcmp(attrs[i], "name")) {
- if (attrs[i + 1] == NULL) {
+ while (attrs[i] != nullptr) {
+ if (strEq(attrs[i], "name")) {
+ if (attrs[++i] == nullptr) {
ALOGE("addMediaCodecFromAttributes: name is null");
return -EINVAL;
}
- name = attrs[i + 1];
- ++i;
- } else if (!strcmp(attrs[i], "type")) {
- if (attrs[i + 1] == NULL) {
+ name = attrs[i];
+ } else if (strEq(attrs[i], "type")) {
+ if (attrs[++i] == nullptr) {
ALOGE("addMediaCodecFromAttributes: type is null");
return -EINVAL;
}
- type = attrs[i + 1];
- ++i;
- } else if (!strcmp(attrs[i], "update")) {
- if (attrs[i + 1] == NULL) {
+ type = attrs[i];
+ } else if (strEq(attrs[i], "update")) {
+ if (attrs[++i] == nullptr) {
ALOGE("addMediaCodecFromAttributes: update is null");
return -EINVAL;
}
- update = attrs[i + 1];
- ++i;
+ update = attrs[i];
} else {
ALOGE("addMediaCodecFromAttributes: unrecognized attribute: %s", attrs[i]);
return -EINVAL;
}
-
++i;
}
- if (name == NULL) {
+ if (name == nullptr) {
ALOGE("addMediaCodecFromAttributes: name not found");
return -EINVAL;
}
- mUpdate = (update != NULL) && ParseBoolean(update);
- if (mUpdate != (mCodecInfos.count(name) > 0)) {
- ALOGE("addMediaCodecFromAttributes: updating non-existing codec or vice versa");
- return -EINVAL;
- }
-
- CodecInfo *info = &mCodecInfos[name];
- if (mUpdate) {
- // existing codec
- mCurrentName = name;
- mCurrentType = info->mTypes.begin();
- if (type != NULL) {
- // existing type
- mCurrentType = findTypeInfo(*info, type);
- if (mCurrentType == info->mTypes.end()) {
+ mUpdate = (update != nullptr) && parseBoolean(update);
+ mCurrentCodec = mCodecMap.find(name);
+ if (mCurrentCodec == mCodecMap.end()) { // New codec name
+ if (mUpdate) {
+ ALOGE("addMediaCodecFromAttributes: updating non-existing codec");
+ return -EINVAL;
+ }
+ // Create a new codec in mCodecMap
+ mCurrentCodec = mCodecMap.insert(
+ Codec(name, CodecProperties())).first;
+ if (type != nullptr) {
+ mCurrentType = mCurrentCodec->second.typeMap.insert(
+ Type(type, AttributeMap())).first;
+ } else {
+ mCurrentType = mCurrentCodec->second.typeMap.end();
+ }
+ mCurrentCodec->second.isEncoder = encoder;
+ mCurrentCodec->second.order = mCodecCounter++;
+ } else { // Existing codec name
+ if (!mUpdate) {
+ ALOGE("addMediaCodecFromAttributes: adding existing codec");
+ return -EINVAL;
+ }
+ if (type != nullptr) {
+ mCurrentType = mCurrentCodec->second.typeMap.find(type);
+ if (mCurrentType == mCurrentCodec->second.typeMap.end()) {
ALOGE("addMediaCodecFromAttributes: updating non-existing type");
return -EINVAL;
}
+ } else {
+ // This should happen only when the codec has at most one type.
+ mCurrentType = mCurrentCodec->second.typeMap.begin();
}
- } else {
- // new codec
- mCurrentName = name;
- mQuirks[name].clear();
- info->mTypes.clear();
- info->mTypes.emplace_back();
- mCurrentType = --info->mTypes.end();
- mCurrentType->mName = type;
- info->mIsEncoder = encoder;
}
return OK;
}
status_t MediaCodecsXmlParser::addQuirk(const char **attrs) {
- const char *name = NULL;
+ const char *name = nullptr;
size_t i = 0;
- while (attrs[i] != NULL) {
- if (!strcmp(attrs[i], "name")) {
- if (attrs[i + 1] == NULL) {
+ while (attrs[i] != nullptr) {
+ if (strEq(attrs[i], "name")) {
+ if (attrs[++i] == nullptr) {
ALOGE("addQuirk: name is null");
return -EINVAL;
}
- name = attrs[i + 1];
- ++i;
+ name = attrs[i];
} else {
ALOGE("addQuirk: unrecognized attribute: %s", attrs[i]);
return -EINVAL;
}
-
++i;
}
- if (name == NULL) {
+ if (name == nullptr) {
ALOGE("addQuirk: name not found");
return -EINVAL;
}
- mQuirks[mCurrentName].emplace_back(name);
+ mCurrentCodec->second.quirkSet.emplace(name);
return OK;
}
status_t MediaCodecsXmlParser::addTypeFromAttributes(const char **attrs, bool encoder) {
- const char *name = NULL;
- const char *update = NULL;
+ const char *name = nullptr;
+ const char *update = nullptr;
size_t i = 0;
- while (attrs[i] != NULL) {
- if (!strcmp(attrs[i], "name")) {
- if (attrs[i + 1] == NULL) {
+ while (attrs[i] != nullptr) {
+ if (strEq(attrs[i], "name")) {
+ if (attrs[++i] == nullptr) {
ALOGE("addTypeFromAttributes: name is null");
return -EINVAL;
}
- name = attrs[i + 1];
- ++i;
- } else if (!strcmp(attrs[i], "update")) {
- if (attrs[i + 1] == NULL) {
+ name = attrs[i];
+ } else if (strEq(attrs[i], "update")) {
+ if (attrs[++i] == nullptr) {
ALOGE("addTypeFromAttributes: update is null");
return -EINVAL;
}
- update = attrs[i + 1];
- ++i;
+ update = attrs[i];
} else {
ALOGE("addTypeFromAttributes: unrecognized attribute: %s", attrs[i]);
return -EINVAL;
}
-
++i;
}
- if (name == NULL) {
+ if (name == nullptr) {
return -EINVAL;
}
- CodecInfo *info = &mCodecInfos[mCurrentName];
- info->mIsEncoder = encoder;
- mCurrentType = findTypeInfo(*info, name);
+ mCurrentCodec->second.isEncoder = encoder;
+ mCurrentType = mCurrentCodec->second.typeMap.find(name);
if (!mUpdate) {
- if (mCurrentType != info->mTypes.end()) {
+ if (mCurrentType != mCurrentCodec->second.typeMap.end()) {
ALOGE("addTypeFromAttributes: re-defining existing type without update");
return -EINVAL;
}
- info->mTypes.emplace_back();
- mCurrentType = --info->mTypes.end();
- } else if (mCurrentType == info->mTypes.end()) {
+ mCurrentType = mCurrentCodec->second.typeMap.insert(
+ Type(name, AttributeMap())).first;
+ } else if (mCurrentType == mCurrentCodec->second.typeMap.end()) {
ALOGE("addTypeFromAttributes: updating non-existing type");
- return -EINVAL;
}
-
return OK;
}
-static status_t limitFoundMissingAttr(const AString &name, const char *attr, bool found = true) {
- ALOGE("limit '%s' with %s'%s' attribute", name.c_str(),
- (found ? "" : "no "), attr);
- return -EINVAL;
-}
-
-static status_t limitError(const AString &name, const char *msg) {
- ALOGE("limit '%s' %s", name.c_str(), msg);
- return -EINVAL;
-}
-
-static status_t limitInvalidAttr(const AString &name, const char *attr, const AString &value) {
- ALOGE("limit '%s' with invalid '%s' attribute (%s)", name.c_str(),
- attr, value.c_str());
- return -EINVAL;
-}
-
status_t MediaCodecsXmlParser::addLimit(const char **attrs) {
- sp<AMessage> msg = new AMessage();
+ const char* a_name = nullptr;
+ const char* a_default = nullptr;
+ const char* a_in = nullptr;
+ const char* a_max = nullptr;
+ const char* a_min = nullptr;
+ const char* a_range = nullptr;
+ const char* a_ranges = nullptr;
+ const char* a_scale = nullptr;
+ const char* a_value = nullptr;
size_t i = 0;
- while (attrs[i] != NULL) {
- if (attrs[i + 1] == NULL) {
- ALOGE("addLimit: limit is not given");
- return -EINVAL;
- }
-
- // attributes with values
- if (!strcmp(attrs[i], "name")
- || !strcmp(attrs[i], "default")
- || !strcmp(attrs[i], "in")
- || !strcmp(attrs[i], "max")
- || !strcmp(attrs[i], "min")
- || !strcmp(attrs[i], "range")
- || !strcmp(attrs[i], "ranges")
- || !strcmp(attrs[i], "scale")
- || !strcmp(attrs[i], "value")) {
- msg->setString(attrs[i], attrs[i + 1]);
- ++i;
+ while (attrs[i] != nullptr) {
+ if (strEq(attrs[i], "name")) {
+ if (attrs[++i] == nullptr) {
+ ALOGE("addLimit: name is null");
+ return -EINVAL;
+ }
+ a_name = attrs[i];
+ } else if (strEq(attrs[i], "default")) {
+ if (attrs[++i] == nullptr) {
+ ALOGE("addLimit: default is null");
+ return -EINVAL;
+ }
+ a_default = attrs[i];
+ } else if (strEq(attrs[i], "in")) {
+ if (attrs[++i] == nullptr) {
+ ALOGE("addLimit: in is null");
+ return -EINVAL;
+ }
+ a_in = attrs[i];
+ } else if (strEq(attrs[i], "max")) {
+ if (attrs[++i] == nullptr) {
+ ALOGE("addLimit: max is null");
+ return -EINVAL;
+ }
+ a_max = attrs[i];
+ } else if (strEq(attrs[i], "min")) {
+ if (attrs[++i] == nullptr) {
+ ALOGE("addLimit: min is null");
+ return -EINVAL;
+ }
+ a_min = attrs[i];
+ } else if (strEq(attrs[i], "range")) {
+ if (attrs[++i] == nullptr) {
+ ALOGE("addLimit: range is null");
+ return -EINVAL;
+ }
+ a_range = attrs[i];
+ } else if (strEq(attrs[i], "ranges")) {
+ if (attrs[++i] == nullptr) {
+ ALOGE("addLimit: ranges is null");
+ return -EINVAL;
+ }
+ a_ranges = attrs[i];
+ } else if (strEq(attrs[i], "scale")) {
+ if (attrs[++i] == nullptr) {
+ ALOGE("addLimit: scale is null");
+ return -EINVAL;
+ }
+ a_scale = attrs[i];
+ } else if (strEq(attrs[i], "value")) {
+ if (attrs[++i] == nullptr) {
+ ALOGE("addLimit: value is null");
+ return -EINVAL;
+ }
+ a_value = attrs[i];
} else {
ALOGE("addLimit: unrecognized limit: %s", attrs[i]);
return -EINVAL;
@@ -660,8 +722,7 @@
++i;
}
- AString name;
- if (!msg->findString("name", &name)) {
+ if (a_name == nullptr) {
ALOGE("limit with no 'name' attribute");
return -EINVAL;
}
@@ -670,109 +731,148 @@
// measured-frame-rate, measured-blocks-per-second: range
// quality: range + default + [scale]
// complexity: range + default
- bool found;
- if (mCurrentType == mCodecInfos[mCurrentName].mTypes.end()) {
+ if (mCurrentType == mCurrentCodec->second.typeMap.end()) {
ALOGW("ignoring null type");
return OK;
}
- if (name == "aspect-ratio" || name == "bitrate" || name == "block-count"
- || name == "blocks-per-second" || name == "complexity"
- || name == "frame-rate" || name == "quality" || name == "size"
- || name == "measured-blocks-per-second" || name.startsWith("measured-frame-rate-")) {
- AString min, max;
- if (msg->findString("min", &min) && msg->findString("max", &max)) {
- min.append("-");
- min.append(max);
- if (msg->contains("range") || msg->contains("value")) {
- return limitError(name, "has 'min' and 'max' as well as 'range' or "
+ std::string range;
+ if (strEq(a_name, "aspect-ratio") ||
+ strEq(a_name, "bitrate") ||
+ strEq(a_name, "block-count") ||
+ strEq(a_name, "blocks-per-second") ||
+ strEq(a_name, "complexity") ||
+ strEq(a_name, "frame-rate") ||
+ strEq(a_name, "quality") ||
+ strEq(a_name, "size") ||
+ strEq(a_name, "measured-blocks-per-second") ||
+ strHasPrefix(a_name, "measured-frame-rate-")) {
+ // "range" is specified in exactly one of the following forms:
+ // 1) min-max
+ // 2) value-value
+ // 3) range
+ if (a_min != nullptr && a_max != nullptr) {
+ // min-max
+ if (a_range != nullptr || a_value != nullptr) {
+ return limitError(a_name, "has 'min' and 'max' as well as 'range' or "
"'value' attributes");
}
- msg->setString("range", min);
- } else if (msg->contains("min") || msg->contains("max")) {
- return limitError(name, "has only 'min' or 'max' attribute");
- } else if (msg->findString("value", &max)) {
- min = max;
- min.append("-");
- min.append(max);
- if (msg->contains("range")) {
- return limitError(name, "has both 'range' and 'value' attributes");
+ range = a_min;
+ range += '-';
+ range += a_max;
+ } else if (a_min != nullptr || a_max != nullptr) {
+ return limitError(a_name, "has only 'min' or 'max' attribute");
+ } else if (a_value != nullptr) {
+ // value-value
+ if (a_range != nullptr) {
+ return limitError(a_name, "has both 'range' and 'value' attributes");
}
- msg->setString("range", min);
- }
-
- AString range, scale = "linear", def, in_;
- if (!msg->findString("range", &range)) {
- return limitError(name, "with no 'range', 'value' or 'min'/'max' attributes");
- }
-
- if ((name == "quality" || name == "complexity") ^
- (found = msg->findString("default", &def))) {
- return limitFoundMissingAttr(name, "default", found);
- }
- if (name != "quality" && msg->findString("scale", &scale)) {
- return limitFoundMissingAttr(name, "scale");
- }
- if ((name == "aspect-ratio") ^ (found = msg->findString("in", &in_))) {
- return limitFoundMissingAttr(name, "in", found);
- }
-
- if (name == "aspect-ratio") {
- if (!(in_ == "pixels") && !(in_ == "blocks")) {
- return limitInvalidAttr(name, "in", in_);
- }
- in_.erase(5, 1); // (pixel|block)-aspect-ratio
- in_.append("-");
- in_.append(name);
- name = in_;
- }
- if (name == "quality") {
- mCurrentType->mDetails["quality-scale"] = scale;
- }
- if (name == "quality" || name == "complexity") {
- AString tag = name;
- tag.append("-default");
- mCurrentType->mDetails[tag] = def;
- }
- AString tag = name;
- tag.append("-range");
- mCurrentType->mDetails[tag] = range;
- } else {
- AString max, value, ranges;
- if (msg->contains("default")) {
- return limitFoundMissingAttr(name, "default");
- } else if (msg->contains("in")) {
- return limitFoundMissingAttr(name, "in");
- } else if ((name == "channel-count" || name == "concurrent-instances") ^
- (found = msg->findString("max", &max))) {
- return limitFoundMissingAttr(name, "max", found);
- } else if (msg->contains("min")) {
- return limitFoundMissingAttr(name, "min");
- } else if (msg->contains("range")) {
- return limitFoundMissingAttr(name, "range");
- } else if ((name == "sample-rate") ^
- (found = msg->findString("ranges", &ranges))) {
- return limitFoundMissingAttr(name, "ranges", found);
- } else if (msg->contains("scale")) {
- return limitFoundMissingAttr(name, "scale");
- } else if ((name == "alignment" || name == "block-size") ^
- (found = msg->findString("value", &value))) {
- return limitFoundMissingAttr(name, "value", found);
- }
-
- if (max.size()) {
- AString tag = "max-";
- tag.append(name);
- mCurrentType->mDetails[tag] = max;
- } else if (value.size()) {
- mCurrentType->mDetails[name] = value;
- } else if (ranges.size()) {
- AString tag = name;
- tag.append("-ranges");
- mCurrentType->mDetails[tag] = ranges;
+ range = a_value;
+ range += '-';
+ range += a_value;
+ } else if (a_range == nullptr) {
+ return limitError(a_name, "with no 'range', 'value' or 'min'/'max' attributes");
} else {
- ALOGW("Ignoring unrecognized limit '%s'", name.c_str());
+ // range
+ range = a_range;
}
+
+ // "aspect-ratio" requires some special treatment.
+ if (strEq(a_name, "aspect-ratio")) {
+ // "aspect-ratio" must have "in".
+ if (a_in == nullptr) {
+ return limitFoundMissingAttr(a_name, "in", false);
+ }
+ // "in" must be either "pixels" or "blocks".
+ if (!strEq(a_in, "pixels") && !strEq(a_in, "blocks")) {
+ return limitInvalidAttr(a_name, "in", a_in);
+ }
+ // name will be "pixel-aspect-ratio-range" or
+ // "block-aspect-ratio-range".
+ mCurrentType->second[
+ std::string(a_in).substr(0, strlen(a_in) - 1) +
+ "-aspect-ratio-range"] = range;
+ } else {
+ // For everything else (apart from "aspect-ratio"), simply append
+ // "-range" to the name for the range-type property.
+ mCurrentType->second[std::string(a_name) + "-range"] = range;
+
+ // Only "quality" may have "scale".
+ if (!strEq(a_name, "quality") && a_scale != nullptr) {
+ return limitFoundMissingAttr(a_name, "scale");
+ } else if (strEq(a_name, "quality")) {
+ // The default value of "quality-scale" is "linear".
+ mCurrentType->second["quality-scale"] = a_scale == nullptr ?
+ "linear" : a_scale;
+ }
+
+ // "quality" and "complexity" must have "default".
+ // Other limits must not have "default".
+ if (strEq(a_name, "quality") || strEq(a_name, "complexity")) {
+ if (a_default == nullptr) {
+ return limitFoundMissingAttr(a_name, "default", false);
+ }
+ // name will be "quality-default" or "complexity-default".
+ mCurrentType->second[std::string(a_name) + "-default"] = a_default;
+ } else if (a_default != nullptr) {
+ return limitFoundMissingAttr(a_name, "default", true);
+ }
+ }
+ } else {
+ if (a_default != nullptr) {
+ return limitFoundMissingAttr(a_name, "default");
+ }
+ if (a_in != nullptr) {
+ return limitFoundMissingAttr(a_name, "in");
+ }
+ if (a_scale != nullptr) {
+ return limitFoundMissingAttr(a_name, "scale");
+ }
+ if (a_range != nullptr) {
+ return limitFoundMissingAttr(a_name, "range");
+ }
+ if (a_min != nullptr) {
+ return limitFoundMissingAttr(a_name, "min");
+ }
+
+ if (a_max != nullptr) {
+ // "max" must exist if and only if name is "channel-count" or
+ // "concurrent-instances".
+ // "min" is not ncessary.
+ if (strEq(a_name, "channel-count") ||
+ strEq(a_name, "concurrent-instances")) {
+ mCurrentType->second[std::string("max-") + a_name] = a_max;
+ } else {
+ return limitFoundMissingAttr(a_name, "max", false);
+ }
+ } else if (strEq(a_name, "channel-count") ||
+ strEq(a_name, "concurrent-instances")) {
+ return limitFoundMissingAttr(a_name, "max");
+ }
+
+ if (a_ranges != nullptr) {
+ // "ranges" must exist if and only if name is "sample-rate".
+ if (strEq(a_name, "sample-rate")) {
+ mCurrentType->second["sample-rate-ranges"] = a_ranges;
+ } else {
+ return limitFoundMissingAttr(a_name, "ranges", false);
+ }
+ } else if (strEq(a_name, "sample-rate")) {
+ return limitFoundMissingAttr(a_name, "ranges");
+ }
+
+ if (a_value != nullptr) {
+ // "value" must exist if and only if name is "alignment" or
+ // "block-size".
+ if (strEq(a_name, "alignment") || strEq(a_name, "block-size")) {
+ mCurrentType->second[a_name] = a_value;
+ } else {
+ return limitFoundMissingAttr(a_name, "value", false);
+ }
+ } else if (strEq(a_name, "alignment") || strEq(a_name, "block-size")) {
+ return limitFoundMissingAttr(a_name, "value", false);
+ }
+
}
return OK;
@@ -780,83 +880,175 @@
status_t MediaCodecsXmlParser::addFeature(const char **attrs) {
size_t i = 0;
- const char *name = NULL;
+ const char *name = nullptr;
int32_t optional = -1;
int32_t required = -1;
- const char *value = NULL;
+ const char *value = nullptr;
- while (attrs[i] != NULL) {
- if (attrs[i + 1] == NULL) {
- ALOGE("addFeature: feature is not given");
- return -EINVAL;
- }
-
- // attributes with values
- if (!strcmp(attrs[i], "name")) {
- name = attrs[i + 1];
- ++i;
- } else if (!strcmp(attrs[i], "optional") || !strcmp(attrs[i], "required")) {
- int value = (int)ParseBoolean(attrs[i + 1]);
- if (!strcmp(attrs[i], "optional")) {
- optional = value;
- } else {
- required = value;
+ while (attrs[i] != nullptr) {
+ if (strEq(attrs[i], "name")) {
+ if (attrs[++i] == nullptr) {
+ ALOGE("addFeature: name is null");
+ return -EINVAL;
}
- ++i;
- } else if (!strcmp(attrs[i], "value")) {
- value = attrs[i + 1];
- ++i;
+ name = attrs[i];
+ } else if (strEq(attrs[i], "optional")) {
+ if (attrs[++i] == nullptr) {
+ ALOGE("addFeature: optional is null");
+ return -EINVAL;
+ }
+ optional = parseBoolean(attrs[i]) ? 1 : 0;
+ } else if (strEq(attrs[i], "required")) {
+ if (attrs[++i] == nullptr) {
+ ALOGE("addFeature: required is null");
+ return -EINVAL;
+ }
+ required = parseBoolean(attrs[i]) ? 1 : 0;
+ } else if (strEq(attrs[i], "value")) {
+ if (attrs[++i] == nullptr) {
+ ALOGE("addFeature: value is null");
+ return -EINVAL;
+ }
+ value = attrs[i];
} else {
ALOGE("addFeature: unrecognized attribute: %s", attrs[i]);
return -EINVAL;
}
++i;
}
- if (name == NULL) {
+
+ // Every feature must have a name.
+ if (name == nullptr) {
ALOGE("feature with no 'name' attribute");
return -EINVAL;
}
- if (optional == required && optional != -1) {
- ALOGE("feature '%s' is both/neither optional and required", name);
- return -EINVAL;
- }
-
- if (mCurrentType == mCodecInfos[mCurrentName].mTypes.end()) {
+ if (mCurrentType == mCurrentCodec->second.typeMap.end()) {
ALOGW("ignoring null type");
return OK;
}
- if (value != NULL) {
- mCurrentType->mStringFeatures[name] = value;
- } else {
- mCurrentType->mBoolFeatures[name] = (required == 1) || (optional == 0);
+
+ if ((optional != -1) || (required != -1)) {
+ if (optional == required) {
+ ALOGE("feature '%s' is both/neither optional and required", name);
+ return -EINVAL;
+ }
+ if ((optional == 1) || (required == 1)) {
+ if (value != nullptr) {
+ ALOGE("feature '%s' cannot have extra 'value'", name);
+ return -EINVAL;
+ }
+ mCurrentType->second[std::string("feature-") + name] =
+ optional == 1 ? "0" : "1";
+ return OK;
+ }
}
+ mCurrentType->second[std::string("feature-") + name] = value == nullptr ?
+ "0" : value;
return OK;
}
-void MediaCodecsXmlParser::getGlobalSettings(
- std::map<AString, AString> *settings) const {
- settings->clear();
- settings->insert(mGlobalSettings.begin(), mGlobalSettings.end());
+const MediaCodecsXmlParser::AttributeMap&
+ MediaCodecsXmlParser::getServiceAttributeMap() const {
+ return mServiceAttributeMap;
}
-status_t MediaCodecsXmlParser::getCodecInfo(const char *name, CodecInfo *info) const {
- if (mCodecInfos.count(name) == 0) {
- ALOGE("Codec not found with name '%s'", name);
- return NAME_NOT_FOUND;
+const MediaCodecsXmlParser::CodecMap&
+ MediaCodecsXmlParser::getCodecMap() const {
+ return mCodecMap;
+}
+
+const MediaCodecsXmlParser::RoleMap&
+ MediaCodecsXmlParser::getRoleMap() const {
+ if (mRoleMap.empty()) {
+ generateRoleMap();
}
- *info = mCodecInfos.at(name);
- return OK;
+ return mRoleMap;
}
-status_t MediaCodecsXmlParser::getQuirks(const char *name, std::vector<AString> *quirks) const {
- if (mQuirks.count(name) == 0) {
- ALOGE("Codec not found with name '%s'", name);
- return NAME_NOT_FOUND;
+const char* MediaCodecsXmlParser::getCommonPrefix() const {
+ if (mCommonPrefix.empty()) {
+ generateCommonPrefix();
}
- quirks->clear();
- quirks->insert(quirks->end(), mQuirks.at(name).begin(), mQuirks.at(name).end());
- return OK;
+ return mCommonPrefix.data();
}
-} // namespace android
+status_t MediaCodecsXmlParser::getParsingStatus() const {
+ return mParsingStatus;
+}
+
+void MediaCodecsXmlParser::generateRoleMap() const {
+ for (const auto& codec : mCodecMap) {
+ const auto& codecName = codec.first;
+ bool isEncoder = codec.second.isEncoder;
+ size_t order = codec.second.order;
+ const auto& typeMap = codec.second.typeMap;
+ for (const auto& type : typeMap) {
+ const auto& typeName = type.first;
+ const char* roleName = GetComponentRole(isEncoder, typeName.data());
+ if (roleName == nullptr) {
+ ALOGE("Cannot find the role for %s of type %s",
+ isEncoder ? "an encoder" : "a decoder",
+ typeName.data());
+ continue;
+ }
+ const auto& typeAttributeMap = type.second;
+
+ auto roleIterator = mRoleMap.find(roleName);
+ std::multimap<size_t, NodeInfo>* nodeList;
+ if (roleIterator == mRoleMap.end()) {
+ RoleProperties roleProperties;
+ roleProperties.type = typeName;
+ roleProperties.isEncoder = isEncoder;
+ auto insertResult = mRoleMap.insert(
+ std::make_pair(roleName, roleProperties));
+ if (!insertResult.second) {
+ ALOGE("Cannot add role %s", roleName);
+ continue;
+ }
+ nodeList = &insertResult.first->second.nodeList;
+ } else {
+ if (roleIterator->second.type != typeName) {
+ ALOGE("Role %s has mismatching types: %s and %s",
+ roleName,
+ roleIterator->second.type.data(),
+ typeName.data());
+ continue;
+ }
+ if (roleIterator->second.isEncoder != isEncoder) {
+ ALOGE("Role %s cannot be both an encoder and a decoder",
+ roleName);
+ continue;
+ }
+ nodeList = &roleIterator->second.nodeList;
+ }
+
+ NodeInfo nodeInfo;
+ nodeInfo.name = codecName;
+ nodeInfo.attributeList.reserve(typeAttributeMap.size());
+ for (const auto& attribute : typeAttributeMap) {
+ nodeInfo.attributeList.push_back(
+ Attribute{attribute.first, attribute.second});
+ }
+ nodeList->insert(std::make_pair(
+ std::move(order), std::move(nodeInfo)));
+ }
+ }
+}
+
+void MediaCodecsXmlParser::generateCommonPrefix() const {
+ if (mCodecMap.empty()) {
+ return;
+ }
+ auto i = mCodecMap.cbegin();
+ auto first = i->first.cbegin();
+ auto last = i->first.cend();
+ for (++i; i != mCodecMap.cend(); ++i) {
+ last = std::mismatch(
+ first, last, i->first.cbegin(), i->first.cend()).first;
+ }
+ mCommonPrefix.insert(mCommonPrefix.begin(), first, last);
+}
+
+} // namespace android
+
diff --git a/media/libstagefright/xmlparser/include/media/stagefright/xmlparser/MediaCodecsXmlParser.h b/media/libstagefright/xmlparser/include/media/stagefright/xmlparser/MediaCodecsXmlParser.h
index b324cd8..cc69e52 100644
--- a/media/libstagefright/xmlparser/include/media/stagefright/xmlparser/MediaCodecsXmlParser.h
+++ b/media/libstagefright/xmlparser/include/media/stagefright/xmlparser/MediaCodecsXmlParser.h
@@ -14,65 +14,107 @@
* limitations under the License.
*/
-#ifndef MEDIA_CODECS_XML_PARSER_H_
-
-#define MEDIA_CODECS_XML_PARSER_H_
-
-#include <map>
-#include <vector>
-
-#include <media/stagefright/foundation/ABase.h>
-#include <media/stagefright/foundation/AString.h>
+#ifndef MEDIA_STAGEFRIGHT_XMLPARSER_H_
+#define MEDIA_STAGEFRIGHT_XMLPARSER_H_
#include <sys/types.h>
#include <utils/Errors.h>
#include <utils/Vector.h>
#include <utils/StrongPointer.h>
+#include <string>
+#include <set>
+#include <map>
+#include <vector>
+
namespace android {
-struct AMessage;
-
-// Quirk still supported, even though deprecated
-enum Quirks {
- kRequiresAllocateBufferOnInputPorts = 1,
- kRequiresAllocateBufferOnOutputPorts = 2,
-
- kQuirksMask = kRequiresAllocateBufferOnInputPorts
- | kRequiresAllocateBufferOnOutputPorts,
-};
-
-// Lightweight struct for querying components.
-struct TypeInfo {
- AString mName;
- std::map<AString, AString> mStringFeatures;
- std::map<AString, bool> mBoolFeatures;
- std::map<AString, AString> mDetails;
-};
-
-struct ProfileLevel {
- uint32_t mProfile;
- uint32_t mLevel;
-};
-
-struct CodecInfo {
- std::vector<TypeInfo> mTypes;
- std::vector<ProfileLevel> mProfileLevels;
- std::vector<uint32_t> mColorFormats;
- uint32_t mFlags;
- bool mIsEncoder;
-};
-
class MediaCodecsXmlParser {
public:
- MediaCodecsXmlParser();
+
+ // Treblized media codec list will be located in /odm/etc or /vendor/etc.
+ static constexpr char const* defaultSearchDirs[] =
+ {"/odm/etc", "/vendor/etc", "/etc", nullptr};
+ static constexpr char const* defaultMainXmlName =
+ "media_codecs.xml";
+ static constexpr char const* defaultPerformanceXmlName =
+ "media_codecs_performance.xml";
+ static constexpr char const* defaultProfilingResultsXmlPath =
+ "/data/misc/media/media_codecs_profiling_results.xml";
+
+ MediaCodecsXmlParser(
+ const char* const* searchDirs = defaultSearchDirs,
+ const char* mainXmlName = defaultMainXmlName,
+ const char* performanceXmlName = defaultPerformanceXmlName,
+ const char* profilingResultsXmlPath = defaultProfilingResultsXmlPath);
~MediaCodecsXmlParser();
- void getGlobalSettings(std::map<AString, AString> *settings) const;
+ typedef std::pair<std::string, std::string> Attribute;
+ typedef std::map<std::string, std::string> AttributeMap;
- status_t getCodecInfo(const char *name, CodecInfo *info) const;
+ typedef std::pair<std::string, AttributeMap> Type;
+ typedef std::map<std::string, AttributeMap> TypeMap;
- status_t getQuirks(const char *name, std::vector<AString> *quirks) const;
+ typedef std::set<std::string> QuirkSet;
+
+ /**
+ * Properties of a codec (node)
+ */
+ struct CodecProperties {
+ bool isEncoder; ///< Whether this codec is an encoder or a decoder
+ size_t order; ///< Order of appearance in the file (starting from 0)
+ QuirkSet quirkSet; ///< Set of quirks requested by this codec
+ TypeMap typeMap; ///< Map of types supported by this codec
+ };
+
+ typedef std::pair<std::string, CodecProperties> Codec;
+ typedef std::map<std::string, CodecProperties> CodecMap;
+
+ /**
+ * Properties of a node (for IOmxStore)
+ */
+ struct NodeInfo {
+ std::string name;
+ std::vector<Attribute> attributeList;
+ };
+
+ /**
+ * Properties of a role (for IOmxStore)
+ */
+ struct RoleProperties {
+ std::string type;
+ bool isEncoder;
+ std::multimap<size_t, NodeInfo> nodeList;
+ };
+
+ typedef std::pair<std::string, RoleProperties> Role;
+ typedef std::map<std::string, RoleProperties> RoleMap;
+
+ /**
+ * Return a map for attributes that are service-specific.
+ */
+ const AttributeMap& getServiceAttributeMap() const;
+
+ /**
+ * Return a map for codecs and their properties.
+ */
+ const CodecMap& getCodecMap() const;
+
+ /**
+ * Return a map for roles and their properties.
+ * This map is generated from the CodecMap.
+ */
+ const RoleMap& getRoleMap() const;
+
+ /**
+ * Return a common prefix of all node names.
+ *
+ * The prefix is not provided in the xml, so it has to be computed by taking
+ * the longest common prefix of all node names.
+ */
+ const char* getCommonPrefix() const;
+
+ status_t getParsingStatus() const;
private:
enum Section {
@@ -87,23 +129,31 @@
SECTION_INCLUDE,
};
- status_t mInitCheck;
+ status_t mParsingStatus;
Section mCurrentSection;
bool mUpdate;
- Vector<Section> mPastSections;
- int32_t mDepth;
- AString mHrefBase;
+ std::vector<Section> mSectionStack;
+ std::string mHrefBase;
- std::map<AString, AString> mGlobalSettings;
+ // Service attributes
+ AttributeMap mServiceAttributeMap;
- // name -> CodecInfo
- std::map<AString, CodecInfo> mCodecInfos;
- std::map<AString, std::vector<AString>> mQuirks;
- AString mCurrentName;
- std::vector<TypeInfo>::iterator mCurrentType;
+ // Codec attributes
+ std::string mCurrentName;
+ std::set<std::string> mCodecSet;
+ Codec mCodecListTemp[2048];
+ CodecMap mCodecMap;
+ size_t mCodecCounter;
+ CodecMap::iterator mCurrentCodec;
+ TypeMap::iterator mCurrentType;
- status_t initCheck() const;
- void parseTopLevelXMLFile(const char *path, bool ignore_errors = false);
+ // Role map
+ mutable RoleMap mRoleMap;
+
+ // Computed longest common prefix
+ mutable std::string mCommonPrefix;
+
+ bool parseTopLevelXMLFile(const char *path, bool ignore_errors = false);
void parseXMLFile(const char *path);
@@ -118,7 +168,8 @@
status_t includeXMLFile(const char **attrs);
status_t addSettingFromAttributes(const char **attrs);
status_t addMediaCodecFromAttributes(bool encoder, const char **attrs);
- void addMediaCodec(bool encoder, const char *name, const char *type = NULL);
+ void addMediaCodec(bool encoder, const char *name,
+ const char *type = nullptr);
status_t addQuirk(const char **attrs);
status_t addTypeFromAttributes(const char **attrs, bool encoder);
@@ -126,10 +177,14 @@
status_t addFeature(const char **attrs);
void addType(const char *name);
- DISALLOW_EVIL_CONSTRUCTORS(MediaCodecsXmlParser);
+ void generateRoleMap() const;
+ void generateCommonPrefix() const;
+
+ MediaCodecsXmlParser(const MediaCodecsXmlParser&) = delete;
+ MediaCodecsXmlParser& operator=(const MediaCodecsXmlParser&) = delete;
};
-} // namespace android
+} // namespace android
-#endif // MEDIA_CODECS_XML_PARSER_H_
+#endif // MEDIA_STAGEFRIGHT_XMLPARSER_H_
diff --git a/media/mtp/MtpDatabase.h b/media/mtp/MtpDatabase.h
index 4e6ac7a..c7021df 100644
--- a/media/mtp/MtpDatabase.h
+++ b/media/mtp/MtpDatabase.h
@@ -103,6 +103,9 @@
virtual MtpProperty* getDevicePropertyDesc(MtpDeviceProperty property) = 0;
+ virtual MtpResponseCode moveObject(MtpObjectHandle handle, MtpObjectHandle newParent,
+ MtpString& newPath) = 0;
+
virtual void sessionStarted() = 0;
virtual void sessionEnded() = 0;
diff --git a/media/mtp/MtpFfsHandle.cpp b/media/mtp/MtpFfsHandle.cpp
index 89b20e5..965985d 100644
--- a/media/mtp/MtpFfsHandle.cpp
+++ b/media/mtp/MtpFfsHandle.cpp
@@ -94,8 +94,11 @@
__le32 fs_count;
__le32 hs_count;
__le32 ss_count;
+ __le32 os_count;
struct func_desc fs_descs, hs_descs;
struct ss_func_desc ss_descs;
+ struct usb_os_desc_header os_header;
+ struct usb_ext_compat_desc os_desc;
} __attribute__((packed));
const struct usb_interface_descriptor mtp_interface_desc = {
@@ -261,6 +264,31 @@
},
};
+struct usb_os_desc_header mtp_os_desc_header = {
+ .interface = htole32(1),
+ .dwLength = htole32(sizeof(usb_os_desc_header) + sizeof(usb_ext_compat_desc)),
+ .bcdVersion = htole16(1),
+ .wIndex = htole16(4),
+ .bCount = htole16(1),
+ .Reserved = htole16(0),
+};
+
+struct usb_ext_compat_desc mtp_os_desc_compat = {
+ .bFirstInterfaceNumber = 0,
+ .Reserved1 = htole32(1),
+ .CompatibleID = { 'M', 'T', 'P' },
+ .SubCompatibleID = {0},
+ .Reserved2 = {0},
+};
+
+struct usb_ext_compat_desc ptp_os_desc_compat = {
+ .bFirstInterfaceNumber = 0,
+ .Reserved1 = htole32(1),
+ .CompatibleID = { 'P', 'T', 'P' },
+ .SubCompatibleID = {0},
+ .Reserved2 = {0},
+};
+
struct mtp_device_status {
uint16_t wLength;
uint16_t wCode;
@@ -336,13 +364,16 @@
v2_descriptor.header.magic = htole32(FUNCTIONFS_DESCRIPTORS_MAGIC_V2);
v2_descriptor.header.length = htole32(sizeof(v2_descriptor));
v2_descriptor.header.flags = FUNCTIONFS_HAS_FS_DESC | FUNCTIONFS_HAS_HS_DESC |
- FUNCTIONFS_HAS_SS_DESC;
+ FUNCTIONFS_HAS_SS_DESC | FUNCTIONFS_HAS_MS_OS_DESC;
v2_descriptor.fs_count = 4;
v2_descriptor.hs_count = 4;
v2_descriptor.ss_count = 7;
+ v2_descriptor.os_count = 1;
v2_descriptor.fs_descs = mPtp ? ptp_fs_descriptors : mtp_fs_descriptors;
v2_descriptor.hs_descs = mPtp ? ptp_hs_descriptors : mtp_hs_descriptors;
v2_descriptor.ss_descs = mPtp ? ptp_ss_descriptors : mtp_ss_descriptors;
+ v2_descriptor.os_header = mtp_os_desc_header;
+ v2_descriptor.os_desc = mPtp ? ptp_os_desc_compat : mtp_os_desc_compat;
if (mControl < 0) { // might have already done this before
mControl.reset(TEMP_FAILURE_RETRY(open(FFS_MTP_EP0, O_RDWR)));
diff --git a/media/mtp/MtpServer.cpp b/media/mtp/MtpServer.cpp
index e148b0c..236f3a9 100644
--- a/media/mtp/MtpServer.cpp
+++ b/media/mtp/MtpServer.cpp
@@ -67,8 +67,8 @@
MTP_OPERATION_SET_DEVICE_PROP_VALUE,
MTP_OPERATION_RESET_DEVICE_PROP_VALUE,
// MTP_OPERATION_TERMINATE_OPEN_CAPTURE,
-// MTP_OPERATION_MOVE_OBJECT,
-// MTP_OPERATION_COPY_OBJECT,
+ MTP_OPERATION_MOVE_OBJECT,
+ MTP_OPERATION_COPY_OBJECT,
MTP_OPERATION_GET_PARTIAL_OBJECT,
// MTP_OPERATION_INITIATE_OPEN_CAPTURE,
MTP_OPERATION_GET_OBJECT_PROPS_SUPPORTED,
@@ -437,6 +437,12 @@
case MTP_OPERATION_DELETE_OBJECT:
response = doDeleteObject();
break;
+ case MTP_OPERATION_COPY_OBJECT:
+ response = doCopyObject();
+ break;
+ case MTP_OPERATION_MOVE_OBJECT:
+ response = doMoveObject();
+ break;
case MTP_OPERATION_GET_OBJECT_PROP_DESC:
response = doGetObjectPropDesc();
break;
@@ -1020,6 +1026,137 @@
return MTP_RESPONSE_OK;
}
+MtpResponseCode MtpServer::doMoveObject() {
+ if (!hasStorage())
+ return MTP_RESPONSE_GENERAL_ERROR;
+ if (mRequest.getParameterCount() < 3)
+ return MTP_RESPONSE_INVALID_PARAMETER;
+ MtpObjectHandle objectHandle = mRequest.getParameter(1);
+ MtpStorageID storageID = mRequest.getParameter(2);
+ MtpStorage* storage = getStorage(storageID);
+ MtpObjectHandle parent = mRequest.getParameter(3);
+ if (!storage)
+ return MTP_RESPONSE_INVALID_STORAGE_ID;
+ MtpString path;
+ MtpResponseCode result;
+
+ MtpString fromPath;
+ int64_t fileLength;
+ MtpObjectFormat format;
+ MtpObjectInfo info(objectHandle);
+ result = mDatabase->getObjectInfo(objectHandle, info);
+ if (result != MTP_RESPONSE_OK)
+ return result;
+ result = mDatabase->getObjectFilePath(objectHandle, fromPath, fileLength, format);
+ if (result != MTP_RESPONSE_OK)
+ return result;
+
+ // special case the root
+ if (parent == 0) {
+ path = storage->getPath();
+ } else {
+ int64_t parentLength;
+ MtpObjectFormat parentFormat;
+ result = mDatabase->getObjectFilePath(parent, path, parentLength, parentFormat);
+ if (result != MTP_RESPONSE_OK)
+ return result;
+ if (parentFormat != MTP_FORMAT_ASSOCIATION)
+ return MTP_RESPONSE_INVALID_PARENT_OBJECT;
+ }
+
+ if (path[path.size() - 1] != '/')
+ path += "/";
+ path += info.mName;
+
+ result = mDatabase->moveObject(objectHandle, parent, path);
+ if (result != MTP_RESPONSE_OK)
+ return result;
+
+ if (info.mStorageID == storageID) {
+ ALOGV("Moving file from %s to %s", (const char*)fromPath, (const char*)path);
+ if (rename(fromPath, path)) {
+ ALOGE("rename() failed from %s to %s", (const char*)fromPath, (const char*)path);
+ result = MTP_RESPONSE_GENERAL_ERROR;
+ }
+ } else {
+ ALOGV("Moving across storages from %s to %s", (const char*)fromPath, (const char*)path);
+ if (copyFile(fromPath, path)) {
+ result = MTP_RESPONSE_GENERAL_ERROR;
+ } else {
+ deletePath(fromPath);
+ }
+ }
+
+ // If the move failed, undo the database change
+ if (result != MTP_RESPONSE_OK)
+ if (mDatabase->moveObject(objectHandle, info.mParent, fromPath) != MTP_RESPONSE_OK)
+ ALOGE("Couldn't undo failed move");
+
+ return result;
+}
+
+MtpResponseCode MtpServer::doCopyObject() {
+ if (!hasStorage())
+ return MTP_RESPONSE_GENERAL_ERROR;
+ MtpResponseCode result = MTP_RESPONSE_OK;
+ if (mRequest.getParameterCount() < 3)
+ return MTP_RESPONSE_INVALID_PARAMETER;
+ MtpObjectHandle objectHandle = mRequest.getParameter(1);
+ MtpStorageID storageID = mRequest.getParameter(2);
+ MtpStorage* storage = getStorage(storageID);
+ MtpObjectHandle parent = mRequest.getParameter(3);
+ if (!storage)
+ return MTP_RESPONSE_INVALID_STORAGE_ID;
+ MtpString path;
+
+ MtpString fromPath;
+ int64_t fileLength;
+ MtpObjectFormat format;
+ MtpObjectInfo info(objectHandle);
+ result = mDatabase->getObjectInfo(objectHandle, info);
+ if (result != MTP_RESPONSE_OK)
+ return result;
+ result = mDatabase->getObjectFilePath(objectHandle, fromPath, fileLength, format);
+ if (result != MTP_RESPONSE_OK)
+ return result;
+
+ // special case the root
+ if (parent == 0) {
+ path = storage->getPath();
+ } else {
+ int64_t parentLength;
+ MtpObjectFormat parentFormat;
+ result = mDatabase->getObjectFilePath(parent, path, parentLength, parentFormat);
+ if (result != MTP_RESPONSE_OK)
+ return result;
+ if (parentFormat != MTP_FORMAT_ASSOCIATION)
+ return MTP_RESPONSE_INVALID_PARENT_OBJECT;
+ }
+
+ // check space first
+ if ((uint64_t) fileLength > storage->getFreeSpace())
+ return MTP_RESPONSE_STORAGE_FULL;
+
+ if (path[path.size() - 1] != '/')
+ path += "/";
+ path += info.mName;
+
+ MtpObjectHandle handle = mDatabase->beginSendObject((const char*)path,
+ format, parent, storageID, fileLength, info.mDateModified);
+ if (handle == kInvalidObjectHandle) {
+ return MTP_RESPONSE_GENERAL_ERROR;
+ }
+
+ ALOGV("Copying file from %s to %s", (const char*)fromPath, (const char*)path);
+ if (copyFile(fromPath, path)) {
+ result = MTP_RESPONSE_GENERAL_ERROR;
+ }
+
+ mDatabase->endSendObject(path, handle, format, result);
+ mResponse.setParameter(1, handle);
+ return result;
+}
+
MtpResponseCode MtpServer::doSendObject() {
if (!hasStorage())
return MTP_RESPONSE_GENERAL_ERROR;
@@ -1124,65 +1261,6 @@
return result;
}
-static void deleteRecursive(const char* path) {
- char pathbuf[PATH_MAX];
- size_t pathLength = strlen(path);
- if (pathLength >= sizeof(pathbuf) - 1) {
- ALOGE("path too long: %s\n", path);
- }
- strcpy(pathbuf, path);
- if (pathbuf[pathLength - 1] != '/') {
- pathbuf[pathLength++] = '/';
- }
- char* fileSpot = pathbuf + pathLength;
- int pathRemaining = sizeof(pathbuf) - pathLength - 1;
-
- DIR* dir = opendir(path);
- if (!dir) {
- ALOGE("opendir %s failed: %s", path, strerror(errno));
- return;
- }
-
- struct dirent* entry;
- while ((entry = readdir(dir))) {
- const char* name = entry->d_name;
-
- // ignore "." and ".."
- if (name[0] == '.' && (name[1] == 0 || (name[1] == '.' && name[2] == 0))) {
- continue;
- }
-
- int nameLength = strlen(name);
- if (nameLength > pathRemaining) {
- ALOGE("path %s/%s too long\n", path, name);
- continue;
- }
- strcpy(fileSpot, name);
-
- if (entry->d_type == DT_DIR) {
- deleteRecursive(pathbuf);
- rmdir(pathbuf);
- } else {
- unlink(pathbuf);
- }
- }
- closedir(dir);
-}
-
-static void deletePath(const char* path) {
- struct stat statbuf;
- if (stat(path, &statbuf) == 0) {
- if (S_ISDIR(statbuf.st_mode)) {
- deleteRecursive(path);
- rmdir(path);
- } else {
- unlink(path);
- }
- } else {
- ALOGE("deletePath stat failed for %s: %s", path, strerror(errno));
- }
-}
-
MtpResponseCode MtpServer::doDeleteObject() {
if (!hasStorage())
return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
diff --git a/media/mtp/MtpServer.h b/media/mtp/MtpServer.h
index 08a9e4a..aafc753 100644
--- a/media/mtp/MtpServer.h
+++ b/media/mtp/MtpServer.h
@@ -161,6 +161,8 @@
MtpResponseCode doSendObjectInfo();
MtpResponseCode doSendObject();
MtpResponseCode doDeleteObject();
+ MtpResponseCode doMoveObject();
+ MtpResponseCode doCopyObject();
MtpResponseCode doGetObjectPropDesc();
MtpResponseCode doGetDevicePropDesc();
MtpResponseCode doSendPartialObject();
diff --git a/media/mtp/MtpUtils.cpp b/media/mtp/MtpUtils.cpp
index ebf3601..036ffe7 100644
--- a/media/mtp/MtpUtils.cpp
+++ b/media/mtp/MtpUtils.cpp
@@ -16,13 +16,23 @@
#define LOG_TAG "MtpUtils"
+#include <android-base/logging.h>
+#include <android-base/unique_fd.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <sys/sendfile.h>
+#include <sys/stat.h>
+#include <sys/types.h>
#include <stdio.h>
#include <time.h>
+#include <unistd.h>
#include "MtpUtils.h"
namespace android {
+constexpr unsigned long FILE_COPY_SIZE = 262144;
+
/*
DateTime strings follow a compatible subset of the definition found in ISO 8601, and
take the form of a Unicode string formatted as: "YYYYMMDDThhmmss.s". In this
@@ -78,4 +88,101 @@
tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec);
}
+int copyFile(const char *fromPath, const char *toPath) {
+ auto start = std::chrono::steady_clock::now();
+
+ android::base::unique_fd fromFd(open(fromPath, O_RDONLY));
+ if (fromFd == -1) {
+ PLOG(ERROR) << "Failed to open copy from " << fromPath;
+ return -1;
+ }
+ android::base::unique_fd toFd(open(toPath, O_CREAT | O_WRONLY, S_IRUSR | S_IWUSR));
+ if (toFd == -1) {
+ PLOG(ERROR) << "Failed to open copy to " << toPath;
+ return -1;
+ }
+ off_t offset = 0;
+
+ struct stat sstat = {};
+ if (stat(fromPath, &sstat) == -1)
+ return -1;
+
+ off_t length = sstat.st_size;
+ int ret = 0;
+
+ while (offset < length) {
+ ssize_t transfer_length = std::min(length - offset, (off_t) FILE_COPY_SIZE);
+ ret = sendfile(toFd, fromFd, &offset, transfer_length);
+ if (ret != transfer_length) {
+ ret = -1;
+ PLOG(ERROR) << "Copying failed!";
+ break;
+ }
+ }
+ auto end = std::chrono::steady_clock::now();
+ std::chrono::duration<double> diff = end - start;
+ LOG(INFO) << "Copied a file with MTP. Time: " << diff.count() << " s, Size: " << length <<
+ ", Rate: " << ((double) length) / diff.count() << " bytes/s";
+ return ret == -1 ? -1 : 0;
+}
+
+void deleteRecursive(const char* path) {
+ char pathbuf[PATH_MAX];
+ size_t pathLength = strlen(path);
+ if (pathLength >= sizeof(pathbuf) - 1) {
+ LOG(ERROR) << "path too long: " << path;
+ }
+ strcpy(pathbuf, path);
+ if (pathbuf[pathLength - 1] != '/') {
+ pathbuf[pathLength++] = '/';
+ }
+ char* fileSpot = pathbuf + pathLength;
+ int pathRemaining = sizeof(pathbuf) - pathLength - 1;
+
+ DIR* dir = opendir(path);
+ if (!dir) {
+ PLOG(ERROR) << "opendir " << path << " failed";
+ return;
+ }
+
+ struct dirent* entry;
+ while ((entry = readdir(dir))) {
+ const char* name = entry->d_name;
+
+ // ignore "." and ".."
+ if (name[0] == '.' && (name[1] == 0 || (name[1] == '.' && name[2] == 0))) {
+ continue;
+ }
+
+ int nameLength = strlen(name);
+ if (nameLength > pathRemaining) {
+ LOG(ERROR) << "path " << path << "/" << name << " too long";
+ continue;
+ }
+ strcpy(fileSpot, name);
+
+ if (entry->d_type == DT_DIR) {
+ deleteRecursive(pathbuf);
+ rmdir(pathbuf);
+ } else {
+ unlink(pathbuf);
+ }
+ }
+ closedir(dir);
+}
+
+void deletePath(const char* path) {
+ struct stat statbuf;
+ if (stat(path, &statbuf) == 0) {
+ if (S_ISDIR(statbuf.st_mode)) {
+ deleteRecursive(path);
+ rmdir(path);
+ } else {
+ unlink(path);
+ }
+ } else {
+ PLOG(ERROR) << "deletePath stat failed for " << path;;
+ }
+}
+
} // namespace android
diff --git a/media/mtp/MtpUtils.h b/media/mtp/MtpUtils.h
index 61f9055..a2bb7e1 100644
--- a/media/mtp/MtpUtils.h
+++ b/media/mtp/MtpUtils.h
@@ -24,6 +24,10 @@
bool parseDateTime(const char* dateTime, time_t& outSeconds);
void formatDateTime(time_t seconds, char* buffer, int bufferLength);
+int copyFile(const char *fromPath, const char *toPath);
+void deleteRecursive(const char* path);
+void deletePath(const char* path);
+
}; // namespace android
#endif // _MTP_UTILS_H
diff --git a/media/ndk/Android.bp b/media/ndk/Android.bp
index 0d48de1..116f156 100644
--- a/media/ndk/Android.bp
+++ b/media/ndk/Android.bp
@@ -69,7 +69,6 @@
"libmedia",
"libmedia_jni",
"libmediadrm",
- "libskia",
"libstagefright",
"libstagefright_foundation",
"liblog",
diff --git a/media/ndk/NdkImage.cpp b/media/ndk/NdkImage.cpp
index 6d28d1b..87b649a 100644
--- a/media/ndk/NdkImage.cpp
+++ b/media/ndk/NdkImage.cpp
@@ -53,21 +53,25 @@
void
AImage::close(int releaseFenceFd) {
+ lockReader();
Mutex::Autolock _l(mLock);
if (mIsClosed) {
return;
}
sp<AImageReader> reader = mReader.promote();
- if (reader == nullptr) {
- LOG_ALWAYS_FATAL("Error: AImage not closed before AImageReader close!");
- return;
+ if (reader != nullptr) {
+ reader->releaseImageLocked(this, releaseFenceFd);
+ } else if (mBuffer != nullptr) {
+ LOG_ALWAYS_FATAL("%s: parent AImageReader closed without releasing image %p",
+ __FUNCTION__, this);
}
- reader->releaseImageLocked(this, releaseFenceFd);
+
// Should have been set to nullptr in releaseImageLocked
// Set to nullptr here for extra safety only
mBuffer = nullptr;
mLockedBuffer = nullptr;
mIsClosed = true;
+ unlockReader();
}
void
@@ -618,9 +622,7 @@
void AImage_deleteAsync(AImage* image, int releaseFenceFd) {
ALOGV("%s", __FUNCTION__);
if (image != nullptr) {
- image->lockReader();
image->close(releaseFenceFd);
- image->unlockReader();
if (!image->isClosed()) {
LOG_ALWAYS_FATAL("Image close failed!");
}
diff --git a/media/ndk/NdkImageReader.cpp b/media/ndk/NdkImageReader.cpp
index a450dd3..e90783d 100644
--- a/media/ndk/NdkImageReader.cpp
+++ b/media/ndk/NdkImageReader.cpp
@@ -349,6 +349,7 @@
for (auto it = mAcquiredImages.begin();
it != mAcquiredImages.end(); it++) {
AImage* image = *it;
+ releaseImageLocked(image, /*releaseFenceFd*/-1);
image->close();
}
diff --git a/media/ndk/NdkMediaDrm.cpp b/media/ndk/NdkMediaDrm.cpp
index 51143ac..eecc858 100644
--- a/media/ndk/NdkMediaDrm.cpp
+++ b/media/ndk/NdkMediaDrm.cpp
@@ -421,7 +421,7 @@
for (size_t i = 0; i < mObj->mQueryResults.size(); i++) {
keyValuePairs[i].mKey = mObj->mQueryResults.keyAt(i).string();
- keyValuePairs[i].mValue = mObj->mQueryResults.keyAt(i).string();
+ keyValuePairs[i].mValue = mObj->mQueryResults.valueAt(i).string();
}
*numPairs = mObj->mQueryResults.size();
return AMEDIA_OK;
diff --git a/media/ndk/include/media/NdkMediaCodec.h b/media/ndk/include/media/NdkMediaCodec.h
index 7e7e81e..faf2d14 100644
--- a/media/ndk/include/media/NdkMediaCodec.h
+++ b/media/ndk/include/media/NdkMediaCodec.h
@@ -39,6 +39,7 @@
#endif
struct ANativeWindow;
+typedef struct ANativeWindow ANativeWindow;
#if __ANDROID_API__ >= 21
@@ -149,6 +150,7 @@
#if (defined(__cplusplus) && __cplusplus >= 201103L) || \
__STDC_VERSION__ >= 201112L
+#include <assert.h>
static_assert(sizeof(_off_t_compat) == sizeof(long),
"_off_t_compat does not match the NDK ABI. See "
"https://github.com/android-ndk/ndk/issues/459.");
@@ -208,6 +210,8 @@
media_status_t AMediaCodec_releaseOutputBufferAtTime(
AMediaCodec *mData, size_t idx, int64_t timestampNs);
+#if __ANDROID_API__ >= 26
+
/**
* Creates a Surface that can be used as the input to encoder, in place of input buffers
*
@@ -278,7 +282,7 @@
*/
media_status_t AMediaCodec_signalEndOfInputStream(AMediaCodec *mData);
-
+#endif /* __ANDROID_API__ >= 26 */
typedef enum {
AMEDIACODECRYPTOINFO_MODE_CLEAR = 0,
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index f906b6f..79e540a 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -329,6 +329,11 @@
thread->configure(attr, streamType, sessionId, callback, *deviceId, portId);
*handle = portId;
} else {
+ if (direction == MmapStreamInterface::DIRECTION_OUTPUT) {
+ AudioSystem::releaseOutput(io, streamType, sessionId);
+ } else {
+ AudioSystem::releaseInput(io, sessionId);
+ }
ret = NO_INIT;
}
@@ -1397,11 +1402,11 @@
// the config change is always sent from playback or record threads to avoid deadlock
// with AudioSystem::gLock
for (size_t i = 0; i < mPlaybackThreads.size(); i++) {
- mPlaybackThreads.valueAt(i)->sendIoConfigEvent(AUDIO_OUTPUT_OPENED, pid);
+ mPlaybackThreads.valueAt(i)->sendIoConfigEvent(AUDIO_OUTPUT_REGISTERED, pid);
}
for (size_t i = 0; i < mRecordThreads.size(); i++) {
- mRecordThreads.valueAt(i)->sendIoConfigEvent(AUDIO_INPUT_OPENED, pid);
+ mRecordThreads.valueAt(i)->sendIoConfigEvent(AUDIO_INPUT_REGISTERED, pid);
}
}
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index 85d19cb..9544e5d 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -1668,7 +1668,8 @@
mScreenState(AudioFlinger::mScreenState),
// index 0 is reserved for normal mixer's submix
mFastTrackAvailMask(((1 << FastMixerState::sMaxFastTracks) - 1) & ~1),
- mHwSupportsPause(false), mHwPaused(false), mFlushPending(false)
+ mHwSupportsPause(false), mHwPaused(false), mFlushPending(false),
+ mLeftVolFloat(-1.0), mRightVolFloat(-1.0)
{
snprintf(mThreadName, kThreadNameLength, "AudioOut_%X", id);
mNBLogWriter = audioFlinger->newWriter_l(kLogSize, mThreadName);
@@ -2253,6 +2254,7 @@
switch (event) {
case AUDIO_OUTPUT_OPENED:
+ case AUDIO_OUTPUT_REGISTERED:
case AUDIO_OUTPUT_CONFIG_CHANGED:
desc->mPatch = mPatch;
desc->mChannelMask = mChannelMask;
@@ -4299,6 +4301,7 @@
param = AudioMixer::RAMP_VOLUME;
}
mAudioMixer->setParameter(name, AudioMixer::RESAMPLE, AudioMixer::RESET, NULL);
+ mLeftVolFloat = -1.0;
// FIXME should not make a decision based on mServer
} else if (cblk->mServer != 0) {
// If the track is stopped before the first frame was mixed,
@@ -4309,6 +4312,10 @@
// compute volume for this track
uint32_t vl, vr; // in U8.24 integer format
float vlf, vrf, vaf; // in [0.0, 1.0] float format
+ // read original volumes with volume control
+ float typeVolume = mStreamTypes[track->streamType()].volume;
+ float v = masterVolume * typeVolume;
+
if (track->isPausing() || mStreamTypes[track->streamType()].mute) {
vl = vr = 0;
vlf = vrf = vaf = 0.;
@@ -4316,10 +4323,6 @@
track->setPaused();
}
} else {
-
- // read original volumes with volume control
- float typeVolume = mStreamTypes[track->streamType()].volume;
- float v = masterVolume * typeVolume;
sp<AudioTrackServerProxy> proxy = track->mAudioTrackServerProxy;
gain_minifloat_packed_t vlr = proxy->getVolumeLR();
vlf = float_from_gain(gain_minifloat_unpack_left(vlr));
@@ -4371,6 +4374,25 @@
track->mHasVolumeController = false;
}
+ // For dedicated VoIP outputs, let the HAL apply the stream volume. Track volume is
+ // still applied by the mixer.
+ if ((mOutput->flags & AUDIO_OUTPUT_FLAG_VOIP_RX) != 0) {
+ v = mStreamTypes[track->streamType()].mute ? 0.0f : v;
+ if (v != mLeftVolFloat) {
+ status_t result = mOutput->stream->setVolume(v, v);
+ ALOGE_IF(result != OK, "Error when setting output stream volume: %d", result);
+ if (result == OK) {
+ mLeftVolFloat = v;
+ }
+ }
+ // if stream volume was successfully sent to the HAL, mLeftVolFloat == v here and we
+ // remove stream volume contribution from software volume.
+ if (v != 0.0f && mLeftVolFloat == v) {
+ vlf = min(1.0f, vlf / v);
+ vrf = min(1.0f, vrf / v);
+ vaf = min(1.0f, vaf / v);
+ }
+ }
// XXX: these things DON'T need to be done each time
mAudioMixer->setBufferProvider(name, track);
mAudioMixer->enable(name);
@@ -4812,7 +4834,6 @@
AudioFlinger::DirectOutputThread::DirectOutputThread(const sp<AudioFlinger>& audioFlinger,
AudioStreamOut* output, audio_io_handle_t id, audio_devices_t device, bool systemReady)
: PlaybackThread(audioFlinger, output, id, device, DIRECT, systemReady)
- // mLeftVolFloat, mRightVolFloat
{
}
@@ -4820,7 +4841,6 @@
AudioStreamOut* output, audio_io_handle_t id, uint32_t device,
ThreadBase::type_t type, bool systemReady)
: PlaybackThread(audioFlinger, output, id, device, type, systemReady)
- // mLeftVolFloat, mRightVolFloat
, mVolumeShaperActive(false)
{
}
@@ -7247,6 +7267,7 @@
switch (event) {
case AUDIO_INPUT_OPENED:
+ case AUDIO_INPUT_REGISTERED:
case AUDIO_INPUT_CONFIG_CHANGED:
desc->mPatch = mPatch;
desc->mChannelMask = mChannelMask;
@@ -7638,6 +7659,10 @@
return NO_ERROR;
}
+ if (!isOutput() && !recordingAllowed(client.packageName, client.clientPid, client.clientUid)) {
+ return PERMISSION_DENIED;
+ }
+
audio_port_handle_t portId = AUDIO_PORT_HANDLE_NONE;
audio_io_handle_t io = mId;
@@ -7914,8 +7939,10 @@
switch (event) {
case AUDIO_INPUT_OPENED:
+ case AUDIO_INPUT_REGISTERED:
case AUDIO_INPUT_CONFIG_CHANGED:
case AUDIO_OUTPUT_OPENED:
+ case AUDIO_OUTPUT_REGISTERED:
case AUDIO_OUTPUT_CONFIG_CHANGED:
desc->mPatch = mPatch;
desc->mChannelMask = mChannelMask;
@@ -8126,10 +8153,8 @@
void AudioFlinger::MmapThread::threadLoop_exit()
{
- sp<MmapStreamCallback> callback = mCallback.promote();
- if (callback != 0) {
- callback->onTearDown();
- }
+ // Do not call callback->onTearDown() because it is redundant for thread exit
+ // and because it can cause a recursive mutex lock on stop().
}
status_t AudioFlinger::MmapThread::setSyncEvent(const sp<SyncEvent>& event __unused)
diff --git a/services/audioflinger/Threads.h b/services/audioflinger/Threads.h
index bfd9a3f..b685e1b 100644
--- a/services/audioflinger/Threads.h
+++ b/services/audioflinger/Threads.h
@@ -1001,6 +1001,9 @@
bool mHwSupportsPause;
bool mHwPaused;
bool mFlushPending;
+ // volumes last sent to audio HAL with stream->setVolume()
+ float mLeftVolFloat;
+ float mRightVolFloat;
};
class MixerThread : public PlaybackThread {
@@ -1118,9 +1121,6 @@
virtual void onAddNewTrack_l();
- // volumes last sent to audio HAL with stream->set_volume()
- float mLeftVolFloat;
- float mRightVolFloat;
bool mVolumeShaperActive;
DirectOutputThread(const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output,
diff --git a/services/audiopolicy/config/primary_audio_policy_configuration.xml b/services/audiopolicy/config/primary_audio_policy_configuration.xml
index bf508ac..5b7ae7f 100644
--- a/services/audiopolicy/config/primary_audio_policy_configuration.xml
+++ b/services/audiopolicy/config/primary_audio_policy_configuration.xml
@@ -13,7 +13,7 @@
</mixPort>
<mixPort name="primary input" role="sink">
<profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
- samplingRates="8000, 16000" channelMasks="AUDIO_CHANNEL_IN_MONO"/>
+ samplingRates="8000,16000" channelMasks="AUDIO_CHANNEL_IN_MONO"/>
</mixPort>
</mixPorts>
<devicePorts>
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
index d2a2855..906e05a 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
@@ -1444,62 +1444,13 @@
"session %d, flags %#x",
attr->source, config->sample_rate, config->format, config->channel_mask, session, flags);
- // special case for mmap capture: if an input IO handle is specified, we reuse this input if
- // possible
- if ((flags & AUDIO_INPUT_FLAG_MMAP_NOIRQ) == AUDIO_INPUT_FLAG_MMAP_NOIRQ &&
- *input != AUDIO_IO_HANDLE_NONE) {
- ssize_t index = mInputs.indexOfKey(*input);
- if (index < 0) {
- ALOGW("getInputForAttr() unknown MMAP input %d", *input);
- return BAD_VALUE;
- }
- sp<AudioInputDescriptor> inputDesc = mInputs.valueAt(index);
- sp<AudioSession> audioSession = inputDesc->getAudioSession(session);
- if (audioSession == 0) {
- ALOGW("getInputForAttr() unknown session %d on input %d", session, *input);
- return BAD_VALUE;
- }
- // For MMAP mode, the first call to getInputForAttr() is made on behalf of audioflinger.
- // The second call is for the first active client and sets the UID. Any further call
- // corresponds to a new client and is only permitted from the same UId.
- if (audioSession->openCount() == 1) {
- audioSession->setUid(uid);
- } else if (audioSession->uid() != uid) {
- ALOGW("getInputForAttr() bad uid %d for session %d uid %d",
- uid, session, audioSession->uid());
- return INVALID_OPERATION;
- }
- audioSession->changeOpenCount(1);
- *inputType = API_INPUT_LEGACY;
- if (*portId == AUDIO_PORT_HANDLE_NONE) {
- *portId = AudioPort::getNextUniqueId();
- }
- DeviceVector inputDevices = mAvailableInputDevices.getDevicesFromType(inputDesc->mDevice);
- *selectedDeviceId = inputDevices.size() > 0 ? inputDevices.itemAt(0)->getId()
- : AUDIO_PORT_HANDLE_NONE;
- ALOGI("%s reusing MMAP input %d for session %d", __FUNCTION__, *input, session);
- return NO_ERROR;
- }
-
- *input = AUDIO_IO_HANDLE_NONE;
- *inputType = API_INPUT_INVALID;
-
- audio_devices_t device;
+ status_t status = NO_ERROR;
// handle legacy remote submix case where the address was not always specified
String8 address = String8("");
- audio_source_t inputSource = attr->source;
audio_source_t halInputSource;
+ audio_source_t inputSource = attr->source;
AudioMix *policyMix = NULL;
-
- if (inputSource == AUDIO_SOURCE_DEFAULT) {
- inputSource = AUDIO_SOURCE_MIC;
- }
- halInputSource = inputSource;
-
- // TODO: check for existing client for this port ID
- if (*portId == AUDIO_PORT_HANDLE_NONE) {
- *portId = AudioPort::getNextUniqueId();
- }
+ DeviceVector inputDevices;
// Explicit routing?
sp<DeviceDescriptor> deviceDesc;
@@ -1513,11 +1464,67 @@
}
mInputRoutes.addRoute(session, SessionRoute::STREAM_TYPE_NA, inputSource, deviceDesc, uid);
+ // special case for mmap capture: if an input IO handle is specified, we reuse this input if
+ // possible
+ if ((flags & AUDIO_INPUT_FLAG_MMAP_NOIRQ) == AUDIO_INPUT_FLAG_MMAP_NOIRQ &&
+ *input != AUDIO_IO_HANDLE_NONE) {
+ ssize_t index = mInputs.indexOfKey(*input);
+ if (index < 0) {
+ ALOGW("getInputForAttr() unknown MMAP input %d", *input);
+ status = BAD_VALUE;
+ goto error;
+ }
+ sp<AudioInputDescriptor> inputDesc = mInputs.valueAt(index);
+ sp<AudioSession> audioSession = inputDesc->getAudioSession(session);
+ if (audioSession == 0) {
+ ALOGW("getInputForAttr() unknown session %d on input %d", session, *input);
+ status = BAD_VALUE;
+ goto error;
+ }
+ // For MMAP mode, the first call to getInputForAttr() is made on behalf of audioflinger.
+ // The second call is for the first active client and sets the UID. Any further call
+ // corresponds to a new client and is only permitted from the same UId.
+ if (audioSession->openCount() == 1) {
+ audioSession->setUid(uid);
+ } else if (audioSession->uid() != uid) {
+ ALOGW("getInputForAttr() bad uid %d for session %d uid %d",
+ uid, session, audioSession->uid());
+ status = INVALID_OPERATION;
+ goto error;
+ }
+ audioSession->changeOpenCount(1);
+ *inputType = API_INPUT_LEGACY;
+ if (*portId == AUDIO_PORT_HANDLE_NONE) {
+ *portId = AudioPort::getNextUniqueId();
+ }
+ inputDevices = mAvailableInputDevices.getDevicesFromType(inputDesc->mDevice);
+ *selectedDeviceId = inputDevices.size() > 0 ? inputDevices.itemAt(0)->getId()
+ : AUDIO_PORT_HANDLE_NONE;
+ ALOGI("%s reusing MMAP input %d for session %d", __FUNCTION__, *input, session);
+
+ return NO_ERROR;
+ }
+
+ *input = AUDIO_IO_HANDLE_NONE;
+ *inputType = API_INPUT_INVALID;
+
+ if (inputSource == AUDIO_SOURCE_DEFAULT) {
+ inputSource = AUDIO_SOURCE_MIC;
+ }
+ halInputSource = inputSource;
+
+ // TODO: check for existing client for this port ID
+ if (*portId == AUDIO_PORT_HANDLE_NONE) {
+ *portId = AudioPort::getNextUniqueId();
+ }
+
+ audio_devices_t device;
+
if (inputSource == AUDIO_SOURCE_REMOTE_SUBMIX &&
strncmp(attr->tags, "addr=", strlen("addr=")) == 0) {
- status_t ret = mPolicyMixes.getInputMixForAttr(*attr, &policyMix);
- if (ret != NO_ERROR) {
- return ret;
+ status = mPolicyMixes.getInputMixForAttr(*attr, &policyMix);
+ if (status != NO_ERROR) {
+ goto error;
}
*inputType = API_INPUT_MIX_EXT_POLICY_REROUTE;
device = AUDIO_DEVICE_IN_REMOTE_SUBMIX;
@@ -1526,7 +1533,8 @@
device = getDeviceAndMixForInputSource(inputSource, &policyMix);
if (device == AUDIO_DEVICE_NONE) {
ALOGW("getInputForAttr() could not find device for source %d", inputSource);
- return BAD_VALUE;
+ status = BAD_VALUE;
+ goto error;
}
if (policyMix != NULL) {
address = policyMix->mDeviceAddress;
@@ -1555,11 +1563,11 @@
config->sample_rate, config->format, config->channel_mask, flags,
policyMix);
if (*input == AUDIO_IO_HANDLE_NONE) {
- mInputRoutes.removeRoute(session);
- return INVALID_OPERATION;
+ status = INVALID_OPERATION;
+ goto error;
}
- DeviceVector inputDevices = mAvailableInputDevices.getDevicesFromType(device);
+ inputDevices = mAvailableInputDevices.getDevicesFromType(device);
*selectedDeviceId = inputDevices.size() > 0 ? inputDevices.itemAt(0)->getId()
: AUDIO_PORT_HANDLE_NONE;
@@ -1567,6 +1575,10 @@
*input, *inputType, *selectedDeviceId);
return NO_ERROR;
+
+error:
+ mInputRoutes.removeRoute(session);
+ return status;
}
@@ -4683,12 +4695,13 @@
// scan the whole RouteMap, for each entry, convert the stream type to a strategy
// (getStrategy(stream)).
// if the strategy from the stream type in the RouteMap is the same as the argument above,
- // and activity count is non-zero
- // the device = the device from the descriptor in the RouteMap, and exit.
+ // and activity count is non-zero and the device in the route descriptor is available
+ // then select this device.
for (size_t routeIndex = 0; routeIndex < mOutputRoutes.size(); routeIndex++) {
sp<SessionRoute> route = mOutputRoutes.valueAt(routeIndex);
routing_strategy routeStrategy = getStrategy(route->mStreamType);
- if ((routeStrategy == strategy) && route->isActive()) {
+ if ((routeStrategy == strategy) && route->isActive() &&
+ (mAvailableOutputDevices.indexOf(route->mDeviceDescriptor) >= 0)) {
return route->mDeviceDescriptor->type();
}
}
@@ -5083,9 +5096,15 @@
audio_devices_t AudioPolicyManager::getDeviceForInputSource(audio_source_t inputSource)
{
+ // Routing
+ // Scan the whole RouteMap to see if we have an explicit route:
+ // if the input source in the RouteMap is the same as the argument above,
+ // and activity count is non-zero and the device in the route descriptor is available
+ // then select this device.
for (size_t routeIndex = 0; routeIndex < mInputRoutes.size(); routeIndex++) {
sp<SessionRoute> route = mInputRoutes.valueAt(routeIndex);
- if (inputSource == route->mSource && route->isActive()) {
+ if ((inputSource == route->mSource) && route->isActive() &&
+ (mAvailableInputDevices.indexOf(route->mDeviceDescriptor) >= 0)) {
return route->mDeviceDescriptor->type();
}
}
diff --git a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
index 1d4386c..b7bce55 100644
--- a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
+++ b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
@@ -694,6 +694,7 @@
audio_io_handle_t *ioHandle,
audio_devices_t *device)
{
+ Mutex::Autolock _l(mLock);
if (mAudioPolicyManager == NULL) {
return NO_INIT;
}
@@ -703,6 +704,7 @@
status_t AudioPolicyService::releaseSoundTriggerSession(audio_session_t session)
{
+ Mutex::Autolock _l(mLock);
if (mAudioPolicyManager == NULL) {
return NO_INIT;
}
diff --git a/services/camera/libcameraservice/Android.mk b/services/camera/libcameraservice/Android.mk
index f1cdea3..7ec3ccb 100644
--- a/services/camera/libcameraservice/Android.mk
+++ b/services/camera/libcameraservice/Android.mk
@@ -77,7 +77,8 @@
android.hardware.camera.common@1.0 \
android.hardware.camera.provider@2.4 \
android.hardware.camera.device@1.0 \
- android.hardware.camera.device@3.2
+ android.hardware.camera.device@3.2 \
+ android.hardware.camera.device@3.3
LOCAL_EXPORT_SHARED_LIBRARY_HEADERS := libbinder libcamera_client libfmq
diff --git a/services/camera/libcameraservice/api1/Camera2Client.cpp b/services/camera/libcameraservice/api1/Camera2Client.cpp
index a28518e..2cf648f 100644
--- a/services/camera/libcameraservice/api1/Camera2Client.cpp
+++ b/services/camera/libcameraservice/api1/Camera2Client.cpp
@@ -72,6 +72,20 @@
return initializeImpl(manager);
}
+bool Camera2Client::isZslEnabledInStillTemplate() {
+ bool zslEnabled = false;
+ CameraMetadata stillTemplate;
+ status_t res = mDevice->createDefaultRequest(CAMERA2_TEMPLATE_STILL_CAPTURE, &stillTemplate);
+ if (res == OK) {
+ camera_metadata_entry_t enableZsl = stillTemplate.find(ANDROID_CONTROL_ENABLE_ZSL);
+ if (enableZsl.count == 1) {
+ zslEnabled = (enableZsl.data.u8[0] == ANDROID_CONTROL_ENABLE_ZSL_TRUE);
+ }
+ }
+
+ return zslEnabled;
+}
+
template<typename TProviderPtr>
status_t Camera2Client::initializeImpl(TProviderPtr providerPtr)
{
@@ -93,6 +107,8 @@
__FUNCTION__, mCameraId, strerror(-res), res);
return NO_INIT;
}
+
+ l.mParameters.isDeviceZslSupported = isZslEnabledInStillTemplate();
}
String8 threadName;
diff --git a/services/camera/libcameraservice/api1/Camera2Client.h b/services/camera/libcameraservice/api1/Camera2Client.h
index 72315d4..5af74eb 100644
--- a/services/camera/libcameraservice/api1/Camera2Client.h
+++ b/services/camera/libcameraservice/api1/Camera2Client.h
@@ -224,6 +224,8 @@
template<typename TProviderPtr>
status_t initializeImpl(TProviderPtr providerPtr);
+
+ bool isZslEnabledInStillTemplate();
};
}; // namespace android
diff --git a/services/camera/libcameraservice/api1/client2/CallbackProcessor.cpp b/services/camera/libcameraservice/api1/client2/CallbackProcessor.cpp
index 3f4017f..0d2dba1 100644
--- a/services/camera/libcameraservice/api1/client2/CallbackProcessor.cpp
+++ b/services/camera/libcameraservice/api1/client2/CallbackProcessor.cpp
@@ -121,18 +121,17 @@
if (mCallbackStreamId != NO_STREAM) {
// Check if stream parameters have to change
- uint32_t currentWidth, currentHeight, currentFormat;
- res = device->getStreamInfo(mCallbackStreamId,
- ¤tWidth, ¤tHeight, ¤tFormat, 0);
+ CameraDeviceBase::StreamInfo streamInfo;
+ res = device->getStreamInfo(mCallbackStreamId, &streamInfo);
if (res != OK) {
ALOGE("%s: Camera %d: Error querying callback output stream info: "
"%s (%d)", __FUNCTION__, mId,
strerror(-res), res);
return res;
}
- if (currentWidth != (uint32_t)params.previewWidth ||
- currentHeight != (uint32_t)params.previewHeight ||
- currentFormat != (uint32_t)callbackFormat) {
+ if (streamInfo.width != (uint32_t)params.previewWidth ||
+ streamInfo.height != (uint32_t)params.previewHeight ||
+ !streamInfo.matchFormat((uint32_t)callbackFormat)) {
// Since size should only change while preview is not running,
// assuming that all existing use of old callback stream is
// completed.
diff --git a/services/camera/libcameraservice/api1/client2/JpegProcessor.cpp b/services/camera/libcameraservice/api1/client2/JpegProcessor.cpp
index 4981ce7..d1bbdaf 100755
--- a/services/camera/libcameraservice/api1/client2/JpegProcessor.cpp
+++ b/services/camera/libcameraservice/api1/client2/JpegProcessor.cpp
@@ -136,17 +136,16 @@
if (mCaptureStreamId != NO_STREAM) {
// Check if stream parameters have to change
- uint32_t currentWidth, currentHeight;
- res = device->getStreamInfo(mCaptureStreamId,
- ¤tWidth, ¤tHeight, 0, 0);
+ CameraDeviceBase::StreamInfo streamInfo;
+ res = device->getStreamInfo(mCaptureStreamId, &streamInfo);
if (res != OK) {
ALOGE("%s: Camera %d: Error querying capture output stream info: "
"%s (%d)", __FUNCTION__,
mId, strerror(-res), res);
return res;
}
- if (currentWidth != (uint32_t)params.pictureWidth ||
- currentHeight != (uint32_t)params.pictureHeight) {
+ if (streamInfo.width != (uint32_t)params.pictureWidth ||
+ streamInfo.height != (uint32_t)params.pictureHeight) {
ALOGV("%s: Camera %d: Deleting stream %d since the buffer dimensions changed",
__FUNCTION__, mId, mCaptureStreamId);
res = device->deleteStream(mCaptureStreamId);
diff --git a/services/camera/libcameraservice/api1/client2/Parameters.cpp b/services/camera/libcameraservice/api1/client2/Parameters.cpp
index 1addcdd..02a7616 100644
--- a/services/camera/libcameraservice/api1/client2/Parameters.cpp
+++ b/services/camera/libcameraservice/api1/client2/Parameters.cpp
@@ -954,7 +954,8 @@
}
}
- if (slowJpegMode || property_get_bool("camera.disable_zsl_mode", false)) {
+ if (isDeviceZslSupported || slowJpegMode ||
+ property_get_bool("camera.disable_zsl_mode", false)) {
ALOGI("Camera %d: Disabling ZSL mode", cameraId);
allowZslMode = false;
} else {
@@ -1997,7 +1998,8 @@
if (previewFpsRange[1] > 1e9/minFrameDurationNs + FPS_MARGIN) {
slowJpegMode = true;
}
- if (slowJpegMode || property_get_bool("camera.disable_zsl_mode", false)) {
+ if (isDeviceZslSupported || slowJpegMode ||
+ property_get_bool("camera.disable_zsl_mode", false)) {
allowZslMode = false;
} else {
allowZslMode = isZslReprocessPresent;
diff --git a/services/camera/libcameraservice/api1/client2/Parameters.h b/services/camera/libcameraservice/api1/client2/Parameters.h
index bea867a..17e3d75 100644
--- a/services/camera/libcameraservice/api1/client2/Parameters.h
+++ b/services/camera/libcameraservice/api1/client2/Parameters.h
@@ -175,6 +175,8 @@
bool slowJpegMode;
// Whether ZSL reprocess is supported by the device.
bool isZslReprocessPresent;
+ // Whether the device supports enableZsl.
+ bool isDeviceZslSupported;
// Overall camera state
enum State {
diff --git a/services/camera/libcameraservice/api1/client2/StreamingProcessor.cpp b/services/camera/libcameraservice/api1/client2/StreamingProcessor.cpp
index d79e430..73dca73 100644
--- a/services/camera/libcameraservice/api1/client2/StreamingProcessor.cpp
+++ b/services/camera/libcameraservice/api1/client2/StreamingProcessor.cpp
@@ -161,18 +161,17 @@
if (mPreviewStreamId != NO_STREAM) {
// Check if stream parameters have to change
- uint32_t currentWidth, currentHeight;
- res = device->getStreamInfo(mPreviewStreamId,
- ¤tWidth, ¤tHeight, 0, 0);
+ CameraDeviceBase::StreamInfo streamInfo;
+ res = device->getStreamInfo(mPreviewStreamId, &streamInfo);
if (res != OK) {
ALOGE("%s: Camera %d: Error querying preview stream info: "
"%s (%d)", __FUNCTION__, mId, strerror(-res), res);
return res;
}
- if (currentWidth != (uint32_t)params.previewWidth ||
- currentHeight != (uint32_t)params.previewHeight) {
+ if (streamInfo.width != (uint32_t)params.previewWidth ||
+ streamInfo.height != (uint32_t)params.previewHeight) {
ALOGV("%s: Camera %d: Preview size switch: %d x %d -> %d x %d",
- __FUNCTION__, mId, currentWidth, currentHeight,
+ __FUNCTION__, mId, streamInfo.width, streamInfo.height,
params.previewWidth, params.previewHeight);
res = device->waitUntilDrained();
if (res != OK) {
@@ -312,10 +311,8 @@
return INVALID_OPERATION;
}
- uint32_t currentWidth, currentHeight, currentFormat;
- android_dataspace currentDataSpace;
- res = device->getStreamInfo(mRecordingStreamId,
- ¤tWidth, ¤tHeight, ¤tFormat, ¤tDataSpace);
+ CameraDeviceBase::StreamInfo streamInfo;
+ res = device->getStreamInfo(mRecordingStreamId, &streamInfo);
if (res != OK) {
ALOGE("%s: Camera %d: Error querying recording output stream info: "
"%s (%d)", __FUNCTION__, mId,
@@ -324,10 +321,10 @@
}
if (mRecordingWindow == nullptr ||
- currentWidth != (uint32_t)params.videoWidth ||
- currentHeight != (uint32_t)params.videoHeight ||
- currentFormat != (uint32_t)params.videoFormat ||
- currentDataSpace != params.videoDataSpace) {
+ streamInfo.width != (uint32_t)params.videoWidth ||
+ streamInfo.height != (uint32_t)params.videoHeight ||
+ !streamInfo.matchFormat((uint32_t)params.videoFormat) ||
+ !streamInfo.matchDataSpace(params.videoDataSpace)) {
*needsUpdate = true;
return res;
}
@@ -348,22 +345,18 @@
if (mRecordingStreamId != NO_STREAM) {
// Check if stream parameters have to change
- uint32_t currentWidth, currentHeight;
- uint32_t currentFormat;
- android_dataspace currentDataSpace;
- res = device->getStreamInfo(mRecordingStreamId,
- ¤tWidth, ¤tHeight,
- ¤tFormat, ¤tDataSpace);
+ CameraDeviceBase::StreamInfo streamInfo;
+ res = device->getStreamInfo(mRecordingStreamId, &streamInfo);
if (res != OK) {
ALOGE("%s: Camera %d: Error querying recording output stream info: "
"%s (%d)", __FUNCTION__, mId,
strerror(-res), res);
return res;
}
- if (currentWidth != (uint32_t)params.videoWidth ||
- currentHeight != (uint32_t)params.videoHeight ||
- currentFormat != (uint32_t)params.videoFormat ||
- currentDataSpace != params.videoDataSpace) {
+ if (streamInfo.width != (uint32_t)params.videoWidth ||
+ streamInfo.height != (uint32_t)params.videoHeight ||
+ !streamInfo.matchFormat((uint32_t)params.videoFormat) ||
+ !streamInfo.matchDataSpace(params.videoDataSpace)) {
// TODO: Should wait to be sure previous recording has finished
res = device->deleteStream(mRecordingStreamId);
diff --git a/services/camera/libcameraservice/api1/client2/ZslProcessor.cpp b/services/camera/libcameraservice/api1/client2/ZslProcessor.cpp
index 9bc31b9..b0607fb 100644
--- a/services/camera/libcameraservice/api1/client2/ZslProcessor.cpp
+++ b/services/camera/libcameraservice/api1/client2/ZslProcessor.cpp
@@ -233,17 +233,16 @@
if ((mZslStreamId != NO_STREAM) || (mInputStreamId != NO_STREAM)) {
// Check if stream parameters have to change
- uint32_t currentWidth, currentHeight;
- res = device->getStreamInfo(mZslStreamId,
- ¤tWidth, ¤tHeight, 0, 0);
+ CameraDeviceBase::StreamInfo streamInfo;
+ res = device->getStreamInfo(mZslStreamId, &streamInfo);
if (res != OK) {
ALOGE("%s: Camera %d: Error querying capture output stream info: "
"%s (%d)", __FUNCTION__,
client->getCameraId(), strerror(-res), res);
return res;
}
- if (currentWidth != (uint32_t)params.fastInfo.arrayWidth ||
- currentHeight != (uint32_t)params.fastInfo.arrayHeight) {
+ if (streamInfo.width != (uint32_t)params.fastInfo.arrayWidth ||
+ streamInfo.height != (uint32_t)params.fastInfo.arrayHeight) {
if (mZslStreamId != NO_STREAM) {
ALOGV("%s: Camera %d: Deleting stream %d since the buffer "
"dimensions changed",
diff --git a/services/camera/libcameraservice/api2/CameraDeviceClient.cpp b/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
index 29bc21c..f985382 100644
--- a/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
+++ b/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
@@ -1354,7 +1354,7 @@
sp<hardware::camera2::ICameraDeviceCallbacks> remoteCb = getRemoteCallback();
if (remoteCb != 0) {
- remoteCb->onRepeatingRequestError(lastFrameNumber);
+ remoteCb->onRepeatingRequestError(lastFrameNumber, mStreamingRequestId);
}
Mutex::Autolock idLock(mStreamingRequestIdLock);
diff --git a/services/camera/libcameraservice/common/CameraDeviceBase.h b/services/camera/libcameraservice/common/CameraDeviceBase.h
index 54fcb0a..3919bfa 100644
--- a/services/camera/libcameraservice/common/CameraDeviceBase.h
+++ b/services/camera/libcameraservice/common/CameraDeviceBase.h
@@ -142,12 +142,51 @@
virtual status_t createInputStream(uint32_t width, uint32_t height,
int32_t format, /*out*/ int32_t *id) = 0;
+ struct StreamInfo {
+ uint32_t width;
+ uint32_t height;
+
+ uint32_t format;
+ bool formatOverridden;
+ uint32_t originalFormat;
+
+ android_dataspace dataSpace;
+ bool dataSpaceOverridden;
+ android_dataspace originalDataSpace;
+
+ StreamInfo() : width(0), height(0), format(0), formatOverridden(false), originalFormat(0),
+ dataSpace(HAL_DATASPACE_UNKNOWN), dataSpaceOverridden(false),
+ originalDataSpace(HAL_DATASPACE_UNKNOWN) {}
+ /**
+ * Check whether the format matches the current or the original one in case
+ * it got overridden.
+ */
+ bool matchFormat(uint32_t clientFormat) const {
+ if ((formatOverridden && (originalFormat == clientFormat)) ||
+ (format == clientFormat)) {
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Check whether the dataspace matches the current or the original one in case
+ * it got overridden.
+ */
+ bool matchDataSpace(android_dataspace clientDataSpace) const {
+ if ((dataSpaceOverridden && (originalDataSpace == clientDataSpace)) ||
+ (dataSpace == clientDataSpace)) {
+ return true;
+ }
+ return false;
+ }
+
+ };
+
/**
* Get information about a given stream.
*/
- virtual status_t getStreamInfo(int id,
- uint32_t *width, uint32_t *height,
- uint32_t *format, android_dataspace *dataSpace) = 0;
+ virtual status_t getStreamInfo(int id, StreamInfo *streamInfo) = 0;
/**
* Set stream gralloc buffer transform
diff --git a/services/camera/libcameraservice/common/CameraProviderManager.cpp b/services/camera/libcameraservice/common/CameraProviderManager.cpp
index 5addaf1..ae3bbc1 100644
--- a/services/camera/libcameraservice/common/CameraProviderManager.cpp
+++ b/services/camera/libcameraservice/common/CameraProviderManager.cpp
@@ -202,12 +202,17 @@
for (auto& provider : mProviders) {
auto deviceInfo = findDeviceInfoLocked(id);
if (deviceInfo != nullptr) {
- provider->mInterface->isSetTorchModeSupported(
+ auto ret = provider->mInterface->isSetTorchModeSupported(
[&support](auto status, bool supported) {
if (status == Status::OK) {
support = supported;
}
});
+ if (!ret.isOk()) {
+ ALOGE("%s: Transaction error checking torch mode support '%s': %s",
+ __FUNCTION__, provider->mProviderName.c_str(), ret.description().c_str());
+ }
+ break;
}
}
return support;
@@ -1339,7 +1344,7 @@
desc->mReverseMapping[reverseIndex]->add(desc->mTagToNameMap.valueFor(tag), tag);
}
- descriptor = desc;
+ descriptor = std::move(desc);
return OK;
}
diff --git a/services/camera/libcameraservice/device1/CameraHardwareInterface.cpp b/services/camera/libcameraservice/device1/CameraHardwareInterface.cpp
index 991b50f..522d521 100644
--- a/services/camera/libcameraservice/device1/CameraHardwareInterface.cpp
+++ b/services/camera/libcameraservice/device1/CameraHardwareInterface.cpp
@@ -97,58 +97,78 @@
ALOGE("%s: CameraHeapMemory has FD %d (expect >= 0)", __FUNCTION__, memPoolId);
return 0;
}
+ std::lock_guard<std::mutex> lock(mHidlMemPoolMapLock);
mHidlMemPoolMap.insert(std::make_pair(memPoolId, mem));
return memPoolId;
}
hardware::Return<void> CameraHardwareInterface::unregisterMemory(uint32_t memId) {
- if (mHidlMemPoolMap.count(memId) == 0) {
- ALOGE("%s: memory pool ID %d not found", __FUNCTION__, memId);
- return hardware::Void();
+ camera_memory_t* mem = nullptr;
+ {
+ std::lock_guard<std::mutex> lock(mHidlMemPoolMapLock);
+ if (mHidlMemPoolMap.count(memId) == 0) {
+ ALOGE("%s: memory pool ID %d not found", __FUNCTION__, memId);
+ return hardware::Void();
+ }
+ mem = mHidlMemPoolMap.at(memId);
+ mHidlMemPoolMap.erase(memId);
}
- camera_memory_t* mem = mHidlMemPoolMap.at(memId);
sPutMemory(mem);
- mHidlMemPoolMap.erase(memId);
return hardware::Void();
}
hardware::Return<void> CameraHardwareInterface::dataCallback(
DataCallbackMsg msgType, uint32_t data, uint32_t bufferIndex,
const hardware::camera::device::V1_0::CameraFrameMetadata& metadata) {
- if (mHidlMemPoolMap.count(data) == 0) {
- ALOGE("%s: memory pool ID %d not found", __FUNCTION__, data);
- return hardware::Void();
+ camera_memory_t* mem = nullptr;
+ {
+ std::lock_guard<std::mutex> lock(mHidlMemPoolMapLock);
+ if (mHidlMemPoolMap.count(data) == 0) {
+ ALOGE("%s: memory pool ID %d not found", __FUNCTION__, data);
+ return hardware::Void();
+ }
+ mem = mHidlMemPoolMap.at(data);
}
camera_frame_metadata_t md;
md.number_of_faces = metadata.faces.size();
md.faces = (camera_face_t*) metadata.faces.data();
- sDataCb((int32_t) msgType, mHidlMemPoolMap.at(data), bufferIndex, &md, this);
+ sDataCb((int32_t) msgType, mem, bufferIndex, &md, this);
return hardware::Void();
}
hardware::Return<void> CameraHardwareInterface::dataCallbackTimestamp(
DataCallbackMsg msgType, uint32_t data,
uint32_t bufferIndex, int64_t timestamp) {
- if (mHidlMemPoolMap.count(data) == 0) {
- ALOGE("%s: memory pool ID %d not found", __FUNCTION__, data);
- return hardware::Void();
+ camera_memory_t* mem = nullptr;
+ {
+ std::lock_guard<std::mutex> lock(mHidlMemPoolMapLock);
+ if (mHidlMemPoolMap.count(data) == 0) {
+ ALOGE("%s: memory pool ID %d not found", __FUNCTION__, data);
+ return hardware::Void();
+ }
+ mem = mHidlMemPoolMap.at(data);
}
- sDataCbTimestamp(timestamp, (int32_t) msgType, mHidlMemPoolMap.at(data), bufferIndex, this);
+ sDataCbTimestamp(timestamp, (int32_t) msgType, mem, bufferIndex, this);
return hardware::Void();
}
hardware::Return<void> CameraHardwareInterface::handleCallbackTimestamp(
DataCallbackMsg msgType, const hidl_handle& frameData, uint32_t data,
uint32_t bufferIndex, int64_t timestamp) {
- if (mHidlMemPoolMap.count(data) == 0) {
- ALOGE("%s: memory pool ID %d not found", __FUNCTION__, data);
- return hardware::Void();
+ camera_memory_t* mem = nullptr;
+ {
+ std::lock_guard<std::mutex> lock(mHidlMemPoolMapLock);
+ if (mHidlMemPoolMap.count(data) == 0) {
+ ALOGE("%s: memory pool ID %d not found", __FUNCTION__, data);
+ return hardware::Void();
+ }
+ mem = mHidlMemPoolMap.at(data);
}
- sp<CameraHeapMemory> mem(static_cast<CameraHeapMemory *>(mHidlMemPoolMap.at(data)->handle));
+ sp<CameraHeapMemory> heapMem(static_cast<CameraHeapMemory *>(mem->handle));
VideoNativeHandleMetadata* md = (VideoNativeHandleMetadata*)
- mem->mBuffers[bufferIndex]->pointer();
+ heapMem->mBuffers[bufferIndex]->pointer();
md->pHandle = const_cast<native_handle_t*>(frameData.getNativeHandle());
- sDataCbTimestamp(timestamp, (int32_t) msgType, mHidlMemPoolMap.at(data), bufferIndex, this);
+ sDataCbTimestamp(timestamp, (int32_t) msgType, mem, bufferIndex, this);
return hardware::Void();
}
@@ -157,27 +177,28 @@
const hardware::hidl_vec<hardware::camera::device::V1_0::HandleTimestampMessage>& messages) {
std::vector<android::HandleTimestampMessage> msgs;
msgs.reserve(messages.size());
+ {
+ std::lock_guard<std::mutex> lock(mHidlMemPoolMapLock);
+ for (const auto& hidl_msg : messages) {
+ if (mHidlMemPoolMap.count(hidl_msg.data) == 0) {
+ ALOGE("%s: memory pool ID %d not found", __FUNCTION__, hidl_msg.data);
+ return hardware::Void();
+ }
+ sp<CameraHeapMemory> mem(
+ static_cast<CameraHeapMemory *>(mHidlMemPoolMap.at(hidl_msg.data)->handle));
- for (const auto& hidl_msg : messages) {
- if (mHidlMemPoolMap.count(hidl_msg.data) == 0) {
- ALOGE("%s: memory pool ID %d not found", __FUNCTION__, hidl_msg.data);
- return hardware::Void();
+ if (hidl_msg.bufferIndex >= mem->mNumBufs) {
+ ALOGE("%s: invalid buffer index %d, max allowed is %d", __FUNCTION__,
+ hidl_msg.bufferIndex, mem->mNumBufs);
+ return hardware::Void();
+ }
+ VideoNativeHandleMetadata* md = (VideoNativeHandleMetadata*)
+ mem->mBuffers[hidl_msg.bufferIndex]->pointer();
+ md->pHandle = const_cast<native_handle_t*>(hidl_msg.frameData.getNativeHandle());
+
+ msgs.push_back({hidl_msg.timestamp, mem->mBuffers[hidl_msg.bufferIndex]});
}
- sp<CameraHeapMemory> mem(
- static_cast<CameraHeapMemory *>(mHidlMemPoolMap.at(hidl_msg.data)->handle));
-
- if (hidl_msg.bufferIndex >= mem->mNumBufs) {
- ALOGE("%s: invalid buffer index %d, max allowed is %d", __FUNCTION__,
- hidl_msg.bufferIndex, mem->mNumBufs);
- return hardware::Void();
- }
- VideoNativeHandleMetadata* md = (VideoNativeHandleMetadata*)
- mem->mBuffers[hidl_msg.bufferIndex]->pointer();
- md->pHandle = const_cast<native_handle_t*>(hidl_msg.frameData.getNativeHandle());
-
- msgs.push_back({hidl_msg.timestamp, mem->mBuffers[hidl_msg.bufferIndex]});
}
-
mDataCbTimestampBatch((int32_t) msgType, msgs, mCbUser);
return hardware::Void();
}
diff --git a/services/camera/libcameraservice/device1/CameraHardwareInterface.h b/services/camera/libcameraservice/device1/CameraHardwareInterface.h
index 6a1b4fb..e519b04 100644
--- a/services/camera/libcameraservice/device1/CameraHardwareInterface.h
+++ b/services/camera/libcameraservice/device1/CameraHardwareInterface.h
@@ -479,6 +479,7 @@
uint64_t mNextBufferId = 1;
static const uint64_t BUFFER_ID_NO_BUFFER = 0;
+ std::mutex mHidlMemPoolMapLock; // protecting mHidlMemPoolMap
std::unordered_map<int, camera_memory_t*> mHidlMemPoolMap;
};
diff --git a/services/camera/libcameraservice/device3/Camera3Device.cpp b/services/camera/libcameraservice/device3/Camera3Device.cpp
index 94e8f3b..ced1d3a 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.cpp
+++ b/services/camera/libcameraservice/device3/Camera3Device.cpp
@@ -154,6 +154,15 @@
resultQueueRet.description().c_str());
return DEAD_OBJECT;
}
+ IF_ALOGV() {
+ session->interfaceChain([](
+ ::android::hardware::hidl_vec<::android::hardware::hidl_string> interfaceChain) {
+ ALOGV("Session interface chain:");
+ for (auto iface : interfaceChain) {
+ ALOGV(" %s", iface.c_str());
+ }
+ });
+ }
mInterface = new HalInterface(session, queue);
std::string providerType;
@@ -460,6 +469,11 @@
return static_cast<uint32_t>(pixelFormat);
}
+android_dataspace Camera3Device::mapToFrameworkDataspace(
+ DataspaceFlags dataSpace) {
+ return static_cast<android_dataspace>(dataSpace);
+}
+
uint64_t Camera3Device::mapConsumerToFrameworkUsage(
BufferUsageFlags usage) {
return usage;
@@ -1345,10 +1359,11 @@
return OK;
}
-status_t Camera3Device::getStreamInfo(int id,
- uint32_t *width, uint32_t *height,
- uint32_t *format, android_dataspace *dataSpace) {
+status_t Camera3Device::getStreamInfo(int id, StreamInfo *streamInfo) {
ATRACE_CALL();
+ if (nullptr == streamInfo) {
+ return BAD_VALUE;
+ }
Mutex::Autolock il(mInterfaceLock);
Mutex::Autolock l(mLock);
@@ -1375,10 +1390,14 @@
return idx;
}
- if (width) *width = mOutputStreams[idx]->getWidth();
- if (height) *height = mOutputStreams[idx]->getHeight();
- if (format) *format = mOutputStreams[idx]->getFormat();
- if (dataSpace) *dataSpace = mOutputStreams[idx]->getDataSpace();
+ streamInfo->width = mOutputStreams[idx]->getWidth();
+ streamInfo->height = mOutputStreams[idx]->getHeight();
+ streamInfo->format = mOutputStreams[idx]->getFormat();
+ streamInfo->dataSpace = mOutputStreams[idx]->getDataSpace();
+ streamInfo->formatOverridden = mOutputStreams[idx]->isFormatOverridden();
+ streamInfo->originalFormat = mOutputStreams[idx]->getOriginalFormat();
+ streamInfo->dataSpaceOverridden = mOutputStreams[idx]->isDataSpaceOverridden();
+ streamInfo->originalDataSpace = mOutputStreams[idx]->getOriginalDataSpace();
return OK;
}
@@ -3193,17 +3212,51 @@
// Invoke configureStreams
- HalStreamConfiguration finalConfiguration;
+ device::V3_3::HalStreamConfiguration finalConfiguration;
common::V1_0::Status status;
- auto err = mHidlSession->configureStreams(requestedConfiguration,
+
+ // See if we have v3.3 HAL
+ sp<device::V3_3::ICameraDeviceSession> hidlSession_3_3;
+ auto castResult = device::V3_3::ICameraDeviceSession::castFrom(mHidlSession);
+ if (castResult.isOk()) {
+ hidlSession_3_3 = castResult;
+ } else {
+ ALOGE("%s: Transaction error when casting ICameraDeviceSession: %s", __FUNCTION__,
+ castResult.description().c_str());
+ }
+ if (hidlSession_3_3 != nullptr) {
+ // We do; use v3.3 for the call
+ ALOGV("%s: v3.3 device found", __FUNCTION__);
+ auto err = hidlSession_3_3->configureStreams_3_3(requestedConfiguration,
[&status, &finalConfiguration]
- (common::V1_0::Status s, const HalStreamConfiguration& halConfiguration) {
+ (common::V1_0::Status s, const device::V3_3::HalStreamConfiguration& halConfiguration) {
finalConfiguration = halConfiguration;
status = s;
});
- if (!err.isOk()) {
- ALOGE("%s: Transaction error: %s", __FUNCTION__, err.description().c_str());
- return DEAD_OBJECT;
+ if (!err.isOk()) {
+ ALOGE("%s: Transaction error: %s", __FUNCTION__, err.description().c_str());
+ return DEAD_OBJECT;
+ }
+ } else {
+ // We don't; use v3.2 call and construct a v3.3 HalStreamConfiguration
+ ALOGV("%s: v3.2 device found", __FUNCTION__);
+ HalStreamConfiguration finalConfiguration_3_2;
+ auto err = mHidlSession->configureStreams(requestedConfiguration,
+ [&status, &finalConfiguration_3_2]
+ (common::V1_0::Status s, const HalStreamConfiguration& halConfiguration) {
+ finalConfiguration_3_2 = halConfiguration;
+ status = s;
+ });
+ if (!err.isOk()) {
+ ALOGE("%s: Transaction error: %s", __FUNCTION__, err.description().c_str());
+ return DEAD_OBJECT;
+ }
+ finalConfiguration.streams.resize(finalConfiguration_3_2.streams.size());
+ for (size_t i = 0; i < finalConfiguration_3_2.streams.size(); i++) {
+ finalConfiguration.streams[i].v3_2 = finalConfiguration_3_2.streams[i];
+ finalConfiguration.streams[i].overrideDataSpace =
+ requestedConfiguration.streams[i].dataSpace;
+ }
}
if (status != common::V1_0::Status::OK ) {
@@ -3220,7 +3273,7 @@
size_t realIdx = i;
bool found = false;
for (size_t idx = 0; idx < finalConfiguration.streams.size(); idx++) {
- if (finalConfiguration.streams[realIdx].id == streamId) {
+ if (finalConfiguration.streams[realIdx].v3_2.id == streamId) {
found = true;
break;
}
@@ -3231,38 +3284,51 @@
__FUNCTION__, streamId);
return INVALID_OPERATION;
}
- HalStream &src = finalConfiguration.streams[realIdx];
+ device::V3_3::HalStream &src = finalConfiguration.streams[realIdx];
- int overrideFormat = mapToFrameworkFormat(src.overrideFormat);
+ Camera3Stream* dstStream = Camera3Stream::cast(dst);
+ dstStream->setFormatOverride(false);
+ dstStream->setDataSpaceOverride(false);
+ int overrideFormat = mapToFrameworkFormat(src.v3_2.overrideFormat);
+ android_dataspace overrideDataSpace = mapToFrameworkDataspace(src.overrideDataSpace);
+
if (dst->format != HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED) {
if (dst->format != overrideFormat) {
ALOGE("%s: Stream %d: Format override not allowed for format 0x%x", __FUNCTION__,
streamId, dst->format);
}
+ if (dst->data_space != overrideDataSpace) {
+ ALOGE("%s: Stream %d: DataSpace override not allowed for format 0x%x", __FUNCTION__,
+ streamId, dst->format);
+ }
} else {
+ dstStream->setFormatOverride((dst->format != overrideFormat) ? true : false);
+ dstStream->setDataSpaceOverride((dst->data_space != overrideDataSpace) ? true : false);
+
// Override allowed with IMPLEMENTATION_DEFINED
dst->format = overrideFormat;
+ dst->data_space = overrideDataSpace;
}
if (dst->stream_type == CAMERA3_STREAM_INPUT) {
- if (src.producerUsage != 0) {
+ if (src.v3_2.producerUsage != 0) {
ALOGE("%s: Stream %d: INPUT streams must have 0 for producer usage",
__FUNCTION__, streamId);
return INVALID_OPERATION;
}
- Camera3Stream::cast(dst)->setUsage(
- mapConsumerToFrameworkUsage(src.consumerUsage));
+ dstStream->setUsage(
+ mapConsumerToFrameworkUsage(src.v3_2.consumerUsage));
} else {
// OUTPUT
- if (src.consumerUsage != 0) {
+ if (src.v3_2.consumerUsage != 0) {
ALOGE("%s: Stream %d: OUTPUT streams must have 0 for consumer usage",
__FUNCTION__, streamId);
return INVALID_OPERATION;
}
- Camera3Stream::cast(dst)->setUsage(
- mapProducerToFrameworkUsage(src.producerUsage));
+ dstStream->setUsage(
+ mapProducerToFrameworkUsage(src.v3_2.producerUsage));
}
- dst->max_buffers = src.maxBuffers;
+ dst->max_buffers = src.v3_2.maxBuffers;
}
return res;
diff --git a/services/camera/libcameraservice/device3/Camera3Device.h b/services/camera/libcameraservice/device3/Camera3Device.h
index 363bd88..fbbbd08 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.h
+++ b/services/camera/libcameraservice/device3/Camera3Device.h
@@ -30,6 +30,7 @@
#include <android/hardware/camera/device/3.2/ICameraDevice.h>
#include <android/hardware/camera/device/3.2/ICameraDeviceSession.h>
+#include <android/hardware/camera/device/3.3/ICameraDeviceSession.h>
#include <android/hardware/camera/device/3.2/ICameraDeviceCallback.h>
#include <fmq/MessageQueue.h>
#include <hardware/camera3.h>
@@ -128,9 +129,7 @@
uint32_t width, uint32_t height, int format,
int *id) override;
- status_t getStreamInfo(int id,
- uint32_t *width, uint32_t *height,
- uint32_t *format, android_dataspace *dataSpace) override;
+ status_t getStreamInfo(int id, StreamInfo *streamInfo) override;
status_t setStreamTransform(int id, int transform) override;
status_t deleteStream(int id) override;
@@ -597,6 +596,8 @@
/*out*/ hardware::camera::device::V3_2::StreamConfigurationMode *mode);
static camera3_buffer_status_t mapHidlBufferStatus(hardware::camera::device::V3_2::BufferStatus status);
static int mapToFrameworkFormat(hardware::graphics::common::V1_0::PixelFormat pixelFormat);
+ static android_dataspace mapToFrameworkDataspace(
+ hardware::camera::device::V3_2::DataspaceFlags);
static uint64_t mapConsumerToFrameworkUsage(
hardware::camera::device::V3_2::BufferUsageFlags usage);
static uint64_t mapProducerToFrameworkUsage(
diff --git a/services/camera/libcameraservice/device3/Camera3Stream.cpp b/services/camera/libcameraservice/device3/Camera3Stream.cpp
index 25e44a5..fbe8f4f 100644
--- a/services/camera/libcameraservice/device3/Camera3Stream.cpp
+++ b/services/camera/libcameraservice/device3/Camera3Stream.cpp
@@ -62,7 +62,9 @@
mPrepared(false),
mPreparedBufferIdx(0),
mLastMaxCount(Camera3StreamInterface::ALLOCATE_PIPELINE_MAX),
- mBufferLimitLatency(kBufferLimitLatencyBinSize) {
+ mBufferLimitLatency(kBufferLimitLatencyBinSize),
+ mFormatOverridden(false),
+ mOriginalFormat(-1) {
camera3_stream::stream_type = type;
camera3_stream::width = width;
@@ -112,6 +114,32 @@
mUsage = usage;
}
+void Camera3Stream::setFormatOverride(bool formatOverridden) {
+ mFormatOverridden = formatOverridden;
+ if (formatOverridden) mOriginalFormat = camera3_stream::format;
+}
+
+bool Camera3Stream::isFormatOverridden() const {
+ return mFormatOverridden;
+}
+
+int Camera3Stream::getOriginalFormat() const {
+ return mOriginalFormat;
+}
+
+void Camera3Stream::setDataSpaceOverride(bool dataSpaceOverridden) {
+ mDataSpaceOverridden = dataSpaceOverridden;
+ if (dataSpaceOverridden) mOriginalDataSpace = camera3_stream::data_space;
+}
+
+bool Camera3Stream::isDataSpaceOverridden() const {
+ return mDataSpaceOverridden;
+}
+
+android_dataspace Camera3Stream::getOriginalDataSpace() const {
+ return mOriginalDataSpace;
+}
+
camera3_stream* Camera3Stream::startConfiguration() {
ATRACE_CALL();
Mutex::Autolock l(mLock);
diff --git a/services/camera/libcameraservice/device3/Camera3Stream.h b/services/camera/libcameraservice/device3/Camera3Stream.h
index 9090f83..6e7912e 100644
--- a/services/camera/libcameraservice/device3/Camera3Stream.h
+++ b/services/camera/libcameraservice/device3/Camera3Stream.h
@@ -146,6 +146,12 @@
android_dataspace getDataSpace() const;
uint64_t getUsage() const;
void setUsage(uint64_t usage);
+ void setFormatOverride(bool formatOverriden);
+ bool isFormatOverridden() const;
+ int getOriginalFormat() const;
+ void setDataSpaceOverride(bool dataSpaceOverriden);
+ bool isDataSpaceOverridden() const;
+ android_dataspace getOriginalDataSpace() const;
camera3_stream* asHalStream() override {
return this;
@@ -514,6 +520,15 @@
// max_buffers.
static const int32_t kBufferLimitLatencyBinSize = 33; //in ms
CameraLatencyHistogram mBufferLimitLatency;
+
+ //Keep track of original format in case it gets overridden
+ bool mFormatOverridden;
+ int mOriginalFormat;
+
+ //Keep track of original dataSpace in case it gets overridden
+ bool mDataSpaceOverridden;
+ android_dataspace mOriginalDataSpace;
+
}; // class Camera3Stream
}; // namespace camera3
diff --git a/services/camera/libcameraservice/device3/Camera3StreamInterface.h b/services/camera/libcameraservice/device3/Camera3StreamInterface.h
index 0544a1b..cc9bf8e 100644
--- a/services/camera/libcameraservice/device3/Camera3StreamInterface.h
+++ b/services/camera/libcameraservice/device3/Camera3StreamInterface.h
@@ -71,6 +71,12 @@
virtual uint32_t getHeight() const = 0;
virtual int getFormat() const = 0;
virtual android_dataspace getDataSpace() const = 0;
+ virtual void setFormatOverride(bool formatOverriden) = 0;
+ virtual bool isFormatOverridden() const = 0;
+ virtual int getOriginalFormat() const = 0;
+ virtual void setDataSpaceOverride(bool dataSpaceOverriden) = 0;
+ virtual bool isDataSpaceOverridden() const = 0;
+ virtual android_dataspace getOriginalDataSpace() const = 0;
/**
* Get a HAL3 handle for the stream, without starting stream configuration.
diff --git a/services/mediaanalytics/MediaAnalyticsService.cpp b/services/mediaanalytics/MediaAnalyticsService.cpp
index 3c39883..c7f9270 100644
--- a/services/mediaanalytics/MediaAnalyticsService.cpp
+++ b/services/mediaanalytics/MediaAnalyticsService.cpp
@@ -222,6 +222,8 @@
// we control these, generally not trusting user input
nsecs_t now = systemTime(SYSTEM_TIME_REALTIME);
+ // round nsecs to seconds
+ now = ((now + 500000000) / 1000000000) * 1000000000;
item->setTimestamp(now);
int pid = IPCThreadState::self()->getCallingPid();
int uid = IPCThreadState::self()->getCallingUid();
@@ -257,9 +259,22 @@
break;
}
- item->setPkgName(getPkgName(item->getUid(), true));
- item->setPkgVersionCode(0);
- ALOGD("info is from uid %d pkg '%s', version %d", item->getUid(), item->getPkgName().c_str(), item->getPkgVersionCode());
+
+ // Overwrite package name and version if the caller was untrusted.
+ if (!isTrusted) {
+ setPkgInfo(item, item->getUid(), true, true);
+ } else if (item->getPkgName().empty()) {
+ // empty, so fill out both parts
+ setPkgInfo(item, item->getUid(), true, true);
+ } else {
+ // trusted, provided a package, do nothing
+ }
+
+ ALOGV("given uid %d; sanitized uid: %d sanitized pkg: %s "
+ "sanitized pkg version: %d",
+ uid_given, item->getUid(),
+ item->getPkgName().c_str(),
+ item->getPkgVersionCode());
mItemsSubmitted++;
@@ -622,9 +637,6 @@
while (l->size() > 0) {
MediaAnalyticsItem * oitem = *(l->begin());
nsecs_t when = oitem->getTimestamp();
-// DEBUGGING -- remove this ALOGD() call
- if(0) ALOGD("@ now=%10" PRId64 " record when=%10" PRId64 "",
- now, when);
// careful about timejumps too
if ((now > when) && (now-when) <= mMaxRecordAgeNs) {
// this (and the rest) are recent enough to keep
@@ -641,11 +653,6 @@
// are they alike enough that nitem can be folded into oitem?
static bool compatibleItems(MediaAnalyticsItem * oitem, MediaAnalyticsItem * nitem) {
- if (0) {
- ALOGD("Compare: o %s n %s",
- oitem->toString().c_str(), nitem->toString().c_str());
- }
-
// general safety
if (nitem->getUid() != oitem->getUid()) {
return false;
@@ -796,85 +803,155 @@
}
-// mapping uids to package names
+// how long we hold package info before we re-fetch it
+#define PKG_EXPIRATION_NS (30*60*1000000000ll) // 30 minutes, in nsecs
// give me the package name, perhaps going to find it
-AString MediaAnalyticsService::getPkgName(uid_t uid, bool addIfMissing) {
+void MediaAnalyticsService::setPkgInfo(MediaAnalyticsItem *item, uid_t uid, bool setName, bool setVersion) {
+ ALOGV("asking for packagename to go with uid=%d", uid);
+
+ if (!setName && !setVersion) {
+ // setting nothing? strange
+ return;
+ }
+
+ nsecs_t now = systemTime(SYSTEM_TIME_REALTIME);
+ struct UidToPkgMap mapping;
+ mapping.uid = (-1);
+
ssize_t i = mPkgMappings.indexOfKey(uid);
if (i >= 0) {
- AString pkg = mPkgMappings.valueAt(i);
- ALOGV("returning pkg '%s' for uid %d", pkg.c_str(), uid);
- return pkg;
- }
-
- AString pkg;
-
- if (addIfMissing == false) {
- return pkg;
- }
-
- struct passwd *pw = getpwuid(uid);
- if (pw) {
- pkg = pw->pw_name;
- } else {
- pkg = "-";
- }
-
- // find the proper value
-
- sp<IBinder> binder = NULL;
- sp<IServiceManager> sm = defaultServiceManager();
- if (sm == NULL) {
- ALOGE("defaultServiceManager failed");
- } else {
- binder = sm->getService(String16("package_native"));
- if (binder == NULL) {
- ALOGE("getService package_native failed");
+ mapping = mPkgMappings.valueAt(i);
+ ALOGV("Expiration? uid %d expiration %" PRId64 " now %" PRId64,
+ uid, mapping.expiration, now);
+ if (mapping.expiration < now) {
+ // purge our current entry and re-query
+ ALOGV("entry for uid %d expired, now= %" PRId64 "", uid, now);
+ mPkgMappings.removeItemsAt(i, 1);
+ // could cheat and use a goto back to the top of the routine.
+ // a good compiler should recognize the local tail recursion...
+ return setPkgInfo(item, uid, setName, setVersion);
}
- }
+ } else {
+ AString pkg;
+ std::string installer = "";
+ int32_t versionCode = 0;
- if (binder != NULL) {
- sp<IPackageManagerNative> package_mgr = interface_cast<IPackageManagerNative>(binder);
+ struct passwd *pw = getpwuid(uid);
+ if (pw) {
+ pkg = pw->pw_name;
+ }
- std::vector<int> uids;
- std::vector<std::string> names;
+ // find the proper value -- should we cache this binder??
- uids.push_back(uid);
-
- binder::Status status = package_mgr->getNamesForUids(uids, &names);
- if (!status.isOk()) {
- ALOGE("package_native::getNamesForUids failed: %s",
- status.exceptionMessage().c_str());
+ sp<IBinder> binder = NULL;
+ sp<IServiceManager> sm = defaultServiceManager();
+ if (sm == NULL) {
+ ALOGE("defaultServiceManager failed");
} else {
- if (!names[0].empty()) {
- pkg = names[0].c_str();
+ binder = sm->getService(String16("package_native"));
+ if (binder == NULL) {
+ ALOGE("getService package_native failed");
+ }
+ }
+
+ if (binder != NULL) {
+ sp<IPackageManagerNative> package_mgr = interface_cast<IPackageManagerNative>(binder);
+ binder::Status status;
+
+ std::vector<int> uids;
+ std::vector<std::string> names;
+
+ uids.push_back(uid);
+
+ status = package_mgr->getNamesForUids(uids, &names);
+ if (!status.isOk()) {
+ ALOGE("package_native::getNamesForUids failed: %s",
+ status.exceptionMessage().c_str());
+ } else {
+ if (!names[0].empty()) {
+ pkg = names[0].c_str();
+ }
+ }
+
+ // strip any leading "shared:" strings that came back
+ if (pkg.startsWith("shared:")) {
+ pkg.erase(0, 7);
+ }
+
+ // determine how pkg was installed and the versionCode
+ //
+ if (pkg.empty()) {
+ // no name for us to manage
+ } else if (strchr(pkg.c_str(), '.') == NULL) {
+ // not of form 'com.whatever...'; assume internal and ok
+ } else if (strncmp(pkg.c_str(), "android.", 8) == 0) {
+ // android.* packages are assumed fine
+ } else {
+ String16 pkgName16(pkg.c_str());
+ status = package_mgr->getInstallerForPackage(pkgName16, &installer);
+ if (!status.isOk()) {
+ ALOGE("package_native::getInstallerForPackage failed: %s",
+ status.exceptionMessage().c_str());
+ }
+
+ // skip if we didn't get an installer
+ if (status.isOk()) {
+ status = package_mgr->getVersionCodeForPackage(pkgName16, &versionCode);
+ if (!status.isOk()) {
+ ALOGE("package_native::getVersionCodeForPackage failed: %s",
+ status.exceptionMessage().c_str());
+ }
+ }
+
+
+ ALOGV("package '%s' installed by '%s' versioncode %d / %08x",
+ pkg.c_str(), installer.c_str(), versionCode, versionCode);
+
+ if (strncmp(installer.c_str(), "com.android.", 12) == 0) {
+ // from play store, we keep info
+ } else if (strncmp(installer.c_str(), "com.google.", 11) == 0) {
+ // some google source, we keep info
+ } else if (strcmp(installer.c_str(), "preload") == 0) {
+ // preloads, we keep the info
+ } else if (installer.c_str()[0] == '\0') {
+ // sideload (no installer); do not report
+ pkg = "";
+ versionCode = 0;
+ } else {
+ // unknown installer; do not report
+ pkg = "";
+ versionCode = 0;
+ }
+ }
+ }
+
+ // add it to the map, to save a subsequent lookup
+ if (!pkg.empty()) {
+ Mutex::Autolock _l(mLock_mappings);
+ ALOGV("Adding uid %d pkg '%s'", uid, pkg.c_str());
+ ssize_t i = mPkgMappings.indexOfKey(uid);
+ if (i < 0) {
+ mapping.uid = uid;
+ mapping.pkg = pkg;
+ mapping.installer = installer.c_str();
+ mapping.versionCode = versionCode;
+ mapping.expiration = now + PKG_EXPIRATION_NS;
+ ALOGV("expiration for uid %d set to %" PRId64 "", uid, mapping.expiration);
+
+ mPkgMappings.add(uid, mapping);
}
}
}
- // XXX determine whether package was side-loaded or from playstore.
- // for privacy, we only list apps loaded from playstore.
-
- // Sanitize the package name for ":"
- // as an example, we get "shared:android.uid.systemui"
- // replace : with something benign (I'm going to use !)
- if (!pkg.empty()) {
- int n = pkg.size();
- char *p = (char *) pkg.c_str();
- for (int i = 0 ; i < n; i++) {
- if (p[i] == ':') {
- p[i] = '!';
- }
+ if (mapping.uid != (uid_t)(-1)) {
+ if (setName) {
+ item->setPkgName(mapping.pkg);
+ }
+ if (setVersion) {
+ item->setPkgVersionCode(mapping.versionCode);
}
}
-
- // add it to the map, to save a subsequent lookup
- if (!pkg.empty()) {
- ALOGV("Adding uid %d pkg '%s'", uid, pkg.c_str());
- mPkgMappings.add(uid, pkg);
- }
-
- return pkg;
}
} // namespace android
diff --git a/services/mediaanalytics/MediaAnalyticsService.h b/services/mediaanalytics/MediaAnalyticsService.h
index 4fe2fb2..52e4631 100644
--- a/services/mediaanalytics/MediaAnalyticsService.h
+++ b/services/mediaanalytics/MediaAnalyticsService.h
@@ -62,6 +62,7 @@
// partitioned a bit so we don't over serialize
mutable Mutex mLock;
mutable Mutex mLock_ids;
+ mutable Mutex mLock_mappings;
// limit how many records we'll retain
// by count (in each queue (open, finalized))
@@ -135,10 +136,13 @@
struct UidToPkgMap {
uid_t uid;
AString pkg;
+ AString installer;
+ int32_t versionCode;
+ nsecs_t expiration;
};
- KeyedVector<uid_t,AString> mPkgMappings;
- AString getPkgName(uid_t uid, bool addIfMissing);
+ KeyedVector<uid_t,struct UidToPkgMap> mPkgMappings;
+ void setPkgInfo(MediaAnalyticsItem *item, uid_t uid, bool setName, bool setVersion);
};
diff --git a/services/mediaanalytics/MetricsSummarizerCodec.cpp b/services/mediaanalytics/MetricsSummarizerCodec.cpp
index 8c74782..6af3c9a 100644
--- a/services/mediaanalytics/MetricsSummarizerCodec.cpp
+++ b/services/mediaanalytics/MetricsSummarizerCodec.cpp
@@ -17,6 +17,8 @@
#define LOG_TAG "MetricsSummarizerCodec"
#include <utils/Log.h>
+#include <stdint.h>
+#include <inttypes.h>
#include <utils/threads.h>
#include <utils/Errors.h>
@@ -40,5 +42,4 @@
ALOGV("MetricsSummarizerCodec::MetricsSummarizerCodec");
}
-
} // namespace android
diff --git a/services/mediaanalytics/mediametrics.rc b/services/mediaanalytics/mediametrics.rc
index 3829f8c..1efde5e 100644
--- a/services/mediaanalytics/mediametrics.rc
+++ b/services/mediaanalytics/mediametrics.rc
@@ -1,5 +1,6 @@
service mediametrics /system/bin/mediametrics
class main
user media
+ group media
ioprio rt 4
writepid /dev/cpuset/foreground/tasks /dev/stune/foreground/tasks
diff --git a/services/mediacodec/Android.mk b/services/mediacodec/Android.mk
index 6997b5a..faeb0a7 100644
--- a/services/mediacodec/Android.mk
+++ b/services/mediacodec/Android.mk
@@ -9,13 +9,8 @@
libgui \
libutils \
liblog \
- libstagefright_omx
-LOCAL_C_INCLUDES := \
- frameworks/av/include \
- frameworks/av/media/libstagefright \
- frameworks/av/media/libstagefright/include \
- frameworks/native/include \
- frameworks/native/include/media/openmax
+ libstagefright_omx \
+ libstagefright_xmlparser
LOCAL_MODULE:= libmediacodecservice
LOCAL_VENDOR_MODULE := true
LOCAL_32_BIT_ONLY := true
@@ -38,15 +33,10 @@
libhwbinder \
libhidltransport \
libstagefright_omx \
+ libstagefright_xmlparser \
android.hardware.media.omx@1.0 \
android.hidl.memory@1.0
-LOCAL_C_INCLUDES := \
- frameworks/av/include \
- frameworks/av/media/libstagefright \
- frameworks/av/media/libstagefright/include \
- frameworks/native/include \
- frameworks/native/include/media/openmax
LOCAL_MODULE := android.hardware.media.omx@1.0-service
LOCAL_MODULE_RELATIVE_PATH := hw
LOCAL_VENDOR_MODULE := true
diff --git a/services/mediacodec/MediaCodecService.cpp b/services/mediacodec/MediaCodecService.cpp
index fc1e5d9..6b510c6 100644
--- a/services/mediacodec/MediaCodecService.cpp
+++ b/services/mediacodec/MediaCodecService.cpp
@@ -24,15 +24,25 @@
sp<IOMX> MediaCodecService::getOMX() {
- Mutex::Autolock autoLock(mLock);
+ Mutex::Autolock autoLock(mOMXLock);
if (mOMX.get() == NULL) {
- mOMX = new OMX;
+ mOMX = new OMX();
}
return mOMX;
}
+sp<IOMXStore> MediaCodecService::getOMXStore() {
+
+ Mutex::Autolock autoLock(mOMXStoreLock);
+
+ if (mOMXStore.get() == NULL) {
+ mOMXStore = new OMXStore();
+ }
+
+ return mOMXStore;
+}
status_t MediaCodecService::onTransact(uint32_t code, const Parcel& data, Parcel* reply,
uint32_t flags)
diff --git a/services/mediacodec/MediaCodecService.h b/services/mediacodec/MediaCodecService.h
index 0d2c9d8..9301135 100644
--- a/services/mediacodec/MediaCodecService.h
+++ b/services/mediacodec/MediaCodecService.h
@@ -20,10 +20,12 @@
#include <binder/BinderService.h>
#include <media/IMediaCodecService.h>
#include <media/stagefright/omx/OMX.h>
+#include <media/stagefright/omx/OMXStore.h>
namespace android {
-class MediaCodecService : public BinderService<MediaCodecService>, public BnMediaCodecService
+class MediaCodecService : public BinderService<MediaCodecService>,
+ public BnMediaCodecService
{
friend class BinderService<MediaCodecService>; // for MediaCodecService()
public:
@@ -31,16 +33,20 @@
virtual ~MediaCodecService() { }
virtual void onFirstRef() { }
- static const char* getServiceName() { return "media.codec"; }
+ static const char* getServiceName() { return "media.codec"; }
- virtual sp<IOMX> getOMX();
+ virtual sp<IOMX> getOMX();
- virtual status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply,
- uint32_t flags);
+ virtual sp<IOMXStore> getOMXStore();
+
+ virtual status_t onTransact(uint32_t code, const Parcel& data,
+ Parcel* reply, uint32_t flags);
private:
- Mutex mLock;
- sp<IOMX> mOMX;
+ Mutex mOMXLock;
+ sp<IOMX> mOMX;
+ Mutex mOMXStoreLock;
+ sp<IOMXStore> mOMXStore;
};
} // namespace android
diff --git a/services/mediacodec/android.hardware.media.omx@1.0-service.rc b/services/mediacodec/android.hardware.media.omx@1.0-service.rc
index ec51d65..3ef9a85 100644
--- a/services/mediacodec/android.hardware.media.omx@1.0-service.rc
+++ b/services/mediacodec/android.hardware.media.omx@1.0-service.rc
@@ -1,4 +1,4 @@
-service mediacodec /vendor/bin/hw/android.hardware.media.omx@1.0-service
+service vendor.media.omx /vendor/bin/hw/android.hardware.media.omx@1.0-service
class main
user mediacodec
group camera drmrpc mediadrm
diff --git a/services/mediacodec/seccomp_policy/mediacodec-arm.policy b/services/mediacodec/seccomp_policy/mediacodec-arm.policy
index 4c4b594..a751b4c 100644
--- a/services/mediacodec/seccomp_policy/mediacodec-arm.policy
+++ b/services/mediacodec/seccomp_policy/mediacodec-arm.policy
@@ -53,6 +53,7 @@
fstatat64: 1
ugetrlimit: 1
getdents64: 1
+getrandom: 1
# for attaching to debuggerd on process crash
sigaction: 1
diff --git a/services/mediaextractor/MediaExtractorService.cpp b/services/mediaextractor/MediaExtractorService.cpp
index 08cbef6..50a4191 100644
--- a/services/mediaextractor/MediaExtractorService.cpp
+++ b/services/mediaextractor/MediaExtractorService.cpp
@@ -33,17 +33,18 @@
sp<DataSource> localSource = DataSource::CreateFromIDataSource(remoteSource);
- sp<IMediaExtractor> ret = MediaExtractor::CreateFromService(localSource, mime);
+ sp<MediaExtractor> extractor = MediaExtractor::CreateFromService(localSource, mime);
ALOGV("extractor service created %p (%s)",
- ret.get(),
- ret == NULL ? "" : ret->name());
+ extractor.get(),
+ extractor == nullptr ? "" : extractor->name());
- if (ret != NULL) {
+ if (extractor != nullptr) {
+ sp<IMediaExtractor> ret = extractor->asIMediaExtractor();
registerMediaExtractor(ret, localSource, mime);
+ return ret;
}
-
- return ret;
+ return nullptr;
}
sp<IDataSource> MediaExtractorService::makeIDataSource(int fd, int64_t offset, int64_t length)
diff --git a/services/mediaextractor/seccomp_policy/mediaextractor-arm.policy b/services/mediaextractor/seccomp_policy/mediaextractor-arm.policy
index e06ac8c..4fa69d7 100644
--- a/services/mediaextractor/seccomp_policy/mediaextractor-arm.policy
+++ b/services/mediaextractor/seccomp_policy/mediaextractor-arm.policy
@@ -39,6 +39,7 @@
getegid32: 1
getgroups32: 1
nanosleep: 1
+getrandom: 1
# for FileSource
readlinkat: 1
diff --git a/services/mediaextractor/seccomp_policy/mediaextractor-arm64.policy b/services/mediaextractor/seccomp_policy/mediaextractor-arm64.policy
index 4b51457..d1278a9 100644
--- a/services/mediaextractor/seccomp_policy/mediaextractor-arm64.policy
+++ b/services/mediaextractor/seccomp_policy/mediaextractor-arm64.policy
@@ -28,10 +28,10 @@
rt_sigreturn: 1
getrlimit: 1
nanosleep: 1
+getrandom: 1
# for FileSource
readlinkat: 1
-_llseek: 1
# for attaching to debuggerd on process crash
tgkill: 1
diff --git a/services/mediaextractor/seccomp_policy/mediaextractor-x86.policy b/services/mediaextractor/seccomp_policy/mediaextractor-x86.policy
index cdff4db..3b37f92 100644
--- a/services/mediaextractor/seccomp_policy/mediaextractor-x86.policy
+++ b/services/mediaextractor/seccomp_policy/mediaextractor-x86.policy
@@ -37,6 +37,7 @@
getegid32: 1
getgroups32: 1
nanosleep: 1
+getrandom: 1
# for FileSource
readlinkat: 1
diff --git a/services/oboeservice/AAudioEndpointManager.cpp b/services/oboeservice/AAudioEndpointManager.cpp
index f41219e..f996f74 100644
--- a/services/oboeservice/AAudioEndpointManager.cpp
+++ b/services/oboeservice/AAudioEndpointManager.cpp
@@ -102,7 +102,7 @@
}
}
- ALOGD("AAudioEndpointManager.findExclusiveEndpoint_l(), found %p for device = %d",
+ ALOGV("AAudioEndpointManager.findExclusiveEndpoint_l(), found %p for device = %d",
endpoint.get(), configuration.getDeviceId());
return endpoint;
}
@@ -118,7 +118,7 @@
}
}
- ALOGD("AAudioEndpointManager.findSharedEndpoint_l(), found %p for device = %d",
+ ALOGV("AAudioEndpointManager.findSharedEndpoint_l(), found %p for device = %d",
endpoint.get(), configuration.getDeviceId());
return endpoint;
}
@@ -238,8 +238,6 @@
std::lock_guard<std::mutex> lock(mExclusiveLock);
int32_t newRefCount = serviceEndpoint->getOpenCount() - 1;
serviceEndpoint->setOpenCount(newRefCount);
- ALOGD("AAudioEndpointManager::closeExclusiveEndpoint(%p) newRefCount = %d",
- serviceEndpoint.get(), newRefCount);
// If no longer in use then close and delete it.
if (newRefCount <= 0) {
@@ -262,8 +260,6 @@
std::lock_guard<std::mutex> lock(mSharedLock);
int32_t newRefCount = serviceEndpoint->getOpenCount() - 1;
serviceEndpoint->setOpenCount(newRefCount);
- ALOGD("AAudioEndpointManager::closeSharedEndpoint(%p) newRefCount = %d",
- serviceEndpoint.get(), newRefCount);
// If no longer in use then close and delete it.
if (newRefCount <= 0) {
diff --git a/services/oboeservice/AAudioService.cpp b/services/oboeservice/AAudioService.cpp
index 855ae69..5a3488d 100644
--- a/services/oboeservice/AAudioService.cpp
+++ b/services/oboeservice/AAudioService.cpp
@@ -18,9 +18,9 @@
//#define LOG_NDEBUG 0
#include <utils/Log.h>
+#include <iomanip>
+#include <iostream>
#include <sstream>
-//#include <time.h>
-//#include <pthread.h>
#include <aaudio/AAudio.h>
#include <mediautils/SchedulingPolicyService.h>
@@ -35,18 +35,13 @@
#include "AAudioServiceStreamMMAP.h"
#include "binding/IAAudioService.h"
#include "ServiceUtilities.h"
-#include "utility/HandleTracker.h"
using namespace android;
using namespace aaudio;
#define MAX_STREAMS_PER_PROCESS 8
-typedef enum
-{
- AAUDIO_HANDLE_TYPE_STREAM
-} aaudio_service_handle_type_t;
-static_assert(AAUDIO_HANDLE_TYPE_STREAM < HANDLE_TRACKER_MAX_TYPES, "Too many handle types.");
+using android::AAudioService;
android::AAudioService::AAudioService()
: BnAAudioService() {
@@ -70,7 +65,8 @@
result = ss.str();
ALOGW("%s", result.c_str());
} else {
- result = mHandleTracker.dump()
+ result = "------------ AAudio Service ------------\n"
+ + mStreamTracker.dump()
+ AAudioClientTracker::getInstance().dump()
+ AAudioEndpointManager::getInstance().dump();
}
@@ -125,7 +121,7 @@
// if SHARED requested or if EXCLUSIVE failed
if (sharingMode == AAUDIO_SHARING_MODE_SHARED
- || (serviceStream == nullptr && !sharingModeMatchRequired)) {
+ || (serviceStream.get() == nullptr && !sharingModeMatchRequired)) {
serviceStream = new AAudioServiceStreamShared(*this);
result = serviceStream->open(request);
}
@@ -136,18 +132,12 @@
result, AAudio_convertResultToText(result));
return result;
} else {
- aaudio_handle_t handle = mHandleTracker.put(AAUDIO_HANDLE_TYPE_STREAM, serviceStream.get());
- if (handle < 0) {
- ALOGE("AAudioService::openStream(): handle table full");
- serviceStream->close();
- serviceStream.clear();
- } else {
- ALOGD("AAudioService::openStream(): handle = 0x%08X", handle);
- serviceStream->setHandle(handle);
- pid_t pid = request.getProcessId();
- AAudioClientTracker::getInstance().registerClientStream(pid, serviceStream);
- configurationOutput.copyFrom(*serviceStream);
- }
+ aaudio_handle_t handle = mStreamTracker.addStreamForHandle(serviceStream.get());
+ ALOGD("AAudioService::openStream(): handle = 0x%08X", handle);
+ serviceStream->setHandle(handle);
+ pid_t pid = request.getProcessId();
+ AAudioClientTracker::getInstance().registerClientStream(pid, serviceStream);
+ configurationOutput.copyFrom(*serviceStream);
return handle;
}
}
@@ -155,30 +145,32 @@
aaudio_result_t AAudioService::closeStream(aaudio_handle_t streamHandle) {
// Check permission and ownership first.
sp<AAudioServiceStreamBase> serviceStream = convertHandleToServiceStream(streamHandle);
- if (serviceStream == nullptr) {
- ALOGE("AAudioService::startStream(), illegal stream handle = 0x%0x", streamHandle);
+ if (serviceStream.get() == nullptr) {
+ ALOGE("AAudioService::closeStream(0x%0x), illegal stream handle", streamHandle);
return AAUDIO_ERROR_INVALID_HANDLE;
}
ALOGD("AAudioService.closeStream(0x%08X)", streamHandle);
// Remove handle from tracker so that we cannot look up the raw address any more.
- serviceStream = (AAudioServiceStreamBase *)
- mHandleTracker.remove(AAUDIO_HANDLE_TYPE_STREAM,
- streamHandle);
- if (serviceStream != nullptr) {
+ // removeStreamByHandle() uses a lock so that if there are two simultaneous closes
+ // then only one will get the pointer and do the close.
+ serviceStream = mStreamTracker.removeStreamByHandle(streamHandle);
+ if (serviceStream.get() != nullptr) {
serviceStream->close();
pid_t pid = serviceStream->getOwnerProcessId();
AAudioClientTracker::getInstance().unregisterClientStream(pid, serviceStream);
return AAUDIO_OK;
+ } else {
+ ALOGW("AAudioService::closeStream(0x%0x) being handled by another thread", streamHandle);
+ return AAUDIO_ERROR_INVALID_HANDLE;
}
- return AAUDIO_ERROR_INVALID_HANDLE;
}
-AAudioServiceStreamBase *AAudioService::convertHandleToServiceStream(
- aaudio_handle_t streamHandle) const {
- AAudioServiceStreamBase *serviceStream = (AAudioServiceStreamBase *)
- mHandleTracker.get(AAUDIO_HANDLE_TYPE_STREAM, (aaudio_handle_t)streamHandle);
- if (serviceStream != nullptr) {
+
+sp<AAudioServiceStreamBase> AAudioService::convertHandleToServiceStream(
+ aaudio_handle_t streamHandle) {
+ sp<AAudioServiceStreamBase> serviceStream = mStreamTracker.getStreamByHandle(streamHandle);
+ if (serviceStream.get() != nullptr) {
// Only allow owner or the aaudio service to access the stream.
const uid_t callingUserId = IPCThreadState::self()->getCallingUid();
const uid_t ownerUserId = serviceStream->getOwnerUserId();
@@ -198,30 +190,30 @@
aaudio_result_t AAudioService::getStreamDescription(
aaudio_handle_t streamHandle,
aaudio::AudioEndpointParcelable &parcelable) {
- AAudioServiceStreamBase *serviceStream = convertHandleToServiceStream(streamHandle);
- if (serviceStream == nullptr) {
+ sp<AAudioServiceStreamBase> serviceStream = convertHandleToServiceStream(streamHandle);
+ if (serviceStream.get() == nullptr) {
ALOGE("AAudioService::getStreamDescription(), illegal stream handle = 0x%0x", streamHandle);
return AAUDIO_ERROR_INVALID_HANDLE;
}
+
aaudio_result_t result = serviceStream->getDescription(parcelable);
// parcelable.dump();
return result;
}
aaudio_result_t AAudioService::startStream(aaudio_handle_t streamHandle) {
- AAudioServiceStreamBase *serviceStream = convertHandleToServiceStream(streamHandle);
- if (serviceStream == nullptr) {
+ sp<AAudioServiceStreamBase> serviceStream = convertHandleToServiceStream(streamHandle);
+ if (serviceStream.get() == nullptr) {
ALOGE("AAudioService::startStream(), illegal stream handle = 0x%0x", streamHandle);
return AAUDIO_ERROR_INVALID_HANDLE;
}
- aaudio_result_t result = serviceStream->start();
- return result;
+ return serviceStream->start();
}
aaudio_result_t AAudioService::pauseStream(aaudio_handle_t streamHandle) {
- AAudioServiceStreamBase *serviceStream = convertHandleToServiceStream(streamHandle);
- if (serviceStream == nullptr) {
+ sp<AAudioServiceStreamBase> serviceStream = convertHandleToServiceStream(streamHandle);
+ if (serviceStream.get() == nullptr) {
ALOGE("AAudioService::pauseStream(), illegal stream handle = 0x%0x", streamHandle);
return AAUDIO_ERROR_INVALID_HANDLE;
}
@@ -230,9 +222,9 @@
}
aaudio_result_t AAudioService::stopStream(aaudio_handle_t streamHandle) {
- AAudioServiceStreamBase *serviceStream = convertHandleToServiceStream(streamHandle);
- if (serviceStream == nullptr) {
- ALOGE("AAudioService::pauseStream(), illegal stream handle = 0x%0x", streamHandle);
+ sp<AAudioServiceStreamBase> serviceStream = convertHandleToServiceStream(streamHandle);
+ if (serviceStream.get() == nullptr) {
+ ALOGE("AAudioService::stopStream(), illegal stream handle = 0x%0x", streamHandle);
return AAUDIO_ERROR_INVALID_HANDLE;
}
aaudio_result_t result = serviceStream->stop();
@@ -240,8 +232,8 @@
}
aaudio_result_t AAudioService::flushStream(aaudio_handle_t streamHandle) {
- AAudioServiceStreamBase *serviceStream = convertHandleToServiceStream(streamHandle);
- if (serviceStream == nullptr) {
+ sp<AAudioServiceStreamBase> serviceStream = convertHandleToServiceStream(streamHandle);
+ if (serviceStream.get() == nullptr) {
ALOGE("AAudioService::flushStream(), illegal stream handle = 0x%0x", streamHandle);
return AAUDIO_ERROR_INVALID_HANDLE;
}
@@ -251,8 +243,8 @@
aaudio_result_t AAudioService::registerAudioThread(aaudio_handle_t streamHandle,
pid_t clientThreadId,
int64_t periodNanoseconds) {
- AAudioServiceStreamBase *serviceStream = convertHandleToServiceStream(streamHandle);
- if (serviceStream == nullptr) {
+ sp<AAudioServiceStreamBase> serviceStream = convertHandleToServiceStream(streamHandle);
+ if (serviceStream.get() == nullptr) {
ALOGE("AAudioService::registerAudioThread(), illegal stream handle = 0x%0x", streamHandle);
return AAUDIO_ERROR_INVALID_HANDLE;
}
@@ -276,8 +268,8 @@
aaudio_result_t AAudioService::unregisterAudioThread(aaudio_handle_t streamHandle,
pid_t clientThreadId) {
- AAudioServiceStreamBase *serviceStream = convertHandleToServiceStream(streamHandle);
- if (serviceStream == nullptr) {
+ sp<AAudioServiceStreamBase> serviceStream = convertHandleToServiceStream(streamHandle);
+ if (serviceStream.get() == nullptr) {
ALOGE("AAudioService::unregisterAudioThread(), illegal stream handle = 0x%0x",
streamHandle);
return AAUDIO_ERROR_INVALID_HANDLE;
@@ -293,8 +285,8 @@
aaudio_result_t AAudioService::startClient(aaudio_handle_t streamHandle,
const android::AudioClient& client,
audio_port_handle_t *clientHandle) {
- AAudioServiceStreamBase *serviceStream = convertHandleToServiceStream(streamHandle);
- if (serviceStream == nullptr) {
+ sp<AAudioServiceStreamBase> serviceStream = convertHandleToServiceStream(streamHandle);
+ if (serviceStream.get() == nullptr) {
ALOGE("AAudioService::startClient(), illegal stream handle = 0x%0x",
streamHandle);
return AAUDIO_ERROR_INVALID_HANDLE;
@@ -304,8 +296,8 @@
aaudio_result_t AAudioService::stopClient(aaudio_handle_t streamHandle,
audio_port_handle_t clientHandle) {
- AAudioServiceStreamBase *serviceStream = convertHandleToServiceStream(streamHandle);
- if (serviceStream == nullptr) {
+ sp<AAudioServiceStreamBase> serviceStream = convertHandleToServiceStream(streamHandle);
+ if (serviceStream.get() == nullptr) {
ALOGE("AAudioService::stopClient(), illegal stream handle = 0x%0x",
streamHandle);
return AAUDIO_ERROR_INVALID_HANDLE;
diff --git a/services/oboeservice/AAudioService.h b/services/oboeservice/AAudioService.h
index ffaf538..eef0824 100644
--- a/services/oboeservice/AAudioService.h
+++ b/services/oboeservice/AAudioService.h
@@ -24,13 +24,13 @@
#include <media/AudioClient.h>
#include <aaudio/AAudio.h>
-#include "utility/HandleTracker.h"
-#include "binding/IAAudioService.h"
-#include "binding/AAudioServiceInterface.h"
-namespace aaudio {
- class AAudioServiceStreamBase;
-};
+#include "binding/AAudioCommon.h"
+#include "binding/AAudioServiceInterface.h"
+#include "binding/IAAudioService.h"
+
+#include "AAudioServiceStreamBase.h"
+#include "AAudioStreamTracker.h"
namespace android {
@@ -51,45 +51,53 @@
virtual void registerClient(const sp<IAAudioClient>& client);
- virtual aaudio_handle_t openStream(const aaudio::AAudioStreamRequest &request,
- aaudio::AAudioStreamConfiguration &configurationOutput);
+ aaudio::aaudio_handle_t openStream(const aaudio::AAudioStreamRequest &request,
+ aaudio::AAudioStreamConfiguration &configurationOutput)
+ override;
- virtual aaudio_result_t closeStream(aaudio_handle_t streamHandle);
+ aaudio_result_t closeStream(aaudio::aaudio_handle_t streamHandle) override;
- virtual aaudio_result_t getStreamDescription(
- aaudio_handle_t streamHandle,
- aaudio::AudioEndpointParcelable &parcelable);
+ aaudio_result_t getStreamDescription(
+ aaudio::aaudio_handle_t streamHandle,
+ aaudio::AudioEndpointParcelable &parcelable) override;
- virtual aaudio_result_t startStream(aaudio_handle_t streamHandle);
+ aaudio_result_t startStream(aaudio::aaudio_handle_t streamHandle) override;
- virtual aaudio_result_t pauseStream(aaudio_handle_t streamHandle);
+ aaudio_result_t pauseStream(aaudio::aaudio_handle_t streamHandle) override;
- virtual aaudio_result_t stopStream(aaudio_handle_t streamHandle);
+ aaudio_result_t stopStream(aaudio::aaudio_handle_t streamHandle) override;
- virtual aaudio_result_t flushStream(aaudio_handle_t streamHandle);
+ aaudio_result_t flushStream(aaudio::aaudio_handle_t streamHandle) override;
- virtual aaudio_result_t registerAudioThread(aaudio_handle_t streamHandle,
+ aaudio_result_t registerAudioThread(aaudio::aaudio_handle_t streamHandle,
pid_t tid,
- int64_t periodNanoseconds) ;
+ int64_t periodNanoseconds) override;
- virtual aaudio_result_t unregisterAudioThread(aaudio_handle_t streamHandle,
- pid_t tid);
+ aaudio_result_t unregisterAudioThread(aaudio::aaudio_handle_t streamHandle,
+ pid_t tid) override;
- virtual aaudio_result_t startClient(aaudio_handle_t streamHandle,
+ aaudio_result_t startClient(aaudio::aaudio_handle_t streamHandle,
const android::AudioClient& client,
- audio_port_handle_t *clientHandle);
+ audio_port_handle_t *clientHandle) override;
- virtual aaudio_result_t stopClient(aaudio_handle_t streamHandle,
- audio_port_handle_t clientHandle);
+ aaudio_result_t stopClient(aaudio::aaudio_handle_t streamHandle,
+ audio_port_handle_t clientHandle) override;
private:
- aaudio::AAudioServiceStreamBase *convertHandleToServiceStream(aaudio_handle_t streamHandle) const;
+ /**
+ * Lookup stream and then validate access to the stream.
+ * @param streamHandle
+ * @return
+ */
+ sp<aaudio::AAudioServiceStreamBase> convertHandleToServiceStream(
+ aaudio::aaudio_handle_t streamHandle);
- HandleTracker mHandleTracker;
android::AudioClient mAudioClient;
+ aaudio::AAudioStreamTracker mStreamTracker;
+
enum constants {
DEFAULT_AUDIO_PRIORITY = 2
};
diff --git a/services/oboeservice/AAudioServiceEndpoint.cpp b/services/oboeservice/AAudioServiceEndpoint.cpp
index cba5bc8..3095bc9 100644
--- a/services/oboeservice/AAudioServiceEndpoint.cpp
+++ b/services/oboeservice/AAudioServiceEndpoint.cpp
@@ -60,7 +60,7 @@
result << " Reference Count: " << mOpenCount << "\n";
result << " Requested Device Id: " << mRequestedDeviceId << "\n";
result << " Device Id: " << getDeviceId() << "\n";
- result << " Registered Streams: " << "\n";
+ result << " Registered Streams:" << "\n";
result << AAudioServiceStreamShared::dumpHeader() << "\n";
for (const auto stream : mRegisteredStreams) {
result << stream->dump() << "\n";
diff --git a/services/oboeservice/AAudioServiceEndpointCapture.cpp b/services/oboeservice/AAudioServiceEndpointCapture.cpp
index 97558ca..c7d9b8e 100644
--- a/services/oboeservice/AAudioServiceEndpointCapture.cpp
+++ b/services/oboeservice/AAudioServiceEndpointCapture.cpp
@@ -84,30 +84,42 @@
std::lock_guard <std::mutex> lock(mLockStreams);
for (const auto clientStream : mRegisteredStreams) {
if (clientStream->isRunning()) {
- AAudioServiceStreamShared *streamShared =
+ int64_t clientFramesWritten = 0;
+ sp<AAudioServiceStreamShared> streamShared =
static_cast<AAudioServiceStreamShared *>(clientStream.get());
- FifoBuffer *fifo = streamShared->getDataFifoBuffer();
+ {
+ // Lock the AudioFifo to protect against close.
+ std::lock_guard <std::mutex> lock(streamShared->getAudioDataQueueLock());
- // Determine offset between framePosition in client's stream vs the underlying
- // MMAP stream.
- int64_t clientFramesWritten = fifo->getWriteCounter();
- // There are two indices that refer to the same frame.
- int64_t positionOffset = mmapFramesRead - clientFramesWritten;
- streamShared->setTimestampPositionOffset(positionOffset);
+ FifoBuffer *fifo = streamShared->getAudioDataFifoBuffer_l();
+ if (fifo != nullptr) {
- if (fifo->getFifoControllerBase()->getEmptyFramesAvailable() <
- getFramesPerBurst()) {
- underflowCount++;
- } else {
- fifo->write(mDistributionBuffer, getFramesPerBurst());
+ // Determine offset between framePosition in client's stream
+ // vs the underlying MMAP stream.
+ clientFramesWritten = fifo->getWriteCounter();
+ // There are two indices that refer to the same frame.
+ int64_t positionOffset = mmapFramesRead - clientFramesWritten;
+ streamShared->setTimestampPositionOffset(positionOffset);
+
+ if (fifo->getFifoControllerBase()->getEmptyFramesAvailable() <
+ getFramesPerBurst()) {
+ underflowCount++;
+ } else {
+ fifo->write(mDistributionBuffer, getFramesPerBurst());
+ }
+ clientFramesWritten = fifo->getWriteCounter();
+ }
}
- // This timestamp represents the completion of data being written into the
- // client buffer. It is sent to the client and used in the timing model
- // to decide when data will be available to read.
- Timestamp timestamp(fifo->getWriteCounter(), AudioClock::getNanoseconds());
- streamShared->markTransferTime(timestamp);
+ if (clientFramesWritten > 0) {
+ // This timestamp represents the completion of data being written into the
+ // client buffer. It is sent to the client and used in the timing model
+ // to decide when data will be available to read.
+ Timestamp timestamp(clientFramesWritten, AudioClock::getNanoseconds());
+ streamShared->markTransferTime(timestamp);
+ }
+
}
}
}
diff --git a/services/oboeservice/AAudioServiceEndpointMMAP.cpp b/services/oboeservice/AAudioServiceEndpointMMAP.cpp
index 58213f8..4be25c8 100644
--- a/services/oboeservice/AAudioServiceEndpointMMAP.cpp
+++ b/services/oboeservice/AAudioServiceEndpointMMAP.cpp
@@ -250,6 +250,8 @@
aaudio_result_t AAudioServiceEndpointMMAP::startStream(sp<AAudioServiceStreamBase> stream,
audio_port_handle_t *clientHandle) {
+ // Start the client on behalf of the AAudio service.
+ // Use the port handle that was provided by openMmapStream().
return startClient(mMmapClient, &mPortHandle);
}
@@ -262,11 +264,12 @@
aaudio_result_t AAudioServiceEndpointMMAP::startClient(const android::AudioClient& client,
audio_port_handle_t *clientHandle) {
if (mMmapStream == nullptr) return AAUDIO_ERROR_NULL;
+ ALOGD("AAudioServiceEndpointMMAP::startClient(%p(uid=%d, pid=%d))",
+ &client, client.clientUid, client.clientPid);
audio_port_handle_t originalHandle = *clientHandle;
- aaudio_result_t result = AAudioConvert_androidToAAudioResult(mMmapStream->start(client,
- clientHandle));
- ALOGD("AAudioServiceEndpointMMAP::startClient(%p(uid=%d, pid=%d), %d => %d) returns %d",
- &client, client.clientUid, client.clientPid,
+ status_t status = mMmapStream->start(client, clientHandle);
+ aaudio_result_t result = AAudioConvert_androidToAAudioResult(status);
+ ALOGD("AAudioServiceEndpointMMAP::startClient() , %d => %d returns %d",
originalHandle, *clientHandle, result);
return result;
}
diff --git a/services/oboeservice/AAudioServiceEndpointPlay.cpp b/services/oboeservice/AAudioServiceEndpointPlay.cpp
index c42a6e2..9b1833a 100644
--- a/services/oboeservice/AAudioServiceEndpointPlay.cpp
+++ b/services/oboeservice/AAudioServiceEndpointPlay.cpp
@@ -81,32 +81,44 @@
std::lock_guard <std::mutex> lock(mLockStreams);
for (const auto clientStream : mRegisteredStreams) {
+ int64_t clientFramesRead = 0;
+
if (!clientStream->isRunning()) {
continue;
}
- AAudioServiceStreamShared *streamShared =
+ sp<AAudioServiceStreamShared> streamShared =
static_cast<AAudioServiceStreamShared *>(clientStream.get());
- FifoBuffer *fifo = streamShared->getDataFifoBuffer();
- // Determine offset between framePosition in client's stream vs the underlying
- // MMAP stream.
- int64_t clientFramesRead = fifo->getReadCounter();
- // These two indices refer to the same frame.
- int64_t positionOffset = mmapFramesWritten - clientFramesRead;
- streamShared->setTimestampPositionOffset(positionOffset);
+ {
+ // Lock the AudioFifo to protect against close.
+ std::lock_guard <std::mutex> lock(streamShared->getAudioDataQueueLock());
- float volume = 1.0; // to match legacy volume
- bool underflowed = mMixer.mix(index, fifo, volume);
+ FifoBuffer *fifo = streamShared->getAudioDataFifoBuffer_l();
+ if (fifo != nullptr) {
- // This timestamp represents the completion of data being read out of the
- // client buffer. It is sent to the client and used in the timing model
- // to decide when the client has room to write more data.
- Timestamp timestamp(fifo->getReadCounter(), AudioClock::getNanoseconds());
- streamShared->markTransferTime(timestamp);
+ // Determine offset between framePosition in client's stream
+ // vs the underlying MMAP stream.
+ clientFramesRead = fifo->getReadCounter();
+ // These two indices refer to the same frame.
+ int64_t positionOffset = mmapFramesWritten - clientFramesRead;
+ streamShared->setTimestampPositionOffset(positionOffset);
- if (underflowed) {
- streamShared->incrementXRunCount();
+ float volume = 1.0; // to match legacy volume
+ bool underflowed = mMixer.mix(index, fifo, volume);
+ if (underflowed) {
+ streamShared->incrementXRunCount();
+ }
+ clientFramesRead = fifo->getReadCounter();
+ }
+ }
+
+ if (clientFramesRead > 0) {
+ // This timestamp represents the completion of data being read out of the
+ // client buffer. It is sent to the client and used in the timing model
+ // to decide when the client has room to write more data.
+ Timestamp timestamp(clientFramesRead, AudioClock::getNanoseconds());
+ streamShared->markTransferTime(timestamp);
}
index++; // just used for labelling tracks in systrace
diff --git a/services/oboeservice/AAudioServiceEndpointShared.cpp b/services/oboeservice/AAudioServiceEndpointShared.cpp
index 43d73b7..cd40066 100644
--- a/services/oboeservice/AAudioServiceEndpointShared.cpp
+++ b/services/oboeservice/AAudioServiceEndpointShared.cpp
@@ -115,16 +115,33 @@
aaudio_result_t AAudioServiceEndpointShared::startStream(sp<AAudioServiceStreamBase> sharedStream,
audio_port_handle_t *clientHandle) {
aaudio_result_t result = AAUDIO_OK;
- if (++mRunningStreamCount == 1) {
- // TODO use real-time technique to avoid mutex, eg. atomic command FIFO
+
+ {
std::lock_guard<std::mutex> lock(mLockStreams);
- result = getStreamInternal()->requestStart();
- startSharingThread_l();
+ if (++mRunningStreamCount == 1) { // atomic
+ result = getStreamInternal()->requestStart();
+ if (result != AAUDIO_OK) {
+ --mRunningStreamCount;
+ } else {
+ result = startSharingThread_l();
+ if (result != AAUDIO_OK) {
+ getStreamInternal()->requestStop();
+ --mRunningStreamCount;
+ }
+ }
+ }
}
+
if (result == AAUDIO_OK) {
- ALOGD("AAudioServiceEndpointShared::startStream() use shared stream client.");
result = getStreamInternal()->startClient(sharedStream->getAudioClient(), clientHandle);
+ if (result != AAUDIO_OK) {
+ if (--mRunningStreamCount == 0) { // atomic
+ stopSharingThread();
+ getStreamInternal()->requestStop();
+ }
+ }
}
+
return result;
}
@@ -142,7 +159,6 @@
return AAUDIO_OK;
}
-
// Get timestamp that was written by the real-time service thread, eg. mixer.
aaudio_result_t AAudioServiceEndpointShared::getFreeRunningPosition(int64_t *positionFrames,
int64_t *timeNanos) {
diff --git a/services/oboeservice/AAudioServiceStreamBase.cpp b/services/oboeservice/AAudioServiceStreamBase.cpp
index 2dc62a0..e670129 100644
--- a/services/oboeservice/AAudioServiceStreamBase.cpp
+++ b/services/oboeservice/AAudioServiceStreamBase.cpp
@@ -42,7 +42,7 @@
AAudioServiceStreamBase::AAudioServiceStreamBase(AAudioService &audioService)
: mUpMessageQueue(nullptr)
- , mAAudioThread()
+ , mTimestampThread()
, mAtomicTimestamp()
, mAudioService(audioService) {
mMmapClient.clientUid = -1;
@@ -54,10 +54,10 @@
ALOGD("AAudioServiceStreamBase::~AAudioServiceStreamBase() destroying %p", this);
// If the stream is deleted when OPEN or in use then audio resources will leak.
// This would indicate an internal error. So we want to find this ASAP.
- LOG_ALWAYS_FATAL_IF(!(mState == AAUDIO_STREAM_STATE_CLOSED
- || mState == AAUDIO_STREAM_STATE_UNINITIALIZED
- || mState == AAUDIO_STREAM_STATE_DISCONNECTED),
- "service stream still open, state = %d", mState);
+ LOG_ALWAYS_FATAL_IF(!(getState() == AAUDIO_STREAM_STATE_CLOSED
+ || getState() == AAUDIO_STREAM_STATE_UNINITIALIZED
+ || getState() == AAUDIO_STREAM_STATE_DISCONNECTED),
+ "service stream still open, state = %d", getState());
}
std::string AAudioServiceStreamBase::dumpHeader() {
@@ -71,7 +71,7 @@
<< std::dec << std::setfill(' ') ;
result << std::setw(6) << mMmapClient.clientUid;
result << std::setw(4) << (isRunning() ? "yes" : " no");
- result << std::setw(6) << mState;
+ result << std::setw(6) << getState();
result << std::setw(7) << getFormat();
result << std::setw(6) << mFramesPerBurst;
result << std::setw(5) << getSamplesPerFrame();
@@ -124,7 +124,7 @@
aaudio_result_t AAudioServiceStreamBase::close() {
aaudio_result_t result = AAUDIO_OK;
- if (mState == AAUDIO_STREAM_STATE_CLOSED) {
+ if (getState() == AAUDIO_STREAM_STATE_CLOSED) {
return AAUDIO_OK;
}
@@ -146,37 +146,50 @@
mUpMessageQueue = nullptr;
}
- mState = AAUDIO_STREAM_STATE_CLOSED;
+ setState(AAUDIO_STREAM_STATE_CLOSED);
return result;
}
+aaudio_result_t AAudioServiceStreamBase::startDevice() {
+ mClientHandle = AUDIO_PORT_HANDLE_NONE;
+ return mServiceEndpoint->startStream(this, &mClientHandle);
+}
+
/**
* Start the flow of audio data.
*
* An AAUDIO_SERVICE_EVENT_STARTED will be sent to the client when complete.
*/
aaudio_result_t AAudioServiceStreamBase::start() {
+ aaudio_result_t result = AAUDIO_OK;
if (isRunning()) {
return AAUDIO_OK;
}
if (mServiceEndpoint == nullptr) {
ALOGE("AAudioServiceStreamBase::start() missing endpoint");
- return AAUDIO_ERROR_INVALID_STATE;
+ result = AAUDIO_ERROR_INVALID_STATE;
+ goto error;
}
+
+ // Start with fresh presentation timestamps.
+ mAtomicTimestamp.clear();
+
mClientHandle = AUDIO_PORT_HANDLE_NONE;
- aaudio_result_t result = mServiceEndpoint->startStream(this, &mClientHandle);
- if (result != AAUDIO_OK) {
- ALOGE("AAudioServiceStreamBase::start() mServiceEndpoint returned %d", result);
- disconnect();
- } else {
- if (result == AAUDIO_OK) {
- sendServiceEvent(AAUDIO_SERVICE_EVENT_STARTED);
- mState = AAUDIO_STREAM_STATE_STARTED;
- mThreadEnabled.store(true);
- result = mAAudioThread.start(this);
- }
- }
+ result = startDevice();
+ if (result != AAUDIO_OK) goto error;
+
+ // This should happen at the end of the start.
+ sendServiceEvent(AAUDIO_SERVICE_EVENT_STARTED);
+ setState(AAUDIO_STREAM_STATE_STARTED);
+ mThreadEnabled.store(true);
+ result = mTimestampThread.start(this);
+ if (result != AAUDIO_OK) goto error;
+
+ return result;
+
+error:
+ disconnect();
return result;
}
@@ -197,13 +210,13 @@
sendCurrentTimestamp();
mThreadEnabled.store(false);
- result = mAAudioThread.stop();
+ result = mTimestampThread.stop();
if (result != AAUDIO_OK) {
disconnect();
return result;
}
sendServiceEvent(AAUDIO_SERVICE_EVENT_PAUSED);
- mState = AAUDIO_STREAM_STATE_PAUSED;
+ setState(AAUDIO_STREAM_STATE_PAUSED);
return result;
}
@@ -234,7 +247,7 @@
}
sendServiceEvent(AAUDIO_SERVICE_EVENT_STOPPED);
- mState = AAUDIO_STREAM_STATE_STOPPED;
+ setState(AAUDIO_STREAM_STATE_STOPPED);
return result;
}
@@ -242,20 +255,20 @@
aaudio_result_t result = AAUDIO_OK;
// clear flag that tells thread to loop
if (mThreadEnabled.exchange(false)) {
- result = mAAudioThread.stop();
+ result = mTimestampThread.stop();
}
return result;
}
aaudio_result_t AAudioServiceStreamBase::flush() {
- if (mState != AAUDIO_STREAM_STATE_PAUSED) {
+ if (getState() != AAUDIO_STREAM_STATE_PAUSED) {
ALOGE("AAudioServiceStreamBase::flush() stream not paused, state = %s",
AAudio_convertStreamStateToText(mState));
return AAUDIO_ERROR_INVALID_STATE;
}
// Data will get flushed when the client receives the FLUSHED event.
sendServiceEvent(AAUDIO_SERVICE_EVENT_FLUSHED);
- mState = AAUDIO_STREAM_STATE_FLUSHED;
+ setState(AAUDIO_STREAM_STATE_FLUSHED);
return AAUDIO_OK;
}
@@ -283,9 +296,9 @@
}
void AAudioServiceStreamBase::disconnect() {
- if (mState != AAUDIO_STREAM_STATE_DISCONNECTED) {
+ if (getState() != AAUDIO_STREAM_STATE_DISCONNECTED) {
sendServiceEvent(AAUDIO_SERVICE_EVENT_DISCONNECTED);
- mState = AAUDIO_STREAM_STATE_DISCONNECTED;
+ setState(AAUDIO_STREAM_STATE_DISCONNECTED);
}
}
@@ -321,6 +334,9 @@
aaudio_result_t result = getFreeRunningPosition(&command.timestamp.position,
&command.timestamp.timestamp);
if (result == AAUDIO_OK) {
+ ALOGV("sendCurrentTimestamp() SERVICE %8lld at %lld",
+ (long long) command.timestamp.position,
+ (long long) command.timestamp.timestamp);
command.what = AAudioServiceMessage::code::TIMESTAMP_SERVICE;
result = writeUpMessageQueue(&command);
@@ -329,13 +345,16 @@
result = getHardwareTimestamp(&command.timestamp.position,
&command.timestamp.timestamp);
if (result == AAUDIO_OK) {
+ ALOGV("sendCurrentTimestamp() HARDWARE %8lld at %lld",
+ (long long) command.timestamp.position,
+ (long long) command.timestamp.timestamp);
command.what = AAudioServiceMessage::code::TIMESTAMP_HARDWARE;
result = writeUpMessageQueue(&command);
}
}
}
- if (result == AAUDIO_ERROR_UNAVAILABLE) {
+ if (result == AAUDIO_ERROR_UNAVAILABLE) { // TODO review best error code
result = AAUDIO_OK; // just not available yet, try again later
}
return result;
@@ -346,10 +365,17 @@
* used to communicate with the underlying HAL or Service.
*/
aaudio_result_t AAudioServiceStreamBase::getDescription(AudioEndpointParcelable &parcelable) {
- // Gather information on the message queue.
- mUpMessageQueue->fillParcelable(parcelable,
- parcelable.mUpMessageQueueParcelable);
- return getDownDataDescription(parcelable);
+ {
+ std::lock_guard<std::mutex> lock(mUpMessageQueueLock);
+ if (mUpMessageQueue == nullptr) {
+ ALOGE("getDescription(): mUpMessageQueue null! - stream not open");
+ return AAUDIO_ERROR_NULL;
+ }
+ // Gather information on the message queue.
+ mUpMessageQueue->fillParcelable(parcelable,
+ parcelable.mUpMessageQueueParcelable);
+ }
+ return getAudioDataDescription(parcelable);
}
void AAudioServiceStreamBase::onVolumeChanged(float volume) {
diff --git a/services/oboeservice/AAudioServiceStreamBase.h b/services/oboeservice/AAudioServiceStreamBase.h
index 301795d..af435b4 100644
--- a/services/oboeservice/AAudioServiceStreamBase.h
+++ b/services/oboeservice/AAudioServiceStreamBase.h
@@ -32,7 +32,10 @@
#include "SharedRingBuffer.h"
#include "AAudioThread.h"
-#include "AAudioService.h"
+
+namespace android {
+ class AAudioService;
+}
namespace aaudio {
@@ -191,6 +194,12 @@
mState = state;
}
+ /**
+ * Device specific startup.
+ * @return AAUDIO_OK or negative error.
+ */
+ virtual aaudio_result_t startDevice();
+
aaudio_result_t writeUpMessageQueue(AAudioServiceMessage *command);
aaudio_result_t sendCurrentTimestamp();
@@ -204,7 +213,7 @@
virtual aaudio_result_t getHardwareTimestamp(int64_t *positionFrames, int64_t *timeNanos) = 0;
- virtual aaudio_result_t getDownDataDescription(AudioEndpointParcelable &parcelable) = 0;
+ virtual aaudio_result_t getAudioDataDescription(AudioEndpointParcelable &parcelable) = 0;
aaudio_stream_state_t mState = AAUDIO_STREAM_STATE_UNINITIALIZED;
@@ -213,7 +222,7 @@
SharedRingBuffer* mUpMessageQueue;
std::mutex mUpMessageQueueLock;
- AAudioThread mAAudioThread;
+ AAudioThread mTimestampThread;
// This is used by one thread to tell another thread to exit. So it must be atomic.
std::atomic<bool> mThreadEnabled{false};
diff --git a/services/oboeservice/AAudioServiceStreamMMAP.cpp b/services/oboeservice/AAudioServiceStreamMMAP.cpp
index 43595a4..44ba1ca 100644
--- a/services/oboeservice/AAudioServiceStreamMMAP.cpp
+++ b/services/oboeservice/AAudioServiceStreamMMAP.cpp
@@ -50,7 +50,7 @@
}
aaudio_result_t AAudioServiceStreamMMAP::close() {
- if (mState == AAUDIO_STREAM_STATE_CLOSED) {
+ if (getState() == AAUDIO_STREAM_STATE_CLOSED) {
return AAUDIO_OK;
}
@@ -67,7 +67,6 @@
aaudio_result_t result = AAudioServiceStreamBase::open(request,
AAUDIO_SHARING_MODE_EXCLUSIVE);
if (result != AAUDIO_OK) {
- ALOGE("AAudioServiceStreamBase open returned %d", result);
return result;
}
@@ -85,14 +84,11 @@
/**
* Start the flow of data.
*/
-aaudio_result_t AAudioServiceStreamMMAP::start() {
- if (isRunning()) {
- return AAUDIO_OK;
- }
-
- aaudio_result_t result = AAudioServiceStreamBase::start();
+aaudio_result_t AAudioServiceStreamMMAP::startDevice() {
+ aaudio_result_t result = AAudioServiceStreamBase::startDevice();
if (!mInService && result == AAUDIO_OK) {
- startClient(mMmapClient, &mClientHandle);
+ // Note that this can sometimes take 200 to 300 msec for a cold start!
+ result = startClient(mMmapClient, &mClientHandle);
}
return result;
}
@@ -107,7 +103,7 @@
aaudio_result_t result = AAudioServiceStreamBase::pause();
// TODO put before base::pause()?
if (!mInService) {
- stopClient(mClientHandle);
+ (void) stopClient(mClientHandle);
}
return result;
}
@@ -119,13 +115,14 @@
aaudio_result_t result = AAudioServiceStreamBase::stop();
// TODO put before base::stop()?
if (!mInService) {
- stopClient(mClientHandle);
+ (void) stopClient(mClientHandle);
}
return result;
}
aaudio_result_t AAudioServiceStreamMMAP::startClient(const android::AudioClient& client,
audio_port_handle_t *clientHandle) {
+ // Start the client on behalf of the application. Generate a new porthandle.
aaudio_result_t result = mServiceEndpoint->startClient(client, clientHandle);
return result;
}
@@ -171,7 +168,8 @@
/**
* Get an immutable description of the data queue from the HAL.
*/
-aaudio_result_t AAudioServiceStreamMMAP::getDownDataDescription(AudioEndpointParcelable &parcelable)
+aaudio_result_t AAudioServiceStreamMMAP::getAudioDataDescription(
+ AudioEndpointParcelable &parcelable)
{
sp<AAudioServiceEndpointMMAP> serviceEndpointMMAP{
static_cast<AAudioServiceEndpointMMAP *>(mServiceEndpoint.get())};
diff --git a/services/oboeservice/AAudioServiceStreamMMAP.h b/services/oboeservice/AAudioServiceStreamMMAP.h
index bf0aab3..e2415d0 100644
--- a/services/oboeservice/AAudioServiceStreamMMAP.h
+++ b/services/oboeservice/AAudioServiceStreamMMAP.h
@@ -53,14 +53,6 @@
aaudio_result_t open(const aaudio::AAudioStreamRequest &request) override;
/**
- * Start the flow of audio data.
- *
- * This is not guaranteed to be synchronous but it currently is.
- * An AAUDIO_SERVICE_EVENT_STARTED will be sent to the client when complete.
- */
- aaudio_result_t start() override;
-
- /**
* Stop the flow of data so that start() can resume without loss of data.
*
* This is not guaranteed to be synchronous but it currently is.
@@ -83,12 +75,18 @@
protected:
- aaudio_result_t getDownDataDescription(AudioEndpointParcelable &parcelable) override;
+ aaudio_result_t getAudioDataDescription(AudioEndpointParcelable &parcelable) override;
aaudio_result_t getFreeRunningPosition(int64_t *positionFrames, int64_t *timeNanos) override;
aaudio_result_t getHardwareTimestamp(int64_t *positionFrames, int64_t *timeNanos) override;
+ /**
+ * Device specific startup.
+ * @return AAUDIO_OK or negative error.
+ */
+ aaudio_result_t startDevice() override;
+
private:
bool mInService = false;
diff --git a/services/oboeservice/AAudioServiceStreamShared.cpp b/services/oboeservice/AAudioServiceStreamShared.cpp
index 834f39f..084f996 100644
--- a/services/oboeservice/AAudioServiceStreamShared.cpp
+++ b/services/oboeservice/AAudioServiceStreamShared.cpp
@@ -167,14 +167,17 @@
goto error;
}
- // Create audio data shared memory buffer for client.
- mAudioDataQueue = new SharedRingBuffer();
- result = mAudioDataQueue->allocate(calculateBytesPerFrame(), getBufferCapacity());
- if (result != AAUDIO_OK) {
- ALOGE("AAudioServiceStreamShared::open() could not allocate FIFO with %d frames",
- getBufferCapacity());
- result = AAUDIO_ERROR_NO_MEMORY;
- goto error;
+ {
+ std::lock_guard<std::mutex> lock(mAudioDataQueueLock);
+ // Create audio data shared memory buffer for client.
+ mAudioDataQueue = new SharedRingBuffer();
+ result = mAudioDataQueue->allocate(calculateBytesPerFrame(), getBufferCapacity());
+ if (result != AAUDIO_OK) {
+ ALOGE("AAudioServiceStreamShared::open() could not allocate FIFO with %d frames",
+ getBufferCapacity());
+ result = AAUDIO_ERROR_NO_MEMORY;
+ goto error;
+ }
}
ALOGD("AAudioServiceStreamShared::open() actual rate = %d, channels = %d, deviceId = %d",
@@ -197,8 +200,11 @@
aaudio_result_t AAudioServiceStreamShared::close() {
aaudio_result_t result = AAudioServiceStreamBase::close();
- delete mAudioDataQueue;
- mAudioDataQueue = nullptr;
+ {
+ std::lock_guard<std::mutex> lock(mAudioDataQueueLock);
+ delete mAudioDataQueue;
+ mAudioDataQueue = nullptr;
+ }
return result;
}
@@ -206,8 +212,14 @@
/**
* Get an immutable description of the data queue created by this service.
*/
-aaudio_result_t AAudioServiceStreamShared::getDownDataDescription(AudioEndpointParcelable &parcelable)
+aaudio_result_t AAudioServiceStreamShared::getAudioDataDescription(
+ AudioEndpointParcelable &parcelable)
{
+ std::lock_guard<std::mutex> lock(mAudioDataQueueLock);
+ if (mAudioDataQueue == nullptr) {
+ ALOGE("getAudioDataDescription(): mUpMessageQueue null! - stream not open");
+ return AAUDIO_ERROR_NULL;
+ }
// Gather information on the data queue.
mAudioDataQueue->fillParcelable(parcelable,
parcelable.mDownDataQueueParcelable);
@@ -237,9 +249,15 @@
aaudio_result_t AAudioServiceStreamShared::getHardwareTimestamp(int64_t *positionFrames,
int64_t *timeNanos) {
- aaudio_result_t result = mServiceEndpoint->getTimestamp(positionFrames, timeNanos);
+ int64_t position = 0;
+ aaudio_result_t result = mServiceEndpoint->getTimestamp(&position, timeNanos);
if (result == AAUDIO_OK) {
- *positionFrames -= mTimestampPositionOffset.load(); // Offset from shared MMAP stream
+ int64_t offset = mTimestampPositionOffset.load();
+ // TODO, do not go below starting value
+ position -= offset; // Offset from shared MMAP stream
+ ALOGV("getHardwareTimestamp() %8lld = %8lld - %8lld",
+ (long long) position, (long long) (position + offset), (long long) offset);
}
+ *positionFrames = position;
return result;
}
diff --git a/services/oboeservice/AAudioServiceStreamShared.h b/services/oboeservice/AAudioServiceStreamShared.h
index bc86dcc..8499ea5 100644
--- a/services/oboeservice/AAudioServiceStreamShared.h
+++ b/services/oboeservice/AAudioServiceStreamShared.h
@@ -54,7 +54,21 @@
aaudio_result_t close() override;
- android::FifoBuffer *getDataFifoBuffer() { return mAudioDataQueue->getFifoBuffer(); }
+ /**
+ * This must be locked when calling getAudioDataFifoBuffer_l() and while
+ * using the FifoBuffer it returns.
+ */
+ std::mutex &getAudioDataQueueLock() {
+ return mAudioDataQueueLock;
+ }
+
+ /**
+ * This must only be call under getAudioDataQueueLock().
+ * @return
+ */
+ android::FifoBuffer *getAudioDataFifoBuffer_l() { return (mAudioDataQueue == nullptr)
+ ? nullptr
+ : mAudioDataQueue->getFifoBuffer(); }
/* Keep a record of when a buffer transfer completed.
* This allows for a more accurate timing model.
@@ -75,7 +89,7 @@
protected:
- aaudio_result_t getDownDataDescription(AudioEndpointParcelable &parcelable) override;
+ aaudio_result_t getAudioDataDescription(AudioEndpointParcelable &parcelable) override;
aaudio_result_t getFreeRunningPosition(int64_t *positionFrames, int64_t *timeNanos) override;
@@ -90,7 +104,8 @@
int32_t framesPerBurst);
private:
- SharedRingBuffer *mAudioDataQueue = nullptr;
+ SharedRingBuffer *mAudioDataQueue = nullptr; // protected by mAudioDataQueueLock
+ std::mutex mAudioDataQueueLock;
std::atomic<int64_t> mTimestampPositionOffset;
std::atomic<int32_t> mXRunCount;
diff --git a/services/oboeservice/AAudioStreamTracker.cpp b/services/oboeservice/AAudioStreamTracker.cpp
new file mode 100644
index 0000000..ef88b34
--- /dev/null
+++ b/services/oboeservice/AAudioStreamTracker.cpp
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "AAudioStreamTracker"
+//#define LOG_NDEBUG 0
+#include <utils/Log.h>
+
+#include <iomanip>
+#include <iostream>
+#include <sstream>
+
+#include <aaudio/AAudio.h>
+#include <utils/String16.h>
+
+#include "AAudioStreamTracker.h"
+
+using namespace android;
+using namespace aaudio;
+
+sp<AAudioServiceStreamBase> AAudioStreamTracker::removeStreamByHandle(
+ aaudio_handle_t streamHandle) {
+ std::lock_guard<std::mutex> lock(mHandleLock);
+ sp<AAudioServiceStreamBase> serviceStream;
+ auto it = mStreamsByHandle.find(streamHandle);
+ if (it != mStreamsByHandle.end()) {
+ serviceStream = it->second;
+ mStreamsByHandle.erase(it);
+ }
+ return serviceStream;
+}
+
+sp<AAudioServiceStreamBase> AAudioStreamTracker::getStreamByHandle(
+ aaudio_handle_t streamHandle) {
+ std::lock_guard<std::mutex> lock(mHandleLock);
+ sp<AAudioServiceStreamBase> serviceStream;
+ auto it = mStreamsByHandle.find(streamHandle);
+ if (it != mStreamsByHandle.end()) {
+ serviceStream = it->second;
+ }
+ return serviceStream;
+}
+
+// advance to next legal handle value
+__attribute__((no_sanitize("integer")))
+aaudio_handle_t AAudioStreamTracker::bumpHandle(aaudio_handle_t handle) {
+ handle++;
+ // Only use positive integers.
+ if (handle <= 0) {
+ handle = 1;
+ }
+ return handle;
+}
+
+aaudio_handle_t AAudioStreamTracker::addStreamForHandle(sp<AAudioServiceStreamBase> serviceStream) {
+ std::lock_guard<std::mutex> lock(mHandleLock);
+ aaudio_handle_t handle = mPreviousHandle.load();
+ // Assign a unique handle.
+ while (true) {
+ handle = bumpHandle(handle);
+ sp<AAudioServiceStreamBase> oldServiceStream = mStreamsByHandle[handle];
+ // Is this an unused handle? It would be extremely unlikely to wrap
+ // around and collide with a very old handle. But just in case.
+ if (oldServiceStream.get() == nullptr) {
+ mStreamsByHandle[handle] = serviceStream;
+ break;
+ }
+ }
+ mPreviousHandle.store(handle);
+ return handle;
+}
+
+std::string AAudioStreamTracker::dump() const {
+ std::stringstream result;
+ const bool isLocked = AAudio_tryUntilTrue(
+ [this]()->bool { return mHandleLock.try_lock(); } /* f */,
+ 50 /* times */,
+ 20 /* sleepMs */);
+ if (!isLocked) {
+ result << "AAudioStreamTracker may be deadlocked\n";
+ } else {
+ result << "Stream Handles:\n";
+ for (const auto& it : mStreamsByHandle) {
+ aaudio_handle_t handle = it.second->getHandle();
+ result << " 0x" << std::setfill('0') << std::setw(8) << std::hex << handle
+ << std::dec << std::setfill(' ') << "\n";
+ }
+
+ mHandleLock.unlock();
+ }
+ return result.str();
+}
diff --git a/services/oboeservice/AAudioStreamTracker.h b/services/oboeservice/AAudioStreamTracker.h
new file mode 100644
index 0000000..70d440d
--- /dev/null
+++ b/services/oboeservice/AAudioStreamTracker.h
@@ -0,0 +1,73 @@
+/*
+ * 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 AAUDIO_AAUDIO_STREAM_TRACKER_H
+#define AAUDIO_AAUDIO_STREAM_TRACKER_H
+
+#include <time.h>
+#include <pthread.h>
+
+#include <aaudio/AAudio.h>
+
+#include "binding/AAudioCommon.h"
+
+#include "AAudioServiceStreamBase.h"
+
+namespace aaudio {
+
+class AAudioStreamTracker {
+
+public:
+ /**
+ * Remove the stream associated with the handle.
+ * @param streamHandle
+ * @return strong pointer to the stream if found or to nullptr
+ */
+ android::sp<AAudioServiceStreamBase> removeStreamByHandle(aaudio_handle_t streamHandle);
+
+ /**
+ * Look up a stream based on the handle.
+ * @param streamHandle
+ * @return strong pointer to the stream if found or to nullptr
+ */
+ android::sp<aaudio::AAudioServiceStreamBase> getStreamByHandle(aaudio_handle_t streamHandle);
+
+ /**
+ * Store a strong pointer to the stream and return a unique handle for future reference.
+ * The handle is guaranteed not to collide with an existing stream.
+ * @param serviceStream
+ * @return handle for identifying the stream
+ */
+ aaudio_handle_t addStreamForHandle(android::sp<AAudioServiceStreamBase> serviceStream);
+
+ /**
+ * @return string that can be added to dumpsys
+ */
+ std::string dump() const;
+
+private:
+ static aaudio_handle_t bumpHandle(aaudio_handle_t handle);
+
+ // Track stream using a unique handle that wraps. Only use positive half.
+ mutable std::mutex mHandleLock;
+ std::atomic<aaudio_handle_t> mPreviousHandle{0};
+ std::map<aaudio_handle_t, android::sp<aaudio::AAudioServiceStreamBase>> mStreamsByHandle;
+};
+
+
+} /* namespace aaudio */
+
+#endif /* AAUDIO_AAUDIO_STREAM_TRACKER_H */
diff --git a/services/oboeservice/Android.mk b/services/oboeservice/Android.mk
index 1b74ad3..584b2ef 100644
--- a/services/oboeservice/Android.mk
+++ b/services/oboeservice/Android.mk
@@ -22,7 +22,6 @@
$(TOP)/frameworks/av/media/libaaudio/src
LOCAL_SRC_FILES += \
- $(LIBAAUDIO_SRC_DIR)/utility/HandleTracker.cpp \
SharedMemoryProxy.cpp \
SharedRingBuffer.cpp \
AAudioClientTracker.cpp \
@@ -37,6 +36,7 @@
AAudioServiceStreamBase.cpp \
AAudioServiceStreamMMAP.cpp \
AAudioServiceStreamShared.cpp \
+ AAudioStreamTracker.cpp \
TimestampScheduler.cpp \
AAudioThread.cpp
diff --git a/services/soundtrigger/SoundTriggerHwService.cpp b/services/soundtrigger/SoundTriggerHwService.cpp
index 8891aba..952acd7 100644
--- a/services/soundtrigger/SoundTriggerHwService.cpp
+++ b/services/soundtrigger/SoundTriggerHwService.cpp
@@ -840,7 +840,7 @@
}
const bool supports_stop_all =
- (mHalInterface != 0) && (mHalInterface->stopAllRecognitions() == ENOSYS);
+ (mHalInterface != 0) && (mHalInterface->stopAllRecognitions() != -ENOSYS);
for (size_t i = 0; i < mModels.size(); i++) {
sp<Model> model = mModels.valueAt(i);