Merge "MediaSession2: Add caller to the callback methods"
diff --git a/media/libaaudio/examples/loopback/src/LoopbackAnalyzer.h b/media/libaaudio/examples/loopback/src/LoopbackAnalyzer.h
index b83851a..1e282d1 100644
--- a/media/libaaudio/examples/loopback/src/LoopbackAnalyzer.h
+++ b/media/libaaudio/examples/loopback/src/LoopbackAnalyzer.h
@@ -410,8 +410,7 @@
static void printAudioScope(float sample) {
- const int maxStars = 80
- ; // arbitrary, fits on one line
+ const int maxStars = 80; // arbitrary, fits on one line
char c = '*';
if (sample < -1.0) {
sample = -1.0;
@@ -555,7 +554,7 @@
break;
case STATE_WAITING_FOR_SILENCE:
- // Output silence.
+ // Output silence and wait for the echos to die down.
numSamples = numFrames * outputChannelCount;
for (int i = 0; i < numSamples; i++) {
outputData[i] = 0;
diff --git a/media/libaaudio/examples/loopback/src/loopback.cpp b/media/libaaudio/examples/loopback/src/loopback.cpp
index f2254ce..39d079e 100644
--- a/media/libaaudio/examples/loopback/src/loopback.cpp
+++ b/media/libaaudio/examples/loopback/src/loopback.cpp
@@ -128,7 +128,7 @@
myData->audioRecording.write(myData->inputData,
myData->actualInputChannelCount,
- numFrames);
+ framesRead);
int32_t numSamples = framesRead * myData->actualInputChannelCount;
convertPcm16ToFloat(myData->inputData, myData->conversionBuffer, numSamples);
@@ -161,17 +161,17 @@
static void usage() {
printf("Usage: aaudio_loopback [OPTION]...\n\n");
AAudioArgsParser::usage();
- printf(" -C{channels} number of input channels\n");
- printf(" -g{gain} recirculating loopback gain\n");
- printf(" -P{inPerf} set input AAUDIO_PERFORMANCE_MODE*\n");
- printf(" n for _NONE\n");
- printf(" l for _LATENCY\n");
- printf(" p for _POWER_SAVING\n");
- printf(" -t{test} select test mode\n");
- printf(" m for sine magnitude\n");
- printf(" e for echo latency (default)\n");
- printf(" f for file latency, analyzes %s\n\n", FILENAME_ECHOS);
- printf(" -X use EXCLUSIVE mode for input\n");
+ printf(" -C{channels} number of input channels\n");
+ printf(" -g{gain} recirculating loopback gain\n");
+ printf(" -P{inPerf} set input AAUDIO_PERFORMANCE_MODE*\n");
+ printf(" n for _NONE\n");
+ printf(" l for _LATENCY\n");
+ printf(" p for _POWER_SAVING\n");
+ printf(" -t{test} select test mode\n");
+ printf(" m for sine magnitude\n");
+ printf(" e for echo latency (default)\n");
+ printf(" f for file latency, analyzes %s\n\n", FILENAME_ECHOS);
+ printf(" -X use EXCLUSIVE mode for input\n");
printf("Example: aaudio_loopback -n2 -pl -Pl -x\n");
}
@@ -448,7 +448,7 @@
}
if (loopbackData.loopbackProcessor->getResult() < 0) {
- printf("Test failed!\n");
+ printf("ERROR: Could not get a good loopback signal. Probably because the volume was too low.\n");
} else {
printf("input error = %d = %s\n",
loopbackData.inputError, AAudio_convertResultToText(loopbackData.inputError));
@@ -467,11 +467,16 @@
}
int written = loopbackData.loopbackProcessor->save(FILENAME_ECHOS);
- printf("main() wrote %d mono samples to %s on Android device\n", written,
- FILENAME_ECHOS);
- printf("main() loopbackData.audioRecording.getSampleRate() = %d\n", loopbackData.audioRecording.getSampleRate());
+ if (written > 0) {
+ printf("main() wrote %8d mono samples to \"%s\" on Android device\n",
+ written, FILENAME_ECHOS);
+ }
+
written = loopbackData.audioRecording.save(FILENAME_ALL);
- printf("main() wrote %d mono samples to %s on Android device\n", written, FILENAME_ALL);
+ if (written > 0) {
+ printf("main() wrote %8d mono samples to \"%s\" on Android device\n",
+ written, FILENAME_ALL);
+ }
}
finish:
@@ -481,9 +486,8 @@
delete[] loopbackData.inputData;
delete[] outputData;
- printf(RESULT_TAG "error = %d = %s\n", result, AAudio_convertResultToText(result));
+ printf(RESULT_TAG "result = %s\n", AAudio_convertResultToText(result));
if ((result != AAUDIO_OK)) {
- printf("error %d = %s\n", result, AAudio_convertResultToText(result));
return EXIT_FAILURE;
} else {
printf("SUCCESS\n");
diff --git a/media/libaaudio/examples/utils/AAudioArgsParser.h b/media/libaaudio/examples/utils/AAudioArgsParser.h
index 4fc5b9f..eb6925a 100644
--- a/media/libaaudio/examples/utils/AAudioArgsParser.h
+++ b/media/libaaudio/examples/utils/AAudioArgsParser.h
@@ -19,7 +19,8 @@
#define MAX_CHANNELS 8
-#include <cctype>
+//#include <cctype>
+#include <dlfcn.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
@@ -29,6 +30,63 @@
#include "AAudioExampleUtils.h"
+
+static void (*s_setUsage)(AAudioStreamBuilder* builder, aaudio_usage_t usage) = nullptr;
+static void (*s_setContentType)(AAudioStreamBuilder* builder,
+ aaudio_content_type_t contentType) = nullptr;
+static void (*s_setInputPreset)(AAudioStreamBuilder* builder,
+ aaudio_input_preset_t inputPreset) = nullptr;
+
+static bool s_loadAttempted = false;
+static aaudio_usage_t (*s_getUsage)(AAudioStream *stream) = nullptr;
+static aaudio_content_type_t (*s_getContentType)(AAudioStream *stream) = nullptr;
+static aaudio_input_preset_t (*s_getInputPreset)(AAudioStream *stream) = nullptr;
+
+// Link to test functions in shared library.
+static void loadFutureFunctions() {
+ if (s_loadAttempted) return; // only try once
+ s_loadAttempted = true;
+
+ void *handle = dlopen("libaaudio.so", RTLD_NOW);
+ if (handle != nullptr) {
+ s_setUsage = (void (*)(AAudioStreamBuilder *, aaudio_usage_t))
+ dlsym(handle, "AAudioStreamBuilder_setUsage");
+ if (s_setUsage == nullptr) goto error;
+
+ s_setContentType = (void (*)(AAudioStreamBuilder *, aaudio_content_type_t))
+ dlsym(handle, "AAudioStreamBuilder_setContentType");
+ if (s_setContentType == nullptr) goto error;
+
+ s_setInputPreset = (void (*)(AAudioStreamBuilder *, aaudio_input_preset_t))
+ dlsym(handle, "AAudioStreamBuilder_setInputPreset");
+ if (s_setInputPreset == nullptr) goto error;
+
+ s_getUsage = (aaudio_usage_t (*)(AAudioStream *))
+ dlsym(handle, "AAudioStream_getUsage");
+ if (s_getUsage == nullptr) goto error;
+
+ s_getContentType = (aaudio_content_type_t (*)(AAudioStream *))
+ dlsym(handle, "AAudioStream_getContentType");
+ if (s_getContentType == nullptr) goto error;
+
+ s_getInputPreset = (aaudio_input_preset_t (*)(AAudioStream *))
+ dlsym(handle, "AAudioStream_getInputPreset");
+ if (s_getInputPreset == nullptr) goto error;
+ }
+ return;
+
+error:
+ // prevent any calls to these functions
+ s_setUsage = nullptr;
+ s_setContentType = nullptr;
+ s_setInputPreset = nullptr;
+ s_getUsage = nullptr;
+ s_getContentType = nullptr;
+ s_getInputPreset = nullptr;
+ dlclose(handle);
+ return;
+}
+
// TODO use this as a base class within AAudio
class AAudioParameters {
public:
@@ -140,9 +198,24 @@
AAudioStreamBuilder_setDeviceId(builder, mDeviceId);
AAudioStreamBuilder_setSharingMode(builder, mSharingMode);
AAudioStreamBuilder_setPerformanceMode(builder, mPerformanceMode);
- AAudioStreamBuilder_setUsage(builder, mUsage);
- AAudioStreamBuilder_setContentType(builder, mContentType);
- AAudioStreamBuilder_setInputPreset(builder, mInputPreset);
+
+ // Call P functions if supported.
+ loadFutureFunctions();
+ if (s_setUsage != nullptr) {
+ s_setUsage(builder, mUsage);
+ } else if (mUsage != AAUDIO_UNSPECIFIED){
+ printf("WARNING: setUsage not supported");
+ }
+ if (s_setContentType != nullptr) {
+ s_setContentType(builder, mContentType);
+ } else if (mUsage != AAUDIO_UNSPECIFIED){
+ printf("WARNING: setContentType not supported");
+ }
+ if (s_setInputPreset != nullptr) {
+ s_setInputPreset(builder, mInputPreset);
+ } else if (mUsage != AAUDIO_UNSPECIFIED){
+ printf("WARNING: setInputPreset not supported");
+ }
}
private:
@@ -332,14 +405,21 @@
printf(" PerformanceMode: requested = %d, actual = %d\n",
getPerformanceMode(), AAudioStream_getPerformanceMode(stream));
- printf(" Usage: requested = %d, actual = %d\n",
- getUsage(), AAudioStream_getUsage(stream));
- printf(" ContentType: requested = %d, actual = %d\n",
- getContentType(), AAudioStream_getContentType(stream));
+ loadFutureFunctions();
- if (AAudioStream_getDirection(stream) == AAUDIO_DIRECTION_INPUT) {
- printf(" InputPreset: requested = %d, actual = %d\n",
- getInputPreset(), AAudioStream_getInputPreset(stream));
+ if (s_setUsage != nullptr) {
+ printf(" Usage: requested = %d, actual = %d\n",
+ getUsage(), s_getUsage(stream));
+ }
+ if (s_getContentType != nullptr) {
+ printf(" ContentType: requested = %d, actual = %d\n",
+ getContentType(), s_getContentType(stream));
+ }
+
+ if (AAudioStream_getDirection(stream) == AAUDIO_DIRECTION_INPUT
+ && s_getInputPreset != nullptr) {
+ printf(" InputPreset: requested = %d, actual = %d\n",
+ getInputPreset(), s_getInputPreset(stream));
}
printf(" Is MMAP used? %s\n", AAudioStream_isMMapUsed(stream)
diff --git a/media/libaaudio/src/client/AudioStreamInternal.cpp b/media/libaaudio/src/client/AudioStreamInternal.cpp
index b611160..6b25302 100644
--- a/media/libaaudio/src/client/AudioStreamInternal.cpp
+++ b/media/libaaudio/src/client/AudioStreamInternal.cpp
@@ -104,7 +104,7 @@
request.setUserId(getuid());
request.setProcessId(getpid());
request.setSharingModeMatchRequired(isSharingModeMatchRequired());
- request.setInService(mInService);
+ request.setInService(isInService());
request.getConfiguration().setDeviceId(getDeviceId());
request.getConfiguration().setSampleRate(getSampleRate());
@@ -118,11 +118,24 @@
request.getConfiguration().setBufferCapacity(builder.getBufferCapacity());
+ mDeviceChannelCount = getSamplesPerFrame(); // Assume it will be the same. Update if not.
+
mServiceStreamHandle = mServiceInterface.openStream(request, configurationOutput);
+ if (mServiceStreamHandle < 0
+ && request.getConfiguration().getSamplesPerFrame() == 1 // mono?
+ && getDirection() == AAUDIO_DIRECTION_OUTPUT
+ && !isInService()) {
+ // if that failed then try switching from mono to stereo if OUTPUT.
+ // Only do this in the client. Otherwise we end up with a mono mixer in the service
+ // that writes to a stereo MMAP stream.
+ ALOGD("%s - openStream() returned %d, try switching from MONO to STEREO",
+ __func__, mServiceStreamHandle);
+ request.getConfiguration().setSamplesPerFrame(2); // stereo
+ mServiceStreamHandle = mServiceInterface.openStream(request, configurationOutput);
+ }
if (mServiceStreamHandle < 0) {
- result = mServiceStreamHandle;
- ALOGE("%s - openStream() returned %d", __func__, result);
- return result;
+ ALOGE("%s - openStream() returned %d", __func__, mServiceStreamHandle);
+ return mServiceStreamHandle;
}
result = configurationOutput.validate();
@@ -130,8 +143,12 @@
goto error;
}
// Save results of the open.
+ if (getSamplesPerFrame() == AAUDIO_UNSPECIFIED) {
+ setSamplesPerFrame(configurationOutput.getSamplesPerFrame());
+ }
+ mDeviceChannelCount = configurationOutput.getSamplesPerFrame();
+
setSampleRate(configurationOutput.getSampleRate());
- setSamplesPerFrame(configurationOutput.getSamplesPerFrame());
setDeviceId(configurationOutput.getDeviceId());
setSessionId(configurationOutput.getSessionId());
setSharingMode(configurationOutput.getSharingMode());
@@ -160,7 +177,6 @@
goto error;
}
-
// Validate result from server.
framesPerBurst = mEndpointDescriptor.dataQueueDescriptor.framesPerBurst;
if (framesPerBurst < MIN_FRAMES_PER_BURST || framesPerBurst > MAX_FRAMES_PER_BURST) {
diff --git a/media/libaaudio/src/client/AudioStreamInternal.h b/media/libaaudio/src/client/AudioStreamInternal.h
index 0f54f8c..0e0724b 100644
--- a/media/libaaudio/src/client/AudioStreamInternal.h
+++ b/media/libaaudio/src/client/AudioStreamInternal.h
@@ -138,7 +138,14 @@
// Calculate timeout for an operation involving framesPerOperation.
int64_t calculateReasonableTimeout(int32_t framesPerOperation);
- aaudio_format_t mDeviceFormat = AAUDIO_FORMAT_UNSPECIFIED;
+ aaudio_format_t getDeviceFormat() const { return mDeviceFormat; }
+
+ int32_t getDeviceChannelCount() const { return mDeviceChannelCount; }
+
+ /**
+ * @return true if running in audio service, versus in app process
+ */
+ bool isInService() const { return mInService; }
IsochronousClockModel mClockModel; // timing model for chasing the HAL
@@ -187,6 +194,11 @@
EndpointDescriptor mEndpointDescriptor; // buffer description with resolved addresses
int64_t mServiceLatencyNanos = 0;
+
+ // Sometimes the hardware is operating with a different format or channel count from the app.
+ // Then we require conversion in AAudio.
+ aaudio_format_t mDeviceFormat = AAUDIO_FORMAT_UNSPECIFIED;
+ int32_t mDeviceChannelCount = 0;
};
} /* namespace aaudio */
diff --git a/media/libaaudio/src/client/AudioStreamInternalCapture.cpp b/media/libaaudio/src/client/AudioStreamInternalCapture.cpp
index 62f0fc8..0719fe1 100644
--- a/media/libaaudio/src/client/AudioStreamInternalCapture.cpp
+++ b/media/libaaudio/src/client/AudioStreamInternalCapture.cpp
@@ -176,16 +176,16 @@
int32_t numSamples = framesToProcess * getSamplesPerFrame();
// TODO factor this out into a utility function
- if (mDeviceFormat == getFormat()) {
+ if (getDeviceFormat() == getFormat()) {
memcpy(destination, wrappingBuffer.data[partIndex], numBytes);
- } else if (mDeviceFormat == AAUDIO_FORMAT_PCM_I16
+ } else if (getDeviceFormat() == AAUDIO_FORMAT_PCM_I16
&& getFormat() == AAUDIO_FORMAT_PCM_FLOAT) {
AAudioConvert_pcm16ToFloat(
(const int16_t *) wrappingBuffer.data[partIndex],
(float *) destination,
numSamples,
1.0f);
- } else if (mDeviceFormat == AAUDIO_FORMAT_PCM_FLOAT
+ } else if (getDeviceFormat() == AAUDIO_FORMAT_PCM_FLOAT
&& getFormat() == AAUDIO_FORMAT_PCM_I16) {
AAudioConvert_floatToPcm16(
(const float *) wrappingBuffer.data[partIndex],
diff --git a/media/libaaudio/src/client/AudioStreamInternalPlay.cpp b/media/libaaudio/src/client/AudioStreamInternalPlay.cpp
index a0a0a54..11b43c3 100644
--- a/media/libaaudio/src/client/AudioStreamInternalPlay.cpp
+++ b/media/libaaudio/src/client/AudioStreamInternalPlay.cpp
@@ -206,7 +206,7 @@
// ALOGD("AudioStreamInternal::writeNowWithConversion(%p, %d)",
// buffer, numFrames);
WrappingBuffer wrappingBuffer;
- uint8_t *source = (uint8_t *) buffer;
+ uint8_t *byteBuffer = (uint8_t *) buffer;
int32_t framesLeft = numFrames;
mAudioEndpoint.getEmptyFramesAvailable(&wrappingBuffer);
@@ -220,69 +220,26 @@
if (framesToWrite > framesAvailable) {
framesToWrite = framesAvailable;
}
+
int32_t numBytes = getBytesPerFrame() * framesToWrite;
- int32_t numSamples = framesToWrite * getSamplesPerFrame();
// Data conversion.
float levelFrom;
float levelTo;
- bool ramping = mVolumeRamp.nextSegment(framesToWrite, &levelFrom, &levelTo);
- // The formats are validated when the stream is opened so we do not have to
- // check for illegal combinations here.
- // TODO factor this out into a utility function
- if (getFormat() == AAUDIO_FORMAT_PCM_FLOAT) {
- if (mDeviceFormat == AAUDIO_FORMAT_PCM_FLOAT) {
- AAudio_linearRamp(
- (const float *) source,
- (float *) wrappingBuffer.data[partIndex],
- framesToWrite,
- getSamplesPerFrame(),
- levelFrom,
- levelTo);
- } else if (mDeviceFormat == AAUDIO_FORMAT_PCM_I16) {
- if (ramping) {
- AAudioConvert_floatToPcm16(
- (const float *) source,
- (int16_t *) wrappingBuffer.data[partIndex],
- framesToWrite,
- getSamplesPerFrame(),
- levelFrom,
- levelTo);
- } else {
- AAudioConvert_floatToPcm16(
- (const float *) source,
- (int16_t *) wrappingBuffer.data[partIndex],
- numSamples,
- levelTo);
- }
- }
- } else if (getFormat() == AAUDIO_FORMAT_PCM_I16) {
- if (mDeviceFormat == AAUDIO_FORMAT_PCM_FLOAT) {
- if (ramping) {
- AAudioConvert_pcm16ToFloat(
- (const int16_t *) source,
- (float *) wrappingBuffer.data[partIndex],
- framesToWrite,
- getSamplesPerFrame(),
- levelFrom,
- levelTo);
- } else {
- AAudioConvert_pcm16ToFloat(
- (const int16_t *) source,
- (float *) wrappingBuffer.data[partIndex],
- numSamples,
- levelTo);
- }
- } else if (mDeviceFormat == AAUDIO_FORMAT_PCM_I16) {
- AAudio_linearRamp(
- (const int16_t *) source,
- (int16_t *) wrappingBuffer.data[partIndex],
- framesToWrite,
- getSamplesPerFrame(),
- levelFrom,
- levelTo);
- }
- }
- source += numBytes;
+ mVolumeRamp.nextSegment(framesToWrite, &levelFrom, &levelTo);
+
+ AAudioDataConverter::FormattedData source(
+ (void *)byteBuffer,
+ getFormat(),
+ getSamplesPerFrame());
+ AAudioDataConverter::FormattedData destination(
+ wrappingBuffer.data[partIndex],
+ getDeviceFormat(),
+ getDeviceChannelCount());
+
+ AAudioDataConverter::convert(source, destination, framesToWrite,
+ levelFrom, levelTo);
+
+ byteBuffer += numBytes;
framesLeft -= framesToWrite;
} else {
break;
diff --git a/media/libaaudio/src/utility/AAudioUtilities.cpp b/media/libaaudio/src/utility/AAudioUtilities.cpp
index 854c691..40b31b9 100644
--- a/media/libaaudio/src/utility/AAudioUtilities.cpp
+++ b/media/libaaudio/src/utility/AAudioUtilities.cpp
@@ -27,6 +27,7 @@
#include <aaudio/AAudioTesting.h>
#include <math.h>
#include <system/audio-base.h>
+#include <assert.h>
#include "utility/AAudioUtilities.h"
@@ -72,7 +73,7 @@
int16_t *destination,
int32_t numSamples,
float amplitude) {
- float scaler = amplitude;
+ const float scaler = amplitude;
for (int i = 0; i < numSamples; i++) {
float sample = *source++;
*destination++ = clipAndClampFloatToPcm16(sample, scaler);
@@ -103,7 +104,7 @@
float *destination,
int32_t numSamples,
float amplitude) {
- float scaler = amplitude / SHORT_SCALE;
+ const float scaler = amplitude / SHORT_SCALE;
for (int i = 0; i < numSamples; i++) {
destination[i] = source[i] * scaler;
}
@@ -117,7 +118,7 @@
float amplitude1,
float amplitude2) {
float scaler = amplitude1 / SHORT_SCALE;
- float delta = (amplitude2 - amplitude1) / (SHORT_SCALE * (float) numFrames);
+ const float delta = (amplitude2 - amplitude1) / (SHORT_SCALE * (float) numFrames);
for (int frameIndex = 0; frameIndex < numFrames; frameIndex++) {
for (int sampleIndex = 0; sampleIndex < samplesPerFrame; sampleIndex++) {
*destination++ = *source++ * scaler;
@@ -134,7 +135,7 @@
float amplitude1,
float amplitude2) {
float scaler = amplitude1;
- float delta = (amplitude2 - amplitude1) / numFrames;
+ const float delta = (amplitude2 - amplitude1) / numFrames;
for (int frameIndex = 0; frameIndex < numFrames; frameIndex++) {
for (int sampleIndex = 0; sampleIndex < samplesPerFrame; sampleIndex++) {
float sample = *source++;
@@ -158,7 +159,7 @@
float amplitude2) {
// Because we are converting from int16 to 1nt16, we do not have to scale by 1/32768.
float scaler = amplitude1;
- float delta = (amplitude2 - amplitude1) / numFrames;
+ const float delta = (amplitude2 - amplitude1) / numFrames;
for (int frameIndex = 0; frameIndex < numFrames; frameIndex++) {
for (int sampleIndex = 0; sampleIndex < samplesPerFrame; sampleIndex++) {
// No need to clip because int16_t range is inherently limited.
@@ -169,6 +170,255 @@
}
}
+// *************************************************************************************
+// Convert Mono To Stereo at the same time as converting format.
+void AAudioConvert_formatMonoToStereo(const float *source,
+ int16_t *destination,
+ int32_t numFrames,
+ float amplitude) {
+ const float scaler = amplitude;
+ for (int i = 0; i < numFrames; i++) {
+ float sample = *source++;
+ int16_t sample16 = clipAndClampFloatToPcm16(sample, scaler);
+ *destination++ = sample16;
+ *destination++ = sample16;
+ }
+}
+
+void AAudioConvert_formatMonoToStereo(const float *source,
+ int16_t *destination,
+ int32_t numFrames,
+ float amplitude1,
+ float amplitude2) {
+ // divide by numFrames so that we almost reach amplitude2
+ const float delta = (amplitude2 - amplitude1) / numFrames;
+ for (int frameIndex = 0; frameIndex < numFrames; frameIndex++) {
+ const float scaler = amplitude1 + (frameIndex * delta);
+ const float sample = *source++;
+ int16_t sample16 = clipAndClampFloatToPcm16(sample, scaler);
+ *destination++ = sample16;
+ *destination++ = sample16;
+ }
+}
+
+void AAudioConvert_formatMonoToStereo(const int16_t *source,
+ float *destination,
+ int32_t numFrames,
+ float amplitude) {
+ const float scaler = amplitude / SHORT_SCALE;
+ for (int i = 0; i < numFrames; i++) {
+ float sample = source[i] * scaler;
+ *destination++ = sample;
+ *destination++ = sample;
+ }
+}
+
+// This code assumes amplitude1 and amplitude2 are between 0.0 and 1.0
+void AAudioConvert_formatMonoToStereo(const int16_t *source,
+ float *destination,
+ int32_t numFrames,
+ float amplitude1,
+ float amplitude2) {
+ const float scaler1 = amplitude1 / SHORT_SCALE;
+ const float delta = (amplitude2 - amplitude1) / (SHORT_SCALE * (float) numFrames);
+ for (int frameIndex = 0; frameIndex < numFrames; frameIndex++) {
+ float scaler = scaler1 + (frameIndex * delta);
+ float sample = source[frameIndex] * scaler;
+ *destination++ = sample;
+ *destination++ = sample;
+ }
+}
+
+// This code assumes amplitude1 and amplitude2 are between 0.0 and 1.0
+void AAudio_linearRampMonoToStereo(const float *source,
+ float *destination,
+ int32_t numFrames,
+ float amplitude1,
+ float amplitude2) {
+ const float delta = (amplitude2 - amplitude1) / numFrames;
+ for (int frameIndex = 0; frameIndex < numFrames; frameIndex++) {
+ float sample = *source++;
+
+ // Clip to valid range of a float sample to prevent excessive volume.
+ if (sample > MAX_HEADROOM) sample = MAX_HEADROOM;
+ else if (sample < MIN_HEADROOM) sample = MIN_HEADROOM;
+
+ const float scaler = amplitude1 + (frameIndex * delta);
+ float sampleScaled = sample * scaler;
+ *destination++ = sampleScaled;
+ *destination++ = sampleScaled;
+ }
+}
+
+// This code assumes amplitude1 and amplitude2 are between 0.0 and 1.0
+void AAudio_linearRampMonoToStereo(const int16_t *source,
+ int16_t *destination,
+ int32_t numFrames,
+ float amplitude1,
+ float amplitude2) {
+ // Because we are converting from int16 to 1nt16, we do not have to scale by 1/32768.
+ const float delta = (amplitude2 - amplitude1) / numFrames;
+ for (int frameIndex = 0; frameIndex < numFrames; frameIndex++) {
+ const float scaler = amplitude1 + (frameIndex * delta);
+ // No need to clip because int16_t range is inherently limited.
+ const float sample = *source++ * scaler;
+ int16_t sample16 = (int16_t) roundf(sample);
+ *destination++ = sample16;
+ *destination++ = sample16;
+ }
+}
+
+// *************************************************************************************
+void AAudioDataConverter::convert(
+ const FormattedData &source,
+ const FormattedData &destination,
+ int32_t numFrames,
+ float levelFrom,
+ float levelTo) {
+
+ if (source.channelCount == 1 && destination.channelCount == 2) {
+ convertMonoToStereo(source,
+ destination,
+ numFrames,
+ levelFrom,
+ levelTo);
+ } else {
+ // We only support mono to stereo conversion. Otherwise source and destination
+ // must match.
+ assert(source.channelCount == destination.channelCount);
+ convertChannelsMatch(source,
+ destination,
+ numFrames,
+ levelFrom,
+ levelTo);
+ }
+}
+
+void AAudioDataConverter::convertMonoToStereo(
+ const FormattedData &source,
+ const FormattedData &destination,
+ int32_t numFrames,
+ float levelFrom,
+ float levelTo) {
+
+ // The formats are validated when the stream is opened so we do not have to
+ // check for illegal combinations here.
+ if (source.format == AAUDIO_FORMAT_PCM_FLOAT) {
+ if (destination.format == AAUDIO_FORMAT_PCM_FLOAT) {
+ AAudio_linearRampMonoToStereo(
+ (const float *) source.data,
+ (float *) destination.data,
+ numFrames,
+ levelFrom,
+ levelTo);
+ } else if (destination.format == AAUDIO_FORMAT_PCM_I16) {
+ if (levelFrom != levelTo) {
+ AAudioConvert_formatMonoToStereo(
+ (const float *) source.data,
+ (int16_t *) destination.data,
+ numFrames,
+ levelFrom,
+ levelTo);
+ } else {
+ AAudioConvert_formatMonoToStereo(
+ (const float *) source.data,
+ (int16_t *) destination.data,
+ numFrames,
+ levelTo);
+ }
+ }
+ } else if (source.format == AAUDIO_FORMAT_PCM_I16) {
+ if (destination.format == AAUDIO_FORMAT_PCM_FLOAT) {
+ if (levelFrom != levelTo) {
+ AAudioConvert_formatMonoToStereo(
+ (const int16_t *) source.data,
+ (float *) destination.data,
+ numFrames,
+ levelFrom,
+ levelTo);
+ } else {
+ AAudioConvert_formatMonoToStereo(
+ (const int16_t *) source.data,
+ (float *) destination.data,
+ numFrames,
+ levelTo);
+ }
+ } else if (destination.format == AAUDIO_FORMAT_PCM_I16) {
+ AAudio_linearRampMonoToStereo(
+ (const int16_t *) source.data,
+ (int16_t *) destination.data,
+ numFrames,
+ levelFrom,
+ levelTo);
+ }
+ }
+}
+
+void AAudioDataConverter::convertChannelsMatch(
+ const FormattedData &source,
+ const FormattedData &destination,
+ int32_t numFrames,
+ float levelFrom,
+ float levelTo) {
+ const int32_t numSamples = numFrames * source.channelCount;
+
+ // The formats are validated when the stream is opened so we do not have to
+ // check for illegal combinations here.
+ if (source.format == AAUDIO_FORMAT_PCM_FLOAT) {
+ if (destination.format == AAUDIO_FORMAT_PCM_FLOAT) {
+ AAudio_linearRamp(
+ (const float *) source.data,
+ (float *) destination.data,
+ numFrames,
+ source.channelCount,
+ levelFrom,
+ levelTo);
+ } else if (destination.format == AAUDIO_FORMAT_PCM_I16) {
+ if (levelFrom != levelTo) {
+ AAudioConvert_floatToPcm16(
+ (const float *) source.data,
+ (int16_t *) destination.data,
+ numFrames,
+ source.channelCount,
+ levelFrom,
+ levelTo);
+ } else {
+ AAudioConvert_floatToPcm16(
+ (const float *) source.data,
+ (int16_t *) destination.data,
+ numSamples,
+ levelTo);
+ }
+ }
+ } else if (source.format == AAUDIO_FORMAT_PCM_I16) {
+ if (destination.format == AAUDIO_FORMAT_PCM_FLOAT) {
+ if (levelFrom != levelTo) {
+ AAudioConvert_pcm16ToFloat(
+ (const int16_t *) source.data,
+ (float *) destination.data,
+ numFrames,
+ source.channelCount,
+ levelFrom,
+ levelTo);
+ } else {
+ AAudioConvert_pcm16ToFloat(
+ (const int16_t *) source.data,
+ (float *) destination.data,
+ numSamples,
+ levelTo);
+ }
+ } else if (destination.format == AAUDIO_FORMAT_PCM_I16) {
+ AAudio_linearRamp(
+ (const int16_t *) source.data,
+ (int16_t *) destination.data,
+ numFrames,
+ source.channelCount,
+ levelFrom,
+ levelTo);
+ }
+ }
+}
+
status_t AAudioConvert_aaudioToAndroidStatus(aaudio_result_t result) {
// This covers the case for AAUDIO_OK and for positive results.
if (result >= 0) {
diff --git a/media/libaaudio/src/utility/AAudioUtilities.h b/media/libaaudio/src/utility/AAudioUtilities.h
index dc6a671..cea88fb 100644
--- a/media/libaaudio/src/utility/AAudioUtilities.h
+++ b/media/libaaudio/src/utility/AAudioUtilities.h
@@ -159,6 +159,41 @@
float amplitude1,
float amplitude2);
+class AAudioDataConverter {
+public:
+
+ struct FormattedData {
+
+ FormattedData(void *data, aaudio_format_t format, int32_t channelCount)
+ : data(data)
+ , format(format)
+ , channelCount(channelCount) {}
+
+ const void *data = nullptr;
+ const aaudio_format_t format = AAUDIO_FORMAT_UNSPECIFIED;
+ const int32_t channelCount = 1;
+ };
+
+ static void convert(const FormattedData &source,
+ const FormattedData &destination,
+ int32_t numFrames,
+ float levelFrom,
+ float levelTo);
+
+private:
+ static void convertMonoToStereo(const FormattedData &source,
+ const FormattedData &destination,
+ int32_t numFrames,
+ float levelFrom,
+ float levelTo);
+
+ static void convertChannelsMatch(const FormattedData &source,
+ const FormattedData &destination,
+ int32_t numFrames,
+ float levelFrom,
+ float levelTo);
+};
+
/**
* Calculate the number of bytes and prevent numeric overflow.
* @param numFrames frame count
diff --git a/media/libaudioclient/ToneGenerator.cpp b/media/libaudioclient/ToneGenerator.cpp
index 5a33975..5716727 100644
--- a/media/libaudioclient/ToneGenerator.cpp
+++ b/media/libaudioclient/ToneGenerator.cpp
@@ -1030,7 +1030,7 @@
bool lResult = false;
status_t lStatus;
- if ((toneType < 0) || (toneType >= NUM_TONES))
+ if (toneType >= NUM_TONES)
return lResult;
toneType = getToneForRegion(toneType);
diff --git a/media/libstagefright/codec2/client/Android.bp b/media/libstagefright/codec2/client/Android.bp
new file mode 100644
index 0000000..0129e15
--- /dev/null
+++ b/media/libstagefright/codec2/client/Android.bp
@@ -0,0 +1,30 @@
+cc_library {
+ name: "libstagefright_codec2_client",
+
+ srcs: [
+ "client.cpp",
+ ],
+
+ shared_libs: [
+ "android.hardware.media.bufferpool@1.0",
+ "libcutils",
+ "libhidlbase",
+ "libhidltransport",
+ "liblog",
+ "libstagefright_codec2",
+ "libstagefright_codec2_vndk",
+ "libstagefright_codec2_hidl@1.0",
+ "libutils",
+ "vendor.google.media.c2@1.0",
+ ],
+
+ export_include_dirs: [
+ "include",
+ ],
+
+ export_shared_lib_headers: [
+ "libstagefright_codec2",
+ ],
+
+}
+
diff --git a/media/libstagefright/codec2/client/client.cpp b/media/libstagefright/codec2/client/client.cpp
new file mode 100644
index 0000000..5a176dc
--- /dev/null
+++ b/media/libstagefright/codec2/client/client.cpp
@@ -0,0 +1,509 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "Codec2Client-interfaces"
+#include <log/log.h>
+
+#include <media/stagefright/codec2/client.h>
+
+#include <codec2/hidl/1.0/types.h>
+
+#include <vendor/google/media/c2/1.0/IComponentListener.h>
+#include <vendor/google/media/c2/1.0/IConfigurable.h>
+#include <vendor/google/media/c2/1.0/IComponentInterface.h>
+#include <vendor/google/media/c2/1.0/IComponent.h>
+#include <vendor/google/media/c2/1.0/IComponentStore.h>
+
+#include <hidl/HidlSupport.h>
+
+#include <limits>
+#include <type_traits>
+
+namespace /* unnamed */ {
+
+// TODO: Find the appropriate error code for this
+constexpr c2_status_t C2_TRANSACTION_FAILED = C2_CORRUPTED;
+
+} // unnamed namespace
+
+namespace android {
+
+using ::android::hardware::hidl_vec;
+using ::android::hardware::hidl_string;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+
+using namespace ::vendor::google::media::c2::V1_0;
+using namespace ::vendor::google::media::c2::V1_0::implementation;
+
+// Codec2ConfigurableClient
+
+const C2String& Codec2ConfigurableClient::getName() const {
+ return mName;
+}
+
+Codec2ConfigurableClient::Base* Codec2ConfigurableClient::base() const {
+ return static_cast<Base*>(mBase.get());
+}
+
+Codec2ConfigurableClient::Codec2ConfigurableClient(
+ const sp<Codec2ConfigurableClient::Base>& base) : mBase(base) {
+ Return<void> transStatus = base->getName(
+ [this](const hidl_string& name) {
+ mName = name.c_str();
+ });
+ if (!transStatus.isOk()) {
+ ALOGE("Cannot obtain name from IConfigurable.");
+ }
+}
+
+c2_status_t Codec2ConfigurableClient::query(
+ const std::vector<C2Param::Index> &indices,
+ c2_blocking_t mayBlock,
+ std::vector<std::unique_ptr<C2Param>>* const params) const {
+ hidl_vec<ParamIndex> hidlIndices(indices.size());
+ size_t i = 0;
+ for (const C2Param::Index& index : indices) {
+ hidlIndices[i++] = static_cast<ParamIndex>(index.operator uint32_t());
+ }
+ c2_status_t status;
+ Return<void> transStatus = base()->query(
+ hidlIndices,
+ mayBlock == C2_MAY_BLOCK,
+ [&status, params](Status s, const Params& p) {
+ status = static_cast<c2_status_t>(s);
+ if (status != C2_OK) {
+ return;
+ }
+ status = copyParamsFromBlob(params, p);
+ });
+ if (!transStatus.isOk()) {
+ ALOGE("query -- transaction failed.");
+ return C2_TRANSACTION_FAILED;
+ }
+ return status;
+}
+
+c2_status_t Codec2ConfigurableClient::config(
+ const std::vector<C2Param*> ¶ms,
+ c2_blocking_t mayBlock,
+ std::vector<std::unique_ptr<C2SettingResult>>* const failures) {
+ Params hidlParams;
+ Status hidlStatus = createParamsBlob(&hidlParams, params);
+ if (hidlStatus != Status::OK) {
+ ALOGE("config -- bad input.");
+ return C2_TRANSACTION_FAILED;
+ }
+ c2_status_t status;
+ Return<void> transStatus = base()->config(
+ hidlParams,
+ mayBlock == C2_MAY_BLOCK,
+ [&status, ¶ms, failures](
+ Status s,
+ const hidl_vec<SettingResult> f,
+ const Params& o) {
+ status = static_cast<c2_status_t>(s);
+ if (status != C2_OK) {
+ return;
+ }
+ failures->clear();
+ failures->resize(f.size());
+ size_t i = 0;
+ for (const SettingResult& sf : f) {
+ status = objcpy(&(*failures)[i++], sf);
+ if (status != C2_OK) {
+ return;
+ }
+ }
+ status = updateParamsFromBlob(params, o);
+ });
+ if (!transStatus.isOk()) {
+ ALOGE("config -- transaction failed.");
+ return C2_TRANSACTION_FAILED;
+ }
+ return status;
+}
+
+c2_status_t Codec2ConfigurableClient::querySupportedParams(
+ std::vector<std::shared_ptr<C2ParamDescriptor>>* const params) const {
+ // TODO: Cache and query properly!
+ c2_status_t status;
+ Return<void> transStatus = base()->querySupportedParams(
+ std::numeric_limits<uint32_t>::min(),
+ std::numeric_limits<uint32_t>::max(),
+ [&status, params](
+ Status s,
+ const hidl_vec<ParamDescriptor>& p) {
+ status = static_cast<c2_status_t>(s);
+ if (status != C2_OK) {
+ return;
+ }
+ params->resize(p.size());
+ size_t i = 0;
+ for (const ParamDescriptor& sp : p) {
+ status = objcpy(&(*params)[i++], sp);
+ if (status != C2_OK) {
+ return;
+ }
+ }
+ });
+ if (!transStatus.isOk()) {
+ ALOGE("querySupportedParams -- transaction failed.");
+ return C2_TRANSACTION_FAILED;
+ }
+ return status;
+}
+
+c2_status_t Codec2ConfigurableClient::querySupportedValues(
+ std::vector<C2FieldSupportedValuesQuery>& fields,
+ c2_blocking_t mayBlock) const {
+ hidl_vec<FieldSupportedValuesQuery> inFields(fields.size());
+ for (size_t i = 0; i < fields.size(); ++i) {
+ Status hidlStatus = objcpy(&inFields[i], fields[i]);
+ if (hidlStatus != Status::OK) {
+ ALOGE("querySupportedValues -- bad input");
+ return C2_TRANSACTION_FAILED;
+ }
+ }
+
+ c2_status_t status;
+ Return<void> transStatus = base()->querySupportedValues(
+ inFields,
+ mayBlock == C2_MAY_BLOCK,
+ [&status, &inFields, &fields](
+ Status s,
+ const hidl_vec<FieldSupportedValuesQueryResult>& r) {
+ status = static_cast<c2_status_t>(s);
+ if (status != C2_OK) {
+ return;
+ }
+ if (r.size() != fields.size()) {
+ ALOGE("querySupportedValues -- input and output lists "
+ "have different sizes.");
+ status = C2_CORRUPTED;
+ return;
+ }
+ for (size_t i = 0; i < fields.size(); ++i) {
+ status = objcpy(&fields[i], inFields[i], r[i]);
+ if (status != C2_OK) {
+ return;
+ }
+ }
+ });
+ if (!transStatus.isOk()) {
+ ALOGE("querySupportedValues -- transaction failed.");
+ return C2_TRANSACTION_FAILED;
+ }
+ return status;
+}
+
+// Codec2Client
+
+Codec2Client::Base* Codec2Client::base() const {
+ return static_cast<Base*>(mBase.get());
+}
+
+Codec2Client::Codec2Client(const sp<Codec2Client::Base>& base) :
+ Codec2ConfigurableClient(base), mListed(false) {
+}
+
+c2_status_t Codec2Client::createComponent(
+ const C2String& name,
+ const std::shared_ptr<Codec2Client::Listener>& listener,
+ std::shared_ptr<Codec2Client::Component>* const component) {
+
+ // TODO: Add support for Bufferpool
+
+ struct HidlListener : public IComponentListener {
+ std::shared_ptr<Codec2Client::Listener> base;
+ std::weak_ptr<Codec2Client::Component> component;
+
+ virtual Return<void> onWorkDone(const WorkBundle& workBundle) override {
+ std::list<std::unique_ptr<C2Work>> workItems;
+ c2_status_t status = objcpy(&workItems, workBundle);
+ if (status != C2_OK) {
+ ALOGE("onWorkDone -- received corrupted WorkBundle. "
+ "Error code: %d", static_cast<int>(status));
+ return Void();
+ }
+ base->onWorkDone(component, workItems);
+ return Void();
+ }
+
+ virtual Return<void> onTripped(
+ const hidl_vec<SettingResult>& settingResults) override {
+ std::vector<std::shared_ptr<C2SettingResult>> c2SettingResults(
+ settingResults.size());
+ c2_status_t status;
+ for (size_t i = 0; i < settingResults.size(); ++i) {
+ std::unique_ptr<C2SettingResult> c2SettingResult;
+ status = objcpy(&c2SettingResult, settingResults[i]);
+ if (status != C2_OK) {
+ ALOGE("onTripped -- received corrupted SettingResult. "
+ "Error code: %d", static_cast<int>(status));
+ return Void();
+ }
+ c2SettingResults[i] = std::move(c2SettingResult);
+ }
+ base->onTripped(component, c2SettingResults);
+ return Void();
+ }
+
+ virtual Return<void> onError(Status s, uint32_t errorCode) override {
+ base->onError(component, s == Status::OK ?
+ errorCode : static_cast<c2_status_t>(s));
+ return Void();
+ }
+ };
+
+ c2_status_t status;
+ sp<HidlListener> hidlListener = new HidlListener();
+ hidlListener->base = listener;
+ Return<void> transStatus = base()->createComponent(
+ name,
+ hidlListener,
+ nullptr,
+ [&status, component](
+ Status s,
+ const sp<IComponent>& c) {
+ status = static_cast<c2_status_t>(s);
+ if (status != C2_OK) {
+ return;
+ }
+ *component = std::make_shared<Codec2Client::Component>(c);
+ });
+ if (!transStatus.isOk()) {
+ ALOGE("createComponent -- failed transaction.");
+ return C2_TRANSACTION_FAILED;
+ }
+ if (status != C2_OK) {
+ ALOGE("createComponent -- failed to create component.");
+ return status;
+ }
+ hidlListener->component = *component;
+ return status;
+}
+
+c2_status_t Codec2Client::createInterface(
+ const C2String& name,
+ std::shared_ptr<Codec2Client::Interface>* const interface) {
+ c2_status_t status;
+ Return<void> transStatus = base()->createInterface(
+ name,
+ [&status, interface](
+ Status s,
+ const sp<IComponentInterface>& i) {
+ status = static_cast<c2_status_t>(s);
+ if (status != C2_OK) {
+ return;
+ }
+ *interface = std::make_shared<Codec2Client::Interface>(i);
+ });
+ if (!transStatus.isOk()) {
+ ALOGE("createInterface -- failed transaction.");
+ return C2_TRANSACTION_FAILED;
+ }
+ return status;
+}
+
+const std::vector<C2Component::Traits>& Codec2Client::listComponents()
+ const {
+ if (mListed) {
+ return mTraitsList;
+ }
+ Return<void> transStatus = base()->listComponents(
+ [this](const hidl_vec<IComponentStore::ComponentTraits>& t) {
+ mTraitsList.resize(t.size());
+ mAliasesBuffer.resize(t.size());
+ for (size_t i = 0; i < t.size(); ++i) {
+ c2_status_t status = objcpy(
+ &mTraitsList[i], &mAliasesBuffer[i], t[i]);
+ if (status != C2_OK) {
+ ALOGE("listComponents -- corrupted output.");
+ return;
+ }
+ }
+ });
+ if (!transStatus.isOk()) {
+ ALOGE("listComponents -- failed transaction.");
+ }
+ mListed = true;
+ return mTraitsList;
+}
+
+c2_status_t Codec2Client::copyBuffer(
+ const std::shared_ptr<C2Buffer>& src,
+ const std::shared_ptr<C2Buffer>& dst) {
+ // TODO: Implement?
+ (void)src;
+ (void)dst;
+ ALOGE("copyBuffer not implemented");
+ return C2_OMITTED;
+}
+
+std::shared_ptr<C2ParamReflector>
+ Codec2Client::getParamReflector() {
+ // TODO: Implement this once there is a way to construct C2StructDescriptor
+ // dynamically.
+ ALOGE("getParamReflector -- not implemented.");
+ return nullptr;
+}
+
+std::shared_ptr<Codec2Client> Codec2Client::CreateFromService(
+ const char* instanceName, bool waitForService) {
+ sp<Base> baseStore = waitForService ?
+ Base::getService(instanceName) :
+ Base::tryGetService(instanceName);
+ if (!baseStore) {
+ if (waitForService) {
+ ALOGE("Codec2.0 service inaccessible. Check the device manifest.");
+ } else {
+ ALOGW("Codec2.0 service not available right now. Try again later.");
+ }
+ return nullptr;
+ }
+ return std::make_shared<Codec2Client>(baseStore);
+}
+
+// Codec2Client::Listener
+
+Codec2Client::Listener::~Listener() {
+}
+
+// Codec2Client::Component
+
+Codec2Client::Component::Base* Codec2Client::Component::base() const {
+ return static_cast<Base*>(mBase.get());
+}
+
+Codec2Client::Component::Component(const sp<Codec2Client::Component::Base>& base) :
+ Codec2Client::Configurable(base) {
+}
+
+c2_status_t Codec2Client::Component::createBlockPool(
+ C2Allocator::id_t id,
+ C2BlockPool::local_id_t* localId,
+ std::shared_ptr<Codec2Client::Configurable>* configurable) {
+ c2_status_t status;
+ Return<void> transStatus = base()->createBlockPool(
+ static_cast<uint32_t>(id),
+ [&status, localId, configurable](
+ Status s,
+ uint64_t pId,
+ const sp<IConfigurable>& c) {
+ status = static_cast<c2_status_t>(s);
+ if (status != C2_OK) {
+ return;
+ }
+ *localId = static_cast<C2BlockPool::local_id_t>(pId);
+ *configurable = std::make_shared<Codec2Client::Configurable>(c);
+ });
+ if (!transStatus.isOk()) {
+ ALOGE("createBlockPool -- transaction failed.");
+ return C2_TRANSACTION_FAILED;
+ }
+ return status;
+}
+
+c2_status_t Codec2Client::Component::queue(
+ std::list<std::unique_ptr<C2Work>>* const items) {
+ WorkBundle workBundle;
+ Status hidlStatus = objcpy(&workBundle, *items);
+ if (hidlStatus != Status::OK) {
+ ALOGE("queue -- bad input.");
+ return C2_TRANSACTION_FAILED;
+ }
+ Return<Status> transStatus = base()->queue(workBundle);
+ if (!transStatus.isOk()) {
+ ALOGE("queue -- transaction failed.");
+ return C2_TRANSACTION_FAILED;
+ }
+ return static_cast<c2_status_t>(static_cast<Status>(transStatus));
+}
+
+c2_status_t Codec2Client::Component::flush(
+ C2Component::flush_mode_t mode,
+ std::list<std::unique_ptr<C2Work>>* const flushedWork) {
+ (void)mode; // Flush mode isn't supported in HIDL yet.
+ c2_status_t status;
+ Return<void> transStatus = base()->flush(
+ [&status, flushedWork](
+ Status s, const WorkBundle& wb) {
+ status = static_cast<c2_status_t>(s);
+ if (status != C2_OK) {
+ return;
+ }
+ status = objcpy(flushedWork, wb);
+ });
+ if (!transStatus.isOk()) {
+ ALOGE("flush -- transaction failed.");
+ return C2_TRANSACTION_FAILED;
+ }
+ return status;
+}
+
+c2_status_t Codec2Client::Component::drain(C2Component::drain_mode_t mode) {
+ Return<Status> transStatus = base()->drain(
+ mode == C2Component::DRAIN_COMPONENT_WITH_EOS);
+ if (!transStatus.isOk()) {
+ ALOGE("drain -- transaction failed.");
+ return C2_TRANSACTION_FAILED;
+ }
+ return static_cast<c2_status_t>(static_cast<Status>(transStatus));
+}
+
+c2_status_t Codec2Client::Component::start() {
+ Return<Status> transStatus = base()->start();
+ if (!transStatus.isOk()) {
+ ALOGE("start -- transaction failed.");
+ return C2_TRANSACTION_FAILED;
+ }
+ return static_cast<c2_status_t>(static_cast<Status>(transStatus));
+}
+
+c2_status_t Codec2Client::Component::stop() {
+ Return<Status> transStatus = base()->stop();
+ if (!transStatus.isOk()) {
+ ALOGE("stop -- transaction failed.");
+ return C2_TRANSACTION_FAILED;
+ }
+ return static_cast<c2_status_t>(static_cast<Status>(transStatus));
+}
+
+c2_status_t Codec2Client::Component::reset() {
+ Return<Status> transStatus = base()->reset();
+ if (!transStatus.isOk()) {
+ ALOGE("reset -- transaction failed.");
+ return C2_TRANSACTION_FAILED;
+ }
+ return static_cast<c2_status_t>(static_cast<Status>(transStatus));
+}
+
+c2_status_t Codec2Client::Component::release() {
+ Return<Status> transStatus = base()->release();
+ if (!transStatus.isOk()) {
+ ALOGE("release -- transaction failed.");
+ return C2_TRANSACTION_FAILED;
+ }
+ return static_cast<c2_status_t>(static_cast<Status>(transStatus));
+}
+
+
+
+
+} // namespace android
+
diff --git a/media/libstagefright/codec2/client/include/media/stagefright/codec2/client.h b/media/libstagefright/codec2/client/include/media/stagefright/codec2/client.h
new file mode 100644
index 0000000..1bbf459
--- /dev/null
+++ b/media/libstagefright/codec2/client/include/media/stagefright/codec2/client.h
@@ -0,0 +1,220 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef CODEC2_CLIENT_INTERFACES_H_
+#define CODEC2_CLIENT_INTERFACES_H_
+
+#include <C2Component.h>
+#include <C2Buffer.h>
+#include <C2Param.h>
+#include <C2.h>
+
+#include <utils/StrongPointer.h>
+
+#include <memory>
+
+/**
+ * This file contains minimal interfaces for the framework to access Codec2.0.
+ *
+ * Codec2Client is the main class that contains the following inner classes:
+ * - Listener
+ * - Configurable
+ * - Interface
+ * - Component
+ *
+ * Classes in Codec2Client, interfaces in Codec2.0, and HIDL interfaces are
+ * related as follows:
+ * - Codec2Client <==> C2ComponentStore <==> IComponentStore
+ * - Codec2Client::Listener <==> C2Component::Listener <==> IComponentListener
+ * - Codec2Client::Configurable <==> [No equivalent] <==> IConfigurable
+ * - Codec2Client::Interface <==> C2ComponentInterface <==> IComponentInterface
+ * - Codec2Client::Component <==> C2Component <==> IComponent
+ *
+ * The entry point is Codec2Client::CreateFromService(), which creates a
+ * Codec2Client object. From Codec2Client, Interface and Component objects can
+ * be created by calling createComponent() and createInterface().
+ *
+ * createComponent() takes a Listener object, which must be implemented by the
+ * user.
+ *
+ * At the present, createBlockPool() is the only method that yields a
+ * Configurable object. Note, however, that Interface, Component and
+ * Codec2Client are all subclasses of Configurable.
+ */
+
+// Forward declaration of HIDL interfaces
+namespace vendor {
+namespace google {
+namespace media {
+namespace c2 {
+namespace V1_0 {
+struct IConfigurable;
+struct IComponentInterface;
+struct IComponent;
+struct IComponentStore;
+} // namespace V1_0
+} // namespace c2
+} // namespace media
+} // namespace google
+} // namespace vendor
+
+namespace android {
+
+// This class is supposed to be called Codec2Client::Configurable, but forward
+// declaration of an inner class is not possible.
+struct Codec2ConfigurableClient {
+
+ typedef ::vendor::google::media::c2::V1_0::IConfigurable Base;
+
+ const C2String& getName() const;
+
+ c2_status_t query(
+ const std::vector<C2Param::Index> &indices,
+ c2_blocking_t mayBlock,
+ std::vector<std::unique_ptr<C2Param>>* const params) const;
+
+ c2_status_t config(
+ const std::vector<C2Param*> ¶ms,
+ c2_blocking_t mayBlock,
+ std::vector<std::unique_ptr<C2SettingResult>>* const failures
+ );
+
+ c2_status_t querySupportedParams(
+ std::vector<std::shared_ptr<C2ParamDescriptor>>* const params
+ ) const;
+
+ c2_status_t querySupportedValues(
+ std::vector<C2FieldSupportedValuesQuery>& fields,
+ c2_blocking_t mayBlock) const;
+
+ // base cannot be null.
+ Codec2ConfigurableClient(const sp<Base>& base);
+
+protected:
+ C2String mName;
+ sp<Base> mBase;
+
+ Base* base() const;
+
+ friend struct Codec2Client;
+};
+
+struct Codec2Client : public Codec2ConfigurableClient {
+
+ typedef ::vendor::google::media::c2::V1_0::IComponentStore Base;
+
+ struct Listener;
+
+ typedef Codec2ConfigurableClient Configurable;
+
+ typedef Configurable Interface; // These two types may diverge in the future.
+
+ struct Component;
+
+ typedef Codec2Client Store;
+
+ c2_status_t createComponent(
+ const C2String& name,
+ const std::shared_ptr<Listener>& listener,
+ std::shared_ptr<Component>* const component);
+
+ c2_status_t createInterface(
+ const C2String& name,
+ std::shared_ptr<Interface>* const interface);
+
+ const std::vector<C2Component::Traits>&
+ listComponents() const;
+
+ c2_status_t copyBuffer(
+ const std::shared_ptr<C2Buffer>& src,
+ const std::shared_ptr<C2Buffer>& dst);
+
+ std::shared_ptr<C2ParamReflector> getParamReflector();
+
+ static std::shared_ptr<Codec2Client> CreateFromService(
+ const char* instanceName,
+ bool waitForService = true);
+
+ // base cannot be null.
+ Codec2Client(const sp<Base>& base);
+
+protected:
+ mutable bool mListed;
+ mutable std::vector<C2Component::Traits> mTraitsList;
+ mutable std::vector<std::unique_ptr<std::vector<std::string>>>
+ mAliasesBuffer;
+
+ Base* base() const;
+};
+
+struct Codec2Client::Listener {
+
+ virtual void onWorkDone(
+ const std::weak_ptr<Codec2Client::Component>& comp,
+ const std::list<std::unique_ptr<C2Work>>& workItems) = 0;
+
+ virtual void onTripped(
+ const std::weak_ptr<Codec2Client::Component>& comp,
+ const std::vector<std::shared_ptr<C2SettingResult>>& settingResults
+ ) = 0;
+
+ virtual void onError(
+ const std::weak_ptr<Codec2Client::Component>& comp,
+ uint32_t errorCode) = 0;
+
+ virtual ~Listener();
+
+};
+
+struct Codec2Client::Component : public Codec2Client::Configurable {
+
+ typedef ::vendor::google::media::c2::V1_0::IComponent Base;
+
+ c2_status_t createBlockPool(
+ C2Allocator::id_t id,
+ C2BlockPool::local_id_t* localId,
+ std::shared_ptr<Codec2Client::Configurable>* configurable);
+
+ c2_status_t queue(
+ std::list<std::unique_ptr<C2Work>>* const items);
+
+ c2_status_t flush(
+ C2Component::flush_mode_t mode,
+ std::list<std::unique_ptr<C2Work>>* const flushedWork);
+
+ c2_status_t drain(C2Component::drain_mode_t mode);
+
+ c2_status_t start();
+
+ c2_status_t stop();
+
+ c2_status_t reset();
+
+ c2_status_t release();
+
+ // base cannot be null.
+ Component(const sp<Base>& base);
+
+protected:
+ Base* base() const;
+
+ friend struct Codec2Client;
+};
+
+} // namespace android
+
+#endif // CODEC2_CLIENT_INTERFACES_H_
+
diff --git a/media/libstagefright/codec2/hidl/impl/1.0/Component.cpp b/media/libstagefright/codec2/hidl/impl/1.0/Component.cpp
index bbcd630..2b34fde 100644
--- a/media/libstagefright/codec2/hidl/impl/1.0/Component.cpp
+++ b/media/libstagefright/codec2/hidl/impl/1.0/Component.cpp
@@ -48,7 +48,7 @@
virtual c2_status_t query(
const std::vector<C2Param::Index>& indices,
c2_blocking_t mayBlock,
- std::vector<std::unique_ptr<C2Param>>* const params) override {
+ std::vector<std::unique_ptr<C2Param>>* const params) const override {
return mIntf->query_vb({}, indices, mayBlock, params);
}
@@ -108,7 +108,9 @@
for (const std::shared_ptr<C2SettingResult> &c2result :
c2settingResult) {
if (c2result) {
- objcpy(&settingResults[ix++], *c2result);
+ if (objcpy(&settingResults[ix++], *c2result) != Status::OK) {
+ break;
+ }
}
}
settingResults.resize(ix);
@@ -166,7 +168,6 @@
if (objcpy(&c2works, workBundle) != C2_OK) {
return Status::CORRUPTED;
}
- (void)workBundle;
return static_cast<Status>(mComponent->queue_nb(&c2works));
}
diff --git a/media/libstagefright/codec2/hidl/impl/1.0/ComponentStore.cpp b/media/libstagefright/codec2/hidl/impl/1.0/ComponentStore.cpp
index d42d67a..4d51fba 100644
--- a/media/libstagefright/codec2/hidl/impl/1.0/ComponentStore.cpp
+++ b/media/libstagefright/codec2/hidl/impl/1.0/ComponentStore.cpp
@@ -54,7 +54,7 @@
c2_status_t query(
const std::vector<C2Param::Index> &indices,
c2_blocking_t mayBlock,
- std::vector<std::unique_ptr<C2Param>> *const params) override {
+ std::vector<std::unique_ptr<C2Param>> *const params) const override {
// Assume all params are blocking
// TODO: Filter for supported params
if (mayBlock == C2_DONT_BLOCK && indices.size() != 0) {
@@ -201,7 +201,7 @@
// TODO implement
(void)src;
(void)dst;
- return Status {};
+ return Status::OMITTED;
}
} // namespace implementation
diff --git a/media/libstagefright/codec2/hidl/impl/1.0/include/codec2/hidl/1.0/ConfigurableC2Intf.h b/media/libstagefright/codec2/hidl/impl/1.0/include/codec2/hidl/1.0/ConfigurableC2Intf.h
index 94d2e6d..da90996 100644
--- a/media/libstagefright/codec2/hidl/impl/1.0/include/codec2/hidl/1.0/ConfigurableC2Intf.h
+++ b/media/libstagefright/codec2/hidl/impl/1.0/include/codec2/hidl/1.0/ConfigurableC2Intf.h
@@ -49,7 +49,7 @@
virtual c2_status_t query(
const std::vector<C2Param::Index> &indices,
c2_blocking_t mayBlock,
- std::vector<std::unique_ptr<C2Param>>* const params) = 0;
+ std::vector<std::unique_ptr<C2Param>>* const params) const = 0;
/** C2ComponentInterface::config_vb */
virtual c2_status_t config(
const std::vector<C2Param*> ¶ms,
@@ -60,7 +60,7 @@
std::vector<std::shared_ptr<C2ParamDescriptor>>* const params) const = 0;
/** C2ComponentInterface::querySupportedParams_nb */
virtual c2_status_t querySupportedValues(
- std::vector<C2FieldSupportedValuesQuery> &fields, c2_blocking_t mayBlock) const = 0;
+ std::vector<C2FieldSupportedValuesQuery>& fields, c2_blocking_t mayBlock) const = 0;
virtual ~ConfigurableC2Intf() = default;
diff --git a/media/libstagefright/codec2/hidl/impl/1.0/include/codec2/hidl/1.0/types.h b/media/libstagefright/codec2/hidl/impl/1.0/include/codec2/hidl/1.0/types.h
index 41ee275..1eace56 100644
--- a/media/libstagefright/codec2/hidl/impl/1.0/include/codec2/hidl/1.0/types.h
+++ b/media/libstagefright/codec2/hidl/impl/1.0/include/codec2/hidl/1.0/types.h
@@ -53,77 +53,79 @@
typedef C2GlobalParam<C2Info, C2Hidl_Rect, 1> C2Hidl_RectInfo;
// C2SettingResult -> SettingResult
-Status C2_HIDE objcpy(
+Status objcpy(
SettingResult* d,
const C2SettingResult& s);
-// SettingResult -> C2SettingResult
-c2_status_t C2_HIDE objcpy(
- C2SettingResult* d,
+// SettingResult -> std::unique_ptr<C2SettingResult>
+c2_status_t objcpy(
+ std::unique_ptr<C2SettingResult>* d,
const SettingResult& s);
// C2ParamDescriptor -> ParamDescriptor
-Status C2_HIDE objcpy(
+Status objcpy(
ParamDescriptor* d,
const C2ParamDescriptor& s);
-// ParamDescriptor -> std::unique_ptr<C2ParamDescriptor>
-c2_status_t C2_HIDE objcpy(
- std::unique_ptr<C2ParamDescriptor>* d,
+// ParamDescriptor -> std::shared_ptr<C2ParamDescriptor>
+c2_status_t objcpy(
+ std::shared_ptr<C2ParamDescriptor>* d,
const ParamDescriptor& s);
// C2FieldSupportedValuesQuery -> FieldSupportedValuesQuery
-Status C2_HIDE objcpy(
+Status objcpy(
FieldSupportedValuesQuery* d,
const C2FieldSupportedValuesQuery& s);
// FieldSupportedValuesQuery -> C2FieldSupportedValuesQuery
-c2_status_t C2_HIDE objcpy(
+c2_status_t objcpy(
C2FieldSupportedValuesQuery* d,
const FieldSupportedValuesQuery& s);
// C2FieldSupportedValuesQuery -> FieldSupportedValuesQueryResult
-Status C2_HIDE objcpy(
+Status objcpy(
FieldSupportedValuesQueryResult* d,
const C2FieldSupportedValuesQuery& s);
// FieldSupportedValuesQuery, FieldSupportedValuesQueryResult -> C2FieldSupportedValuesQuery
-c2_status_t C2_HIDE objcpy(
+c2_status_t objcpy(
C2FieldSupportedValuesQuery* d,
const FieldSupportedValuesQuery& sq,
const FieldSupportedValuesQueryResult& sr);
// C2Component::Traits -> ComponentTraits
-Status C2_HIDE objcpy(
+Status objcpy(
IComponentStore::ComponentTraits* d,
const C2Component::Traits& s);
-// ComponentTraits -> C2Component::Traits
-c2_status_t C2_HIDE objcpy(
+// ComponentTraits -> C2Component::Traits, std::unique_ptr<std::vector<std::string>>
+// Note: The output d is only valid as long as aliasesBuffer remains alive.
+c2_status_t objcpy(
C2Component::Traits* d,
+ std::unique_ptr<std::vector<std::string>>* aliasesBuffer,
const IComponentStore::ComponentTraits& s);
// C2StructDescriptor -> StructDescriptor
-Status C2_HIDE objcpy(
+Status objcpy(
StructDescriptor* d,
const C2StructDescriptor& s);
// StructDescriptor -> C2StructDescriptor
// TODO: This cannot be implemented yet because C2StructDescriptor does not
// allow dynamic construction/modification.
-c2_status_t C2_HIDE objcpy(
+c2_status_t objcpy(
C2StructDescriptor* d,
const StructDescriptor& s);
// std::list<std::unique_ptr<C2Work>> -> WorkBundle
// TODO: Connect with Bufferpool
-Status C2_HIDE objcpy(
+Status objcpy(
WorkBundle* d,
const std::list<std::unique_ptr<C2Work>>& s);
// WorkBundle -> std::list<std::unique_ptr<C2Work>>
// TODO: Connect with Bufferpool
-c2_status_t C2_HIDE objcpy(
+c2_status_t objcpy(
std::list<std::unique_ptr<C2Work>>* d,
const WorkBundle& s);
@@ -159,6 +161,29 @@
hidl_vec<uint8_t> *blob,
const std::vector<std::unique_ptr<C2Tuning>> ¶ms);
+/**
+ * Parses a params blob and create a vector of C2Params whose members are copies
+ * of the params in the blob.
+ * \param[out] params the resulting vector
+ * \param[in] blob parameter blob to parse
+ * \retval C2_OK if the full blob was parsed and params was constructed
+ * \retval C2_BAD_VALUE otherwise
+ */
+c2_status_t copyParamsFromBlob(
+ std::vector<std::unique_ptr<C2Param>>* params,
+ Params blob);
+
+/**
+ * Parses a params blob and applies updates to params
+ * \param[in,out] params params to be updated
+ * \param[in] blob parameter blob containing updates
+ * \retval C2_OK if the full blob was parsed and params was updated
+ * \retval C2_BAD_VALUE otherwise
+ */
+c2_status_t updateParamsFromBlob(
+ const std::vector<C2Param*>& params,
+ const Params& blob);
+
} // namespace implementation
} // namespace V1_0
} // namespace c2
diff --git a/media/libstagefright/codec2/hidl/impl/1.0/types.cpp b/media/libstagefright/codec2/hidl/impl/1.0/types.cpp
index 14ced2e..f14c21a 100644
--- a/media/libstagefright/codec2/hidl/impl/1.0/types.cpp
+++ b/media/libstagefright/codec2/hidl/impl/1.0/types.cpp
@@ -31,6 +31,7 @@
#include <C2Component.h>
#include <util/C2ParamUtils.h>
+#include <unordered_map>
#include <algorithm>
namespace vendor {
@@ -264,25 +265,26 @@
return Status::OK;
}
-// ComponentTraits -> C2Component::Traits
+// ComponentTraits -> C2Component::Traits, std::unique_ptr<std::vector<std::string>>
c2_status_t objcpy(
C2Component::Traits* d,
+ std::unique_ptr<std::vector<std::string>>* aliasesBuffer,
const IComponentStore::ComponentTraits& s) {
- d->name = s.name;
-
- // TODO: Currently, we do not have any domain values defined in Codec2.0.
+ d->name = s.name.c_str();
d->domain = static_cast<C2Component::domain_t>(s.domainOther);
-
- // TODO: Currently, we do not have any kind values defined in Codec2.0.
d->kind = static_cast<C2Component::kind_t>(s.kindOther);
-
d->rank = static_cast<C2Component::rank_t>(s.rank);
-
d->mediaType = s.mediaType.c_str();
- // TODO: Currently, aliases are pointers to static strings. This is not
- // supported by HIDL.
- d->aliases.clear();
+ // aliasesBuffer must not be resized after this.
+ *aliasesBuffer = std::make_unique<std::vector<std::string>>(
+ s.aliases.size());
+ (*aliasesBuffer)->resize(s.aliases.size());
+ std::vector<C2StringLiteral> dAliases(s.aliases.size());
+ for (size_t i = 0; i < s.aliases.size(); ++i) {
+ (**aliasesBuffer)[i] = s.aliases[i].c_str();
+ d->aliases[i] = (**aliasesBuffer)[i].c_str();
+ }
return C2_OK;
}
@@ -366,48 +368,60 @@
return Status::OK;
}
-// SettingResult -> C2SettingResult
-c2_status_t objcpy(C2SettingResult *d, const SettingResult &s) {
+// SettingResult -> std::unique_ptr<C2SettingResult>
+c2_status_t objcpy(std::unique_ptr<C2SettingResult> *d, const SettingResult &s) {
+ *d = std::unique_ptr<C2SettingResult>(new C2SettingResult {
+ .field = C2ParamFieldValues(C2ParamFieldBuilder()) });
+ if (!*d) {
+ return C2_NO_MEMORY;
+ }
+
+ // failure
switch (s.failure) {
case SettingResult::Failure::READ_ONLY:
- d->failure = C2SettingResult::READ_ONLY;
+ (*d)->failure = C2SettingResult::READ_ONLY;
break;
case SettingResult::Failure::MISMATCH:
- d->failure = C2SettingResult::MISMATCH;
+ (*d)->failure = C2SettingResult::MISMATCH;
break;
case SettingResult::Failure::BAD_VALUE:
- d->failure = C2SettingResult::BAD_VALUE;
+ (*d)->failure = C2SettingResult::BAD_VALUE;
break;
case SettingResult::Failure::BAD_TYPE:
- d->failure = C2SettingResult::BAD_TYPE;
+ (*d)->failure = C2SettingResult::BAD_TYPE;
break;
case SettingResult::Failure::BAD_PORT:
- d->failure = C2SettingResult::BAD_PORT;
+ (*d)->failure = C2SettingResult::BAD_PORT;
break;
case SettingResult::Failure::BAD_INDEX:
- d->failure = C2SettingResult::BAD_INDEX;
+ (*d)->failure = C2SettingResult::BAD_INDEX;
break;
case SettingResult::Failure::CONFLICT:
- d->failure = C2SettingResult::CONFLICT;
+ (*d)->failure = C2SettingResult::CONFLICT;
break;
case SettingResult::Failure::UNSUPPORTED:
- d->failure = C2SettingResult::UNSUPPORTED;
+ (*d)->failure = C2SettingResult::UNSUPPORTED;
break;
case SettingResult::Failure::INFO_CONFLICT:
- d->failure = C2SettingResult::INFO_CONFLICT;
+ (*d)->failure = C2SettingResult::INFO_CONFLICT;
break;
default:
- d->failure = static_cast<C2SettingResult::Failure>(s.failureOther);
+ (*d)->failure = static_cast<C2SettingResult::Failure>(s.failureOther);
}
- c2_status_t status = objcpy(&d->field, s.field);
+
+ // field
+ c2_status_t status = objcpy(&(*d)->field, s.field);
if (status != C2_OK) {
return status;
}
- d->conflicts.clear();
+
+ // conflicts
+ (*d)->conflicts.clear();
+ (*d)->conflicts.reserve(s.conflicts.size());
for (const ParamFieldValues& sConflict : s.conflicts) {
- d->conflicts.emplace_back(
+ (*d)->conflicts.emplace_back(
C2ParamFieldValues{ C2ParamFieldBuilder(), nullptr });
- status = objcpy(&d->conflicts.back(), sConflict);
+ status = objcpy(&(*d)->conflicts.back(), sConflict);
if (status != C2_OK) {
return status;
}
@@ -426,13 +440,13 @@
}
// ParamDescriptor -> C2ParamDescriptor
-c2_status_t objcpy(std::unique_ptr<C2ParamDescriptor> *d, const ParamDescriptor &s) {
+c2_status_t objcpy(std::shared_ptr<C2ParamDescriptor> *d, const ParamDescriptor &s) {
std::vector<C2Param::Index> dDependencies;
dDependencies.reserve(s.dependencies.size());
for (const ParamIndex& sDependency : s.dependencies) {
dDependencies.emplace_back(static_cast<uint32_t>(sDependency));
}
- *d = std::make_unique<C2ParamDescriptor>(
+ *d = std::make_shared<C2ParamDescriptor>(
C2Param::Index(static_cast<uint32_t>(s.index)),
static_cast<C2ParamDescriptor::attrib_t>(s.attrib),
C2String(s.name.c_str()),
@@ -1058,10 +1072,8 @@
dWorklet->failures.clear();
dWorklet->failures.reserve(sWorklet.failures.size());
for (const SettingResult& sFailure : sWorklet.failures) {
- std::unique_ptr<C2SettingResult> dFailure(
- new C2SettingResult { .field = C2ParamFieldValues {
- C2ParamFieldBuilder(), nullptr } });
- status = objcpy(dFailure.get(), sFailure);
+ std::unique_ptr<C2SettingResult> dFailure;
+ status = objcpy(&dFailure, sFailure);
if (status != C2_OK) {
ALOGE("Failed to create C2SettingResult in C2Worklet.");
return C2_BAD_VALUE;
@@ -1176,6 +1188,74 @@
return _createParamsBlob(blob, params);
}
+// Params -> std::vector<std::unique_ptr<C2Param>>
+c2_status_t copyParamsFromBlob(
+ std::vector<std::unique_ptr<C2Param>>* params,
+ Params blob) {
+ std::vector<C2Param*> paramPointers;
+ c2_status_t status = parseParamsBlob(¶mPointers, blob);
+ if (status != C2_OK) {
+ ALOGE("copyParamsFromBlob -- blob parsing failed.");
+ return status;
+ }
+ params->resize(paramPointers.size());
+ size_t i = 0;
+ for (C2Param* const& paramPointer : paramPointers) {
+ if (!paramPointer) {
+ ALOGE("copyParamsFromBlob -- corrupted params blob.");
+ return C2_BAD_VALUE;
+ }
+ (*params)[i++] = C2Param::Copy(*paramPointer);
+ }
+ return C2_OK;
+}
+
+// Params -> update std::vector<std::unique_ptr<C2Param>>
+c2_status_t updateParamsFromBlob(
+ const std::vector<C2Param*>& params,
+ const Params& blob) {
+ std::unordered_map<uint32_t, C2Param*> index2param;
+ for (C2Param* const& param : params) {
+ if (!param) {
+ ALOGE("updateParamsFromBlob -- corrupted input params.");
+ return C2_BAD_VALUE;
+ }
+ if (index2param.find(param->index()) == index2param.end()) {
+ index2param.emplace(param->index(), param);
+ }
+ }
+
+ std::vector<C2Param*> paramPointers;
+ c2_status_t status = parseParamsBlob(¶mPointers, blob);
+ if (status != C2_OK) {
+ ALOGE("updateParamsFromBlob -- blob parsing failed.");
+ return status;
+ }
+
+ for (C2Param* const& paramPointer : paramPointers) {
+ if (!paramPointer) {
+ ALOGE("updateParamsFromBlob -- corrupted param in blob.");
+ return C2_BAD_VALUE;
+ }
+ decltype(index2param)::iterator i = index2param.find(
+ paramPointer->index());
+ if (i == index2param.end()) {
+ ALOGW("updateParamsFromBlob -- unseen param index.");
+ continue;
+ }
+ if (!i->second->updateFrom(*paramPointer)) {
+ ALOGE("updateParamsFromBlob -- mismatching sizes: "
+ "%u vs %u (index = %u).",
+ static_cast<unsigned>(params.size()),
+ static_cast<unsigned>(paramPointer->size()),
+ static_cast<unsigned>(i->first));
+ return C2_BAD_VALUE;
+ }
+ }
+ return C2_OK;
+}
+
+
} // namespace implementation
} // namespace V1_0
} // namespace c2
diff --git a/media/libstagefright/codec2/vndk/bufferpool/Accessor.cpp b/media/libstagefright/codec2/vndk/bufferpool/Accessor.cpp
index 3571ed5..1b1b9be 100644
--- a/media/libstagefright/codec2/vndk/bufferpool/Accessor.cpp
+++ b/media/libstagefright/codec2/vndk/bufferpool/Accessor.cpp
@@ -43,8 +43,8 @@
return Void();
}
-Accessor::Accessor(const std::shared_ptr<C2Allocator> &allocator, bool linear)
- : mImpl(new Impl(allocator, linear)) {}
+Accessor::Accessor(const std::shared_ptr<BufferPoolAllocator> &allocator)
+ : mImpl(new Impl(allocator)) {}
Accessor::~Accessor() {
}
diff --git a/media/libstagefright/codec2/vndk/bufferpool/Accessor.h b/media/libstagefright/codec2/vndk/bufferpool/Accessor.h
index 6fd82ba..ad42245 100644
--- a/media/libstagefright/codec2/vndk/bufferpool/Accessor.h
+++ b/media/libstagefright/codec2/vndk/bufferpool/Accessor.h
@@ -20,7 +20,6 @@
#include <android/hardware/media/bufferpool/1.0/IAccessor.h>
#include <hidl/MQDescriptor.h>
#include <hidl/Status.h>
-#include <C2Buffer.h>
#include <BufferPoolTypes.h>
#include "BufferStatus.h"
@@ -53,9 +52,8 @@
* Creates a buffer pool accessor which uses the specified allocator.
*
* @param allocator buffer allocator.
- * @param linear whether the allocator is linear or not.
*/
- Accessor(const std::shared_ptr<C2Allocator> &allocator, bool linear);
+ explicit Accessor(const std::shared_ptr<BufferPoolAllocator> &allocator);
/** Destructs a buffer pool accessor. */
~Accessor();
diff --git a/media/libstagefright/codec2/vndk/bufferpool/AccessorImpl.cpp b/media/libstagefright/codec2/vndk/bufferpool/AccessorImpl.cpp
index f8aec53..32d76c0 100644
--- a/media/libstagefright/codec2/vndk/bufferpool/AccessorImpl.cpp
+++ b/media/libstagefright/codec2/vndk/bufferpool/AccessorImpl.cpp
@@ -22,7 +22,6 @@
#include <time.h>
#include <unistd.h>
#include <utils/Log.h>
-#include <C2Buffer.h>
#include "AccessorImpl.h"
#include "Connection.h"
@@ -38,48 +37,18 @@
BufferId mId;
size_t mOwnerCount;
size_t mTransactionCount;
- const bool mLinear;
- const std::shared_ptr<C2LinearAllocation> mLinearAllocation;
- const std::shared_ptr<C2GraphicAllocation> mGraphicAllocation;
+ const std::shared_ptr<BufferPoolAllocation> mAllocation;
const std::vector<uint8_t> mConfig;
InternalBuffer(
BufferId id,
- const std::shared_ptr<C2LinearAllocation> &alloc,
+ const std::shared_ptr<BufferPoolAllocation> &alloc,
const std::vector<uint8_t> &allocConfig)
: mId(id), mOwnerCount(0), mTransactionCount(0),
- mLinear(true), mLinearAllocation(alloc),
- mGraphicAllocation(nullptr),
- mConfig(allocConfig) {}
-
- InternalBuffer(
- BufferId id,
- const std::shared_ptr<C2GraphicAllocation> &alloc,
- const std::vector<uint8_t> &allocConfig)
- : mId(id), mOwnerCount(0), mTransactionCount(0),
- mLinear(false), mLinearAllocation(nullptr),
- mGraphicAllocation(alloc),
- mConfig(allocConfig) {}
+ mAllocation(alloc), mConfig(allocConfig) {}
const native_handle_t *handle() {
- if (mLinear) {
- return mLinearAllocation->handle();
- } else {
- return mGraphicAllocation->handle();
- }
- }
-
- // TODO : support non exact matching. e.g) capacity
- bool isRecyclable(const std::vector<uint8_t> &config) {
- if (mConfig.size() == config.size()) {
- for (size_t i = 0; i < config.size(); ++i) {
- if (mConfig[i] != config[i]) {
- return false;
- }
- }
- return true;
- }
- return false;
+ return mAllocation->handle();
}
};
@@ -154,8 +123,8 @@
uint32_t Accessor::Impl::sSeqId = time(NULL);
Accessor::Impl::Impl(
- const std::shared_ptr<C2Allocator> &allocator, bool linear)
- : mAllocator(allocator), mLinear(linear) {}
+ const std::shared_ptr<BufferPoolAllocator> &allocator)
+ : mAllocator(allocator) {}
Accessor::Impl::~Impl() {
}
@@ -193,14 +162,8 @@
std::lock_guard<std::mutex> lock(mBufferPool.mMutex);
mBufferPool.processStatusMessages();
ResultStatus status = ResultStatus::OK;
- if (!mBufferPool.getFreeBuffer(params, bufferId, handle)) {
- if (mLinear) {
- status = mBufferPool.getNewLinearBuffer(
- mAllocator, params, bufferId, handle);
- } else {
- status = mBufferPool.getNewGraphicBuffer(
- mAllocator, params, bufferId, handle);
- }
+ if (!mBufferPool.getFreeBuffer(mAllocator, params, bufferId, handle)) {
+ status = mBufferPool.getNewBuffer(mAllocator, params, bufferId, handle);
ALOGV("create a buffer %d : %u %p",
status == ResultStatus::OK, *bufferId, *handle);
}
@@ -437,12 +400,13 @@
}
bool Accessor::Impl::BufferPool::getFreeBuffer(
+ const std::shared_ptr<BufferPoolAllocator> &allocator,
const std::vector<uint8_t> ¶ms, BufferId *pId,
const native_handle_t** handle) {
auto bufferIt = mFreeBuffers.begin();
for (;bufferIt != mFreeBuffers.end(); ++bufferIt) {
BufferId bufferId = *bufferIt;
- if (mBuffers[bufferId]->isRecyclable(params)) {
+ if (allocator->compatible(params, mBuffers[bufferId]->mConfig)) {
break;
}
}
@@ -457,81 +421,30 @@
return false;
}
-ResultStatus Accessor::Impl::BufferPool::getNewLinearBuffer(
- const std::shared_ptr<C2Allocator> &allocator,
+ResultStatus Accessor::Impl::BufferPool::getNewBuffer(
+ const std::shared_ptr<BufferPoolAllocator> &allocator,
const std::vector<uint8_t> ¶ms, BufferId *pId,
const native_handle_t** handle) {
- union LinearParam {
- struct {
- uint32_t capacity;
- C2MemoryUsage usage;
- } data;
- uint8_t array[0];
- LinearParam() : data{0, {0, 0}} {}
- } linearParam;
- memcpy(&linearParam, params.data(),
- std::min(sizeof(linearParam), params.size()));
- std::shared_ptr<C2LinearAllocation> linearAlloc;
- c2_status_t status = allocator->newLinearAllocation(
- linearParam.data.capacity, linearParam.data.usage, &linearAlloc);
- if (status == C2_OK) {
+ std::shared_ptr<BufferPoolAllocation> alloc;
+ ResultStatus status = allocator->allocate(params, &alloc);
+
+ if (status == ResultStatus::OK) {
BufferId bufferId = mSeq++;
std::unique_ptr<InternalBuffer> buffer =
std::make_unique<InternalBuffer>(
- bufferId, linearAlloc, params);
+ bufferId, alloc, params);
if (buffer) {
auto res = mBuffers.insert(std::make_pair(
bufferId, std::move(buffer)));
if (res.second) {
- *handle = linearAlloc->handle();
+ *handle = alloc->handle();
*pId = bufferId;
return ResultStatus::OK;
}
}
return ResultStatus::NO_MEMORY;
}
- // TODO: map C2 error code
- return ResultStatus::CRITICAL_ERROR;
-}
-
-ResultStatus Accessor::Impl::BufferPool::getNewGraphicBuffer(
- const std::shared_ptr<C2Allocator> &allocator,
- const std::vector<uint8_t> ¶ms, BufferId *pId,
- const native_handle_t** handle) {
- union GraphicParam {
- struct {
- uint32_t width;
- uint32_t height;
- uint32_t format;
- C2MemoryUsage usage;
- } data;
- uint8_t array[0];
- GraphicParam() : data{0, 0, 0, {0, 0}} {}
- } graphicParam;
- memcpy(&graphicParam, params.data(),
- std::min(sizeof(graphicParam), params.size()));
- std::shared_ptr<C2GraphicAllocation> graphicAlloc;
- c2_status_t status = allocator->newGraphicAllocation(
- graphicParam.data.width, graphicParam.data.height,
- graphicParam.data.format, graphicParam.data.usage, &graphicAlloc);
- if (status == C2_OK) {
- BufferId bufferId = mSeq;
- std::unique_ptr<InternalBuffer> buffer =
- std::make_unique<InternalBuffer>(
- bufferId, graphicAlloc, params);
- if (buffer) {
- auto res = mBuffers.insert(std::make_pair(
- bufferId, std::move(buffer)));
- if (res.second) {
- *handle = graphicAlloc->handle();
- *pId = bufferId;
- return ResultStatus::OK;
- }
- }
- return ResultStatus::NO_MEMORY;
- }
- // TODO: map C2 error code
- return ResultStatus::CRITICAL_ERROR;
+ return status;
}
} // namespace implementation
diff --git a/media/libstagefright/codec2/vndk/bufferpool/AccessorImpl.h b/media/libstagefright/codec2/vndk/bufferpool/AccessorImpl.h
index 92d926c..1260550 100644
--- a/media/libstagefright/codec2/vndk/bufferpool/AccessorImpl.h
+++ b/media/libstagefright/codec2/vndk/bufferpool/AccessorImpl.h
@@ -35,7 +35,7 @@
* An implementation of a buffer pool accessor(or a buffer pool implementation.) */
class Accessor::Impl {
public:
- Impl(const std::shared_ptr<C2Allocator> &allocator, bool linear);
+ Impl(const std::shared_ptr<BufferPoolAllocator> &allocator);
~Impl();
@@ -64,8 +64,7 @@
static uint32_t sSeqId;
static int32_t sPid;
- const std::shared_ptr<C2Allocator> mAllocator;
- bool mLinear;
+ const std::shared_ptr<BufferPoolAllocator> mAllocator;
/**
* Buffer pool implementation.
@@ -172,6 +171,7 @@
/**
* Recycles a existing free buffer if it is possible.
*
+ * @param allocator the buffer allocator
* @param params the allocation parameters.
* @param pId the id of the recycled buffer.
* @param handle the native handle of the recycled buffer.
@@ -179,40 +179,25 @@
* @return {@code true} when a buffer is recycled, {@code false}
* otherwise.
*/
- bool getFreeBuffer(const std::vector<uint8_t> ¶ms, BufferId *pId,
- const native_handle_t **handle);
+ bool getFreeBuffer(
+ const std::shared_ptr<BufferPoolAllocator> &allocator,
+ const std::vector<uint8_t> ¶ms,
+ BufferId *pId, const native_handle_t **handle);
/**
- * Creates a new linear buffer.
+ * Creates a new buffer.
*
- * @param allocator the linear buffer allocator
+ * @param allocator the buffer allocator
* @param params the allocator parameters
* @param pId the buffer id for the newly allocated buffer.
* @param handle the native handle for the newly allocated buffer.
*
- * @return OK when a linear allocation is successfully allocated.
+ * @return OK when an allocation is successfully allocated.
* NO_MEMORY when there is no memory.
* CRITICAL_ERROR otherwise.
*/
- ResultStatus getNewLinearBuffer(
- const std::shared_ptr<C2Allocator> &allocator,
- const std::vector<uint8_t> ¶ms, BufferId *pId,
- const native_handle_t **handle);
-
- /**
- * Creates a new graphic buffer.
- *
- * @param allocator the graphic buffer allocator
- * @param params the allocator parameters
- * @param pId the buffer id for the newly allocated buffer.
- * @param handle the native handle for the newly allocated buffer.
- *
- * @return OK when a graphic allocation is successfully allocated.
- * NO_MEMORY when there is no memory.
- * CRITICAL_ERROR otherwise.
- */
- ResultStatus getNewGraphicBuffer(
- const std::shared_ptr<C2Allocator> &allocator,
+ ResultStatus getNewBuffer(
+ const std::shared_ptr<BufferPoolAllocator> &allocator,
const std::vector<uint8_t> ¶ms, BufferId *pId,
const native_handle_t **handle);
diff --git a/media/libstagefright/codec2/vndk/bufferpool/Android.bp b/media/libstagefright/codec2/vndk/bufferpool/Android.bp
index c7aa07b..1ea1f35 100644
--- a/media/libstagefright/codec2/vndk/bufferpool/Android.bp
+++ b/media/libstagefright/codec2/vndk/bufferpool/Android.bp
@@ -24,7 +24,6 @@
"libhidlbase",
"libhidltransport",
"liblog",
- "libstagefright_codec2",
"libutils",
"android.hardware.media.bufferpool@1.0",
],
diff --git a/media/libstagefright/codec2/vndk/bufferpool/ClientManager.cpp b/media/libstagefright/codec2/vndk/bufferpool/ClientManager.cpp
index 97efee4..89aee8b 100644
--- a/media/libstagefright/codec2/vndk/bufferpool/ClientManager.cpp
+++ b/media/libstagefright/codec2/vndk/bufferpool/ClientManager.cpp
@@ -36,8 +36,7 @@
ResultStatus registerSender(const sp<IAccessor> &accessor,
ConnectionId *pConnectionId);
- ResultStatus create(const std::shared_ptr<C2Allocator> &allocator,
- bool linear,
+ ResultStatus create(const std::shared_ptr<BufferPoolAllocator> &allocator,
ConnectionId *pConnectionId);
ResultStatus close(ConnectionId connectionId);
@@ -139,10 +138,9 @@
}
ResultStatus ClientManager::Impl::create(
- const std::shared_ptr<C2Allocator> &allocator,
- bool linear,
+ const std::shared_ptr<BufferPoolAllocator> &allocator,
ConnectionId *pConnectionId) {
- const sp<Accessor> accessor = new Accessor(allocator, linear);
+ const sp<Accessor> accessor = new Accessor(allocator);
if (!accessor || !accessor->isValid()) {
return ResultStatus::CRITICAL_ERROR;
}
@@ -273,11 +271,10 @@
}
ResultStatus ClientManager::create(
- const std::shared_ptr<C2Allocator> &allocator,
- bool linear,
+ const std::shared_ptr<BufferPoolAllocator> &allocator,
ConnectionId *pConnectionId) {
if (mImpl) {
- return mImpl->create(allocator, linear, pConnectionId);
+ return mImpl->create(allocator, pConnectionId);
}
return ResultStatus::CRITICAL_ERROR;
}
diff --git a/media/libstagefright/codec2/vndk/bufferpool/include/BufferPoolTypes.h b/media/libstagefright/codec2/vndk/bufferpool/include/BufferPoolTypes.h
index 4b7363f..0cf023c 100644
--- a/media/libstagefright/codec2/vndk/bufferpool/include/BufferPoolTypes.h
+++ b/media/libstagefright/codec2/vndk/bufferpool/include/BufferPoolTypes.h
@@ -22,9 +22,8 @@
#include <fmq/MessageQueue.h>
#include <hidl/MQDescriptor.h>
#include <hidl/Status.h>
-#include <C2Buffer.h>
-struct C2_HIDE _C2BlockPoolData {
+struct __attribute__((visibility("hidden"))) _C2BlockPoolData {
uint32_t mId; //BufferId
native_handle_t *mHandle;
@@ -57,6 +56,52 @@
typedef android::hardware::MessageQueue<BufferStatusMessage, kSynchronizedReadWrite> BufferStatusQueue;
typedef BufferStatusQueue::Descriptor QueueDescriptor;
+/**
+ * Allocation wrapper class for buffer pool.
+ */
+struct BufferPoolAllocation {
+ const native_handle_t *mHandle;
+
+ const native_handle_t *handle() {
+ return mHandle;
+ }
+
+ BufferPoolAllocation(const native_handle_t *handle) : mHandle(handle) {}
+
+ ~BufferPoolAllocation() {};
+};
+
+/**
+ * Allocator wrapper class for buffer pool.
+ */
+class BufferPoolAllocator {
+public:
+
+ /**
+ * Allocate an allocation(buffer) for bufer pool.
+ *
+ * @param params allocation parameters
+ * @param alloc created allocation
+ *
+ * @return OK when an allocation is created successfully.
+ */
+ virtual ResultStatus allocate(
+ const std::vector<uint8_t> ¶ms,
+ std::shared_ptr<BufferPoolAllocation> *alloc) = 0;
+
+ /**
+ * Returns whether allocation parameters of an old allocation are
+ * compatible with new allocation parameters.
+ */
+ virtual bool compatible(const std::vector<uint8_t> &newParams,
+ const std::vector<uint8_t> &oldParams) = 0;
+
+protected:
+ BufferPoolAllocator() = default;
+
+ virtual ~BufferPoolAllocator() = default;
+};
+
} // namespace implementation
} // namespace V1_0
} // namespace bufferpool
diff --git a/media/libstagefright/codec2/vndk/bufferpool/include/ClientManager.h b/media/libstagefright/codec2/vndk/bufferpool/include/ClientManager.h
index 412fa59..f91f46b 100644
--- a/media/libstagefright/codec2/vndk/bufferpool/include/ClientManager.h
+++ b/media/libstagefright/codec2/vndk/bufferpool/include/ClientManager.h
@@ -20,7 +20,6 @@
#include <android/hardware/media/bufferpool/1.0/IClientManager.h>
#include <hidl/MQDescriptor.h>
#include <hidl/Status.h>
-#include <C2Buffer.h>
#include <memory>
#include <BufferPoolTypes.h>
@@ -52,7 +51,6 @@
* Creates a local connection with a newly created buffer pool.
*
* @param allocator for new buffer allocation.
- * @param linear whether the allocator is linear or not.
* @param pConnectionId Id of the created connection. This is
* system-wide unique.
*
@@ -61,8 +59,7 @@
* NO_MEMORY when there is no memory.
* CRITICAL_ERROR otherwise.
*/
- ResultStatus create(const std::shared_ptr<C2Allocator> &allocator,
- bool linear,
+ ResultStatus create(const std::shared_ptr<BufferPoolAllocator> &allocator,
ConnectionId *pConnectionId);
/**
diff --git a/media/libstagefright/codec2/vndk/bufferpool/vts/Android.bp b/media/libstagefright/codec2/vndk/bufferpool/vts/Android.bp
index 074d4bc..62286f3 100644
--- a/media/libstagefright/codec2/vndk/bufferpool/vts/Android.bp
+++ b/media/libstagefright/codec2/vndk/bufferpool/vts/Android.bp
@@ -18,6 +18,7 @@
name: "VtsVndkHidlBufferpoolV1_0TargetSingleTest",
defaults: ["VtsHalTargetTestDefaults"],
srcs: [
+ "allocator.cpp",
"single.cpp",
],
static_libs: [
@@ -37,6 +38,7 @@
name: "VtsVndkHidlBufferpoolV1_0TargetMultiTest",
defaults: ["VtsHalTargetTestDefaults"],
srcs: [
+ "allocator.cpp",
"multi.cpp",
],
static_libs: [
diff --git a/media/libstagefright/codec2/vndk/bufferpool/vts/allocator.cpp b/media/libstagefright/codec2/vndk/bufferpool/vts/allocator.cpp
new file mode 100644
index 0000000..230ee3f
--- /dev/null
+++ b/media/libstagefright/codec2/vndk/bufferpool/vts/allocator.cpp
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <C2Buffer.h>
+#include "allocator.h"
+
+union Params {
+ struct {
+ uint32_t capacity;
+ C2MemoryUsage usage;
+ } data;
+ uint8_t array[0];
+ Params() : data{0, {0, 0}} {}
+ Params(uint32_t size)
+ : data{size, {C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE}} {}
+};
+
+struct AllocationDtor {
+ AllocationDtor(const std::shared_ptr<C2LinearAllocation> &alloc)
+ : mAlloc(alloc) {}
+
+ void operator()(BufferPoolAllocation *poolAlloc) { delete poolAlloc; }
+
+ const std::shared_ptr<C2LinearAllocation> mAlloc;
+};
+
+ResultStatus VtsBufferPoolAllocator::allocate(
+ const std::vector<uint8_t> ¶ms,
+ std::shared_ptr<BufferPoolAllocation> *alloc) {
+ Params ionParams;
+ memcpy(&ionParams, params.data(), std::min(sizeof(Params), params.size()));
+
+ std::shared_ptr<C2LinearAllocation> linearAlloc;
+ c2_status_t status = mAllocator->newLinearAllocation(
+ ionParams.data.capacity, ionParams.data.usage, &linearAlloc);
+ if (status == C2_OK && linearAlloc) {
+ BufferPoolAllocation *ptr = new BufferPoolAllocation(linearAlloc->handle());
+ if (ptr) {
+ *alloc = std::shared_ptr<BufferPoolAllocation>(
+ ptr, AllocationDtor(linearAlloc));
+ if (*alloc) {
+ return ResultStatus::OK;
+ }
+ delete ptr;
+ return ResultStatus::NO_MEMORY;
+ }
+ }
+ return ResultStatus::CRITICAL_ERROR;
+}
+
+bool VtsBufferPoolAllocator::compatible(const std::vector<uint8_t> &newParams,
+ const std::vector<uint8_t> &oldParams) {
+ size_t newSize = newParams.size();
+ size_t oldSize = oldParams.size();
+ if (newSize == oldSize) {
+ for (size_t i = 0; i < newSize; ++i) {
+ if (newParams[i] != oldParams[i]) {
+ return false;
+ }
+ }
+ return true;
+ }
+ return false;
+}
+
+void getVtsAllocatorParams(std::vector<uint8_t> *params) {
+ constexpr static int kAllocationSize = 1024 * 10;
+ Params ionParams(kAllocationSize);
+
+ params->assign(ionParams.array, ionParams.array + sizeof(ionParams));
+}
diff --git a/media/libstagefright/codec2/vndk/bufferpool/vts/allocator.h b/media/libstagefright/codec2/vndk/bufferpool/vts/allocator.h
new file mode 100644
index 0000000..2fbb7fb
--- /dev/null
+++ b/media/libstagefright/codec2/vndk/bufferpool/vts/allocator.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef VTS_VNDK_HIDL_BUFFERPOOL_V1_0_ALLOCATOR_H
+#define VTS_VNDK_HIDL_BUFFERPOOL_V1_0_ALLOCATOR_H
+
+#include <BufferPoolTypes.h>
+
+using android::hardware::media::bufferpool::V1_0::ResultStatus;
+using android::hardware::media::bufferpool::V1_0::implementation::
+ BufferPoolAllocation;
+using android::hardware::media::bufferpool::V1_0::implementation::
+ BufferPoolAllocator;
+
+// buffer allocator for the tests
+class VtsBufferPoolAllocator : public BufferPoolAllocator {
+ public:
+ VtsBufferPoolAllocator(const std::shared_ptr<C2Allocator> &allocator)
+ : mAllocator(allocator) {}
+
+ ~VtsBufferPoolAllocator() override {}
+
+ ResultStatus allocate(const std::vector<uint8_t> ¶ms,
+ std::shared_ptr<BufferPoolAllocation> *alloc) override;
+
+ bool compatible(const std::vector<uint8_t> &newParams,
+ const std::vector<uint8_t> &oldParams) override;
+
+ private:
+ const std::shared_ptr<C2Allocator> mAllocator;
+};
+
+// retrieve buffer allocator paramters
+void getVtsAllocatorParams(std::vector<uint8_t> *params);
+
+#endif // VTS_VNDK_HIDL_BUFFERPOOL_V1_0_ALLOCATOR_H
diff --git a/media/libstagefright/codec2/vndk/bufferpool/vts/multi.cpp b/media/libstagefright/codec2/vndk/bufferpool/vts/multi.cpp
index 3ad8b6c..35127b8 100644
--- a/media/libstagefright/codec2/vndk/bufferpool/vts/multi.cpp
+++ b/media/libstagefright/codec2/vndk/bufferpool/vts/multi.cpp
@@ -35,6 +35,7 @@
#include <iostream>
#include <memory>
#include <vector>
+#include "allocator.h"
using android::C2AllocatorIon;
using android::C2PlatformAllocatorStore;
@@ -50,9 +51,6 @@
namespace {
-// Buffer allocation size for tests.
-constexpr static int kAllocationSize = 1024 * 10;
-
// communication message types between processes.
enum PipeCommand : int32_t {
INIT_OK = 0,
@@ -95,11 +93,14 @@
mManager = ClientManager::getInstance();
ASSERT_NE(mManager, nullptr);
- mAllocator =
+ std::shared_ptr<C2Allocator> allocator =
std::make_shared<C2AllocatorIon>(C2PlatformAllocatorStore::ION);
+ ASSERT_TRUE((bool)allocator);
+
+ mAllocator = std::make_shared<VtsBufferPoolAllocator>(allocator);
ASSERT_TRUE((bool)mAllocator);
- status = mManager->create(mAllocator, true, &mConnectionId);
+ status = mManager->create(mAllocator, &mConnectionId);
ASSERT_TRUE(status == ResultStatus::OK);
status = mManager->getAccessor(mConnectionId, &mAccessor);
@@ -121,26 +122,12 @@
android::sp<ClientManager> mManager;
android::sp<IAccessor> mAccessor;
- std::shared_ptr<C2Allocator> mAllocator;
+ std::shared_ptr<BufferPoolAllocator> mAllocator;
ConnectionId mConnectionId;
pid_t mReceiverPid;
int mCommandPipeFds[2];
int mResultPipeFds[2];
- void getAllocationParams(std::vector<uint8_t>* vecParams) {
- union Params {
- struct {
- uint32_t capacity;
- C2MemoryUsage usage;
- } data;
- uint8_t array[0];
- Params()
- : data{kAllocationSize,
- {C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE}} {}
- } params;
- vecParams->assign(params.array, params.array + sizeof(params));
- }
-
bool sendMessage(int *pipes, const PipeMessage &message) {
int ret = write(pipes[1], message.array, sizeof(PipeMessage));
return ret == sizeof(PipeMessage);
@@ -211,7 +198,7 @@
int64_t postUs;
std::vector<uint8_t> vecParams;
- getAllocationParams(&vecParams);
+ getVtsAllocatorParams(&vecParams);
status = mManager->allocate(mConnectionId, vecParams, &sbuffer);
ASSERT_TRUE(status == ResultStatus::OK);
diff --git a/media/libstagefright/codec2/vndk/bufferpool/vts/single.cpp b/media/libstagefright/codec2/vndk/bufferpool/vts/single.cpp
index 89eb631..c8878f3 100644
--- a/media/libstagefright/codec2/vndk/bufferpool/vts/single.cpp
+++ b/media/libstagefright/codec2/vndk/bufferpool/vts/single.cpp
@@ -32,6 +32,7 @@
#include <iostream>
#include <memory>
#include <vector>
+#include "allocator.h"
using android::C2AllocatorIon;
using android::C2PlatformAllocatorStore;
@@ -45,9 +46,6 @@
namespace {
-// Buffer allocation size for tests.
-constexpr static int kAllocationSize = 1024 * 10;
-
// Number of iteration for buffer allocation test.
constexpr static int kNumAllocationTest = 3;
@@ -63,11 +61,14 @@
mManager = ClientManager::getInstance();
ASSERT_NE(mManager, nullptr);
- mAllocator =
+ std::shared_ptr<C2Allocator> allocator =
std::make_shared<C2AllocatorIon>(C2PlatformAllocatorStore::ION);
+ ASSERT_TRUE((bool)allocator);
+
+ mAllocator = std::make_shared<VtsBufferPoolAllocator>(allocator);
ASSERT_TRUE((bool)mAllocator);
- status = mManager->create(mAllocator, true, &mConnectionId);
+ status = mManager->create(mAllocator, &mConnectionId);
ASSERT_TRUE(status == ResultStatus::OK);
status = mManager->getAccessor(mConnectionId, &mAccessor);
@@ -91,23 +92,10 @@
android::sp<ClientManager> mManager;
android::sp<IAccessor> mAccessor;
- std::shared_ptr<C2Allocator> mAllocator;
+ std::shared_ptr<BufferPoolAllocator> mAllocator;
ConnectionId mConnectionId;
ConnectionId mReceiverId;
- void getAllocationParams(std::vector<uint8_t>* vecParams) {
- union Params {
- struct {
- uint32_t capacity;
- C2MemoryUsage usage;
- } data;
- uint8_t array[0];
- Params()
- : data{kAllocationSize,
- {C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE}} {}
- } params;
- vecParams->assign(params.array, params.array + sizeof(params));
- }
};
// Buffer allocation test.
@@ -116,7 +104,7 @@
TEST_F(BufferpoolSingleTest, AllocateBuffer) {
ResultStatus status;
std::vector<uint8_t> vecParams;
- getAllocationParams(&vecParams);
+ getVtsAllocatorParams(&vecParams);
std::shared_ptr<_C2BlockPoolData> buffer[kNumAllocationTest];
for (int i = 0; i < kNumAllocationTest; ++i) {
@@ -136,7 +124,7 @@
TEST_F(BufferpoolSingleTest, RecycleBuffer) {
ResultStatus status;
std::vector<uint8_t> vecParams;
- getAllocationParams(&vecParams);
+ getVtsAllocatorParams(&vecParams);
BufferId bid[kNumRecycleTest];
for (int i = 0; i < kNumRecycleTest; ++i) {
@@ -156,7 +144,7 @@
TEST_F(BufferpoolSingleTest, TransferBuffer) {
ResultStatus status;
std::vector<uint8_t> vecParams;
- getAllocationParams(&vecParams);
+ getVtsAllocatorParams(&vecParams);
std::shared_ptr<_C2BlockPoolData> sbuffer, rbuffer;
TransactionId transactionId;
diff --git a/packages/MediaComponents/test/src/android/media/MediaSessionManager_MediaSession2.java b/packages/MediaComponents/test/src/android/media/MediaSessionManager_MediaSession2.java
index 471d3f4..17b200f 100644
--- a/packages/MediaComponents/test/src/android/media/MediaSessionManager_MediaSession2.java
+++ b/packages/MediaComponents/test/src/android/media/MediaSessionManager_MediaSession2.java
@@ -20,6 +20,7 @@
import android.media.MediaSession2.ControllerInfo;
import android.media.MediaSession2.SessionCallback;
import android.media.session.MediaSessionManager;
+import android.media.session.MediaSessionManager.OnSessionTokensChangedListener;
import android.media.session.PlaybackState;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
@@ -30,7 +31,9 @@
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.UUID;
import java.util.List;
+import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import static org.junit.Assert.*;
@@ -210,9 +213,128 @@
assertTrue(foundTestLibraryService);
}
+ @Test
+ public void testAddOnSessionTokensChangedListener() throws InterruptedException {
+ TokensChangedListener listener = new TokensChangedListener();
+ mManager.addOnSessionTokensChangedListener(sHandlerExecutor, listener);
+
+ listener.reset();
+ MediaSession2 session1 = new MediaSession2.Builder(mContext)
+ .setPlayer(new MockPlayer(0))
+ .setId(UUID.randomUUID().toString())
+ .build();
+ assertTrue(listener.await());
+ assertTrue(listener.findToken(session1.getToken()));
+
+ listener.reset();
+ session1.close();
+ assertTrue(listener.await());
+ assertFalse(listener.findToken(session1.getToken()));
+
+ listener.reset();
+ MediaSession2 session2 = new MediaSession2.Builder(mContext)
+ .setPlayer(new MockPlayer(0))
+ .setId(UUID.randomUUID().toString())
+ .build();
+ assertTrue(listener.await());
+ assertFalse(listener.findToken(session1.getToken()));
+ assertTrue(listener.findToken(session2.getToken()));
+
+ listener.reset();
+ MediaSession2 session3 = new MediaSession2.Builder(mContext)
+ .setPlayer(new MockPlayer(0))
+ .setId(UUID.randomUUID().toString())
+ .build();
+ assertTrue(listener.await());
+ assertFalse(listener.findToken(session1.getToken()));
+ assertTrue(listener.findToken(session2.getToken()));
+ assertTrue(listener.findToken(session3.getToken()));
+
+ listener.reset();
+ session2.close();
+ assertTrue(listener.await());
+ assertFalse(listener.findToken(session1.getToken()));
+ assertFalse(listener.findToken(session2.getToken()));
+ assertTrue(listener.findToken(session3.getToken()));
+
+ listener.reset();
+ session3.close();
+ assertTrue(listener.await());
+ assertFalse(listener.findToken(session1.getToken()));
+ assertFalse(listener.findToken(session2.getToken()));
+ assertFalse(listener.findToken(session3.getToken()));
+
+ mManager.removeOnSessionTokensChangedListener(listener);
+ }
+
+ @Test
+ public void testRemoveOnSessionTokensChangedListener() throws InterruptedException {
+ TokensChangedListener listener = new TokensChangedListener();
+ mManager.addOnSessionTokensChangedListener(sHandlerExecutor, listener);
+
+ listener.reset();
+ MediaSession2 session1 = new MediaSession2.Builder(mContext)
+ .setPlayer(new MockPlayer(0))
+ .setId(UUID.randomUUID().toString())
+ .build();
+ assertTrue(listener.await());
+
+ mManager.removeOnSessionTokensChangedListener(listener);
+
+ listener.reset();
+ session1.close();
+ assertFalse(listener.await());
+
+ listener.reset();
+ MediaSession2 session2 = new MediaSession2.Builder(mContext)
+ .setPlayer(new MockPlayer(0))
+ .setId(UUID.randomUUID().toString())
+ .build();
+ assertFalse(listener.await());
+
+ listener.reset();
+ MediaSession2 session3 = new MediaSession2.Builder(mContext)
+ .setPlayer(new MockPlayer(0))
+ .setId(UUID.randomUUID().toString())
+ .build();
+ assertFalse(listener.await());
+
+ listener.reset();
+ session2.close();
+ assertFalse(listener.await());
+
+ listener.reset();
+ session3.close();
+ assertFalse(listener.await());
+ }
+
// Ensures if the session creation/release is notified to the server.
private void ensureChangeInSession() throws InterruptedException {
// TODO(jaewan): Wait by listener.
Thread.sleep(WAIT_TIME_MS);
}
+
+ private class TokensChangedListener implements OnSessionTokensChangedListener {
+ private CountDownLatch mLatch;
+ private List<SessionToken2> mTokens;
+
+ private void reset() {
+ mLatch = new CountDownLatch(1);
+ mTokens = null;
+ }
+
+ private boolean await() throws InterruptedException {
+ return mLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS);
+ }
+
+ private boolean findToken(SessionToken2 token) {
+ return mTokens.contains(token);
+ }
+
+ @Override
+ public void onSessionTokensChanged(List<SessionToken2> tokens) {
+ mTokens = tokens;
+ mLatch.countDown();
+ }
+ }
}
diff --git a/packages/MediaComponents/test/src/android/media/MockPlayer.java b/packages/MediaComponents/test/src/android/media/MockPlayer.java
index da4acb5..05962cf 100644
--- a/packages/MediaComponents/test/src/android/media/MockPlayer.java
+++ b/packages/MediaComponents/test/src/android/media/MockPlayer.java
@@ -63,6 +63,11 @@
}
@Override
+ public void reset() {
+ // no-op
+ }
+
+ @Override
public void play() {
mPlayCalled = true;
if (mCountDownLatch != null) {
diff --git a/services/oboeservice/AAudioService.cpp b/services/oboeservice/AAudioService.cpp
index ac3202b..c708fee 100644
--- a/services/oboeservice/AAudioService.cpp
+++ b/services/oboeservice/AAudioService.cpp
@@ -32,7 +32,6 @@
#include "AAudioService.h"
#include "AAudioServiceStreamMMAP.h"
#include "AAudioServiceStreamShared.h"
-#include "AAudioServiceStreamMMAP.h"
#include "binding/IAAudioService.h"
#include "ServiceUtilities.h"