Merge "MediaPlayer: enable more logs for extractor failure." into oc-dev
diff --git a/CleanSpec.mk b/CleanSpec.mk
index 789eda2..5c11bfa 100644
--- a/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -69,6 +69,7 @@
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/etc/init/mediacodec.rc)
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib/libeffects.so)
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib64/libeffects.so)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/libeffects_intermediates)
# ************************************************
# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
diff --git a/camera/include/camera/ndk/NdkCameraMetadataTags.h b/camera/include/camera/ndk/NdkCameraMetadataTags.h
index ced6034..25d364e 100644
--- a/camera/include/camera/ndk/NdkCameraMetadataTags.h
+++ b/camera/include/camera/ndk/NdkCameraMetadataTags.h
@@ -1542,7 +1542,11 @@
* request A.</p>
* <p>Note that when enableZsl is <code>true</code>, it is not guaranteed to get output images captured in the
* past for requests with STILL_CAPTURE capture intent.</p>
- * <p>The value of enableZsl in capture templates is always <code>false</code> if present.</p>
+ * <p>For applications targeting SDK versions O and newer, the value of enableZsl in
+ * TEMPLATE_STILL_CAPTURE template may be <code>true</code>. The value in other templates is always
+ * <code>false</code> if present.</p>
+ * <p>For applications targeting SDK versions older than O, the value of enableZsl in all
+ * capture templates is always <code>false</code> if present.</p>
*
* @see ACAMERA_CONTROL_CAPTURE_INTENT
* @see ACAMERA_SENSOR_TIMESTAMP
diff --git a/drm/mediacas/plugins/clearkey/Android.mk b/drm/mediacas/plugins/clearkey/Android.mk
index 0c2b357..8fd866c 100644
--- a/drm/mediacas/plugins/clearkey/Android.mk
+++ b/drm/mediacas/plugins/clearkey/Android.mk
@@ -28,7 +28,8 @@
LOCAL_MODULE := libclearkeycasplugin
-LOCAL_PROPRIETARY_MODULE := true
+#TODO: move this back to /vendor/lib after conversion to treble
+#LOCAL_PROPRIETARY_MODULE := true
LOCAL_MODULE_RELATIVE_PATH := mediacas
LOCAL_SHARED_LIBRARIES := \
diff --git a/drm/mediacas/plugins/clearkey/tests/Android.mk b/drm/mediacas/plugins/clearkey/tests/Android.mk
index 5418c1d..cbf7be7 100644
--- a/drm/mediacas/plugins/clearkey/tests/Android.mk
+++ b/drm/mediacas/plugins/clearkey/tests/Android.mk
@@ -26,7 +26,7 @@
# the plugin is not in standard library search path. Without this .so
# loading fails at run-time (linking is okay).
LOCAL_LDFLAGS := \
- -Wl,--rpath,\$${ORIGIN}/../../../system/vendor/lib/mediacas -Wl,--enable-new-dtags
+ -Wl,--rpath,\$${ORIGIN}/../../../system/lib/mediacas -Wl,--enable-new-dtags
LOCAL_SHARED_LIBRARIES := \
libutils libclearkeycasplugin libstagefright_foundation libprotobuf-cpp-lite liblog
diff --git a/include/media/omx/1.0/WGraphicBufferSource.h b/include/media/omx/1.0/WGraphicBufferSource.h
index 0ca5f44..397e576 100644
--- a/include/media/omx/1.0/WGraphicBufferSource.h
+++ b/include/media/omx/1.0/WGraphicBufferSource.h
@@ -67,14 +67,11 @@
struct LWGraphicBufferSource : public BnGraphicBufferSource {
sp<TGraphicBufferSource> mBase;
LWGraphicBufferSource(sp<TGraphicBufferSource> const& base);
- BnStatus configure(
- const sp<IOMXNode>& omxNode, int32_t dataSpace) override;
+ BnStatus configure(const sp<IOMXNode>& omxNode, int32_t dataSpace) override;
BnStatus setSuspend(bool suspend, int64_t timeUs) override;
- BnStatus setRepeatPreviousFrameDelayUs(
- int64_t repeatAfterUs) override;
+ BnStatus setRepeatPreviousFrameDelayUs(int64_t repeatAfterUs) override;
BnStatus setMaxFps(float maxFps) override;
- BnStatus setTimeLapseConfig(
- int64_t timePerFrameUs, int64_t timePerCaptureUs) override;
+ BnStatus setTimeLapseConfig(double fps, double captureFps) override;
BnStatus setStartTimeUs(int64_t startTimeUs) override;
BnStatus setStopTimeUs(int64_t stopTimeUs) override;
BnStatus setColorAspects(int32_t aspects) override;
diff --git a/include/ndk/NdkImage.h b/include/ndk/NdkImage.h
index 3d1bacc..66005cb 100644
--- a/include/ndk/NdkImage.h
+++ b/include/ndk/NdkImage.h
@@ -136,7 +136,7 @@
* <p>
* Corresponding formats:
* <ul>
- * <li>AHardwareBuffer: AHARDWAREBUFFER_FORMAT_R16G16B16A16_SFLOAT</li>
+ * <li>AHardwareBuffer: AHARDWAREBUFFER_FORMAT_R16G16B16A16_FLOAT</li>
* <li>Vulkan: VK_FORMAT_R16G16B16A16_SFLOAT</li>
* <li>OpenGL ES: GL_RGBA16F</li>
* </ul>
diff --git a/media/libaaudio/examples/write_sine/jni/Android.mk b/media/libaaudio/examples/write_sine/jni/Android.mk
index 8cd0f03..5a884e1 100644
--- a/media/libaaudio/examples/write_sine/jni/Android.mk
+++ b/media/libaaudio/examples/write_sine/jni/Android.mk
@@ -18,9 +18,9 @@
$(call include-path-for, audio-utils) \
frameworks/av/media/libaaudio/include
-LOCAL_SRC_FILES:= ../src/write_sine_threaded.cpp
+LOCAL_SRC_FILES:= ../src/write_sine_callback.cpp
LOCAL_SHARED_LIBRARIES := libaaudio
-LOCAL_MODULE := write_sine_threaded_ndk
+LOCAL_MODULE := write_sine_callback_ndk
include $(BUILD_EXECUTABLE)
include $(CLEAR_VARS)
diff --git a/media/libaaudio/examples/write_sine/src/SineGenerator.h b/media/libaaudio/examples/write_sine/src/SineGenerator.h
index 64b772d..f2eb984 100644
--- a/media/libaaudio/examples/write_sine/src/SineGenerator.h
+++ b/media/libaaudio/examples/write_sine/src/SineGenerator.h
@@ -79,7 +79,7 @@
}
}
- double mAmplitude = 0.05; // unitless scaler
+ double mAmplitude = 0.005; // unitless scaler
double mPhase = 0.0;
double mPhaseIncrement = 440 * M_PI * 2 / 48000;
double mFrameRate = 48000;
diff --git a/media/libaaudio/examples/write_sine/src/write_sine.cpp b/media/libaaudio/examples/write_sine/src/write_sine.cpp
index d8e5ec1..6525c0a 100644
--- a/media/libaaudio/examples/write_sine/src/write_sine.cpp
+++ b/media/libaaudio/examples/write_sine/src/write_sine.cpp
@@ -23,11 +23,15 @@
#include "SineGenerator.h"
#define SAMPLE_RATE 48000
-#define NUM_SECONDS 10
+#define NUM_SECONDS 5
#define NANOS_PER_MICROSECOND ((int64_t)1000)
#define NANOS_PER_MILLISECOND (NANOS_PER_MICROSECOND * 1000)
#define NANOS_PER_SECOND (NANOS_PER_MILLISECOND * 1000)
+#define REQUESTED_FORMAT AAUDIO_FORMAT_PCM_I16
+#define REQUESTED_SHARING_MODE AAUDIO_SHARING_MODE_SHARED
+//#define REQUESTED_SHARING_MODE AAUDIO_SHARING_MODE_EXCLUSIVE
+
static const char *getSharingModeText(aaudio_sharing_mode_t mode) {
const char *modeText = "unknown";
switch (mode) {
@@ -63,23 +67,21 @@
int actualSamplesPerFrame = 0;
const int requestedSampleRate = SAMPLE_RATE;
int actualSampleRate = 0;
- const aaudio_audio_format_t requestedDataFormat = AAUDIO_FORMAT_PCM_I16;
- aaudio_audio_format_t actualDataFormat = AAUDIO_FORMAT_PCM_I16;
+ aaudio_audio_format_t actualDataFormat = AAUDIO_FORMAT_UNSPECIFIED;
- //const aaudio_sharing_mode_t requestedSharingMode = AAUDIO_SHARING_MODE_EXCLUSIVE;
- const aaudio_sharing_mode_t requestedSharingMode = AAUDIO_SHARING_MODE_SHARED;
aaudio_sharing_mode_t actualSharingMode = AAUDIO_SHARING_MODE_SHARED;
AAudioStreamBuilder *aaudioBuilder = nullptr;
AAudioStream *aaudioStream = nullptr;
aaudio_stream_state_t state = AAUDIO_STREAM_STATE_UNINITIALIZED;
- int32_t framesPerBurst = 0;
- int32_t framesPerWrite = 0;
- int32_t bufferCapacity = 0;
- int32_t framesToPlay = 0;
- int32_t framesLeft = 0;
- int32_t xRunCount = 0;
- int16_t *data = nullptr;
+ int32_t framesPerBurst = 0;
+ int32_t framesPerWrite = 0;
+ int32_t bufferCapacity = 0;
+ int32_t framesToPlay = 0;
+ int32_t framesLeft = 0;
+ int32_t xRunCount = 0;
+ float *floatData = nullptr;
+ int16_t *shortData = nullptr;
SineGenerator sineOsc1;
SineGenerator sineOsc2;
@@ -88,7 +90,7 @@
// in a buffer if we hang or crash.
setvbuf(stdout, nullptr, _IONBF, (size_t) 0);
- printf("%s - Play a sine wave using AAudio\n", argv[0]);
+ printf("%s - Play a sine wave using AAudio, Z2\n", argv[0]);
// Use an AAudioStreamBuilder to contain requested parameters.
result = AAudio_createStreamBuilder(&aaudioBuilder);
@@ -99,8 +101,8 @@
// Request stream properties.
AAudioStreamBuilder_setSampleRate(aaudioBuilder, requestedSampleRate);
AAudioStreamBuilder_setSamplesPerFrame(aaudioBuilder, requestedSamplesPerFrame);
- AAudioStreamBuilder_setFormat(aaudioBuilder, requestedDataFormat);
- AAudioStreamBuilder_setSharingMode(aaudioBuilder, requestedSharingMode);
+ AAudioStreamBuilder_setFormat(aaudioBuilder, REQUESTED_FORMAT);
+ AAudioStreamBuilder_setSharingMode(aaudioBuilder, REQUESTED_SHARING_MODE);
// Create an AAudioStream using the Builder.
result = AAudioStreamBuilder_openStream(aaudioBuilder, &aaudioStream);
@@ -124,15 +126,16 @@
actualSharingMode = AAudioStream_getSharingMode(aaudioStream);
printf("SharingMode: requested = %s, actual = %s\n",
- getSharingModeText(requestedSharingMode),
+ getSharingModeText(REQUESTED_SHARING_MODE),
getSharingModeText(actualSharingMode));
// This is the number of frames that are read in one chunk by a DMA controller
// or a DSP or a mixer.
framesPerBurst = AAudioStream_getFramesPerBurst(aaudioStream);
- printf("DataFormat: framesPerBurst = %d\n",framesPerBurst);
+ printf("Buffer: framesPerBurst = %d\n",framesPerBurst);
+ printf("Buffer: bufferSize = %d\n", AAudioStream_getBufferSizeInFrames(aaudioStream));
bufferCapacity = AAudioStream_getBufferCapacityInFrames(aaudioStream);
- printf("DataFormat: bufferCapacity = %d, remainder = %d\n",
+ printf("Buffer: bufferCapacity = %d, remainder = %d\n",
bufferCapacity, bufferCapacity % framesPerBurst);
// Some DMA might use very short bursts of 16 frames. We don't need to write such small
@@ -144,14 +147,16 @@
printf("DataFormat: framesPerWrite = %d\n",framesPerWrite);
actualDataFormat = AAudioStream_getFormat(aaudioStream);
- printf("DataFormat: requested = %d, actual = %d\n", requestedDataFormat, actualDataFormat);
+ printf("DataFormat: requested = %d, actual = %d\n", REQUESTED_FORMAT, actualDataFormat);
// TODO handle other data formats
// Allocate a buffer for the audio data.
- data = new int16_t[framesPerWrite * actualSamplesPerFrame];
- if (data == nullptr) {
- fprintf(stderr, "ERROR - could not allocate data buffer\n");
- result = AAUDIO_ERROR_NO_MEMORY;
+ if (actualDataFormat == AAUDIO_FORMAT_PCM_FLOAT) {
+ floatData = new float[framesPerWrite * actualSamplesPerFrame];
+ } else if (actualDataFormat == AAUDIO_FORMAT_PCM_I16) {
+ shortData = new int16_t[framesPerWrite * actualSamplesPerFrame];
+ } else {
+ printf("ERROR Unsupported data format!\n");
goto finish;
}
@@ -170,26 +175,41 @@
framesToPlay = actualSampleRate * NUM_SECONDS;
framesLeft = framesToPlay;
while (framesLeft > 0) {
- // Render sine waves to left and right channels.
- sineOsc1.render(&data[0], actualSamplesPerFrame, framesPerWrite);
- if (actualSamplesPerFrame > 1) {
- sineOsc2.render(&data[1], actualSamplesPerFrame, framesPerWrite);
+
+ if (actualDataFormat == AAUDIO_FORMAT_PCM_FLOAT) {
+ // Render sine waves to left and right channels.
+ sineOsc1.render(&floatData[0], actualSamplesPerFrame, framesPerWrite);
+ if (actualSamplesPerFrame > 1) {
+ sineOsc2.render(&floatData[1], actualSamplesPerFrame, framesPerWrite);
+ }
+ } else if (actualDataFormat == AAUDIO_FORMAT_PCM_I16) {
+ // Render sine waves to left and right channels.
+ sineOsc1.render(&shortData[0], actualSamplesPerFrame, framesPerWrite);
+ if (actualSamplesPerFrame > 1) {
+ sineOsc2.render(&shortData[1], actualSamplesPerFrame, framesPerWrite);
+ }
}
// Write audio data to the stream.
- int64_t timeoutNanos = 100 * NANOS_PER_MILLISECOND;
- int minFrames = (framesToPlay < framesPerWrite) ? framesToPlay : framesPerWrite;
- int actual = AAudioStream_write(aaudioStream, data, minFrames, timeoutNanos);
+ int64_t timeoutNanos = 1000 * NANOS_PER_MILLISECOND;
+ int32_t minFrames = (framesToPlay < framesPerWrite) ? framesToPlay : framesPerWrite;
+ int32_t actual = 0;
+ if (actualDataFormat == AAUDIO_FORMAT_PCM_FLOAT) {
+ actual = AAudioStream_write(aaudioStream, floatData, minFrames, timeoutNanos);
+ } else if (actualDataFormat == AAUDIO_FORMAT_PCM_I16) {
+ actual = AAudioStream_write(aaudioStream, shortData, minFrames, timeoutNanos);
+ }
if (actual < 0) {
- fprintf(stderr, "ERROR - AAudioStream_write() returned %zd\n", actual);
+ fprintf(stderr, "ERROR - AAudioStream_write() returned %d\n", actual);
goto finish;
} else if (actual == 0) {
- fprintf(stderr, "WARNING - AAudioStream_write() returned %zd\n", actual);
+ fprintf(stderr, "WARNING - AAudioStream_write() returned %d\n", actual);
goto finish;
}
framesLeft -= actual;
// Use timestamp to estimate latency.
+ /*
{
int64_t presentationFrame;
int64_t presentationTime;
@@ -208,13 +228,15 @@
printf("estimatedLatencyMillis %d\n", (int)estimatedLatencyMillis);
}
}
+ */
}
xRunCount = AAudioStream_getXRunCount(aaudioStream);
printf("AAudioStream_getXRunCount %d\n", xRunCount);
finish:
- delete[] data;
+ delete[] floatData;
+ delete[] shortData;
AAudioStream_close(aaudioStream);
AAudioStreamBuilder_delete(aaudioBuilder);
printf("exiting - AAudio result = %d = %s\n", result, AAudio_convertResultToText(result));
diff --git a/media/libaaudio/examples/write_sine/src/write_sine_callback.cpp b/media/libaaudio/examples/write_sine/src/write_sine_callback.cpp
index 9414236..8c1072d 100644
--- a/media/libaaudio/examples/write_sine/src/write_sine_callback.cpp
+++ b/media/libaaudio/examples/write_sine/src/write_sine_callback.cpp
@@ -31,8 +31,6 @@
//#define SHARING_MODE AAUDIO_SHARING_MODE_EXCLUSIVE
#define SHARING_MODE AAUDIO_SHARING_MODE_SHARED
-#define CALLBACK_SIZE_FRAMES 128
-
// TODO refactor common code into a single SimpleAAudio class
/**
* Simple wrapper for AAudio that opens a default stream and then calls
@@ -87,8 +85,8 @@
AAudioStreamBuilder_setSharingMode(mBuilder, mRequestedSharingMode);
AAudioStreamBuilder_setDataCallback(mBuilder, dataProc, userContext);
AAudioStreamBuilder_setFormat(mBuilder, AAUDIO_FORMAT_PCM_FLOAT);
- AAudioStreamBuilder_setFramesPerDataCallback(mBuilder, CALLBACK_SIZE_FRAMES);
- // AAudioStreamBuilder_setBufferCapacityInFrames(mBuilder, CALLBACK_SIZE_FRAMES * 4);
+ // AAudioStreamBuilder_setFramesPerDataCallback(mBuilder, CALLBACK_SIZE_FRAMES);
+ AAudioStreamBuilder_setBufferCapacityInFrames(mBuilder, 48 * 8);
// Open an AAudioStream using the Builder.
result = AAudioStreamBuilder_openStream(mBuilder, &mStream);
@@ -136,7 +134,7 @@
aaudio_result_t start() {
aaudio_result_t result = AAudioStream_requestStart(mStream);
if (result != AAUDIO_OK) {
- fprintf(stderr, "ERROR - AAudioStream_requestStart() returned %d %s\n",
+ printf("ERROR - AAudioStream_requestStart() returned %d %s\n",
result, AAudio_convertResultToText(result));
}
return result;
@@ -146,7 +144,7 @@
aaudio_result_t stop() {
aaudio_result_t result = AAudioStream_requestStop(mStream);
if (result != AAUDIO_OK) {
- fprintf(stderr, "ERROR - AAudioStream_requestStop() returned %d %s\n",
+ printf("ERROR - AAudioStream_requestStop() returned %d %s\n",
result, AAudio_convertResultToText(result));
}
int32_t xRunCount = AAudioStream_getXRunCount(mStream);
@@ -169,9 +167,6 @@
typedef struct SineThreadedData_s {
SineGenerator sineOsc1;
SineGenerator sineOsc2;
- // Remove these variables used for testing.
- int32_t numFrameCounts;
- int32_t frameCounts[MAX_FRAME_COUNT_RECORDS];
int scheduler;
bool schedulerChecked;
} SineThreadedData_t;
@@ -186,10 +181,6 @@
SineThreadedData_t *sineData = (SineThreadedData_t *) userData;
- if (sineData->numFrameCounts < MAX_FRAME_COUNT_RECORDS) {
- sineData->frameCounts[sineData->numFrameCounts++] = numFrames;
- }
-
if (!sineData->schedulerChecked) {
sineData->scheduler = sched_getscheduler(gettid());
sineData->schedulerChecked = true;
@@ -236,11 +227,10 @@
// Make printf print immediately so that debug info is not stuck
// in a buffer if we hang or crash.
setvbuf(stdout, nullptr, _IONBF, (size_t) 0);
- printf("%s - Play a sine sweep using an AAudio callback\n", argv[0]);
+ printf("%s - Play a sine sweep using an AAudio callback, Z1\n", argv[0]);
player.setSharingMode(SHARING_MODE);
- myData.numFrameCounts = 0;
myData.schedulerChecked = false;
result = player.open(MyDataCallbackProc, &myData);
@@ -291,19 +281,17 @@
}
printf("Woke up now.\n");
+ printf("call stop()\n");
result = player.stop();
if (result != AAUDIO_OK) {
goto error;
}
+ printf("call close()\n");
result = player.close();
if (result != AAUDIO_OK) {
goto error;
}
- // Report data gathered in the callback.
- for (int i = 0; i < myData.numFrameCounts; i++) {
- printf("numFrames[%4d] = %4d\n", i, myData.frameCounts[i]);
- }
if (myData.schedulerChecked) {
printf("scheduler = 0x%08x, SCHED_FIFO = 0x%08X\n",
myData.scheduler,
diff --git a/media/libaaudio/examples/write_sine/src/write_sine_threaded.cpp b/media/libaaudio/examples/write_sine/src/write_sine_threaded.cpp
deleted file mode 100644
index 9bc5886..0000000
--- a/media/libaaudio/examples/write_sine/src/write_sine_threaded.cpp
+++ /dev/null
@@ -1,386 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-// Play sine waves using an AAudio background thread.
-
-//#include <assert.h>
-#include <atomic>
-#include <unistd.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <math.h>
-#include <time.h>
-#include <aaudio/AAudio.h>
-#include "SineGenerator.h"
-
-#define NUM_SECONDS 5
-#define NANOS_PER_MICROSECOND ((int64_t)1000)
-#define NANOS_PER_MILLISECOND (NANOS_PER_MICROSECOND * 1000)
-#define MILLIS_PER_SECOND 1000
-#define NANOS_PER_SECOND (NANOS_PER_MILLISECOND * MILLIS_PER_SECOND)
-
-#define SHARING_MODE AAUDIO_SHARING_MODE_EXCLUSIVE
-//#define SHARING_MODE AAUDIO_SHARING_MODE_SHARED
-
-// Prototype for a callback.
-typedef int audio_callback_proc_t(float *outputBuffer,
- int32_t numFrames,
- void *userContext);
-
-static void *SimpleAAudioPlayerThreadProc(void *arg);
-
-// TODO merge into common code
-static int64_t getNanoseconds(clockid_t clockId = CLOCK_MONOTONIC) {
- struct timespec time;
- int result = clock_gettime(clockId, &time);
- if (result < 0) {
- return -errno; // TODO standardize return value
- }
- return (time.tv_sec * NANOS_PER_SECOND) + time.tv_nsec;
-}
-
-/**
- * Simple wrapper for AAudio that opens a default stream and then calls
- * a callback function to fill the output buffers.
- */
-class SimpleAAudioPlayer {
-public:
- SimpleAAudioPlayer() {}
- ~SimpleAAudioPlayer() {
- close();
- };
-
- void setSharingMode(aaudio_sharing_mode_t requestedSharingMode) {
- mRequestedSharingMode = requestedSharingMode;
- }
-
- /** Also known as "sample rate"
- */
- int32_t getFramesPerSecond() {
- return mFramesPerSecond;
- }
-
- int32_t getSamplesPerFrame() {
- return mSamplesPerFrame;
- }
-
- /**
- * Open a stream
- */
- aaudio_result_t open(audio_callback_proc_t *proc, void *userContext) {
- mCallbackProc = proc;
- mUserContext = userContext;
- aaudio_result_t result = AAUDIO_OK;
-
- // Use an AAudioStreamBuilder to contain requested parameters.
- result = AAudio_createStreamBuilder(&mBuilder);
- if (result != AAUDIO_OK) return result;
-
- AAudioStreamBuilder_setSharingMode(mBuilder, mRequestedSharingMode);
- AAudioStreamBuilder_setSampleRate(mBuilder, 48000);
-
- // Open an AAudioStream using the Builder.
- result = AAudioStreamBuilder_openStream(mBuilder, &mStream);
- if (result != AAUDIO_OK) goto error;
-
- printf("Requested sharing mode = %d\n", mRequestedSharingMode);
- printf("Actual sharing mode = %d\n", AAudioStream_getSharingMode(mStream));
-
- // Check to see what kind of stream we actually got.
- mFramesPerSecond = AAudioStream_getSampleRate(mStream);
- printf("Actual framesPerSecond = %d\n", mFramesPerSecond);
-
- mSamplesPerFrame = AAudioStream_getSamplesPerFrame(mStream);
- printf("Actual samplesPerFrame = %d\n", mSamplesPerFrame);
-
- {
- int32_t bufferCapacity = AAudioStream_getBufferCapacityInFrames(mStream);
- printf("Actual bufferCapacity = %d\n", bufferCapacity);
- }
-
- // This is the number of frames that are read in one chunk by a DMA controller
- // or a DSP or a mixer.
- mFramesPerBurst = AAudioStream_getFramesPerBurst(mStream);
- // Some DMA might use very short bursts. We don't need to write such small
- // buffers. But it helps to use a multiple of the burst size for predictable scheduling.
- while (mFramesPerBurst < 48) {
- mFramesPerBurst *= 2;
- }
- printf("Actual framesPerBurst = %d\n",mFramesPerBurst);
-
- mDataFormat = AAudioStream_getFormat(mStream);
- printf("Actual dataFormat = %d\n", mDataFormat);
-
- // Allocate a buffer for the audio data.
- mOutputBuffer = new float[mFramesPerBurst * mSamplesPerFrame];
- if (mOutputBuffer == nullptr) {
- fprintf(stderr, "ERROR - could not allocate data buffer\n");
- result = AAUDIO_ERROR_NO_MEMORY;
- }
-
- // If needed allocate a buffer for converting float to int16_t.
- if (mDataFormat == AAUDIO_FORMAT_PCM_I16) {
- printf("Allocate data conversion buffer for float=>pcm16\n");
- mConversionBuffer = new int16_t[mFramesPerBurst * mSamplesPerFrame];
- if (mConversionBuffer == nullptr) {
- fprintf(stderr, "ERROR - could not allocate conversion buffer\n");
- result = AAUDIO_ERROR_NO_MEMORY;
- }
- }
- return result;
-
- error:
- AAudioStreamBuilder_delete(mBuilder);
- mBuilder = nullptr;
- return result;
- }
-
- aaudio_result_t close() {
- if (mStream != nullptr) {
- stop();
- printf("call AAudioStream_close(%p)\n", mStream); fflush(stdout);
- AAudioStream_close(mStream);
- mStream = nullptr;
- AAudioStreamBuilder_delete(mBuilder);
- mBuilder = nullptr;
- delete mOutputBuffer;
- mOutputBuffer = nullptr;
- delete mConversionBuffer;
- mConversionBuffer = nullptr;
- }
- return AAUDIO_OK;
- }
-
- // Start a thread that will call the callback proc.
- aaudio_result_t start() {
- mEnabled.store(true);
- int64_t nanosPerBurst = mFramesPerBurst * NANOS_PER_SECOND
- / mFramesPerSecond;
- return AAudioStream_createThread(mStream, nanosPerBurst,
- SimpleAAudioPlayerThreadProc,
- this);
- }
-
- // Tell the thread to stop.
- aaudio_result_t stop() {
- mEnabled.store(false);
- return AAudioStream_joinThread(mStream, nullptr, 2 * NANOS_PER_SECOND);
- }
-
- bool isEnabled() const {
- return mEnabled.load();
- }
-
- aaudio_result_t callbackLoop() {
- aaudio_result_t result = 0;
- int64_t framesWritten = 0;
- int32_t xRunCount = 0;
- bool started = false;
- int64_t framesInBuffer =
- AAudioStream_getFramesWritten(mStream) -
- AAudioStream_getFramesRead(mStream);
- int64_t framesAvailable =
- AAudioStream_getBufferSizeInFrames(mStream) - framesInBuffer;
-
- int64_t startTime = 0;
- int64_t startPosition = 0;
- int32_t loopCount = 0;
-
- // Give up after several burst periods have passed.
- const int burstsPerTimeout = 8;
- int64_t nanosPerTimeout = 0;
- int64_t runningNanosPerTimeout = 500 * NANOS_PER_MILLISECOND;
-
- while (isEnabled() && result >= 0) {
- // Call application's callback function to fill the buffer.
- if (mCallbackProc(mOutputBuffer, mFramesPerBurst, mUserContext)) {
- mEnabled.store(false);
- }
-
- // if needed, convert from float to int16_t PCM
- //printf("app callbackLoop writing %d frames, state = %s\n", mFramesPerBurst,
- // AAudio_convertStreamStateToText(AAudioStream_getState(mStream)));
- if (mConversionBuffer != nullptr) {
- int32_t numSamples = mFramesPerBurst * mSamplesPerFrame;
- for (int i = 0; i < numSamples; i++) {
- mConversionBuffer[i] = (int16_t)(32767.0 * mOutputBuffer[i]);
- }
- // Write the application data to stream.
- result = AAudioStream_write(mStream, mConversionBuffer,
- mFramesPerBurst, nanosPerTimeout);
- } else {
- // Write the application data to stream.
- result = AAudioStream_write(mStream, mOutputBuffer,
- mFramesPerBurst, nanosPerTimeout);
- }
-
- if (result < 0) {
- fprintf(stderr, "ERROR - AAudioStream_write() returned %d %s\n", result,
- AAudio_convertResultToText(result));
- break;
- } else if (started && result != mFramesPerBurst) {
- fprintf(stderr, "ERROR - AAudioStream_write() timed out! %d\n", result);
- break;
- } else {
- framesWritten += result;
- }
-
- if (startTime > 0 && ((loopCount & 0x01FF) == 0)) {
- double elapsedFrames = (double)(framesWritten - startPosition);
- int64_t elapsedTime = getNanoseconds() - startTime;
- double measuredRate = elapsedFrames * NANOS_PER_SECOND / elapsedTime;
- printf("app callbackLoop write() measured rate %f\n", measuredRate);
- }
- loopCount++;
-
- if (!started && framesWritten >= framesAvailable) {
- // Start buffer if fully primed.{
- result = AAudioStream_requestStart(mStream);
- printf("app callbackLoop requestStart returned %d\n", result);
- if (result != AAUDIO_OK) {
- fprintf(stderr, "ERROR - AAudioStream_requestStart() returned %d %s\n", result,
- AAudio_convertResultToText(result));
- mEnabled.store(false);
- return result;
- }
- started = true;
- nanosPerTimeout = runningNanosPerTimeout;
- startPosition = framesWritten;
- startTime = getNanoseconds();
- }
-
- {
- int32_t tempXRunCount = AAudioStream_getXRunCount(mStream);
- if (tempXRunCount != xRunCount) {
- xRunCount = tempXRunCount;
- printf("AAudioStream_getXRunCount returns %d at frame %d\n",
- xRunCount, (int) framesWritten);
- }
- }
- }
-
- result = AAudioStream_requestStop(mStream);
- if (result != AAUDIO_OK) {
- fprintf(stderr, "ERROR - AAudioStream_requestStop() returned %d %s\n", result,
- AAudio_convertResultToText(result));
- return result;
- }
-
- return result;
- }
-
-private:
- AAudioStreamBuilder *mBuilder = nullptr;
- AAudioStream *mStream = nullptr;
- float *mOutputBuffer = nullptr;
- int16_t *mConversionBuffer = nullptr;
-
- audio_callback_proc_t *mCallbackProc = nullptr;
- void *mUserContext = nullptr;
- aaudio_sharing_mode_t mRequestedSharingMode = SHARING_MODE;
- int32_t mSamplesPerFrame = 0;
- int32_t mFramesPerSecond = 0;
- int32_t mFramesPerBurst = 0;
- aaudio_audio_format_t mDataFormat = AAUDIO_FORMAT_PCM_I16;
-
- std::atomic<bool> mEnabled; // used to request that callback exit its loop
-};
-
-static void *SimpleAAudioPlayerThreadProc(void *arg) {
- SimpleAAudioPlayer *player = (SimpleAAudioPlayer *) arg;
- player->callbackLoop();
- return nullptr;
-}
-
-// Application data that gets passed to the callback.
-typedef struct SineThreadedData_s {
- SineGenerator sineOsc1;
- SineGenerator sineOsc2;
- int32_t samplesPerFrame = 0;
-} SineThreadedData_t;
-
-// Callback function that fills the audio output buffer.
-int MyCallbackProc(float *outputBuffer, int32_t numFrames, void *userContext) {
- SineThreadedData_t *data = (SineThreadedData_t *) userContext;
- // Render sine waves to left and right channels.
- data->sineOsc1.render(&outputBuffer[0], data->samplesPerFrame, numFrames);
- if (data->samplesPerFrame > 1) {
- data->sineOsc2.render(&outputBuffer[1], data->samplesPerFrame, numFrames);
- }
- return 0;
-}
-
-int main(int argc, char **argv)
-{
- (void)argc; // unused
- SimpleAAudioPlayer player;
- SineThreadedData_t myData;
- aaudio_result_t result;
-
- // Make printf print immediately so that debug info is not stuck
- // in a buffer if we hang or crash.
- setvbuf(stdout, nullptr, _IONBF, (size_t) 0);
- printf("%s - Play a sine wave using an AAudio Thread\n", argv[0]);
-
- result = player.open(MyCallbackProc, &myData);
- if (result != AAUDIO_OK) {
- fprintf(stderr, "ERROR - player.open() returned %d\n", result);
- goto error;
- }
- printf("player.getFramesPerSecond() = %d\n", player.getFramesPerSecond());
- printf("player.getSamplesPerFrame() = %d\n", player.getSamplesPerFrame());
- myData.sineOsc1.setup(440.0, 48000);
- myData.sineOsc1.setSweep(300.0, 600.0, 5.0);
- myData.sineOsc2.setup(660.0, 48000);
- myData.sineOsc2.setSweep(350.0, 900.0, 7.0);
- myData.samplesPerFrame = player.getSamplesPerFrame();
-
- result = player.start();
- if (result != AAUDIO_OK) {
- fprintf(stderr, "ERROR - player.start() returned %d\n", result);
- goto error;
- }
-
- printf("Sleep for %d seconds while audio plays in a background thread.\n", NUM_SECONDS);
- for (int i = 0; i < NUM_SECONDS && player.isEnabled(); i++) {
- // FIXME sleep is not an NDK API
- // sleep(NUM_SECONDS);
- const struct timespec request = { .tv_sec = 1, .tv_nsec = 0 };
- (void) clock_nanosleep(CLOCK_MONOTONIC, 0 /*flags*/, &request, NULL /*remain*/);
- }
- printf("Woke up now!\n");
-
- result = player.stop();
- if (result != AAUDIO_OK) {
- fprintf(stderr, "ERROR - player.stop() returned %d\n", result);
- goto error;
- }
-
- printf("Player stopped.\n");
- result = player.close();
- if (result != AAUDIO_OK) {
- fprintf(stderr, "ERROR - player.close() returned %d\n", result);
- goto error;
- }
-
- printf("SUCCESS\n");
- return EXIT_SUCCESS;
-error:
- player.close();
- printf("exiting - AAudio result = %d = %s\n", result, AAudio_convertResultToText(result));
- return EXIT_FAILURE;
-}
-
diff --git a/media/libaaudio/examples/write_sine/static/Android.mk b/media/libaaudio/examples/write_sine/static/Android.mk
index c02b91c..e4da6a8 100644
--- a/media/libaaudio/examples/write_sine/static/Android.mk
+++ b/media/libaaudio/examples/write_sine/static/Android.mk
@@ -18,25 +18,6 @@
include $(BUILD_EXECUTABLE)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE_TAGS := tests
-LOCAL_C_INCLUDES := \
- $(call include-path-for, audio-utils) \
- frameworks/av/media/libaaudio/include
-
-LOCAL_SRC_FILES:= ../src/write_sine_threaded.cpp
-
-LOCAL_SHARED_LIBRARIES := libaudioutils libmedia \
- libbinder libcutils libutils \
- libaudioclient liblog
-LOCAL_STATIC_LIBRARIES := libaaudio
-
-LOCAL_MODULE := write_sine_threaded
-include $(BUILD_EXECUTABLE)
-
-
-
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := tests
LOCAL_C_INCLUDES := \
diff --git a/media/libaaudio/include/aaudio/AAudio.h b/media/libaaudio/include/aaudio/AAudio.h
index d0c7c22..4c1ea55 100644
--- a/media/libaaudio/include/aaudio/AAudio.h
+++ b/media/libaaudio/include/aaudio/AAudio.h
@@ -54,9 +54,7 @@
AAUDIO_FORMAT_INVALID = -1,
AAUDIO_FORMAT_UNSPECIFIED = 0,
AAUDIO_FORMAT_PCM_I16,
- AAUDIO_FORMAT_PCM_FLOAT,
- AAUDIO_FORMAT_PCM_I8_24,
- AAUDIO_FORMAT_PCM_I32
+ AAUDIO_FORMAT_PCM_FLOAT
};
typedef int32_t aaudio_format_t;
@@ -584,61 +582,10 @@
int32_t numFrames,
int64_t timeoutNanoseconds);
-
-// ============================================================
-// High priority audio threads
-// ============================================================
-
-/**
- * @deprecated Use AudioStreamBuilder_setCallback()
- */
-typedef void *(*aaudio_audio_thread_proc_t)(void *);
-
-/**
- * @deprecated Use AudioStreamBuilder_setCallback()
- *
- * Create a thread associated with a stream. The thread has special properties for
- * low latency audio performance. This thread can be used to implement a callback API.
- *
- * Only one thread may be associated with a stream.
- *
- * If you are using multiple streams then we recommend that you only do
- * blocking reads or writes on one stream. You can do non-blocking I/O on the
- * other streams by setting the timeout to zero.
- * This thread should be created for the stream that you will block on.
- *
- * Note that this API is in flux.
- *
- * @param stream A stream created using AAudioStreamBuilder_openStream().
- * @param periodNanoseconds the estimated period at which the audio thread will need to wake up
- * @param threadProc your thread entry point
- * @param arg an argument that will be passed to your thread entry point
- * @return AAUDIO_OK or a negative error.
- */
-AAUDIO_API aaudio_result_t AAudioStream_createThread(AAudioStream* stream,
- int64_t periodNanoseconds,
- aaudio_audio_thread_proc_t threadProc,
- void *arg);
-
-/**
- * @deprecated Use AudioStreamBuilder_setCallback()
- *
- * Wait until the thread exits or an error occurs.
- *
- * @param stream A stream created using AAudioStreamBuilder_openStream().
- * @param returnArg a pointer to a variable to receive the return value
- * @param timeoutNanoseconds Maximum number of nanoseconds to wait for completion.
- * @return AAUDIO_OK or a negative error.
- */
-AAUDIO_API aaudio_result_t AAudioStream_joinThread(AAudioStream* stream,
- void **returnArg,
- int64_t timeoutNanoseconds);
-
// ============================================================
// Stream - queries
// ============================================================
-
/**
* This can be used to adjust the latency of the buffer by changing
* the threshold where blocking will occur.
diff --git a/media/libaaudio/libaaudio.map.txt b/media/libaaudio/libaaudio.map.txt
index f22fdfe..1024e1f 100644
--- a/media/libaaudio/libaaudio.map.txt
+++ b/media/libaaudio/libaaudio.map.txt
@@ -24,8 +24,6 @@
AAudioStream_waitForStateChange;
AAudioStream_read;
AAudioStream_write;
- AAudioStream_createThread;
- AAudioStream_joinThread;
AAudioStream_setBufferSizeInFrames;
AAudioStream_getBufferSizeInFrames;
AAudioStream_getFramesPerDataCallback;
diff --git a/media/libaaudio/src/binding/AAudioBinderClient.cpp b/media/libaaudio/src/binding/AAudioBinderClient.cpp
index 8315c40..3f1bba3 100644
--- a/media/libaaudio/src/binding/AAudioBinderClient.cpp
+++ b/media/libaaudio/src/binding/AAudioBinderClient.cpp
@@ -75,6 +75,10 @@
return gAAudioService;
}
+static void dropAAudioService() {
+ Mutex::Autolock _l(gServiceLock);
+ gAAudioService.clear(); // force a reconnect
+}
AAudioBinderClient::AAudioBinderClient()
: AAudioServiceInterface() {}
@@ -88,14 +92,26 @@
*/
aaudio_handle_t AAudioBinderClient::openStream(const AAudioStreamRequest &request,
AAudioStreamConfiguration &configurationOutput) {
+ aaudio_handle_t stream;
+ for (int i = 0; i < 2; i++) {
+ const sp<IAAudioService> &service = getAAudioService();
+ if (service == 0) {
+ return AAUDIO_ERROR_NO_SERVICE;
+ }
- const sp<IAAudioService> &service = getAAudioService();
- if (service == 0) return AAUDIO_ERROR_NO_SERVICE;
- return service->openStream(request, configurationOutput);
+ stream = service->openStream(request, configurationOutput);
+
+ if (stream == AAUDIO_ERROR_NO_SERVICE) {
+ ALOGE("AAudioBinderClient: lost connection to AAudioService.");
+ dropAAudioService(); // force a reconnect
+ } else {
+ break;
+ }
+ }
+ return stream;
}
aaudio_result_t AAudioBinderClient::closeStream(aaudio_handle_t streamHandle) {
-
const sp<IAAudioService> &service = getAAudioService();
if (service == 0) return AAUDIO_ERROR_NO_SERVICE;
return service->closeStream(streamHandle);
@@ -106,37 +122,33 @@
*/
aaudio_result_t AAudioBinderClient::getStreamDescription(aaudio_handle_t streamHandle,
AudioEndpointParcelable &parcelable) {
-
const sp<IAAudioService> &service = getAAudioService();
if (service == 0) return AAUDIO_ERROR_NO_SERVICE;
return service->getStreamDescription(streamHandle, parcelable);
}
-/**
-* Start the flow of data.
-*/
aaudio_result_t AAudioBinderClient::startStream(aaudio_handle_t streamHandle) {
const sp<IAAudioService> &service = getAAudioService();
if (service == 0) return AAUDIO_ERROR_NO_SERVICE;
return service->startStream(streamHandle);
}
-/**
-* Stop the flow of data such that start() can resume without loss of data.
-*/
aaudio_result_t AAudioBinderClient::pauseStream(aaudio_handle_t streamHandle) {
const sp<IAAudioService> &service = getAAudioService();
if (service == 0) return AAUDIO_ERROR_NO_SERVICE;
- return service->startStream(streamHandle);
+ return service->pauseStream(streamHandle);
}
-/**
-* Discard any data held by the underlying HAL or Service.
-*/
+aaudio_result_t AAudioBinderClient::stopStream(aaudio_handle_t streamHandle) {
+ const sp<IAAudioService> &service = getAAudioService();
+ if (service == 0) return AAUDIO_ERROR_NO_SERVICE;
+ return service->stopStream(streamHandle);
+}
+
aaudio_result_t AAudioBinderClient::flushStream(aaudio_handle_t streamHandle) {
const sp<IAAudioService> &service = getAAudioService();
if (service == 0) return AAUDIO_ERROR_NO_SERVICE;
- return service->startStream(streamHandle);
+ return service->flushStream(streamHandle);
}
/**
@@ -163,5 +175,3 @@
clientProcessId,
clientThreadId);
}
-
-
diff --git a/media/libaaudio/src/binding/AAudioBinderClient.h b/media/libaaudio/src/binding/AAudioBinderClient.h
index 1497177..f7f2808 100644
--- a/media/libaaudio/src/binding/AAudioBinderClient.h
+++ b/media/libaaudio/src/binding/AAudioBinderClient.h
@@ -66,6 +66,8 @@
*/
aaudio_result_t pauseStream(aaudio_handle_t streamHandle) override;
+ aaudio_result_t stopStream(aaudio_handle_t streamHandle) override;
+
/**
* Discard any data held by the underlying HAL or Service.
* This is asynchronous. When complete, the service will send a FLUSHED event.
diff --git a/media/libaaudio/src/binding/AAudioServiceDefinitions.h b/media/libaaudio/src/binding/AAudioServiceDefinitions.h
index 0d5bae5..2de560b 100644
--- a/media/libaaudio/src/binding/AAudioServiceDefinitions.h
+++ b/media/libaaudio/src/binding/AAudioServiceDefinitions.h
@@ -35,6 +35,7 @@
GET_STREAM_DESCRIPTION,
START_STREAM,
PAUSE_STREAM,
+ STOP_STREAM,
FLUSH_STREAM,
REGISTER_AUDIO_THREAD,
UNREGISTER_AUDIO_THREAD
diff --git a/media/libaaudio/src/binding/AAudioServiceInterface.h b/media/libaaudio/src/binding/AAudioServiceInterface.h
index 62fd894..b565499 100644
--- a/media/libaaudio/src/binding/AAudioServiceInterface.h
+++ b/media/libaaudio/src/binding/AAudioServiceInterface.h
@@ -63,6 +63,11 @@
virtual aaudio_result_t pauseStream(aaudio_handle_t streamHandle) = 0;
/**
+ * Stop the flow of data after data currently inthe buffer has played.
+ */
+ virtual aaudio_result_t stopStream(aaudio_handle_t streamHandle) = 0;
+
+ /**
* Discard any data held by the underlying HAL or Service.
*/
virtual aaudio_result_t flushStream(aaudio_handle_t streamHandle) = 0;
diff --git a/media/libaaudio/src/binding/AAudioServiceMessage.h b/media/libaaudio/src/binding/AAudioServiceMessage.h
index 19d6d52..d75aa32 100644
--- a/media/libaaudio/src/binding/AAudioServiceMessage.h
+++ b/media/libaaudio/src/binding/AAudioServiceMessage.h
@@ -35,6 +35,7 @@
typedef enum aaudio_service_event_e : uint32_t {
AAUDIO_SERVICE_EVENT_STARTED,
AAUDIO_SERVICE_EVENT_PAUSED,
+ AAUDIO_SERVICE_EVENT_STOPPED,
AAUDIO_SERVICE_EVENT_FLUSHED,
AAUDIO_SERVICE_EVENT_CLOSED,
AAUDIO_SERVICE_EVENT_DISCONNECTED,
diff --git a/media/libaaudio/src/binding/AAudioStreamConfiguration.cpp b/media/libaaudio/src/binding/AAudioStreamConfiguration.cpp
index 5adb477..09eaa42 100644
--- a/media/libaaudio/src/binding/AAudioStreamConfiguration.cpp
+++ b/media/libaaudio/src/binding/AAudioStreamConfiguration.cpp
@@ -43,7 +43,6 @@
status = parcel->writeInt32(mSamplesPerFrame);
if (status != NO_ERROR) goto error;
status = parcel->writeInt32((int32_t) mSharingMode);
- ALOGD("AAudioStreamConfiguration.writeToParcel(): mSharingMode = %d", mSharingMode);
if (status != NO_ERROR) goto error;
status = parcel->writeInt32((int32_t) mAudioFormat);
if (status != NO_ERROR) goto error;
@@ -66,7 +65,6 @@
status = parcel->readInt32(&temp);
if (status != NO_ERROR) goto error;
mSharingMode = (aaudio_sharing_mode_t) temp;
- ALOGD("AAudioStreamConfiguration.readFromParcel(): mSharingMode = %d", mSharingMode);
status = parcel->readInt32(&temp);
if (status != NO_ERROR) goto error;
mAudioFormat = (aaudio_audio_format_t) temp;
@@ -93,8 +91,6 @@
switch (mAudioFormat) {
case AAUDIO_FORMAT_PCM_I16:
case AAUDIO_FORMAT_PCM_FLOAT:
- case AAUDIO_FORMAT_PCM_I8_24:
- case AAUDIO_FORMAT_PCM_I32:
break;
default:
ALOGE("AAudioStreamConfiguration.validate() invalid audioFormat = %d", mAudioFormat);
diff --git a/media/libaaudio/src/binding/AAudioStreamRequest.cpp b/media/libaaudio/src/binding/AAudioStreamRequest.cpp
index ec21f8a..a5c27b9 100644
--- a/media/libaaudio/src/binding/AAudioStreamRequest.cpp
+++ b/media/libaaudio/src/binding/AAudioStreamRequest.cpp
@@ -49,6 +49,10 @@
if (status != NO_ERROR) goto error;
status = parcel->writeInt32((int32_t) mDirection);
if (status != NO_ERROR) goto error;
+
+ status = parcel->writeBool(mSharingModeMatchRequired);
+ if (status != NO_ERROR) goto error;
+
status = mConfiguration.writeToParcel(parcel);
if (status != NO_ERROR) goto error;
return NO_ERROR;
@@ -63,12 +67,18 @@
status_t status = parcel->readInt32(&temp);
if (status != NO_ERROR) goto error;
mUserId = (uid_t) temp;
+
status = parcel->readInt32(&temp);
if (status != NO_ERROR) goto error;
mProcessId = (pid_t) temp;
+
status = parcel->readInt32(&temp);
if (status != NO_ERROR) goto error;
mDirection = (aaudio_direction_t) temp;
+
+ status = parcel->readBool(&mSharingModeMatchRequired);
+ if (status != NO_ERROR) goto error;
+
status = mConfiguration.readFromParcel(parcel);
if (status != NO_ERROR) goto error;
return NO_ERROR;
diff --git a/media/libaaudio/src/binding/AAudioStreamRequest.h b/media/libaaudio/src/binding/AAudioStreamRequest.h
index 992e978..d4bfbe1 100644
--- a/media/libaaudio/src/binding/AAudioStreamRequest.h
+++ b/media/libaaudio/src/binding/AAudioStreamRequest.h
@@ -60,6 +60,15 @@
mDirection = direction;
}
+ bool isSharingModeMatchRequired() const {
+ return mSharingModeMatchRequired;
+ }
+
+ void setSharingModeMatchRequired(bool required) {
+ mSharingModeMatchRequired = required;
+ }
+
+
const AAudioStreamConfiguration &getConstantConfiguration() const {
return mConfiguration;
}
@@ -81,6 +90,7 @@
uid_t mUserId;
pid_t mProcessId;
aaudio_direction_t mDirection;
+ bool mSharingModeMatchRequired = false;
};
} /* namespace aaudio */
diff --git a/media/libaaudio/src/binding/IAAudioService.cpp b/media/libaaudio/src/binding/IAAudioService.cpp
index 03fc088..b8ef611 100644
--- a/media/libaaudio/src/binding/IAAudioService.cpp
+++ b/media/libaaudio/src/binding/IAAudioService.cpp
@@ -45,16 +45,25 @@
Parcel data, reply;
// send command
data.writeInterfaceToken(IAAudioService::getInterfaceDescriptor());
- ALOGE("BpAAudioService::client openStream request dump --------------------");
- request.dump();
+ ALOGV("BpAAudioService::client openStream --------------------");
+ // request.dump();
request.writeToParcel(&data);
status_t err = remote()->transact(OPEN_STREAM, data, &reply);
+ ALOGV("BpAAudioService::client openStream returned %d", err);
if (err != NO_ERROR) {
+ ALOGE("BpAAudioService::client openStream transact failed %d", err);
return AAudioConvert_androidToAAudioResult(err);
}
// parse reply
aaudio_handle_t stream;
- reply.readInt32(&stream);
+ err = reply.readInt32(&stream);
+ if (err != NO_ERROR) {
+ ALOGE("BpAAudioService::client transact(OPEN_STREAM) readInt %d", err);
+ return AAudioConvert_androidToAAudioResult(err);
+ } else if (stream < 0) {
+ ALOGE("BpAAudioService::client OPEN_STREAM passed stream %d", stream);
+ return stream;
+ }
err = configurationOutput.readFromParcel(&reply);
if (err != NO_ERROR) {
ALOGE("BpAAudioService::client openStream readFromParcel failed %d", err);
@@ -71,6 +80,7 @@
data.writeInt32(streamHandle);
status_t err = remote()->transact(CLOSE_STREAM, data, &reply);
if (err != NO_ERROR) {
+ ALOGE("BpAAudioService::client closeStream transact failed %d", err);
return AAudioConvert_androidToAAudioResult(err);
}
// parse reply
@@ -145,6 +155,21 @@
return res;
}
+ virtual aaudio_result_t stopStream(aaudio_handle_t streamHandle) override {
+ Parcel data, reply;
+ // send command
+ data.writeInterfaceToken(IAAudioService::getInterfaceDescriptor());
+ data.writeInt32(streamHandle);
+ status_t err = remote()->transact(STOP_STREAM, data, &reply);
+ if (err != NO_ERROR) {
+ return AAudioConvert_androidToAAudioResult(err);
+ }
+ // parse reply
+ aaudio_result_t res;
+ reply.readInt32(&res);
+ return res;
+ }
+
virtual aaudio_result_t flushStream(aaudio_handle_t streamHandle) override {
Parcel data, reply;
// send command
@@ -226,11 +251,11 @@
case OPEN_STREAM: {
request.readFromParcel(&data);
- ALOGD("BnAAudioService::client openStream request dump --------------------");
- request.dump();
+ //ALOGD("BnAAudioService::client openStream request dump --------------------");
+ //request.dump();
stream = openStream(request, configuration);
- ALOGV("BnAAudioService::onTransact OPEN_STREAM server handle = 0x%08X", stream);
+ //ALOGD("BnAAudioService::onTransact OPEN_STREAM server handle = 0x%08X", stream);
reply->writeInt32(stream);
configuration.writeToParcel(reply);
return NO_ERROR;
@@ -238,18 +263,17 @@
case CLOSE_STREAM: {
data.readInt32(&stream);
- ALOGV("BnAAudioService::onTransact CLOSE_STREAM 0x%08X", stream);
result = closeStream(stream);
+ //ALOGD("BnAAudioService::onTransact CLOSE_STREAM 0x%08X, result = %d",
+ // stream, result);
reply->writeInt32(result);
return NO_ERROR;
} break;
case GET_STREAM_DESCRIPTION: {
data.readInt32(&stream);
- ALOGI("BnAAudioService::onTransact GET_STREAM_DESCRIPTION 0x%08X", stream);
aaudio::AudioEndpointParcelable parcelable;
result = getStreamDescription(stream, parcelable);
- ALOGI("BnAAudioService::onTransact getStreamDescription() returns %d", result);
if (result != AAUDIO_OK) {
return AAudioConvert_aaudioToAndroidStatus(result);
}
@@ -277,7 +301,16 @@
data.readInt32(&stream);
result = pauseStream(stream);
ALOGV("BnAAudioService::onTransact PAUSE_STREAM 0x%08X, result = %d",
- stream, result);
+ stream, result);
+ reply->writeInt32(result);
+ return NO_ERROR;
+ } break;
+
+ case STOP_STREAM: {
+ data.readInt32(&stream);
+ result = stopStream(stream);
+ ALOGV("BnAAudioService::onTransact STOP_STREAM 0x%08X, result = %d",
+ stream, result);
reply->writeInt32(result);
return NO_ERROR;
} break;
diff --git a/media/libaaudio/src/binding/IAAudioService.h b/media/libaaudio/src/binding/IAAudioService.h
index ab7fd1b..2cee651 100644
--- a/media/libaaudio/src/binding/IAAudioService.h
+++ b/media/libaaudio/src/binding/IAAudioService.h
@@ -69,6 +69,12 @@
virtual aaudio_result_t pauseStream(aaudio::aaudio_handle_t streamHandle) = 0;
/**
+ * Stop the flow of data such that the data currently in the buffer is played.
+ * This is asynchronous. When complete, the service will send a STOPPED event.
+ */
+ virtual aaudio_result_t stopStream(aaudio::aaudio_handle_t streamHandle) = 0;
+
+ /**
* Discard any data held by the underlying HAL or Service.
* This is asynchronous. When complete, the service will send a FLUSHED event.
*/
diff --git a/media/libaaudio/src/binding/SharedMemoryParcelable.cpp b/media/libaaudio/src/binding/SharedMemoryParcelable.cpp
index 649c884..0f501dd 100644
--- a/media/libaaudio/src/binding/SharedMemoryParcelable.cpp
+++ b/media/libaaudio/src/binding/SharedMemoryParcelable.cpp
@@ -61,9 +61,8 @@
return status;
}
if (mSizeInBytes > 0) {
-// FIXME mFd = dup(parcel->readFileDescriptor());
- // Why is the ALSA resource not getting freed?!
- mFd = fcntl(parcel->readFileDescriptor(), F_DUPFD_CLOEXEC, 0);
+ int originalFD = parcel->readFileDescriptor();
+ mFd = fcntl(originalFD, F_DUPFD_CLOEXEC, 0);
if (mFd == -1) {
status = -errno;
ALOGE("SharedMemoryParcelable readFileDescriptor fcntl() failed : %d", status);
@@ -101,11 +100,6 @@
return AAUDIO_ERROR_OUT_OF_RANGE;
}
if (mResolvedAddress == nullptr) {
- /* TODO remove
- int fd = fcntl(mFd, F_DUPFD_CLOEXEC, 0);
- ALOGE_IF(fd==-1, "cannot dup fd=%d, size=%zd, (%s)",
- mFd, mSizeInBytes, strerror(errno));
- */
mResolvedAddress = (uint8_t *) mmap(0, mSizeInBytes, PROT_READ|PROT_WRITE,
MAP_SHARED, mFd, 0);
if (mResolvedAddress == nullptr) {
diff --git a/media/libaaudio/src/client/AudioEndpoint.cpp b/media/libaaudio/src/client/AudioEndpoint.cpp
index fe049b2..6f87df6 100644
--- a/media/libaaudio/src/client/AudioEndpoint.cpp
+++ b/media/libaaudio/src/client/AudioEndpoint.cpp
@@ -59,35 +59,35 @@
ALOGE("AudioEndpoint_validateQueueDescriptor() NULL dataAddress");
return AAUDIO_ERROR_NULL;
}
- ALOGD("AudioEndpoint_validateQueueDescriptor %s, dataAddress at %p ====================",
+ ALOGV("AudioEndpoint_validateQueueDescriptor %s, dataAddress at %p ====================",
type,
descriptor->dataAddress);
- ALOGD("AudioEndpoint_validateQueueDescriptor readCounter at %p, writeCounter at %p",
+ ALOGV("AudioEndpoint_validateQueueDescriptor readCounter at %p, writeCounter at %p",
descriptor->readCounterAddress,
descriptor->writeCounterAddress);
// Try to READ from the data area.
// This code will crash if the mmap failed.
uint8_t value = descriptor->dataAddress[0];
- ALOGD("AudioEndpoint_validateQueueDescriptor() dataAddress[0] = %d, then try to write",
+ ALOGV("AudioEndpoint_validateQueueDescriptor() dataAddress[0] = %d, then try to write",
(int) value);
// Try to WRITE to the data area.
descriptor->dataAddress[0] = value * 3;
- ALOGD("AudioEndpoint_validateQueueDescriptor() wrote successfully");
+ ALOGV("AudioEndpoint_validateQueueDescriptor() wrote successfully");
if (descriptor->readCounterAddress) {
fifo_counter_t counter = *descriptor->readCounterAddress;
- ALOGD("AudioEndpoint_validateQueueDescriptor() *readCounterAddress = %d, now write",
+ ALOGV("AudioEndpoint_validateQueueDescriptor() *readCounterAddress = %d, now write",
(int) counter);
*descriptor->readCounterAddress = counter;
- ALOGD("AudioEndpoint_validateQueueDescriptor() wrote readCounterAddress successfully");
+ ALOGV("AudioEndpoint_validateQueueDescriptor() wrote readCounterAddress successfully");
}
if (descriptor->writeCounterAddress) {
fifo_counter_t counter = *descriptor->writeCounterAddress;
- ALOGD("AudioEndpoint_validateQueueDescriptor() *writeCounterAddress = %d, now write",
+ ALOGV("AudioEndpoint_validateQueueDescriptor() *writeCounterAddress = %d, now write",
(int) counter);
*descriptor->writeCounterAddress = counter;
- ALOGD("AudioEndpoint_validateQueueDescriptor() wrote writeCounterAddress successfully");
+ ALOGV("AudioEndpoint_validateQueueDescriptor() wrote writeCounterAddress successfully");
}
return AAUDIO_OK;
}
@@ -107,7 +107,7 @@
// TODO maybe remove after debugging
aaudio_result_t result = AudioEndpoint_validateDescriptor(pEndpointDescriptor);
if (result != AAUDIO_OK) {
- ALOGD("AudioEndpoint_validateQueueDescriptor returned %d %s",
+ ALOGE("AudioEndpoint_validateQueueDescriptor returned %d %s",
result, AAudio_convertResultToText(result));
return result;
}
@@ -142,10 +142,10 @@
assert(descriptor->framesPerBurst > 0);
assert(descriptor->framesPerBurst < 8 * 1024); // FIXME just for initial debugging
assert(descriptor->dataAddress != nullptr);
- ALOGD("AudioEndpoint::configure() data framesPerBurst = %d", descriptor->framesPerBurst);
- ALOGD("AudioEndpoint::configure() data readCounterAddress = %p", descriptor->readCounterAddress);
+ ALOGV("AudioEndpoint::configure() data framesPerBurst = %d", descriptor->framesPerBurst);
+ ALOGV("AudioEndpoint::configure() data readCounterAddress = %p", descriptor->readCounterAddress);
mOutputFreeRunning = descriptor->readCounterAddress == nullptr;
- ALOGD("AudioEndpoint::configure() mOutputFreeRunning = %d", mOutputFreeRunning ? 1 : 0);
+ ALOGV("AudioEndpoint::configure() mOutputFreeRunning = %d", mOutputFreeRunning ? 1 : 0);
int64_t *readCounterAddress = (descriptor->readCounterAddress == nullptr)
? &mDataReadCounter
: descriptor->readCounterAddress;
diff --git a/media/libaaudio/src/client/AudioStreamInternal.cpp b/media/libaaudio/src/client/AudioStreamInternal.cpp
index 7304205..af4b93a 100644
--- a/media/libaaudio/src/client/AudioStreamInternal.cpp
+++ b/media/libaaudio/src/client/AudioStreamInternal.cpp
@@ -40,9 +40,6 @@
#define LOG_TIMESTAMPS 0
using android::String16;
-using android::IServiceManager;
-using android::defaultServiceManager;
-using android::interface_cast;
using android::Mutex;
using android::WrappingBuffer;
@@ -53,7 +50,10 @@
// Wait at least this many times longer than the operation should take.
#define MIN_TIMEOUT_OPERATIONS 4
-#define ALOG_CONDITION (mInService == false)
+//static int64_t s_logCounter = 0;
+//#define MYLOG_CONDITION (mInService == true && s_logCounter++ < 500)
+//#define MYLOG_CONDITION (s_logCounter++ < 500000)
+#define MYLOG_CONDITION (1)
AudioStreamInternal::AudioStreamInternal(AAudioServiceInterface &serviceInterface, bool inService)
: AudioStream()
@@ -62,8 +62,7 @@
, mServiceStreamHandle(AAUDIO_HANDLE_INVALID)
, mFramesPerBurst(16)
, mServiceInterface(serviceInterface)
- , mInService(inService)
-{
+ , mInService(inService) {
}
AudioStreamInternal::~AudioStreamInternal() {
@@ -84,27 +83,26 @@
if (getFormat() == AAUDIO_UNSPECIFIED) {
setFormat(AAUDIO_FORMAT_PCM_FLOAT);
}
+ // Request FLOAT for the shared mixer.
+ request.getConfiguration().setAudioFormat(AAUDIO_FORMAT_PCM_FLOAT);
// Build the request to send to the server.
request.setUserId(getuid());
request.setProcessId(getpid());
request.setDirection(getDirection());
+ request.setSharingModeMatchRequired(isSharingModeMatchRequired());
request.getConfiguration().setDeviceId(getDeviceId());
request.getConfiguration().setSampleRate(getSampleRate());
request.getConfiguration().setSamplesPerFrame(getSamplesPerFrame());
- request.getConfiguration().setAudioFormat(getFormat());
- aaudio_sharing_mode_t sharingMode = getSharingMode();
- ALOGE("AudioStreamInternal.open(): sharingMode %d", sharingMode);
- request.getConfiguration().setSharingMode(sharingMode);
+ request.getConfiguration().setSharingMode(getSharingMode());
+
request.getConfiguration().setBufferCapacity(builder.getBufferCapacity());
mServiceStreamHandle = mServiceInterface.openStream(request, configuration);
- ALOGD_IF(ALOG_CONDITION, "AudioStreamInternal.open(): openStream returned mServiceStreamHandle = 0x%08X",
- (unsigned int)mServiceStreamHandle);
if (mServiceStreamHandle < 0) {
result = mServiceStreamHandle;
- ALOGE("AudioStreamInternal.open(): openStream() returned %d", result);
+ ALOGE("AudioStreamInternal.open(): %s openStream() returned %d", getLocationName(), result);
} else {
result = configuration.validate();
if (result != AAUDIO_OK) {
@@ -120,10 +118,9 @@
mDeviceFormat = configuration.getAudioFormat();
result = mServiceInterface.getStreamDescription(mServiceStreamHandle, mEndPointParcelable);
- ALOGD_IF(ALOG_CONDITION, "AudioStreamInternal.open(): getStreamDescriptor(0x%08X) returns %d",
- mServiceStreamHandle, result);
if (result != AAUDIO_OK) {
- ALOGE("AudioStreamInternal.open(): getStreamDescriptor returns %d", result);
+ ALOGE("AudioStreamInternal.open(): %s getStreamDescriptor returns %d",
+ getLocationName(), result);
mServiceInterface.closeStream(mServiceStreamHandle);
return result;
}
@@ -140,8 +137,19 @@
mAudioEndpoint.configure(&mEndpointDescriptor);
mFramesPerBurst = mEndpointDescriptor.downDataQueueDescriptor.framesPerBurst;
- assert(mFramesPerBurst >= 16);
- assert(mEndpointDescriptor.downDataQueueDescriptor.capacityInFrames < 10 * 1024);
+ int32_t capacity = mEndpointDescriptor.downDataQueueDescriptor.capacityInFrames;
+
+ ALOGD_IF(MYLOG_CONDITION, "AudioStreamInternal.open() %s framesPerBurst = %d, capacity = %d",
+ getLocationName(), mFramesPerBurst, capacity);
+ // Validate result from server.
+ if (mFramesPerBurst < 16 || mFramesPerBurst > 16 * 1024) {
+ ALOGE("AudioStream::open(): framesPerBurst out of range = %d", mFramesPerBurst);
+ return AAUDIO_ERROR_OUT_OF_RANGE;
+ }
+ if (capacity < mFramesPerBurst || capacity > 32 * 1024) {
+ ALOGE("AudioStream::open(): bufferCapacity out of range = %d", capacity);
+ return AAUDIO_ERROR_OUT_OF_RANGE;
+ }
mClockModel.setSampleRate(getSampleRate());
mClockModel.setFramesPerBurst(mFramesPerBurst);
@@ -149,7 +157,8 @@
if (getDataCallbackProc()) {
mCallbackFrames = builder.getFramesPerDataCallback();
if (mCallbackFrames > getBufferCapacity() / 2) {
- ALOGE("AudioStreamInternal.open(): framesPerCallback too large");
+ ALOGE("AudioStreamInternal.open(): framesPerCallback too large = %d, capacity = %d",
+ mCallbackFrames, getBufferCapacity());
mServiceInterface.closeStream(mServiceStreamHandle);
return AAUDIO_ERROR_OUT_OF_RANGE;
@@ -175,7 +184,8 @@
}
aaudio_result_t AudioStreamInternal::close() {
- ALOGD_IF(ALOG_CONDITION, "AudioStreamInternal.close(): mServiceStreamHandle = 0x%08X", mServiceStreamHandle);
+ ALOGD_IF(MYLOG_CONDITION, "AudioStreamInternal.close(): mServiceStreamHandle = 0x%08X",
+ mServiceStreamHandle);
if (mServiceStreamHandle != AAUDIO_HANDLE_INVALID) {
aaudio_handle_t serviceStreamHandle = mServiceStreamHandle;
mServiceStreamHandle = AAUDIO_HANDLE_INVALID;
@@ -250,7 +260,7 @@
aaudio_result_t AudioStreamInternal::requestStart()
{
int64_t startTime;
- ALOGD_IF(ALOG_CONDITION, "AudioStreamInternal(): start()");
+ ALOGD_IF(MYLOG_CONDITION, "AudioStreamInternal(): start()");
if (mServiceStreamHandle == AAUDIO_HANDLE_INVALID) {
return AAUDIO_ERROR_INVALID_STATE;
}
@@ -275,8 +285,10 @@
int64_t AudioStreamInternal::calculateReasonableTimeout(int32_t framesPerOperation) {
// Wait for at least a second or some number of callbacks to join the thread.
- int64_t timeoutNanoseconds = (MIN_TIMEOUT_OPERATIONS * framesPerOperation * AAUDIO_NANOS_PER_SECOND)
- / getSampleRate();
+ int64_t timeoutNanoseconds = (MIN_TIMEOUT_OPERATIONS
+ * framesPerOperation
+ * AAUDIO_NANOS_PER_SECOND)
+ / getSampleRate();
if (timeoutNanoseconds < MIN_TIMEOUT_NANOS) { // arbitrary number of seconds
timeoutNanoseconds = MIN_TIMEOUT_NANOS;
}
@@ -295,28 +307,34 @@
aaudio_result_t AudioStreamInternal::requestPauseInternal()
{
- ALOGD("AudioStreamInternal(): pause()");
if (mServiceStreamHandle == AAUDIO_HANDLE_INVALID) {
+ ALOGE("AudioStreamInternal(): requestPauseInternal() mServiceStreamHandle invalid = 0x%08X",
+ mServiceStreamHandle);
return AAUDIO_ERROR_INVALID_STATE;
}
mClockModel.stop(AudioClock::getNanoseconds());
setState(AAUDIO_STREAM_STATE_PAUSING);
- return mServiceInterface.startStream(mServiceStreamHandle);
+ return mServiceInterface.pauseStream(mServiceStreamHandle);
}
aaudio_result_t AudioStreamInternal::requestPause()
{
+ ALOGD_IF(MYLOG_CONDITION, "AudioStreamInternal(): %s requestPause()", getLocationName());
aaudio_result_t result = stopCallback();
if (result != AAUDIO_OK) {
return result;
}
- return requestPauseInternal();
+ result = requestPauseInternal();
+ ALOGD("AudioStreamInternal(): requestPause() returns %d", result);
+ return result;
}
aaudio_result_t AudioStreamInternal::requestFlush() {
- ALOGD_IF(ALOG_CONDITION, "AudioStreamInternal(): flush()");
+ ALOGD_IF(MYLOG_CONDITION, "AudioStreamInternal(): requestFlush()");
if (mServiceStreamHandle == AAUDIO_HANDLE_INVALID) {
+ ALOGE("AudioStreamInternal(): requestFlush() mServiceStreamHandle invalid = 0x%08X",
+ mServiceStreamHandle);
return AAUDIO_ERROR_INVALID_STATE;
}
@@ -325,35 +343,45 @@
}
void AudioStreamInternal::onFlushFromServer() {
- ALOGD_IF(ALOG_CONDITION, "AudioStreamInternal(): onFlushFromServer()");
+ ALOGD_IF(MYLOG_CONDITION, "AudioStreamInternal(): onFlushFromServer()");
int64_t readCounter = mAudioEndpoint.getDownDataReadCounter();
int64_t writeCounter = mAudioEndpoint.getDownDataWriteCounter();
+
// Bump offset so caller does not see the retrograde motion in getFramesRead().
int64_t framesFlushed = writeCounter - readCounter;
mFramesOffsetFromService += framesFlushed;
+
// Flush written frames by forcing writeCounter to readCounter.
// This is because we cannot move the read counter in the hardware.
mAudioEndpoint.setDownDataWriteCounter(readCounter);
}
+aaudio_result_t AudioStreamInternal::requestStopInternal()
+{
+ if (mServiceStreamHandle == AAUDIO_HANDLE_INVALID) {
+ ALOGE("AudioStreamInternal(): requestStopInternal() mServiceStreamHandle invalid = 0x%08X",
+ mServiceStreamHandle);
+ return AAUDIO_ERROR_INVALID_STATE;
+ }
+
+ mClockModel.stop(AudioClock::getNanoseconds());
+ setState(AAUDIO_STREAM_STATE_STOPPING);
+ return mServiceInterface.stopStream(mServiceStreamHandle);
+}
+
aaudio_result_t AudioStreamInternal::requestStop()
{
- // TODO better implementation of requestStop()
- aaudio_result_t result = requestPause();
- if (result == AAUDIO_OK) {
- aaudio_stream_state_t state;
- result = waitForStateChange(AAUDIO_STREAM_STATE_PAUSING,
- &state,
- 500 * AAUDIO_NANOS_PER_MILLISECOND);// TODO temporary code
- if (result == AAUDIO_OK) {
- result = requestFlush();
- }
+ ALOGD_IF(MYLOG_CONDITION, "AudioStreamInternal(): %s requestStop()", getLocationName());
+ aaudio_result_t result = stopCallback();
+ if (result != AAUDIO_OK) {
+ return result;
}
+ result = requestStopInternal();
+ ALOGD("AudioStreamInternal(): requestStop() returns %d", result);
return result;
}
aaudio_result_t AudioStreamInternal::registerThread() {
- ALOGD_IF(ALOG_CONDITION, "AudioStreamInternal(): registerThread()");
if (mServiceStreamHandle == AAUDIO_HANDLE_INVALID) {
return AAUDIO_ERROR_INVALID_STATE;
}
@@ -364,7 +392,6 @@
}
aaudio_result_t AudioStreamInternal::unregisterThread() {
- ALOGD_IF(ALOG_CONDITION, "AudioStreamInternal(): unregisterThread()");
if (mServiceStreamHandle == AAUDIO_HANDLE_INVALID) {
return AAUDIO_ERROR_INVALID_STATE;
}
@@ -394,16 +421,16 @@
static int64_t oldTime = 0;
int64_t framePosition = command.timestamp.position;
int64_t nanoTime = command.timestamp.timestamp;
- ALOGD_IF(ALOG_CONDITION, "AudioStreamInternal() timestamp says framePosition = %08lld at nanoTime %llu",
+ ALOGD_IF(MYLOG_CONDITION, "AudioStreamInternal() timestamp says framePosition = %08lld at nanoTime %llu",
(long long) framePosition,
(long long) nanoTime);
int64_t nanosDelta = nanoTime - oldTime;
if (nanosDelta > 0 && oldTime > 0) {
int64_t framesDelta = framePosition - oldPosition;
int64_t rate = (framesDelta * AAUDIO_NANOS_PER_SECOND) / nanosDelta;
- ALOGD_IF(ALOG_CONDITION, "AudioStreamInternal() - framesDelta = %08lld", (long long) framesDelta);
- ALOGD_IF(ALOG_CONDITION, "AudioStreamInternal() - nanosDelta = %08lld", (long long) nanosDelta);
- ALOGD_IF(ALOG_CONDITION, "AudioStreamInternal() - measured rate = %llu", (unsigned long long) rate);
+ ALOGD_IF(MYLOG_CONDITION, "AudioStreamInternal() - framesDelta = %08lld", (long long) framesDelta);
+ ALOGD_IF(MYLOG_CONDITION, "AudioStreamInternal() - nanosDelta = %08lld", (long long) nanosDelta);
+ ALOGD_IF(MYLOG_CONDITION, "AudioStreamInternal() - measured rate = %llu", (unsigned long long) rate);
}
oldPosition = framePosition;
oldTime = nanoTime;
@@ -422,23 +449,27 @@
aaudio_result_t AudioStreamInternal::onEventFromServer(AAudioServiceMessage *message) {
aaudio_result_t result = AAUDIO_OK;
- ALOGD_IF(ALOG_CONDITION, "processCommands() got event %d", message->event.event);
+ ALOGD_IF(MYLOG_CONDITION, "processCommands() got event %d", message->event.event);
switch (message->event.event) {
case AAUDIO_SERVICE_EVENT_STARTED:
- ALOGD_IF(ALOG_CONDITION, "processCommands() got AAUDIO_SERVICE_EVENT_STARTED");
+ ALOGD_IF(MYLOG_CONDITION, "processCommands() got AAUDIO_SERVICE_EVENT_STARTED");
setState(AAUDIO_STREAM_STATE_STARTED);
break;
case AAUDIO_SERVICE_EVENT_PAUSED:
- ALOGD_IF(ALOG_CONDITION, "processCommands() got AAUDIO_SERVICE_EVENT_PAUSED");
+ ALOGD_IF(MYLOG_CONDITION, "processCommands() got AAUDIO_SERVICE_EVENT_PAUSED");
setState(AAUDIO_STREAM_STATE_PAUSED);
break;
+ case AAUDIO_SERVICE_EVENT_STOPPED:
+ ALOGD_IF(MYLOG_CONDITION, "processCommands() got AAUDIO_SERVICE_EVENT_STOPPED");
+ setState(AAUDIO_STREAM_STATE_STOPPED);
+ break;
case AAUDIO_SERVICE_EVENT_FLUSHED:
- ALOGD_IF(ALOG_CONDITION, "processCommands() got AAUDIO_SERVICE_EVENT_FLUSHED");
+ ALOGD_IF(MYLOG_CONDITION, "processCommands() got AAUDIO_SERVICE_EVENT_FLUSHED");
setState(AAUDIO_STREAM_STATE_FLUSHED);
onFlushFromServer();
break;
case AAUDIO_SERVICE_EVENT_CLOSED:
- ALOGD_IF(ALOG_CONDITION, "processCommands() got AAUDIO_SERVICE_EVENT_CLOSED");
+ ALOGD_IF(MYLOG_CONDITION, "processCommands() got AAUDIO_SERVICE_EVENT_CLOSED");
setState(AAUDIO_STREAM_STATE_CLOSED);
break;
case AAUDIO_SERVICE_EVENT_DISCONNECTED:
@@ -448,7 +479,7 @@
break;
case AAUDIO_SERVICE_EVENT_VOLUME:
mVolume = message->event.dataDouble;
- ALOGD_IF(ALOG_CONDITION, "processCommands() AAUDIO_SERVICE_EVENT_VOLUME %f", mVolume);
+ ALOGD_IF(MYLOG_CONDITION, "processCommands() AAUDIO_SERVICE_EVENT_VOLUME %f", mVolume);
break;
default:
ALOGW("WARNING - processCommands() Unrecognized event = %d",
@@ -463,7 +494,7 @@
aaudio_result_t result = AAUDIO_OK;
while (result == AAUDIO_OK) {
- //ALOGD_IF(ALOG_CONDITION, "AudioStreamInternal::processCommands() - looping, %d", result);
+ //ALOGD_IF(MYLOG_CONDITION, "AudioStreamInternal::processCommands() - looping, %d", result);
AAudioServiceMessage message;
if (mAudioEndpoint.readUpCommand(&message) != 1) {
break; // no command this time, no problem
@@ -478,7 +509,7 @@
break;
default:
- ALOGW("WARNING - AudioStreamInternal::processCommands() Unrecognized what = %d",
+ ALOGE("WARNING - AudioStreamInternal::processCommands() Unrecognized what = %d",
(int) message.what);
result = AAUDIO_ERROR_UNEXPECTED_VALUE;
break;
@@ -497,19 +528,13 @@
int64_t currentTimeNanos = AudioClock::getNanoseconds();
int64_t deadlineNanos = currentTimeNanos + timeoutNanoseconds;
int32_t framesLeft = numFrames;
- //ALOGD_IF(ALOG_CONDITION, "AudioStreamInternal::write(%p, %d) at time %08llu , mState = %s",
- // buffer, numFrames, (unsigned long long) currentTimeNanos,
- // AAudio_convertStreamStateToText(getState()));
// Write until all the data has been written or until a timeout occurs.
while (framesLeft > 0) {
- //ALOGD_IF(ALOG_CONDITION, "AudioStreamInternal::write() loop: framesLeft = %d, loopCount = %d =====",
- // framesLeft, loopCount++);
// The call to writeNow() will not block. It will just write as much as it can.
int64_t wakeTimeNanos = 0;
aaudio_result_t framesWritten = writeNow(source, framesLeft,
currentTimeNanos, &wakeTimeNanos);
- //ALOGD_IF(ALOG_CONDITION, "AudioStreamInternal::write() loop: framesWritten = %d", framesWritten);
if (framesWritten < 0) {
ALOGE("AudioStreamInternal::write() loop: writeNow returned %d", framesWritten);
result = framesWritten;
@@ -522,7 +547,6 @@
if (timeoutNanoseconds == 0) {
break; // don't block
} else if (framesLeft > 0) {
- //ALOGD_IF(ALOG_CONDITION, "AudioStreamInternal:: original wakeTimeNanos %lld", (long long) wakeTimeNanos);
// clip the wake time to something reasonable
if (wakeTimeNanos < currentTimeNanos) {
wakeTimeNanos = currentTimeNanos;
@@ -534,16 +558,13 @@
break;
}
- //ALOGD_IF(ALOG_CONDITION, "AudioStreamInternal:: sleep until %lld, dur = %lld", (long long) wakeTimeNanos,
- // (long long) (wakeTimeNanos - currentTimeNanos));
- AudioClock::sleepForNanos(wakeTimeNanos - currentTimeNanos);
+ int64_t sleepForNanos = wakeTimeNanos - currentTimeNanos;
+ AudioClock::sleepForNanos(sleepForNanos);
currentTimeNanos = AudioClock::getNanoseconds();
}
}
// return error or framesWritten
- //ALOGD_IF(ALOG_CONDITION, "AudioStreamInternal::write() result = %d, framesLeft = %d, #%d",
- // result, framesLeft, loopCount);
(void) loopCount;
return (result < 0) ? result : numFrames - framesLeft;
}
@@ -552,17 +573,15 @@
aaudio_result_t AudioStreamInternal::writeNow(const void *buffer, int32_t numFrames,
int64_t currentNanoTime, int64_t *wakeTimePtr) {
- //ALOGD_IF(ALOG_CONDITION, "AudioStreamInternal::writeNow(%p) - enter", buffer);
{
aaudio_result_t result = processCommands();
- //ALOGD_IF(ALOG_CONDITION, "AudioStreamInternal::writeNow() - processCommands() returned %d", result);
if (result != AAUDIO_OK) {
return result;
}
}
if (mAudioEndpoint.isOutputFreeRunning()) {
- ALOGD_IF(ALOG_CONDITION, "AudioStreamInternal::writeNow() - update read counter");
+ //ALOGD_IF(MYLOG_CONDITION, "AudioStreamInternal::writeNow() - update read counter");
// Update data queue based on the timing model.
int64_t estimatedReadCounter = mClockModel.convertTimeToPosition(currentNanoTime);
mAudioEndpoint.setDownDataReadCounter(estimatedReadCounter);
@@ -575,9 +594,9 @@
}
// Write some data to the buffer.
- //ALOGD_IF(ALOG_CONDITION, "AudioStreamInternal::writeNow() - writeNowWithConversion(%d)", numFrames);
+ //ALOGD_IF(MYLOG_CONDITION, "AudioStreamInternal::writeNow() - writeNowWithConversion(%d)", numFrames);
int32_t framesWritten = writeNowWithConversion(buffer, numFrames);
- //ALOGD_IF(ALOG_CONDITION, "AudioStreamInternal::writeNow() - tried to write %d frames, wrote %d",
+ //ALOGD_IF(MYLOG_CONDITION, "AudioStreamInternal::writeNow() - tried to write %d frames, wrote %d",
// numFrames, framesWritten);
// Calculate an ideal time to wake up.
@@ -585,7 +604,7 @@
// By default wake up a few milliseconds from now. // TODO review
int64_t wakeTime = currentNanoTime + (1 * AAUDIO_NANOS_PER_MILLISECOND);
aaudio_stream_state_t state = getState();
- //ALOGD_IF(ALOG_CONDITION, "AudioStreamInternal::writeNow() - wakeTime based on %s",
+ //ALOGD_IF(MYLOG_CONDITION, "AudioStreamInternal::writeNow() - wakeTime based on %s",
// AAudio_convertStreamStateToText(state));
switch (state) {
case AAUDIO_STREAM_STATE_OPEN:
@@ -612,7 +631,7 @@
*wakeTimePtr = wakeTime;
}
-// ALOGD_IF(ALOG_CONDITION, "AudioStreamInternal::writeNow finished: now = %llu, read# = %llu, wrote# = %llu",
+// ALOGD_IF(MYLOG_CONDITION, "AudioStreamInternal::writeNow finished: now = %llu, read# = %llu, wrote# = %llu",
// (unsigned long long)currentNanoTime,
// (unsigned long long)mAudioEndpoint.getDownDataReadCounter(),
// (unsigned long long)mAudioEndpoint.getDownDataWriteCounter());
@@ -623,9 +642,8 @@
// TODO this function needs a major cleanup.
aaudio_result_t AudioStreamInternal::writeNowWithConversion(const void *buffer,
int32_t numFrames) {
- // ALOGD_IF(ALOG_CONDITION, "AudioStreamInternal::writeNowWithConversion(%p, %d)", buffer, numFrames);
+ // ALOGD_IF(MYLOG_CONDITION, "AudioStreamInternal::writeNowWithConversion(%p, %d)", buffer, numFrames);
WrappingBuffer wrappingBuffer;
- mAudioEndpoint.getEmptyRoomAvailable(&wrappingBuffer);
uint8_t *source = (uint8_t *) buffer;
int32_t framesLeft = numFrames;
@@ -640,18 +658,25 @@
if (framesToWrite > framesAvailable) {
framesToWrite = framesAvailable;
}
- int32_t numBytes = getBytesPerFrame();
+ int32_t numBytes = getBytesPerFrame() * framesToWrite;
// TODO handle volume scaling
if (getFormat() == mDeviceFormat) {
// Copy straight through.
memcpy(wrappingBuffer.data[partIndex], source, numBytes);
} else if (getFormat() == AAUDIO_FORMAT_PCM_FLOAT
- && mDeviceFormat == AAUDIO_FORMAT_PCM_I16) {
+ && mDeviceFormat == AAUDIO_FORMAT_PCM_I16) {
// Data conversion.
AAudioConvert_floatToPcm16(
(const float *) source,
framesToWrite * getSamplesPerFrame(),
(int16_t *) wrappingBuffer.data[partIndex]);
+ } else if (getFormat() == AAUDIO_FORMAT_PCM_I16
+ && mDeviceFormat == AAUDIO_FORMAT_PCM_FLOAT) {
+ // Data conversion.
+ AAudioConvert_pcm16ToFloat(
+ (const int16_t *) source,
+ framesToWrite * getSamplesPerFrame(),
+ (float *) wrappingBuffer.data[partIndex]);
} else {
// TODO handle more conversions
ALOGE("AudioStreamInternal::writeNowWithConversion() unsupported formats: %d, %d",
@@ -661,6 +686,8 @@
source += numBytes;
framesLeft -= framesToWrite;
+ } else {
+ break;
}
partIndex++;
}
@@ -670,7 +697,7 @@
if (framesWritten > 0) {
incrementFramesWritten(framesWritten);
}
- // ALOGD_IF(ALOG_CONDITION, "AudioStreamInternal::writeNowWithConversion() returns %d", framesWritten);
+ // ALOGD_IF(MYLOG_CONDITION, "AudioStreamInternal::writeNowWithConversion() returns %d", framesWritten);
return framesWritten;
}
@@ -680,7 +707,15 @@
aaudio_result_t AudioStreamInternal::setBufferSize(int32_t requestedFrames) {
int32_t actualFrames = 0;
+ // Round to the next highest burst size.
+ if (getFramesPerBurst() > 0) {
+ int32_t numBursts = (requestedFrames + getFramesPerBurst() - 1) / getFramesPerBurst();
+ requestedFrames = numBursts * getFramesPerBurst();
+ }
+
aaudio_result_t result = mAudioEndpoint.setBufferSizeInFrames(requestedFrames, &actualFrames);
+ ALOGD_IF(MYLOG_CONDITION, "AudioStreamInternal::setBufferSize() %s req = %d => %d",
+ getLocationName(), requestedFrames, actualFrames);
if (result < 0) {
return result;
} else {
@@ -714,7 +749,7 @@
} else {
mLastFramesRead = framesRead;
}
- ALOGD_IF(ALOG_CONDITION, "AudioStreamInternal::getFramesRead() returns %lld", (long long)framesRead);
+ ALOGD_IF(MYLOG_CONDITION, "AudioStreamInternal::getFramesRead() returns %lld", (long long)framesRead);
return framesRead;
}
diff --git a/media/libaaudio/src/client/AudioStreamInternal.h b/media/libaaudio/src/client/AudioStreamInternal.h
index 1aa3b0f..8244311 100644
--- a/media/libaaudio/src/client/AudioStreamInternal.h
+++ b/media/libaaudio/src/client/AudioStreamInternal.h
@@ -94,6 +94,7 @@
aaudio_result_t processCommands();
aaudio_result_t requestPauseInternal();
+ aaudio_result_t requestStopInternal();
aaudio_result_t stopCallback();
@@ -129,6 +130,11 @@
int32_t numFrames);
void processTimestamp(uint64_t position, int64_t time);
+
+ const char *getLocationName() const {
+ return mInService ? "SERVICE" : "CLIENT";
+ }
+
// Adjust timing model based on timestamp from service.
IsochronousClockModel mClockModel; // timing model for chasing the HAL
diff --git a/media/libaaudio/src/client/IsochronousClockModel.cpp b/media/libaaudio/src/client/IsochronousClockModel.cpp
index c278c8b..21e3e70 100644
--- a/media/libaaudio/src/client/IsochronousClockModel.cpp
+++ b/media/libaaudio/src/client/IsochronousClockModel.cpp
@@ -101,13 +101,13 @@
// or we may be drifting due to a slow HW clock.
mMarkerFramePosition = framePosition;
mMarkerNanoTime = nanoTime;
- ALOGI("processTimestamp() - STATE_RUNNING - %d < %d micros - EARLY",
+ ALOGV("processTimestamp() - STATE_RUNNING - %d < %d micros - EARLY",
(int) (nanosDelta / 1000), (int)(expectedNanosDelta / 1000));
} else if (nanosDelta > (expectedNanosDelta + mMaxLatenessInNanos)) {
// Later than expected timestamp.
mMarkerFramePosition = framePosition;
mMarkerNanoTime = nanoTime - mMaxLatenessInNanos;
- ALOGI("processTimestamp() - STATE_RUNNING - %d > %d + %d micros - LATE",
+ ALOGV("processTimestamp() - STATE_RUNNING - %d > %d + %d micros - LATE",
(int) (nanosDelta / 1000), (int)(expectedNanosDelta / 1000),
(int) (mMaxLatenessInNanos / 1000));
}
diff --git a/media/libaaudio/src/core/AAudioAudio.cpp b/media/libaaudio/src/core/AAudioAudio.cpp
index b17309c..97726e6 100644
--- a/media/libaaudio/src/core/AAudioAudio.cpp
+++ b/media/libaaudio/src/core/AAudioAudio.cpp
@@ -168,16 +168,15 @@
void *userData)
{
AudioStreamBuilder *streamBuilder = convertAAudioBuilderToStreamBuilder(builder);
- ALOGD("AAudioStreamBuilder_setCallback(): userData = %p", userData);
streamBuilder->setDataCallbackProc(callback);
streamBuilder->setDataCallbackUserData(userData);
}
+
AAUDIO_API void AAudioStreamBuilder_setErrorCallback(AAudioStreamBuilder* builder,
AAudioStream_errorCallback callback,
void *userData)
{
AudioStreamBuilder *streamBuilder = convertAAudioBuilderToStreamBuilder(builder);
- ALOGD("AAudioStreamBuilder_setCallback(): userData = %p", userData);
streamBuilder->setErrorCallbackProc(callback);
streamBuilder->setErrorCallbackUserData(userData);
}
@@ -186,10 +185,10 @@
int32_t frames)
{
AudioStreamBuilder *streamBuilder = convertAAudioBuilderToStreamBuilder(builder);
- ALOGD("%s: frames = %d", __func__, frames);
streamBuilder->setFramesPerDataCallback(frames);
}
+// TODO merge AAudioInternal_openStream into AAudioStreamBuilder_openStream
static aaudio_result_t AAudioInternal_openStream(AudioStreamBuilder *streamBuilder,
AAudioStream** streamPtr)
{
@@ -206,7 +205,7 @@
AAUDIO_API aaudio_result_t AAudioStreamBuilder_openStream(AAudioStreamBuilder* builder,
AAudioStream** streamPtr)
{
- ALOGD("AAudioStreamBuilder_openStream(): builder = %p", builder);
+ ALOGD("AAudioStreamBuilder_openStream() ----------------------------------------------");
AudioStreamBuilder *streamBuilder = COMMON_GET_FROM_BUILDER_OR_RETURN(streamPtr);
return AAudioInternal_openStream(streamBuilder, streamPtr);
}
@@ -228,6 +227,7 @@
if (audioStream != nullptr) {
audioStream->close();
delete audioStream;
+ ALOGD("AAudioStream_close() ----------------------------------------------");
return AAUDIO_OK;
}
return AAUDIO_ERROR_INVALID_HANDLE;
@@ -325,29 +325,6 @@
}
// ============================================================
-// Miscellaneous
-// ============================================================
-
-AAUDIO_API aaudio_result_t AAudioStream_createThread(AAudioStream* stream,
- int64_t periodNanoseconds,
- aaudio_audio_thread_proc_t threadProc, void *arg)
-{
- AudioStream *audioStream = convertAAudioStreamToAudioStream(stream);
- if (audioStream->getDataCallbackProc() != nullptr) {
- return AAUDIO_ERROR_INCOMPATIBLE;
- }
- return audioStream->createThread(periodNanoseconds, threadProc, arg);
-}
-
-AAUDIO_API aaudio_result_t AAudioStream_joinThread(AAudioStream* stream,
- void **returnArg,
- int64_t timeoutNanoseconds)
-{
- AudioStream *audioStream = convertAAudioStreamToAudioStream(stream);
- return audioStream->joinThread(returnArg, timeoutNanoseconds);
-}
-
-// ============================================================
// Stream - queries
// ============================================================
diff --git a/media/libaaudio/src/core/AudioStream.cpp b/media/libaaudio/src/core/AudioStream.cpp
index 7c0b5ae..9690848 100644
--- a/media/libaaudio/src/core/AudioStream.cpp
+++ b/media/libaaudio/src/core/AudioStream.cpp
@@ -38,7 +38,6 @@
aaudio_result_t AudioStream::open(const AudioStreamBuilder& builder)
{
-
// Copy parameters from the Builder because the Builder may be deleted after this call.
mSamplesPerFrame = builder.getSamplesPerFrame();
mSampleRate = builder.getSampleRate();
@@ -46,6 +45,7 @@
mFormat = builder.getFormat();
mDirection = builder.getDirection();
mSharingMode = builder.getSharingMode();
+ mSharingModeMatchRequired = builder.isSharingModeMatchRequired();
// callbacks
mFramesPerDataCallback = builder.getFramesPerDataCallback();
@@ -53,10 +53,19 @@
mErrorCallbackProc = builder.getErrorCallbackProc();
mDataCallbackUserData = builder.getDataCallbackUserData();
- // TODO validate more parameters.
- if (mErrorCallbackProc != nullptr && mDataCallbackProc == nullptr) {
- ALOGE("AudioStream::open(): disconnect callback cannot be used without a data callback.");
- return AAUDIO_ERROR_UNEXPECTED_VALUE;
+ // This is very helpful for debugging in the future.
+ ALOGI("AudioStream.open(): rate = %d, channels = %d, format = %d, sharing = %d",
+ mSampleRate, mSamplesPerFrame, mFormat, mSharingMode);
+
+ // Check for values that are ridiculously out of range to prevent math overflow exploits.
+ // The service will do a better check.
+ if (mSamplesPerFrame < 0 || mSamplesPerFrame > 128) {
+ ALOGE("AudioStream::open(): samplesPerFrame out of range = %d", mSamplesPerFrame);
+ return AAUDIO_ERROR_OUT_OF_RANGE;
+ }
+ if (mSampleRate < 0 || mSampleRate > 1000000) {
+ ALOGE("AudioStream::open(): mSampleRate out of range = %d", mSampleRate);
+ return AAUDIO_ERROR_INVALID_RATE;
}
if (mDirection != AAUDIO_DIRECTION_INPUT && mDirection != AAUDIO_DIRECTION_OUTPUT) {
ALOGE("AudioStream::open(): illegal direction %d", mDirection);
@@ -70,27 +79,6 @@
close();
}
-aaudio_result_t AudioStream::waitForStateTransition(aaudio_stream_state_t startingState,
- aaudio_stream_state_t endingState,
- int64_t timeoutNanoseconds)
-{
- aaudio_stream_state_t state = getState();
- aaudio_stream_state_t nextState = state;
- if (state == startingState && state != endingState) {
- aaudio_result_t result = waitForStateChange(state, &nextState, timeoutNanoseconds);
- if (result != AAUDIO_OK) {
- return result;
- }
- }
-// It's OK if the expected transition has already occurred.
-// But if we reach an unexpected state then that is an error.
- if (nextState != endingState) {
- return AAUDIO_ERROR_UNEXPECTED_STATE;
- } else {
- return AAUDIO_OK;
- }
-}
-
aaudio_result_t AudioStream::waitForStateChange(aaudio_stream_state_t currentState,
aaudio_stream_state_t *nextState,
int64_t timeoutNanoseconds)
@@ -123,16 +111,15 @@
return (state == currentState) ? AAUDIO_ERROR_TIMEOUT : AAUDIO_OK;
}
-// This registers the app's background audio thread with the server before
+// This registers the callback thread with the server before
// passing control to the app. This gives the server an opportunity to boost
// the thread's performance characteristics.
void* AudioStream::wrapUserThread() {
void* procResult = nullptr;
mThreadRegistrationResult = registerThread();
if (mThreadRegistrationResult == AAUDIO_OK) {
- // Call application procedure. This may take a very long time.
+ // Run callback loop. This may take a very long time.
procResult = mThreadProc(mThreadArg);
- ALOGD("AudioStream::mThreadProc() returned");
mThreadRegistrationResult = unregisterThread();
}
return procResult;
diff --git a/media/libaaudio/src/core/AudioStream.h b/media/libaaudio/src/core/AudioStream.h
index da71906..916870b 100644
--- a/media/libaaudio/src/core/AudioStream.h
+++ b/media/libaaudio/src/core/AudioStream.h
@@ -27,6 +27,8 @@
namespace aaudio {
+typedef void *(*aaudio_audio_thread_proc_t)(void *);
+
class AudioStreamBuilder;
/**
@@ -152,6 +154,10 @@
return mSharingMode;
}
+ bool isSharingModeMatchRequired() const {
+ return mSharingModeMatchRequired;
+ }
+
aaudio_direction_t getDirection() const {
return mDirection;
}
@@ -225,16 +231,6 @@
}
/**
- * Wait for a transition from one state to another.
- * @return AAUDIO_OK if the endingState was observed, or AAUDIO_ERROR_UNEXPECTED_STATE
- * if any state that was not the startingState or endingState was observed
- * or AAUDIO_ERROR_TIMEOUT
- */
- virtual aaudio_result_t waitForStateTransition(aaudio_stream_state_t startingState,
- aaudio_stream_state_t endingState,
- int64_t timeoutNanoseconds);
-
- /**
* This should not be called after the open() call.
*/
void setSampleRate(int32_t sampleRate) {
@@ -292,6 +288,7 @@
int32_t mSampleRate = AAUDIO_UNSPECIFIED;
int32_t mDeviceId = AAUDIO_UNSPECIFIED;
aaudio_sharing_mode_t mSharingMode = AAUDIO_SHARING_MODE_SHARED;
+ bool mSharingModeMatchRequired = false; // must match sharing mode requested
aaudio_audio_format_t mFormat = AAUDIO_FORMAT_UNSPECIFIED;
aaudio_direction_t mDirection = AAUDIO_DIRECTION_OUTPUT;
aaudio_stream_state_t mState = AAUDIO_STREAM_STATE_UNINITIALIZED;
diff --git a/media/libaaudio/src/core/AudioStreamBuilder.cpp b/media/libaaudio/src/core/AudioStreamBuilder.cpp
index a4d1970..4e0b8c6 100644
--- a/media/libaaudio/src/core/AudioStreamBuilder.cpp
+++ b/media/libaaudio/src/core/AudioStreamBuilder.cpp
@@ -30,10 +30,11 @@
#include "legacy/AudioStreamRecord.h"
#include "legacy/AudioStreamTrack.h"
-// Enable a mixer in AAudio service that will mix stream to an ALSA MMAP buffer.
+// Enable a mixer in AAudio service that will mix streams to an ALSA MMAP buffer.
#define MMAP_SHARED_ENABLED 0
-// Enable AAUDIO_SHARING_MODE_EXCLUSIVE that uses an ALSA MMAP buffer.
-#define MMAP_EXCLUSIVE_ENABLED 1
+
+// Enable AAUDIO_SHARING_MODE_EXCLUSIVE that uses an ALSA MMAP buffer directly.
+#define MMAP_EXCLUSIVE_ENABLED 0
using namespace aaudio;
@@ -50,7 +51,7 @@
AudioStream* audioStream = nullptr;
AAudioBinderClient *aaudioClient = nullptr;
const aaudio_sharing_mode_t sharingMode = getSharingMode();
- ALOGD("AudioStreamBuilder.build() sharingMode = %d", sharingMode);
+
switch (getDirection()) {
case AAUDIO_DIRECTION_INPUT:
diff --git a/media/libaaudio/src/core/AudioStreamBuilder.h b/media/libaaudio/src/core/AudioStreamBuilder.h
index c0ee6fe..25baf4c 100644
--- a/media/libaaudio/src/core/AudioStreamBuilder.h
+++ b/media/libaaudio/src/core/AudioStreamBuilder.h
@@ -82,6 +82,15 @@
return this;
}
+ bool isSharingModeMatchRequired() const {
+ return mSharingModeMatchRequired;
+ }
+
+ AudioStreamBuilder* setSharingModeMatchRequired(bool required) {
+ mSharingModeMatchRequired = required;
+ return this;
+ }
+
int32_t getBufferCapacity() const {
return mBufferCapacity;
}
@@ -109,7 +118,6 @@
return this;
}
-
void *getDataCallbackUserData() const {
return mDataCallbackUserData;
}
@@ -153,6 +161,7 @@
int32_t mSampleRate = AAUDIO_UNSPECIFIED;
int32_t mDeviceId = AAUDIO_DEVICE_UNSPECIFIED;
aaudio_sharing_mode_t mSharingMode = AAUDIO_SHARING_MODE_SHARED;
+ bool mSharingModeMatchRequired = false; // must match sharing mode requested
aaudio_audio_format_t mFormat = AAUDIO_FORMAT_UNSPECIFIED;
aaudio_direction_t mDirection = AAUDIO_DIRECTION_OUTPUT;
int32_t mBufferCapacity = AAUDIO_UNSPECIFIED;
diff --git a/media/libaaudio/src/fifo/FifoBuffer.cpp b/media/libaaudio/src/fifo/FifoBuffer.cpp
index 857780c..6b4a772 100644
--- a/media/libaaudio/src/fifo/FifoBuffer.cpp
+++ b/media/libaaudio/src/fifo/FifoBuffer.cpp
@@ -60,14 +60,11 @@
, mFramesUnderrunCount(0)
, mUnderrunCount(0)
{
- // TODO Handle possible failures to allocate. Move out of constructor?
mFifo = new FifoControllerIndirect(capacityInFrames,
capacityInFrames,
readIndexAddress,
writeIndexAddress);
mStorageOwned = false;
- ALOGD("FifoProcessor: capacityInFrames = %d, bytesPerFrame = %d",
- capacityInFrames, bytesPerFrame);
}
FifoBuffer::~FifoBuffer() {
@@ -132,8 +129,6 @@
while (framesLeft > 0 && partIndex < WrappingBuffer::SIZE) {
fifo_frames_t framesToRead = framesLeft;
fifo_frames_t framesAvailable = wrappingBuffer.numFrames[partIndex];
- //ALOGD("FifoProcessor::read() framesAvailable = %d, partIndex = %d",
- // framesAvailable, partIndex);
if (framesAvailable > 0) {
if (framesToRead > framesAvailable) {
framesToRead = framesAvailable;
@@ -143,6 +138,8 @@
destination += numBytes;
framesLeft -= framesToRead;
+ } else {
+ break;
}
partIndex++;
}
@@ -172,6 +169,8 @@
source += numBytes;
framesLeft -= framesToWrite;
+ } else {
+ break;
}
partIndex++;
}
diff --git a/media/libaaudio/src/utility/AAudioUtilities.cpp b/media/libaaudio/src/utility/AAudioUtilities.cpp
index 5637f0d..efbbfc5 100644
--- a/media/libaaudio/src/utility/AAudioUtilities.cpp
+++ b/media/libaaudio/src/utility/AAudioUtilities.cpp
@@ -33,10 +33,6 @@
case AAUDIO_FORMAT_PCM_I16:
size = sizeof(int16_t);
break;
- case AAUDIO_FORMAT_PCM_I32:
- case AAUDIO_FORMAT_PCM_I8_24:
- size = sizeof(int32_t);
- break;
case AAUDIO_FORMAT_PCM_FLOAT:
size = sizeof(float);
break;
@@ -61,7 +57,7 @@
}
}
-void AAudioConvert_pcm16ToFloat(const float *source, int32_t numSamples, int16_t *destination) {
+void AAudioConvert_pcm16ToFloat(const int16_t *source, int32_t numSamples, float *destination) {
for (int i = 0; i < numSamples; i++) {
destination[i] = source[i] * (1.0f / 32768.0f);
}
@@ -82,6 +78,8 @@
status = INVALID_OPERATION;
break;
case AAUDIO_ERROR_UNEXPECTED_VALUE: // TODO redundant?
+ case AAUDIO_ERROR_INVALID_RATE:
+ case AAUDIO_ERROR_INVALID_FORMAT:
case AAUDIO_ERROR_ILLEGAL_ARGUMENT:
status = BAD_VALUE;
break;
@@ -107,7 +105,7 @@
result = AAUDIO_ERROR_INVALID_HANDLE;
break;
case DEAD_OBJECT:
- result = AAUDIO_ERROR_DISCONNECTED;
+ result = AAUDIO_ERROR_NO_SERVICE;
break;
case INVALID_OPERATION:
result = AAUDIO_ERROR_INVALID_STATE;
@@ -135,12 +133,6 @@
case AAUDIO_FORMAT_PCM_FLOAT:
androidFormat = AUDIO_FORMAT_PCM_FLOAT;
break;
- case AAUDIO_FORMAT_PCM_I8_24:
- androidFormat = AUDIO_FORMAT_PCM_8_24_BIT;
- break;
- case AAUDIO_FORMAT_PCM_I32:
- androidFormat = AUDIO_FORMAT_PCM_32_BIT;
- break;
default:
androidFormat = AUDIO_FORMAT_DEFAULT;
ALOGE("AAudioConvert_aaudioToAndroidDataFormat 0x%08X unrecognized", aaudioFormat);
@@ -158,12 +150,6 @@
case AUDIO_FORMAT_PCM_FLOAT:
aaudioFormat = AAUDIO_FORMAT_PCM_FLOAT;
break;
- case AUDIO_FORMAT_PCM_32_BIT:
- aaudioFormat = AAUDIO_FORMAT_PCM_I32;
- break;
- case AUDIO_FORMAT_PCM_8_24_BIT:
- aaudioFormat = AAUDIO_FORMAT_PCM_I8_24;
- break;
default:
aaudioFormat = AAUDIO_FORMAT_INVALID;
ALOGE("AAudioConvert_androidToAAudioDataFormat 0x%08X unrecognized", androidFormat);
diff --git a/media/libmedia/IMediaSource.cpp b/media/libmedia/IMediaSource.cpp
index fdbc869..724b3a0 100644
--- a/media/libmedia/IMediaSource.cpp
+++ b/media/libmedia/IMediaSource.cpp
@@ -389,7 +389,7 @@
}
}
if (transferBuf != nullptr) { // Using shared buffers.
- if (!transferBuf->isObserved()) {
+ if (!transferBuf->isObserved() && transferBuf != buf) {
// Transfer buffer must be part of a MediaBufferGroup.
ALOGV("adding shared memory buffer %p to local group", transferBuf);
mGroup->add_buffer(transferBuf);
diff --git a/media/libmedia/aidl/android/IGraphicBufferSource.aidl b/media/libmedia/aidl/android/IGraphicBufferSource.aidl
index 325c631..f3c7abc 100644
--- a/media/libmedia/aidl/android/IGraphicBufferSource.aidl
+++ b/media/libmedia/aidl/android/IGraphicBufferSource.aidl
@@ -28,10 +28,10 @@
void setSuspend(boolean suspend, long suspendTimeUs);
void setRepeatPreviousFrameDelayUs(long repeatAfterUs);
void setMaxFps(float maxFps);
- void setTimeLapseConfig(long timePerFrameUs, long timePerCaptureUs);
+ void setTimeLapseConfig(double fps, double captureFps);
void setStartTimeUs(long startTimeUs);
void setStopTimeUs(long stopTimeUs);
void setColorAspects(int aspects);
void setTimeOffsetUs(long timeOffsetsUs);
void signalEndOfInputStream();
-}
\ No newline at end of file
+}
diff --git a/media/libmedia/omx/1.0/WGraphicBufferSource.cpp b/media/libmedia/omx/1.0/WGraphicBufferSource.cpp
index b4e2975..4c543fa 100644
--- a/media/libmedia/omx/1.0/WGraphicBufferSource.cpp
+++ b/media/libmedia/omx/1.0/WGraphicBufferSource.cpp
@@ -53,9 +53,8 @@
}
BnStatus LWGraphicBufferSource::setTimeLapseConfig(
- int64_t timePerFrameUs, int64_t timePerCaptureUs) {
- return toBinderStatus(mBase->setTimeLapseConfig(
- timePerFrameUs, timePerCaptureUs));
+ double fps, double captureFps) {
+ return toBinderStatus(mBase->setTimeLapseConfig(fps, captureFps));
}
BnStatus LWGraphicBufferSource::setStartTimeUs(
diff --git a/media/libmediaplayerservice/StagefrightRecorder.cpp b/media/libmediaplayerservice/StagefrightRecorder.cpp
index 95f378f..e1d762f 100644
--- a/media/libmediaplayerservice/StagefrightRecorder.cpp
+++ b/media/libmediaplayerservice/StagefrightRecorder.cpp
@@ -163,7 +163,7 @@
// TBD mTrackEveryTimeDurationUs = 0;
mAnalyticsItem->setInt32(kRecorderCaptureFpsEnable, mCaptureFpsEnable);
mAnalyticsItem->setDouble(kRecorderCaptureFps, mCaptureFps);
- // TBD mTimeBetweenCaptureUs = -1;
+ // TBD mCaptureFps = -1.0;
// TBD mCameraSourceTimeLapse = NULL;
// TBD mMetaDataStoredInVideoBuffers = kMetadataBufferTypeInvalid;
// TBD mEncoderProfiles = MediaProfiles::getInstance();
@@ -709,26 +709,11 @@
status_t StagefrightRecorder::setParamCaptureFps(double fps) {
ALOGV("setParamCaptureFps: %.2f", fps);
- constexpr int64_t k1E12 = 1000000000000ll;
- int64_t fpsx1e12 = k1E12 * fps;
- if (fpsx1e12 == 0) {
- ALOGE("FPS is zero or too small");
+ if (!(fps >= 1.0 / 86400)) {
+ ALOGE("FPS is too small");
return BAD_VALUE;
}
-
- // This does not overflow since 10^6 * 10^12 < 2^63
- int64_t timeUs = 1000000ll * k1E12 / fpsx1e12;
-
- // Not allowing time more than a day and a millisecond for error margin.
- // Note: 1e12 / 86400 = 11574074.(074) and 1e18 / 11574074 = 86400000553;
- // therefore 1 ms of margin should be sufficient.
- if (timeUs <= 0 || timeUs > 86400001000ll) {
- ALOGE("Time between frame capture (%lld) is out of range [0, 1 Day]", (long long)timeUs);
- return BAD_VALUE;
- }
-
mCaptureFps = fps;
- mTimeBetweenCaptureUs = timeUs;
return OK;
}
@@ -1582,16 +1567,15 @@
videoSize.width = mVideoWidth;
videoSize.height = mVideoHeight;
if (mCaptureFpsEnable) {
- if (mTimeBetweenCaptureUs < 0) {
- ALOGE("Invalid mTimeBetweenTimeLapseFrameCaptureUs value: %lld",
- (long long)mTimeBetweenCaptureUs);
+ if (!(mCaptureFps > 0.)) {
+ ALOGE("Invalid mCaptureFps value: %lf", mCaptureFps);
return BAD_VALUE;
}
mCameraSourceTimeLapse = CameraSourceTimeLapse::CreateFromCamera(
mCamera, mCameraProxy, mCameraId, mClientName, mClientUid, mClientPid,
videoSize, mFrameRate, mPreviewSurface,
- mTimeBetweenCaptureUs);
+ std::llround(1e6 / mCaptureFps));
*cameraSource = mCameraSourceTimeLapse;
} else {
*cameraSource = CameraSource::CreateFromCamera(
@@ -1687,12 +1671,11 @@
// set up time lapse/slow motion for surface source
if (mCaptureFpsEnable) {
- if (mTimeBetweenCaptureUs <= 0) {
- ALOGE("Invalid mTimeBetweenCaptureUs value: %lld",
- (long long)mTimeBetweenCaptureUs);
+ if (!(mCaptureFps > 0.)) {
+ ALOGE("Invalid mCaptureFps value: %lf", mCaptureFps);
return BAD_VALUE;
}
- format->setInt64("time-lapse", mTimeBetweenCaptureUs);
+ format->setDouble("time-lapse-fps", mCaptureFps);
}
}
@@ -2083,8 +2066,7 @@
mMaxFileSizeBytes = 0;
mTrackEveryTimeDurationUs = 0;
mCaptureFpsEnable = false;
- mCaptureFps = 0.0;
- mTimeBetweenCaptureUs = -1;
+ mCaptureFps = -1.0;
mCameraSourceTimeLapse = NULL;
mMetaDataStoredInVideoBuffers = kMetadataBufferTypeInvalid;
mEncoderProfiles = MediaProfiles::getInstance();
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
index 9e579f9..a4a5861 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
@@ -95,7 +95,11 @@
}
NuPlayer::Decoder::~Decoder() {
- mCodec->release();
+ // Need to stop looper first since mCodec could be accessed on the mDecoderLooper.
+ stopLooper();
+ if (mCodec != NULL) {
+ mCodec->release();
+ }
releaseAndResetMediaBuffers();
}
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoderBase.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDecoderBase.cpp
index 1210dc9..d0de7b0 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoderBase.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoderBase.cpp
@@ -43,8 +43,7 @@
}
NuPlayer::DecoderBase::~DecoderBase() {
- mDecoderLooper->unregisterHandler(id());
- mDecoderLooper->stop();
+ stopLooper();
}
static
@@ -73,6 +72,11 @@
mDecoderLooper->registerHandler(this);
}
+void NuPlayer::DecoderBase::stopLooper() {
+ mDecoderLooper->unregisterHandler(id());
+ mDecoderLooper->stop();
+}
+
void NuPlayer::DecoderBase::setParameters(const sp<AMessage> ¶ms) {
sp<AMessage> msg = new AMessage(kWhatSetParameters, this);
msg->setMessage("params", params);
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoderBase.h b/media/libmediaplayerservice/nuplayer/NuPlayerDecoderBase.h
index dcdfcaf..d44c396 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoderBase.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoderBase.h
@@ -69,6 +69,8 @@
virtual ~DecoderBase();
+ void stopLooper();
+
virtual void onMessageReceived(const sp<AMessage> &msg);
virtual void onConfigure(const sp<AMessage> &format) = 0;
diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp
index 63b9571..8b91541 100644
--- a/media/libstagefright/ACodec.cpp
+++ b/media/libstagefright/ACodec.cpp
@@ -546,8 +546,8 @@
mRepeatFrameDelayUs(-1ll),
mMaxPtsGapUs(-1ll),
mMaxFps(-1),
- mTimePerFrameUs(-1ll),
- mTimePerCaptureUs(-1ll),
+ mFps(-1.0),
+ mCaptureFps(-1.0),
mCreateInputBuffersSuspended(false),
mLatency(0),
mTunneled(false),
@@ -1802,8 +1802,8 @@
mMaxFps = -1;
}
- if (!msg->findInt64("time-lapse", &mTimePerCaptureUs)) {
- mTimePerCaptureUs = -1ll;
+ if (!msg->findDouble("time-lapse-fps", &mCaptureFps)) {
+ mCaptureFps = -1.0;
}
if (!msg->findInt32(
@@ -3739,17 +3739,18 @@
def.nBufferSize = (video_def->nStride * video_def->nSliceHeight * 3) / 2;
- float frameRate;
- if (!msg->findFloat("frame-rate", &frameRate)) {
+ float framerate;
+ if (!msg->findFloat("frame-rate", &framerate)) {
int32_t tmp;
if (!msg->findInt32("frame-rate", &tmp)) {
return INVALID_OPERATION;
}
- frameRate = (float)tmp;
- mTimePerFrameUs = (int64_t) (1000000.0f / frameRate);
+ mFps = (double)tmp;
+ } else {
+ mFps = (double)framerate;
}
- video_def->xFramerate = (OMX_U32)(frameRate * 65536.0f);
+ video_def->xFramerate = (OMX_U32)(mFps * 65536);
video_def->eCompressionFormat = OMX_VIDEO_CodingUnused;
// this is redundant as it was already set up in setVideoPortFormatType
// FIXME for now skip this only for flexible YUV formats
@@ -6597,11 +6598,10 @@
}
}
- if (mCodec->mTimePerCaptureUs > 0ll
- && mCodec->mTimePerFrameUs > 0ll) {
+ if (mCodec->mCaptureFps > 0. && mCodec->mFps > 0.) {
err = statusFromBinderStatus(
mCodec->mGraphicBufferSource->setTimeLapseConfig(
- mCodec->mTimePerFrameUs, mCodec->mTimePerCaptureUs));
+ mCodec->mFps, mCodec->mCaptureFps));
if (err != OK) {
ALOGE("[%s] Unable to configure time lapse (err %d)",
diff --git a/media/libstagefright/Android.mk b/media/libstagefright/Android.mk
index 61b8f9d..372b11a 100644
--- a/media/libstagefright/Android.mk
+++ b/media/libstagefright/Android.mk
@@ -78,6 +78,7 @@
libaudioutils \
libbinder \
libcamera_client \
+ libcrypto \
libcutils \
libdl \
libdrmframework \
diff --git a/media/libstagefright/httplive/PlaylistFetcher.cpp b/media/libstagefright/httplive/PlaylistFetcher.cpp
index bbcea51..00cf142 100644
--- a/media/libstagefright/httplive/PlaylistFetcher.cpp
+++ b/media/libstagefright/httplive/PlaylistFetcher.cpp
@@ -26,6 +26,7 @@
#include "include/avc_utils.h"
#include "include/ID3.h"
#include "mpeg2ts/AnotherPacketSource.h"
+#include "mpeg2ts/HlsSampleDecryptor.h"
#include <media/stagefright/foundation/ABitReader.h>
#include <media/stagefright/foundation/ABuffer.h>
@@ -36,7 +37,6 @@
#include <ctype.h>
#include <inttypes.h>
-#include <openssl/aes.h>
#define FLOGV(fmt, ...) ALOGV("[fetcher-%d] " fmt, mFetcherID, ##__VA_ARGS__)
#define FSLOGV(stream, fmt, ...) ALOGV("[fetcher-%d] [%s] " fmt, mFetcherID, \
@@ -167,11 +167,15 @@
mFirstPTSValid(false),
mFirstTimeUs(-1ll),
mVideoBuffer(new AnotherPacketSource(NULL)),
+ mSampleAesKeyItemChanged(false),
mThresholdRatio(-1.0f),
mDownloadState(new DownloadState()),
mHasMetadata(false) {
memset(mPlaylistHash, 0, sizeof(mPlaylistHash));
mHTTPDownloader = mSession->getHTTPDownloader();
+
+ memset(mKeyData, 0, sizeof(mKeyData));
+ memset(mAESInitVec, 0, sizeof(mAESInitVec));
}
PlaylistFetcher::~PlaylistFetcher() {
@@ -306,6 +310,15 @@
}
}
+ // TODO: Revise this when we add support for KEYFORMAT
+ // If method has changed (e.g., -> NONE); sufficient to check at the segment boundary
+ if (mSampleAesKeyItem != NULL && first && found && method != "SAMPLE-AES") {
+ ALOGI("decryptBuffer: resetting mSampleAesKeyItem(%p) with method %s",
+ mSampleAesKeyItem.get(), method.c_str());
+ mSampleAesKeyItem = NULL;
+ mSampleAesKeyItemChanged = true;
+ }
+
if (!found) {
method = "NONE";
}
@@ -313,6 +326,8 @@
if (method == "NONE") {
return OK;
+ } else if (method == "SAMPLE-AES") {
+ ALOGV("decryptBuffer: Non-Widevine SAMPLE-AES is supported now.");
} else if (!(method == "AES-128")) {
ALOGE("Unsupported cipher method '%s'", method.c_str());
return ERROR_UNSUPPORTED;
@@ -345,6 +360,79 @@
mAESKeyForURI.add(keyURI, key);
}
+ if (first) {
+ // If decrypting the first block in a file, read the iv from the manifest
+ // or derive the iv from the file's sequence number.
+
+ unsigned char AESInitVec[AES_BLOCK_SIZE];
+ AString iv;
+ if (itemMeta->findString("cipher-iv", &iv)) {
+ if ((!iv.startsWith("0x") && !iv.startsWith("0X"))
+ || iv.size() > 16 * 2 + 2) {
+ ALOGE("malformed cipher IV '%s'.", iv.c_str());
+ return ERROR_MALFORMED;
+ }
+
+ while (iv.size() < 16 * 2 + 2) {
+ iv.insert("0", 1, 2);
+ }
+
+ memset(AESInitVec, 0, sizeof(AESInitVec));
+ for (size_t i = 0; i < 16; ++i) {
+ char c1 = tolower(iv.c_str()[2 + 2 * i]);
+ char c2 = tolower(iv.c_str()[3 + 2 * i]);
+ if (!isxdigit(c1) || !isxdigit(c2)) {
+ ALOGE("malformed cipher IV '%s'.", iv.c_str());
+ return ERROR_MALFORMED;
+ }
+ uint8_t nibble1 = isdigit(c1) ? c1 - '0' : c1 - 'a' + 10;
+ uint8_t nibble2 = isdigit(c2) ? c2 - '0' : c2 - 'a' + 10;
+
+ AESInitVec[i] = nibble1 << 4 | nibble2;
+ }
+ } else {
+ memset(AESInitVec, 0, sizeof(AESInitVec));
+ AESInitVec[15] = mSeqNumber & 0xff;
+ AESInitVec[14] = (mSeqNumber >> 8) & 0xff;
+ AESInitVec[13] = (mSeqNumber >> 16) & 0xff;
+ AESInitVec[12] = (mSeqNumber >> 24) & 0xff;
+ }
+
+ bool newKey = memcmp(mKeyData, key->data(), AES_BLOCK_SIZE) != 0;
+ bool newInitVec = memcmp(mAESInitVec, AESInitVec, AES_BLOCK_SIZE) != 0;
+ bool newSampleAesKeyItem = newKey || newInitVec;
+ ALOGV("decryptBuffer: SAMPLE-AES newKeyItem %d/%d (Key %d initVec %d)",
+ mSampleAesKeyItemChanged, newSampleAesKeyItem, newKey, newInitVec);
+
+ if (newSampleAesKeyItem) {
+ memcpy(mKeyData, key->data(), AES_BLOCK_SIZE);
+ memcpy(mAESInitVec, AESInitVec, AES_BLOCK_SIZE);
+
+ if (method == "SAMPLE-AES") {
+ mSampleAesKeyItemChanged = true;
+
+ sp<ABuffer> keyDataBuffer = ABuffer::CreateAsCopy(mKeyData, sizeof(mKeyData));
+ sp<ABuffer> initVecBuffer = ABuffer::CreateAsCopy(mAESInitVec, sizeof(mAESInitVec));
+
+ // always allocating a new one rather than updating the old message
+ // lower layer might still have a reference to the old message
+ mSampleAesKeyItem = new AMessage();
+ mSampleAesKeyItem->setBuffer("keyData", keyDataBuffer);
+ mSampleAesKeyItem->setBuffer("initVec", initVecBuffer);
+
+ ALOGV("decryptBuffer: New SampleAesKeyItem: Key: %s IV: %s",
+ HlsSampleDecryptor::aesBlockToStr(mKeyData).c_str(),
+ HlsSampleDecryptor::aesBlockToStr(mAESInitVec).c_str());
+ } // SAMPLE-AES
+ } // newSampleAesKeyItem
+ } // first
+
+ if (method == "SAMPLE-AES") {
+ ALOGV("decryptBuffer: skipping full-seg decrypt for SAMPLE-AES");
+ return OK;
+ }
+
+
AES_KEY aes_key;
if (AES_set_decrypt_key(key->data(), 128, &aes_key) != 0) {
ALOGE("failed to set AES decryption key.");
@@ -361,44 +449,6 @@
return ERROR_MALFORMED;
}
- if (first) {
- // If decrypting the first block in a file, read the iv from the manifest
- // or derive the iv from the file's sequence number.
-
- AString iv;
- if (itemMeta->findString("cipher-iv", &iv)) {
- if ((!iv.startsWith("0x") && !iv.startsWith("0X"))
- || iv.size() > 16 * 2 + 2) {
- ALOGE("malformed cipher IV '%s'.", iv.c_str());
- return ERROR_MALFORMED;
- }
-
- while (iv.size() < 16 * 2 + 2) {
- iv.insert("0", 1, 2);
- }
-
- memset(mAESInitVec, 0, sizeof(mAESInitVec));
- for (size_t i = 0; i < 16; ++i) {
- char c1 = tolower(iv.c_str()[2 + 2 * i]);
- char c2 = tolower(iv.c_str()[3 + 2 * i]);
- if (!isxdigit(c1) || !isxdigit(c2)) {
- ALOGE("malformed cipher IV '%s'.", iv.c_str());
- return ERROR_MALFORMED;
- }
- uint8_t nibble1 = isdigit(c1) ? c1 - '0' : c1 - 'a' + 10;
- uint8_t nibble2 = isdigit(c2) ? c2 - '0' : c2 - 'a' + 10;
-
- mAESInitVec[i] = nibble1 << 4 | nibble2;
- }
- } else {
- memset(mAESInitVec, 0, sizeof(mAESInitVec));
- mAESInitVec[15] = mSeqNumber & 0xff;
- mAESInitVec[14] = (mSeqNumber >> 8) & 0xff;
- mAESInitVec[13] = (mSeqNumber >> 16) & 0xff;
- mAESInitVec[12] = (mSeqNumber >> 24) & 0xff;
- }
- }
-
AES_cbc_encrypt(
buffer->data(), buffer->data(), buffer->size(),
&aes_key, mAESInitVec, AES_DECRYPT);
@@ -409,7 +459,7 @@
status_t PlaylistFetcher::checkDecryptPadding(const sp<ABuffer> &buffer) {
AString method;
CHECK(buffer->meta()->findString("cipher-method", &method));
- if (method == "NONE") {
+ if (method == "NONE" || method == "SAMPLE-AES") {
return OK;
}
@@ -1656,6 +1706,11 @@
mNextPTSTimeUs = -1ll;
}
+ if (mSampleAesKeyItemChanged) {
+ mTSParser->signalNewSampleAesKey(mSampleAesKeyItem);
+ mSampleAesKeyItemChanged = false;
+ }
+
size_t offset = 0;
while (offset + 188 <= buffer->size()) {
status_t err = mTSParser->feedTSPacket(buffer->data() + offset, 188);
@@ -2038,10 +2093,24 @@
}
}
+ sp<HlsSampleDecryptor> sampleDecryptor = NULL;
+ if (mSampleAesKeyItem != NULL) {
+ ALOGV("extractAndQueueAccessUnits[%d] SampleAesKeyItem: Key: %s IV: %s",
+ mSeqNumber,
+ HlsSampleDecryptor::aesBlockToStr(mKeyData).c_str(),
+ HlsSampleDecryptor::aesBlockToStr(mAESInitVec).c_str());
+
+ sampleDecryptor = new HlsSampleDecryptor(mSampleAesKeyItem);
+ }
+
+ int frameId = 0;
+
size_t offset = 0;
while (offset < buffer->size()) {
const uint8_t *adtsHeader = buffer->data() + offset;
CHECK_LT(offset + 5, buffer->size());
+ // non-const pointer for decryption if needed
+ uint8_t *adtsFrame = buffer->data() + offset;
unsigned aac_frame_length =
((adtsHeader[3] & 3) << 11)
@@ -2099,6 +2168,18 @@
}
}
+ if (sampleDecryptor != NULL) {
+ bool protection_absent = (adtsHeader[1] & 0x1);
+ size_t headerSize = protection_absent ? 7 : 9;
+ if (frameId == 0) {
+ ALOGV("extractAndQueueAAC[%d] protection_absent %d (%02x) headerSize %zu",
+ mSeqNumber, protection_absent, adtsHeader[1], headerSize);
+ }
+
+ sampleDecryptor->processAAC(headerSize, adtsFrame, aac_frame_length);
+ }
+ frameId++;
+
sp<ABuffer> unit = new ABuffer(aac_frame_length);
memcpy(unit->data(), adtsHeader, aac_frame_length);
diff --git a/media/libstagefright/httplive/PlaylistFetcher.h b/media/libstagefright/httplive/PlaylistFetcher.h
index ee7d3a1..d7db54a 100644
--- a/media/libstagefright/httplive/PlaylistFetcher.h
+++ b/media/libstagefright/httplive/PlaylistFetcher.h
@@ -19,6 +19,7 @@
#define PLAYLIST_FETCHER_H_
#include <media/stagefright/foundation/AHandler.h>
+#include <openssl/aes.h>
#include "mpeg2ts/ATSParser.h"
#include "LiveSession.h"
@@ -175,7 +176,10 @@
// Stores the initialization vector to decrypt the next block of cipher text, which can
// either be derived from the sequence number, read from the manifest, or copied from
// the last block of cipher text (cipher-block chaining).
- unsigned char mAESInitVec[16];
+ unsigned char mAESInitVec[AES_BLOCK_SIZE];
+ unsigned char mKeyData[AES_BLOCK_SIZE];
+ bool mSampleAesKeyItemChanged;
+ sp<AMessage> mSampleAesKeyItem;
Mutex mThresholdLock;
float mThresholdRatio;
diff --git a/media/libstagefright/include/ACodec.h b/media/libstagefright/include/ACodec.h
index 6c1a5c6..06ee0e8 100644
--- a/media/libstagefright/include/ACodec.h
+++ b/media/libstagefright/include/ACodec.h
@@ -293,8 +293,8 @@
int64_t mRepeatFrameDelayUs;
int64_t mMaxPtsGapUs;
float mMaxFps;
- int64_t mTimePerFrameUs;
- int64_t mTimePerCaptureUs;
+ double mFps;
+ double mCaptureFps;
bool mCreateInputBuffersSuspended;
uint32_t mLatency;
diff --git a/media/libstagefright/mpeg2ts/ATSParser.cpp b/media/libstagefright/mpeg2ts/ATSParser.cpp
index 8099edb..31edb21 100644
--- a/media/libstagefright/mpeg2ts/ATSParser.cpp
+++ b/media/libstagefright/mpeg2ts/ATSParser.cpp
@@ -105,6 +105,8 @@
void updateCasSessions();
+ void signalNewSampleAesKey(const sp<AMessage> &keyItem);
+
private:
struct StreamInfo {
unsigned mType;
@@ -119,6 +121,7 @@
bool mFirstPTSValid;
uint64_t mFirstPTS;
int64_t mLastRecoveredPTS;
+ sp<AMessage> mSampleAesKeyItem;
status_t parseProgramMap(ABitReader *br);
int64_t recoverPTS(uint64_t PTS_33bit);
@@ -168,6 +171,8 @@
bool isVideo() const;
bool isMeta() const;
+ void signalNewSampleAesKey(const sp<AMessage> &keyItem);
+
protected:
virtual ~Stream();
@@ -194,6 +199,8 @@
ElementaryStreamQueue *mQueue;
bool mScrambled;
+ bool mSampleEncrypted;
+ sp<AMessage> mSampleAesKeyItem;
sp<IMemory> mMem;
sp<MemoryDealer> mDealer;
sp<ABuffer> mDescrambledBuffer;
@@ -586,6 +593,10 @@
sp<Stream> stream = new Stream(
this, info.mPID, info.mType, PCR_PID, info.mCASystemId);
+ if (mSampleAesKeyItem != NULL) {
+ stream->signalNewSampleAesKey(mSampleAesKeyItem);
+ }
+
isAddingScrambledStream |= info.mCASystemId >= 0;
mStreams.add(info.mPID, stream);
}
@@ -710,22 +721,32 @@
mPrevPTS(0),
mQueue(NULL),
mScrambled(CA_system_ID >= 0) {
- ALOGV("new stream PID 0x%02x, type 0x%02x, scrambled %d",
- elementaryPID, streamType, mScrambled);
- uint32_t flags = (isVideo() && mScrambled) ?
- ElementaryStreamQueue::kFlag_ScrambledData : 0;
+ mSampleEncrypted =
+ mStreamType == STREAMTYPE_H264_ENCRYPTED ||
+ mStreamType == STREAMTYPE_AAC_ENCRYPTED ||
+ mStreamType == STREAMTYPE_AC3_ENCRYPTED;
+
+ ALOGV("new stream PID 0x%02x, type 0x%02x, scrambled %d, SampleEncrypted: %d",
+ elementaryPID, streamType, mScrambled, mSampleEncrypted);
+
+ uint32_t flags =
+ (isVideo() && mScrambled) ? ElementaryStreamQueue::kFlag_ScrambledData :
+ (mSampleEncrypted) ? ElementaryStreamQueue::kFlag_SampleEncryptedData :
+ 0;
ElementaryStreamQueue::Mode mode = ElementaryStreamQueue::INVALID;
switch (mStreamType) {
case STREAMTYPE_H264:
+ case STREAMTYPE_H264_ENCRYPTED:
mode = ElementaryStreamQueue::H264;
flags |= (mProgram->parserFlags() & ALIGNED_VIDEO_DATA) ?
ElementaryStreamQueue::kFlag_AlignedData : 0;
break;
case STREAMTYPE_MPEG2_AUDIO_ADTS:
+ case STREAMTYPE_AAC_ENCRYPTED:
mode = ElementaryStreamQueue::AAC;
break;
@@ -745,6 +766,7 @@
case STREAMTYPE_LPCM_AC3:
case STREAMTYPE_AC3:
+ case STREAMTYPE_AC3_ENCRYPTED:
mode = ElementaryStreamQueue::AC3;
break;
@@ -761,6 +783,10 @@
mQueue = new ElementaryStreamQueue(mode, flags);
if (mQueue != NULL) {
+ if (mSampleAesKeyItem != NULL) {
+ mQueue->signalNewSampleAesKey(mSampleAesKeyItem);
+ }
+
ensureBufferCapacity(kInitialStreamBufferSize);
if (mScrambled && (isAudio() || isVideo())) {
@@ -913,6 +939,7 @@
bool ATSParser::Stream::isVideo() const {
switch (mStreamType) {
case STREAMTYPE_H264:
+ case STREAMTYPE_H264_ENCRYPTED:
case STREAMTYPE_MPEG1_VIDEO:
case STREAMTYPE_MPEG2_VIDEO:
case STREAMTYPE_MPEG4_VIDEO:
@@ -930,6 +957,8 @@
case STREAMTYPE_MPEG2_AUDIO_ADTS:
case STREAMTYPE_LPCM_AC3:
case STREAMTYPE_AC3:
+ case STREAMTYPE_AAC_ENCRYPTED:
+ case STREAMTYPE_AC3_ENCRYPTED:
return true;
default:
@@ -1454,7 +1483,7 @@
mPrevPTS = PTS;
#endif
- ALOGV("onPayloadData mStreamType=0x%02x", mStreamType);
+ ALOGV("onPayloadData mStreamType=0x%02x size: %zu", mStreamType, size);
int64_t timeUs = 0ll; // no presentation timestamp available.
if (PTS_DTS_flags == 2 || PTS_DTS_flags == 3) {
@@ -1492,6 +1521,8 @@
}
mSource = new AnotherPacketSource(meta);
mSource->queueAccessUnit(accessUnit);
+ ALOGV("onPayloadData: created AnotherPacketSource PID 0x%08x of type 0x%02x",
+ mElementaryPID, mStreamType);
}
} else if (mQueue->getFormat() != NULL) {
// After a discontinuity we invalidate the queue's format
@@ -1730,6 +1761,9 @@
if (!found) {
mPrograms.push(
new Program(this, program_number, programMapPID, mLastRecoveredPTS));
+ if (mSampleAesKeyItem != NULL) {
+ mPrograms.top()->signalNewSampleAesKey(mSampleAesKeyItem);
+ }
}
if (mPSISections.indexOfKey(programMapPID) < 0) {
@@ -2228,4 +2262,40 @@
ALOGV("crc: %08x\n", crc);
return (crc == 0);
}
+
+// SAMPLE_AES key handling
+// TODO: Merge these to their respective class after Widevine-HLS
+void ATSParser::signalNewSampleAesKey(const sp<AMessage> &keyItem) {
+ ALOGD("signalNewSampleAesKey: %p", keyItem.get());
+
+ mSampleAesKeyItem = keyItem;
+
+ // a NULL key item will propagate to existing ElementaryStreamQueues
+ for (size_t i = 0; i < mPrograms.size(); ++i) {
+ mPrograms[i]->signalNewSampleAesKey(keyItem);
+ }
+}
+
+void ATSParser::Program::signalNewSampleAesKey(const sp<AMessage> &keyItem) {
+ ALOGD("Program::signalNewSampleAesKey: %p", keyItem.get());
+
+ mSampleAesKeyItem = keyItem;
+
+ // a NULL key item will propagate to existing ElementaryStreamQueues
+ for (size_t i = 0; i < mStreams.size(); ++i) {
+ mStreams[i]->signalNewSampleAesKey(keyItem);
+ }
+}
+
+void ATSParser::Stream::signalNewSampleAesKey(const sp<AMessage> &keyItem) {
+ ALOGD("Stream::signalNewSampleAesKey: 0x%04x size = %zu keyItem: %p",
+ mElementaryPID, mBuffer->size(), keyItem.get());
+
+ // a NULL key item will propagate to existing ElementaryStreamQueues
+ mSampleAesKeyItem = keyItem;
+
+ flush(NULL);
+ mQueue->signalNewSampleAesKey(keyItem);
+}
+
} // namespace android
diff --git a/media/libstagefright/mpeg2ts/ATSParser.h b/media/libstagefright/mpeg2ts/ATSParser.h
index 4a88713..374e011 100644
--- a/media/libstagefright/mpeg2ts/ATSParser.h
+++ b/media/libstagefright/mpeg2ts/ATSParser.h
@@ -131,6 +131,8 @@
int64_t getFirstPTSTimeUs();
+ void signalNewSampleAesKey(const sp<AMessage> &keyItem);
+
enum {
// From ISO/IEC 13818-1: 2000 (E), Table 2-29
STREAMTYPE_RESERVED = 0x00,
@@ -149,6 +151,11 @@
// Stream type 0x83 is non-standard,
// it could be LPCM or TrueHD AC3
STREAMTYPE_LPCM_AC3 = 0x83,
+
+ //Sample Encrypted types
+ STREAMTYPE_H264_ENCRYPTED = 0xDB,
+ STREAMTYPE_AAC_ENCRYPTED = 0xCF,
+ STREAMTYPE_AC3_ENCRYPTED = 0xC1,
};
protected:
@@ -181,6 +188,8 @@
size_t mNumTSPacketsParsed;
+ sp<AMessage> mSampleAesKeyItem;
+
void parseProgramAssociationTable(ABitReader *br);
void parseProgramMap(ABitReader *br);
// Parse PES packet where br is pointing to. If the PES contains a sync
diff --git a/media/libstagefright/mpeg2ts/Android.mk b/media/libstagefright/mpeg2ts/Android.mk
index 5140e66..20acfe7 100644
--- a/media/libstagefright/mpeg2ts/Android.mk
+++ b/media/libstagefright/mpeg2ts/Android.mk
@@ -7,6 +7,7 @@
ATSParser.cpp \
CasManager.cpp \
ESQueue.cpp \
+ HlsSampleDecryptor.cpp \
MPEG2PSExtractor.cpp \
MPEG2TSExtractor.cpp \
@@ -18,7 +19,9 @@
LOCAL_SANITIZE := unsigned-integer-overflow signed-integer-overflow cfi
LOCAL_SANITIZE_DIAG := cfi
-LOCAL_SHARED_LIBRARIES := libmedia
+LOCAL_SHARED_LIBRARIES := \
+ libcrypto \
+ libmedia \
LOCAL_MODULE:= libstagefright_mpeg2ts
diff --git a/media/libstagefright/mpeg2ts/ESQueue.cpp b/media/libstagefright/mpeg2ts/ESQueue.cpp
index ae7ec77..f1b44ae 100644
--- a/media/libstagefright/mpeg2ts/ESQueue.cpp
+++ b/media/libstagefright/mpeg2ts/ESQueue.cpp
@@ -42,7 +42,15 @@
: mMode(mode),
mFlags(flags),
mEOSReached(false),
- mCASystemId(0) {
+ mCASystemId(0),
+ mAUIndex(0) {
+
+ ALOGV("ElementaryStreamQueue(%p) mode %x flags %x isScrambled %d isSampleEncrypted %d",
+ this, mode, flags, isScrambled(), isSampleEncrypted());
+
+ // Create the decryptor anyway since we don't know the use-case unless key is provided
+ // Won't decrypt if key info not available (e.g., scanner/extractor just parsing ts files)
+ mSampleDecryptor = isSampleEncrypted() ? new HlsSampleDecryptor : NULL;
}
sp<MetaData> ElementaryStreamQueue::getFormat() {
@@ -659,6 +667,9 @@
unsigned syncStartPos = 0; // in bytes
unsigned payloadSize = 0;
sp<MetaData> format = new MetaData;
+
+ ALOGV("dequeueAccessUnit_AC3[%d]: mBuffer %p(%zu)", mAUIndex, mBuffer->data(), mBuffer->size());
+
while (true) {
if (syncStartPos + 2 >= mBuffer->size()) {
return NULL;
@@ -671,6 +682,10 @@
if (payloadSize > 0) {
break;
}
+
+ ALOGV("dequeueAccessUnit_AC3[%d]: syncStartPos %u payloadSize %u",
+ mAUIndex, syncStartPos, payloadSize);
+
++syncStartPos;
}
@@ -683,14 +698,22 @@
mFormat = format;
}
- sp<ABuffer> accessUnit = new ABuffer(syncStartPos + payloadSize);
- memcpy(accessUnit->data(), mBuffer->data(), syncStartPos + payloadSize);
int64_t timeUs = fetchTimestamp(syncStartPos + payloadSize);
if (timeUs < 0ll) {
ALOGE("negative timeUs");
return NULL;
}
+
+ // Not decrypting if key info not available (e.g., scanner/extractor parsing ts files)
+ if (mSampleDecryptor != NULL) {
+ mSampleDecryptor->processAC3(mBuffer->data() + syncStartPos, payloadSize);
+ }
+ mAUIndex++;
+
+ sp<ABuffer> accessUnit = new ABuffer(syncStartPos + payloadSize);
+ memcpy(accessUnit->data(), mBuffer->data(), syncStartPos + payloadSize);
+
accessUnit->meta()->setInt64("timeUs", timeUs);
accessUnit->meta()->setInt32("isSync", 1);
@@ -791,6 +814,17 @@
return NULL;
}
+ ALOGV("dequeueAccessUnit_AAC[%d]: mBuffer %zu info.mLength %zu",
+ mAUIndex, mBuffer->size(), info.mLength);
+
+ struct ADTSPosition {
+ size_t offset;
+ size_t headerSize;
+ size_t length;
+ };
+
+ Vector<ADTSPosition> frames;
+
// The idea here is consume all AAC frames starting at offsets before
// info.mLength so we can assign a meaningful timestamp without
// having to interpolate.
@@ -811,7 +845,7 @@
return NULL;
}
bits.skipBits(3); // ID, layer
- bool protection_absent __unused = bits.getBits(1) != 0;
+ bool protection_absent = bits.getBits(1) != 0;
if (mFormat == NULL) {
unsigned profile = bits.getBits(2);
@@ -873,11 +907,36 @@
return NULL;
}
- size_t headerSize __unused = protection_absent ? 7 : 9;
+ size_t headerSize = protection_absent ? 7 : 9;
+
+ // tracking the frame positions first then decrypt only if an accessUnit to be generated
+ if (mSampleDecryptor != NULL) {
+ ADTSPosition frame = {
+ .offset = offset,
+ .headerSize = headerSize,
+ .length = aac_frame_length
+ };
+
+ frames.push(frame);
+ }
offset += aac_frame_length;
}
+ // Decrypting only if the loop didn't exit early and an accessUnit is about to be generated
+ // Not decrypting if key info not available (e.g., scanner/extractor parsing ts files)
+ if (mSampleDecryptor != NULL) {
+ for (size_t frameId = 0; frameId < frames.size(); frameId++) {
+ const ADTSPosition &frame = frames.itemAt(frameId);
+
+ mSampleDecryptor->processAAC(frame.headerSize,
+ mBuffer->data() + frame.offset, frame.length);
+// ALOGV("dequeueAccessUnitAAC[%zu]: while offset %zu headerSize %zu frame_len %zu",
+// frameId, frame.offset, frame.headerSize, frame.length);
+ }
+ }
+ mAUIndex++;
+
int64_t timeUs = fetchTimestamp(offset);
sp<ABuffer> accessUnit = new ABuffer(offset);
@@ -970,6 +1029,9 @@
size_t nalSize;
bool foundSlice = false;
bool foundIDR = false;
+
+ ALOGV("dequeueAccessUnit_H264[%d] %p/%zu", mAUIndex, data, size);
+
while ((err = getNextNALUnit(&data, &size, &nalStart, &nalSize)) == OK) {
if (nalSize == 0) continue;
@@ -981,6 +1043,7 @@
foundIDR = true;
}
if (foundSlice) {
+ //TODO: Shouldn't this have been called with nalSize-1?
ABitReader br(nalStart + 1, nalSize);
unsigned first_mb_in_slice = parseUE(&br);
@@ -1021,6 +1084,7 @@
size_t dstOffset = 0;
size_t seiIndex = 0;
+ size_t shrunkBytes = 0;
for (size_t i = 0; i < nals.size(); ++i) {
const NALPosition &pos = nals.itemAt(i);
@@ -1047,11 +1111,30 @@
memcpy(accessUnit->data() + dstOffset, "\x00\x00\x00\x01", 4);
- memcpy(accessUnit->data() + dstOffset + 4,
- mBuffer->data() + pos.nalOffset,
- pos.nalSize);
+ if (mSampleDecryptor != NULL && (nalType == 1 || nalType == 5)) {
+ uint8_t *nalData = mBuffer->data() + pos.nalOffset;
+ size_t newSize = mSampleDecryptor->processNal(nalData, pos.nalSize);
+ // Note: the data can shrink due to unescaping
+ memcpy(accessUnit->data() + dstOffset + 4,
+ nalData,
+ newSize);
+ dstOffset += newSize + 4;
- dstOffset += pos.nalSize + 4;
+ size_t thisShrunkBytes = pos.nalSize - newSize;
+ //ALOGV("dequeueAccessUnitH264[%d]: nalType: %d -> %zu (%zu)",
+ // nalType, (int)pos.nalSize, newSize, thisShrunkBytes);
+
+ shrunkBytes += thisShrunkBytes;
+ }
+ else {
+ memcpy(accessUnit->data() + dstOffset + 4,
+ mBuffer->data() + pos.nalOffset,
+ pos.nalSize);
+
+ dstOffset += pos.nalSize + 4;
+ //ALOGV("dequeueAccessUnitH264 [%d] %d @%d",
+ // nalType, (int)pos.nalSize, (int)pos.nalOffset);
+ }
}
#if !LOG_NDEBUG
@@ -1082,6 +1165,18 @@
mFormat = MakeAVCCodecSpecificData(accessUnit);
}
+ if (mSampleDecryptor != NULL && shrunkBytes > 0) {
+ size_t adjustedSize = accessUnit->size() - shrunkBytes;
+ ALOGV("dequeueAccessUnitH264[%d]: AU size adjusted %zu -> %zu",
+ mAUIndex, accessUnit->size(), adjustedSize);
+ accessUnit->setRange(0, adjustedSize);
+ }
+
+ ALOGV("dequeueAccessUnitH264[%d]: AU %p(%zu) dstOffset:%zu, nals:%zu, totalSize:%zu ",
+ mAUIndex, accessUnit->data(), accessUnit->size(),
+ dstOffset, nals.size(), totalSize);
+ mAUIndex++;
+
return accessUnit;
}
@@ -1612,4 +1707,15 @@
return accessUnit;
}
+void ElementaryStreamQueue::signalNewSampleAesKey(const sp<AMessage> &keyItem) {
+ if (mSampleDecryptor == NULL) {
+ ALOGE("signalNewSampleAesKey: Stream %x is not encrypted; keyItem: %p",
+ mMode, keyItem.get());
+ return;
+ }
+
+ mSampleDecryptor->signalNewSampleAesKey(keyItem);
+}
+
+
} // namespace android
diff --git a/media/libstagefright/mpeg2ts/ESQueue.h b/media/libstagefright/mpeg2ts/ESQueue.h
index 11e1af7..ffcb502 100644
--- a/media/libstagefright/mpeg2ts/ESQueue.h
+++ b/media/libstagefright/mpeg2ts/ESQueue.h
@@ -19,11 +19,14 @@
#define ES_QUEUE_H_
#include <media/stagefright/foundation/ABase.h>
+#include <media/stagefright/foundation/AMessage.h>
#include <utils/Errors.h>
#include <utils/List.h>
#include <utils/RefBase.h>
#include <vector>
+#include "HlsSampleDecryptor.h"
+
namespace android {
struct ABuffer;
@@ -46,6 +49,7 @@
// Data appended to the queue is always at access unit boundaries.
kFlag_AlignedData = 1,
kFlag_ScrambledData = 2,
+ kFlag_SampleEncryptedData = 4,
};
explicit ElementaryStreamQueue(Mode mode, uint32_t flags = 0);
@@ -69,6 +73,8 @@
void setCasInfo(int32_t systemId, const std::vector<uint8_t> &sessionId);
+ void signalNewSampleAesKey(const sp<AMessage> &keyItem);
+
private:
struct RangeInfo {
int64_t mTimestampUs;
@@ -100,6 +106,13 @@
sp<MetaData> mFormat;
+ sp<HlsSampleDecryptor> mSampleDecryptor;
+ int mAUIndex;
+
+ bool isSampleEncrypted() const {
+ return (mFlags & kFlag_SampleEncryptedData) != 0;
+ }
+
sp<ABuffer> dequeueAccessUnitH264();
sp<ABuffer> dequeueAccessUnitAAC();
sp<ABuffer> dequeueAccessUnitAC3();
diff --git a/media/libstagefright/mpeg2ts/HlsSampleDecryptor.cpp b/media/libstagefright/mpeg2ts/HlsSampleDecryptor.cpp
new file mode 100644
index 0000000..e32f676
--- /dev/null
+++ b/media/libstagefright/mpeg2ts/HlsSampleDecryptor.cpp
@@ -0,0 +1,336 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "HlsSampleDecryptor"
+
+#include "HlsSampleDecryptor.h"
+
+#include <media/stagefright/foundation/ABuffer.h>
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/Utils.h>
+
+
+namespace android {
+
+HlsSampleDecryptor::HlsSampleDecryptor()
+ : mValidKeyInfo(false) {
+}
+
+HlsSampleDecryptor::HlsSampleDecryptor(const sp<AMessage> &sampleAesKeyItem)
+ : mValidKeyInfo(false) {
+
+ signalNewSampleAesKey(sampleAesKeyItem);
+}
+
+void HlsSampleDecryptor::signalNewSampleAesKey(const sp<AMessage> &sampleAesKeyItem) {
+
+ if (sampleAesKeyItem == NULL) {
+ mValidKeyInfo = false;
+ ALOGW("signalNewSampleAesKey: sampleAesKeyItem is NULL");
+ return;
+ }
+
+ sp<ABuffer> keyDataBuffer, initVecBuffer;
+ sampleAesKeyItem->findBuffer("keyData", &keyDataBuffer);
+ sampleAesKeyItem->findBuffer("initVec", &initVecBuffer);
+
+ if (keyDataBuffer != NULL && keyDataBuffer->size() == AES_BLOCK_SIZE &&
+ initVecBuffer != NULL && initVecBuffer->size() == AES_BLOCK_SIZE) {
+
+ ALOGV("signalNewSampleAesKey: Key: %s IV: %s",
+ aesBlockToStr(keyDataBuffer->data()).c_str(),
+ aesBlockToStr(initVecBuffer->data()).c_str());
+
+ uint8_t KeyData[AES_BLOCK_SIZE];
+ memcpy(KeyData, keyDataBuffer->data(), AES_BLOCK_SIZE);
+ memcpy(mAESInitVec, initVecBuffer->data(), AES_BLOCK_SIZE);
+
+ mValidKeyInfo = (AES_set_decrypt_key(KeyData, 8*AES_BLOCK_SIZE/*128*/, &mAesKey) == 0);
+ if (!mValidKeyInfo) {
+ ALOGE("signalNewSampleAesKey: failed to set AES decryption key.");
+ }
+
+ } else {
+ // Media scanner might try extract/parse the TS files without knowing the key.
+ // Otherwise, shouldn't get here (unless an invalid playlist has swaped SAMPLE-AES with
+ // NONE method while still sample-encrypted stream is parsed).
+
+ mValidKeyInfo = false;
+ ALOGE("signalNewSampleAesKey Can't decrypt; keyDataBuffer: %p(%zu) initVecBuffer: %p(%zu)",
+ keyDataBuffer.get(), (keyDataBuffer.get() == NULL)? -1 : keyDataBuffer->size(),
+ initVecBuffer.get(), (initVecBuffer.get() == NULL)? -1 : initVecBuffer->size());
+ }
+}
+
+size_t HlsSampleDecryptor::processNal(uint8_t *nalData, size_t nalSize) {
+
+ unsigned nalType = nalData[0] & 0x1f;
+ if (!mValidKeyInfo) {
+ ALOGV("processNal[%d]: (%p)/%zu Skipping due to invalid key", nalType, nalData, nalSize);
+ return nalSize;
+ }
+
+ bool isEncrypted = (nalSize > VIDEO_CLEAR_LEAD + AES_BLOCK_SIZE);
+ ALOGV("processNal[%d]: (%p)/%zu isEncrypted: %d", nalType, nalData, nalSize, isEncrypted);
+
+ if (isEncrypted) {
+ // Encrypted NALUs have extra start code emulation prevention that must be
+ // stripped out before we can decrypt it.
+ size_t newSize = unescapeStream(nalData, nalSize);
+
+ ALOGV("processNal:unescapeStream[%d]: %zu -> %zu", nalType, nalSize, newSize);
+ nalSize = newSize;
+
+ //Encrypted_nal_unit () {
+ // nal_unit_type_byte // 1 byte
+ // unencrypted_leader // 31 bytes
+ // while (bytes_remaining() > 0) {
+ // if (bytes_remaining() > 16) {
+ // encrypted_block // 16 bytes
+ // }
+ // unencrypted_block // MIN(144, bytes_remaining()) bytes
+ // }
+ //}
+
+ size_t offset = VIDEO_CLEAR_LEAD;
+ size_t remainingBytes = nalSize - VIDEO_CLEAR_LEAD;
+
+ // a copy of initVec as decryptBlock updates it
+ unsigned char AESInitVec[AES_BLOCK_SIZE];
+ memcpy(AESInitVec, mAESInitVec, AES_BLOCK_SIZE);
+
+ while (remainingBytes > 0) {
+ // encrypted_block: protected block uses 10% skip encryption
+ if (remainingBytes > AES_BLOCK_SIZE) {
+ uint8_t *encrypted = nalData + offset;
+ status_t ret = decryptBlock(encrypted, AES_BLOCK_SIZE, AESInitVec);
+ if (ret != OK) {
+ ALOGE("processNal failed with %d", ret);
+ return nalSize; // revisit this
+ }
+
+ offset += AES_BLOCK_SIZE;
+ remainingBytes -= AES_BLOCK_SIZE;
+ }
+
+ // unencrypted_block
+ size_t clearBytes = std::min(remainingBytes, (size_t)(9 * AES_BLOCK_SIZE));
+
+ offset += clearBytes;
+ remainingBytes -= clearBytes;
+ } // while
+
+ } else { // isEncrypted == false
+ ALOGV("processNal[%d]: Unencrypted NALU (%p)/%zu", nalType, nalData, nalSize);
+ }
+
+ return nalSize;
+}
+
+void HlsSampleDecryptor::processAAC(size_t adtsHdrSize, uint8_t *data, size_t size) {
+
+ if (!mValidKeyInfo) {
+ ALOGV("processAAC: (%p)/%zu Skipping due to invalid key", data, size);
+ return;
+ }
+
+ // ADTS header is included in the size
+ size_t offset = adtsHdrSize;
+ size_t remainingBytes = size - adtsHdrSize;
+
+ bool isEncrypted = (remainingBytes >= AUDIO_CLEAR_LEAD + AES_BLOCK_SIZE);
+ ALOGV("processAAC: header: %zu data: %p(%zu) isEncrypted: %d",
+ adtsHdrSize, data, size, isEncrypted);
+
+ //Encrypted_AAC_Frame () {
+ // ADTS_Header // 7 or 9 bytes
+ // unencrypted_leader // 16 bytes
+ // while (bytes_remaining() >= 16) {
+ // encrypted_block // 16 bytes
+ // }
+ // unencrypted_trailer // 0-15 bytes
+ //}
+
+ // with lead bytes
+ if (remainingBytes >= AUDIO_CLEAR_LEAD) {
+ offset += AUDIO_CLEAR_LEAD;
+ remainingBytes -= AUDIO_CLEAR_LEAD;
+
+ // encrypted_block
+ if (remainingBytes >= AES_BLOCK_SIZE) {
+
+ size_t encryptedBytes = (remainingBytes / AES_BLOCK_SIZE) * AES_BLOCK_SIZE;
+ unsigned char AESInitVec[AES_BLOCK_SIZE];
+ memcpy(AESInitVec, mAESInitVec, AES_BLOCK_SIZE);
+
+ // decrypting all blocks at once
+ uint8_t *encrypted = data + offset;
+ status_t ret = decryptBlock(encrypted, encryptedBytes, AESInitVec);
+ if (ret != OK) {
+ ALOGE("processAAC: decryptBlock failed with %d", ret);
+ return;
+ }
+
+ offset += encryptedBytes;
+ remainingBytes -= encryptedBytes;
+ } // encrypted
+
+ // unencrypted_trailer
+ size_t clearBytes = remainingBytes;
+ if (clearBytes > 0) {
+ CHECK(clearBytes < AES_BLOCK_SIZE);
+ }
+
+ } else { // without lead bytes
+ ALOGV("processAAC: Unencrypted frame (without lead bytes) size %zu = %zu (hdr) + %zu (rem)",
+ size, adtsHdrSize, remainingBytes);
+ }
+
+}
+
+void HlsSampleDecryptor::processAC3(uint8_t *data, size_t size) {
+
+ if (!mValidKeyInfo) {
+ ALOGV("processAC3: (%p)/%zu Skipping due to invalid key", data, size);
+ return;
+ }
+
+ bool isEncrypted = (size >= AUDIO_CLEAR_LEAD + AES_BLOCK_SIZE);
+ ALOGV("processAC3 %p(%zu) isEncrypted: %d", data, size, isEncrypted);
+
+ //Encrypted_AC3_Frame () {
+ // unencrypted_leader // 16 bytes
+ // while (bytes_remaining() >= 16) {
+ // encrypted_block // 16 bytes
+ // }
+ // unencrypted_trailer // 0-15 bytes
+ //}
+
+ if (size >= AUDIO_CLEAR_LEAD) {
+ // unencrypted_leader
+ size_t offset = AUDIO_CLEAR_LEAD;
+ size_t remainingBytes = size - AUDIO_CLEAR_LEAD;
+
+ if (remainingBytes >= AES_BLOCK_SIZE) {
+
+ size_t encryptedBytes = (remainingBytes / AES_BLOCK_SIZE) * AES_BLOCK_SIZE;
+
+ // encrypted_block
+ unsigned char AESInitVec[AES_BLOCK_SIZE];
+ memcpy(AESInitVec, mAESInitVec, AES_BLOCK_SIZE);
+
+ // decrypting all blocks at once
+ uint8_t *encrypted = data + offset;
+ status_t ret = decryptBlock(encrypted, encryptedBytes, AESInitVec);
+ if (ret != OK) {
+ ALOGE("processAC3: decryptBlock failed with %d", ret);
+ return;
+ }
+
+ offset += encryptedBytes;
+ remainingBytes -= encryptedBytes;
+ } // encrypted
+
+ // unencrypted_trailer
+ size_t clearBytes = remainingBytes;
+ if (clearBytes > 0) {
+ CHECK(clearBytes < AES_BLOCK_SIZE);
+ }
+
+ } else {
+ ALOGV("processAC3: Unencrypted frame (without lead bytes) size %zu", size);
+ }
+}
+
+// Unescapes data replacing occurrences of [0, 0, 3] with [0, 0] and returns the new size
+size_t HlsSampleDecryptor::unescapeStream(uint8_t *data, size_t limit) const {
+ Vector<size_t> scratchEscapePositions;
+ size_t position = 0;
+
+ while (position < limit) {
+ position = findNextUnescapeIndex(data, position, limit);
+ if (position < limit) {
+ scratchEscapePositions.add(position);
+ position += 3;
+ }
+ }
+
+ size_t scratchEscapeCount = scratchEscapePositions.size();
+ size_t escapedPosition = 0; // The position being read from.
+ size_t unescapedPosition = 0; // The position being written to.
+ for (size_t i = 0; i < scratchEscapeCount; i++) {
+ size_t nextEscapePosition = scratchEscapePositions[i];
+ //TODO: add 2 and get rid of the later = 0 assignments
+ size_t copyLength = nextEscapePosition - escapedPosition;
+ memmove(data+unescapedPosition, data+escapedPosition, copyLength);
+ unescapedPosition += copyLength;
+ data[unescapedPosition++] = 0;
+ data[unescapedPosition++] = 0;
+ escapedPosition += copyLength + 3;
+ }
+
+ size_t unescapedLength = limit - scratchEscapeCount;
+ size_t remainingLength = unescapedLength - unescapedPosition;
+ memmove(data+unescapedPosition, data+escapedPosition, remainingLength);
+
+ return unescapedLength;
+}
+
+size_t HlsSampleDecryptor::findNextUnescapeIndex(uint8_t *data, size_t offset, size_t limit) const {
+ for (size_t i = offset; i < limit - 2; i++) {
+ //TODO: speed
+ if (data[i] == 0x00 && data[i + 1] == 0x00 && data[i + 2] == 0x03) {
+ return i;
+ }
+ }
+ return limit;
+}
+
+status_t HlsSampleDecryptor::decryptBlock(uint8_t *buffer, size_t size,
+ uint8_t AESInitVec[AES_BLOCK_SIZE]) {
+ if (size == 0) {
+ return OK;
+ }
+
+ if ((size % AES_BLOCK_SIZE) != 0) {
+ ALOGE("decryptBlock: size (%zu) not a multiple of block size", size);
+ return ERROR_MALFORMED;
+ }
+
+ ALOGV("decryptBlock: %p (%zu)", buffer, size);
+
+ AES_cbc_encrypt(buffer, buffer, size, &mAesKey, AESInitVec, AES_DECRYPT);
+
+ return OK;
+}
+
+AString HlsSampleDecryptor::aesBlockToStr(uint8_t block[AES_BLOCK_SIZE]) {
+ AString result;
+
+ if (block == NULL) {
+ result = AString("null");
+ } else {
+ result = AStringPrintf("0x%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X",
+ block[0], block[1], block[2], block[3], block[4], block[5], block[6], block[7],
+ block[8], block[9], block[10], block[11], block[12], block[13], block[14], block[15]);
+ }
+
+ return result;
+}
+
+
+} // namespace android
diff --git a/media/libstagefright/mpeg2ts/HlsSampleDecryptor.h b/media/libstagefright/mpeg2ts/HlsSampleDecryptor.h
new file mode 100644
index 0000000..2c76620
--- /dev/null
+++ b/media/libstagefright/mpeg2ts/HlsSampleDecryptor.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SAMPLE_AES_PROCESSOR_H_
+
+#define SAMPLE_AES_PROCESSOR_H_
+
+#include <media/stagefright/foundation/AMessage.h>
+#include <media/stagefright/foundation/AString.h>
+
+#include <openssl/aes.h>
+
+#include <utils/Errors.h>
+#include <utils/List.h>
+#include <utils/RefBase.h>
+#include <utils/Vector.h>
+
+namespace android {
+
+struct HlsSampleDecryptor : RefBase {
+
+ HlsSampleDecryptor();
+ explicit HlsSampleDecryptor(const sp<AMessage> &sampleAesKeyItem);
+
+ void signalNewSampleAesKey(const sp<AMessage> &sampleAesKeyItem);
+
+ size_t processNal(uint8_t *nalData, size_t nalSize);
+ void processAAC(size_t adtsHdrSize, uint8_t *data, size_t size);
+ void processAC3(uint8_t *data, size_t size);
+
+ static AString aesBlockToStr(uint8_t block[AES_BLOCK_SIZE]);
+
+private:
+ size_t unescapeStream(uint8_t *data, size_t limit) const;
+ size_t findNextUnescapeIndex(uint8_t *data, size_t offset, size_t limit) const;
+ status_t decryptBlock(uint8_t *buffer, size_t size, uint8_t AESInitVec[AES_BLOCK_SIZE]);
+
+ static const int VIDEO_CLEAR_LEAD = 32;
+ static const int AUDIO_CLEAR_LEAD = 16;
+
+ AES_KEY mAesKey;
+ uint8_t mAESInitVec[AES_BLOCK_SIZE];
+ bool mValidKeyInfo;
+
+ DISALLOW_EVIL_CONSTRUCTORS(HlsSampleDecryptor);
+};
+
+} // namespace android
+
+#endif // SAMPLE_AES_PROCESSOR_H_
diff --git a/media/libstagefright/omx/1.0/WGraphicBufferSource.cpp b/media/libstagefright/omx/1.0/WGraphicBufferSource.cpp
index 3c2face..e876306 100644
--- a/media/libstagefright/omx/1.0/WGraphicBufferSource.cpp
+++ b/media/libstagefright/omx/1.0/WGraphicBufferSource.cpp
@@ -192,8 +192,8 @@
}
Return<Status> TWGraphicBufferSource::setTimeLapseConfig(
- int64_t timePerFrameUs, int64_t timePerCaptureUs) {
- return toStatus(mBase->setTimeLapseConfig(timePerFrameUs, timePerCaptureUs));
+ double fps, double captureFps) {
+ return toStatus(mBase->setTimeLapseConfig(fps, captureFps));
}
Return<Status> TWGraphicBufferSource::setStartTimeUs(int64_t startTimeUs) {
@@ -204,6 +204,13 @@
return toStatus(mBase->setStopTimeUs(stopTimeUs));
}
+Return<void> TWGraphicBufferSource::getStopTimeOffsetUs(
+ getStopTimeOffsetUs_cb _hidl_cb) {
+ // TODO: Implement this when needed.
+ _hidl_cb(Status::OK, 0);
+ return Void();
+}
+
Return<Status> TWGraphicBufferSource::setColorAspects(
const ColorAspects& aspects) {
return toStatus(mBase->setColorAspects(toCompactColorAspects(aspects)));
diff --git a/media/libstagefright/omx/1.0/WGraphicBufferSource.h b/media/libstagefright/omx/1.0/WGraphicBufferSource.h
index 73b86b8..4549c97 100644
--- a/media/libstagefright/omx/1.0/WGraphicBufferSource.h
+++ b/media/libstagefright/omx/1.0/WGraphicBufferSource.h
@@ -78,10 +78,10 @@
Return<Status> setSuspend(bool suspend, int64_t timeUs) override;
Return<Status> setRepeatPreviousFrameDelayUs(int64_t repeatAfterUs) override;
Return<Status> setMaxFps(float maxFps) override;
- Return<Status> setTimeLapseConfig(
- int64_t timePerFrameUs, int64_t timePerCaptureUs) override;
+ Return<Status> setTimeLapseConfig(double fps, double captureFps) override;
Return<Status> setStartTimeUs(int64_t startTimeUs) override;
Return<Status> setStopTimeUs(int64_t stopTimeUs) override;
+ Return<void> getStopTimeOffsetUs(getStopTimeOffsetUs_cb _hidl_cb) override;
Return<Status> setColorAspects(const ColorAspects& aspects) override;
Return<Status> setTimeOffsetUs(int64_t timeOffsetUs) override;
Return<Status> signalEndOfInputStream() override;
diff --git a/media/libstagefright/omx/BWGraphicBufferSource.cpp b/media/libstagefright/omx/BWGraphicBufferSource.cpp
index 4e0f6dd..f2a454f 100644
--- a/media/libstagefright/omx/BWGraphicBufferSource.cpp
+++ b/media/libstagefright/omx/BWGraphicBufferSource.cpp
@@ -145,9 +145,9 @@
}
::android::binder::Status BWGraphicBufferSource::setTimeLapseConfig(
- int64_t timePerFrameUs, int64_t timePerCaptureUs) {
+ double fps, double captureFps) {
return Status::fromStatusT(mBase->setTimeLapseConfig(
- timePerFrameUs, timePerCaptureUs));
+ fps, captureFps));
}
::android::binder::Status BWGraphicBufferSource::setStartTimeUs(
diff --git a/media/libstagefright/omx/BWGraphicBufferSource.h b/media/libstagefright/omx/BWGraphicBufferSource.h
index f1ce2af..43763c2 100644
--- a/media/libstagefright/omx/BWGraphicBufferSource.h
+++ b/media/libstagefright/omx/BWGraphicBufferSource.h
@@ -50,7 +50,7 @@
int64_t repeatAfterUs) override;
Status setMaxFps(float maxFps) override;
Status setTimeLapseConfig(
- int64_t timePerFrameUs, int64_t timePerCaptureUs) override;
+ double fps, double captureFps) override;
Status setStartTimeUs(int64_t startTimeUs) override;
Status setStopTimeUs(int64_t stopTimeUs) override;
Status setColorAspects(int32_t aspects) override;
diff --git a/media/libstagefright/omx/GraphicBufferSource.cpp b/media/libstagefright/omx/GraphicBufferSource.cpp
index 5257b50..0521460 100644
--- a/media/libstagefright/omx/GraphicBufferSource.cpp
+++ b/media/libstagefright/omx/GraphicBufferSource.cpp
@@ -42,6 +42,7 @@
#include <functional>
#include <memory>
+#include <cmath>
namespace android {
@@ -270,8 +271,11 @@
mRepeatLastFrameGeneration(0),
mOutstandingFrameRepeatCount(0),
mFrameRepeatBlockedOnCodecBuffer(false),
- mTimePerCaptureUs(-1ll),
- mTimePerFrameUs(-1ll),
+ mFps(-1.0),
+ mCaptureFps(-1.0),
+ mBaseCaptureUs(-1ll),
+ mBaseFrameUs(-1ll),
+ mFrameCount(0),
mPrevCaptureUs(-1ll),
mPrevFrameUs(-1ll),
mInputBufferTimeOffsetUs(0ll) {
@@ -713,26 +717,31 @@
int64_t timeUs = bufferTimeNs / 1000;
timeUs += mInputBufferTimeOffsetUs;
- if (mTimePerCaptureUs > 0ll
- && (mTimePerCaptureUs > 2 * mTimePerFrameUs
- || mTimePerFrameUs > 2 * mTimePerCaptureUs)) {
+ if (mCaptureFps > 0.
+ && (mFps > 2 * mCaptureFps
+ || mCaptureFps > 2 * mFps)) {
// Time lapse or slow motion mode
if (mPrevCaptureUs < 0ll) {
// first capture
- mPrevCaptureUs = timeUs;
+ mPrevCaptureUs = mBaseCaptureUs = timeUs;
// adjust the first sample timestamp.
- mPrevFrameUs = (timeUs * mTimePerFrameUs) / mTimePerCaptureUs;
+ mPrevFrameUs = mBaseFrameUs =
+ std::llround((timeUs * mCaptureFps) / mFps);
+ mFrameCount = 0;
} else {
// snap to nearest capture point
- int64_t nFrames = (timeUs + mTimePerCaptureUs / 2 - mPrevCaptureUs)
- / mTimePerCaptureUs;
+ int64_t nFrames = std::llround(
+ (timeUs - mPrevCaptureUs) * mCaptureFps);
if (nFrames <= 0) {
// skip this frame as it's too close to previous capture
ALOGV("skipping frame, timeUs %lld", static_cast<long long>(timeUs));
return false;
}
- mPrevCaptureUs += mTimePerCaptureUs * nFrames;
- mPrevFrameUs += mTimePerFrameUs * nFrames;
+ mFrameCount += nFrames;
+ mPrevCaptureUs = mBaseCaptureUs + std::llround(
+ mFrameCount / mCaptureFps);
+ mPrevFrameUs = mBaseFrameUs + std::llround(
+ mFrameCount / mFps);
}
ALOGV("timeUs %lld, captureUs %lld, frameUs %lld",
@@ -1054,10 +1063,13 @@
mOutstandingFrameRepeatCount = 0;
mLatestBuffer.mBuffer.reset();
mFrameRepeatBlockedOnCodecBuffer = false;
- mTimePerCaptureUs = -1ll;
- mTimePerFrameUs = -1ll;
+ mFps = -1.0;
+ mCaptureFps = -1.0;
+ mBaseCaptureUs = -1ll;
+ mBaseFrameUs = -1ll;
mPrevCaptureUs = -1ll;
mPrevFrameUs = -1ll;
+ mFrameCount = 0;
mInputBufferTimeOffsetUs = 0;
mStopTimeUs = -1;
mActionQueue.clear();
@@ -1202,18 +1214,18 @@
return OK;
}
-status_t GraphicBufferSource::setTimeLapseConfig(int64_t timePerFrameUs, int64_t timePerCaptureUs) {
- ALOGV("setTimeLapseConfig: timePerFrameUs=%lld, timePerCaptureUs=%lld",
- (long long)timePerFrameUs, (long long)timePerCaptureUs);
+status_t GraphicBufferSource::setTimeLapseConfig(double fps, double captureFps) {
+ ALOGV("setTimeLapseConfig: fps=%lg, captureFps=%lg",
+ fps, captureFps);
Mutex::Autolock autoLock(mMutex);
- if (mExecuting || timePerFrameUs <= 0ll || timePerCaptureUs <= 0ll) {
+ if (mExecuting || !(fps > 0) || !(captureFps > 0)) {
return INVALID_OPERATION;
}
- mTimePerFrameUs = timePerFrameUs;
- mTimePerCaptureUs = timePerCaptureUs;
+ mFps = fps;
+ mCaptureFps = captureFps;
return OK;
}
diff --git a/media/libstagefright/omx/GraphicBufferSource.h b/media/libstagefright/omx/GraphicBufferSource.h
index 635cfd6..3df1aa1 100644
--- a/media/libstagefright/omx/GraphicBufferSource.h
+++ b/media/libstagefright/omx/GraphicBufferSource.h
@@ -162,7 +162,7 @@
// Sets the time lapse (or slow motion) parameters.
// When set, the sample's timestamp will be modified to playback framerate,
// and capture timestamp will be modified to capture rate.
- status_t setTimeLapseConfig(int64_t timePerFrameUs, int64_t timePerCaptureUs);
+ status_t setTimeLapseConfig(double fps, double captureFps);
// Sets the start time us (in system time), samples before which should
// be dropped and not submitted to encoder
@@ -417,27 +417,39 @@
// Time lapse / slow motion configuration
// --------------------------------------
- // desired time interval between captured frames (capture interval) - value <= 0 if undefined
- int64_t mTimePerCaptureUs;
+ // desired frame rate for encoding - value <= 0 if undefined
+ double mFps;
- // desired time interval between encoded frames (media time interval) - value <= 0 if undefined
- int64_t mTimePerFrameUs;
+ // desired frame rate for capture - value <= 0 if undefined
+ double mCaptureFps;
- // Time lapse mode is enabled if capture interval is defined and it is more than twice the
- // media time interval (if defined). In this mode frames that come in between the capture
- // interval are dropped and the media timestamp is adjusted to have exactly the desired
- // media time interval.
+ // Time lapse mode is enabled if the capture frame rate is defined and it is
+ // smaller than half the encoding frame rate (if defined). In this mode,
+ // frames that come in between the capture interval (the reciprocal of the
+ // capture frame rate) are dropped and the encoding timestamp is adjusted to
+ // match the desired encoding frame rate.
//
- // Slow motion mode is enabled if both media and capture intervals are defined and the media
- // time interval is more than twice the capture interval. In this mode frames that come in
- // between the capture interval are dropped (though there isn't expected to be any, but there
- // could eventually be a frame drop if the actual capture interval is smaller than the
- // configured capture interval). The media timestamp is adjusted to have exactly the desired
- // media time interval.
+ // Slow motion mode is enabled if both encoding and capture frame rates are
+ // defined and the encoding frame rate is less than half the capture frame
+ // rate. In this mode, the source is expected to produce frames with an even
+ // timestamp interval (after rounding) with the configured capture fps. The
+ // first source timestamp is used as the source base time. Afterwards, the
+ // timestamp of each source frame is snapped to the nearest expected capture
+ // timestamp and scaled to match the configured encoding frame rate.
// These modes must be enabled before using this source.
- // adjusted capture timestamp for previous frame
+ // adjusted capture timestamp of the base frame
+ int64_t mBaseCaptureUs;
+
+ // adjusted encoding timestamp of the base frame
+ int64_t mBaseFrameUs;
+
+ // number of frames from the base time
+ int64_t mFrameCount;
+
+ // adjusted capture timestamp for previous frame (negative if there were
+ // none)
int64_t mPrevCaptureUs;
// adjusted media timestamp for previous frame (negative if there were none)
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index 42e9c6b..9f19dfd 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -2676,12 +2676,14 @@
// checkPlaybackThread_l() must be called with AudioFlinger::mLock held
AudioFlinger::VolumeInterface *AudioFlinger::getVolumeInterface_l(audio_io_handle_t output) const
{
- VolumeInterface *volumeInterface = (VolumeInterface *)mPlaybackThreads.valueFor(output).get();
+ VolumeInterface *volumeInterface = mPlaybackThreads.valueFor(output).get();
if (volumeInterface == nullptr) {
MmapThread *mmapThread = mMmapThreads.valueFor(output).get();
if (mmapThread != nullptr) {
if (mmapThread->isOutput()) {
- volumeInterface = (VolumeInterface *)mmapThread;
+ MmapPlaybackThread *mmapPlaybackThread =
+ static_cast<MmapPlaybackThread *>(mmapThread);
+ volumeInterface = mmapPlaybackThread;
}
}
}
@@ -2692,11 +2694,13 @@
{
Vector <VolumeInterface *> volumeInterfaces;
for (size_t i = 0; i < mPlaybackThreads.size(); i++) {
- volumeInterfaces.add((VolumeInterface *)mPlaybackThreads.valueAt(i).get());
+ volumeInterfaces.add(mPlaybackThreads.valueAt(i).get());
}
for (size_t i = 0; i < mMmapThreads.size(); i++) {
if (mMmapThreads.valueAt(i)->isOutput()) {
- volumeInterfaces.add((VolumeInterface *)mMmapThreads.valueAt(i).get());
+ MmapPlaybackThread *mmapPlaybackThread =
+ static_cast<MmapPlaybackThread *>(mMmapThreads.valueAt(i).get());
+ volumeInterfaces.add(mmapPlaybackThread);
}
}
return volumeInterfaces;
diff --git a/services/camera/libcameraservice/api1/CameraClient.cpp b/services/camera/libcameraservice/api1/CameraClient.cpp
index df8726e..f12cc7b 100644
--- a/services/camera/libcameraservice/api1/CameraClient.cpp
+++ b/services/camera/libcameraservice/api1/CameraClient.cpp
@@ -478,14 +478,23 @@
// stop recording mode
void CameraClient::stopRecording() {
LOG1("stopRecording (pid %d)", getCallingPid());
- Mutex::Autolock lock(mLock);
- if (checkPidAndHardware() != NO_ERROR) return;
+ {
+ Mutex::Autolock lock(mLock);
+ if (checkPidAndHardware() != NO_ERROR) return;
- disableMsgType(CAMERA_MSG_VIDEO_FRAME);
- mHardware->stopRecording();
- sCameraService->playSound(CameraService::SOUND_RECORDING_STOP);
+ disableMsgType(CAMERA_MSG_VIDEO_FRAME);
+ mHardware->stopRecording();
+ sCameraService->playSound(CameraService::SOUND_RECORDING_STOP);
- mPreviewBuffer.clear();
+ mPreviewBuffer.clear();
+ }
+
+ {
+ Mutex::Autolock l(mAvailableCallbackBuffersLock);
+ if (!mAvailableCallbackBuffers.empty()) {
+ mAvailableCallbackBuffers.clear();
+ }
+ }
}
// release a recording frame
diff --git a/services/camera/libcameraservice/device3/Camera3Device.h b/services/camera/libcameraservice/device3/Camera3Device.h
index eaffad8..8b76a97 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.h
+++ b/services/camera/libcameraservice/device3/Camera3Device.h
@@ -294,9 +294,7 @@
size_t result = 1;
result = 31 * result + buf->numFds;
- result = 31 * result + buf->numInts;
- int length = buf->numFds + buf->numInts;
- for (int i = 0; i < length; i++) {
+ for (int i = 0; i < buf->numFds; i++) {
result = 31 * result + buf->data[i];
}
return result;
@@ -305,9 +303,8 @@
struct BufferComparator {
bool operator()(const buffer_handle_t& buf1, const buffer_handle_t& buf2) const {
- if (buf1->numFds == buf2->numFds && buf1->numInts == buf2->numInts) {
- int length = buf1->numFds + buf1->numInts;
- for (int i = 0; i < length; i++) {
+ if (buf1->numFds == buf2->numFds) {
+ for (int i = 0; i < buf1->numFds; i++) {
if (buf1->data[i] != buf2->data[i]) {
return false;
}
diff --git a/services/mediacodec/seccomp_policy/mediacodec-arm.policy b/services/mediacodec/seccomp_policy/mediacodec-arm.policy
index 890d777..b8a5e90 100644
--- a/services/mediacodec/seccomp_policy/mediacodec-arm.policy
+++ b/services/mediacodec/seccomp_policy/mediacodec-arm.policy
@@ -29,6 +29,7 @@
setpriority: 1
getuid32: 1
fstat64: 1
+fstatfs64: 1
pread64: 1
faccessat: 1
readlinkat: 1
diff --git a/services/mediadrm/FactoryLoader.h b/services/mediadrm/FactoryLoader.h
index 1e03e9b..d7f1118 100644
--- a/services/mediadrm/FactoryLoader.h
+++ b/services/mediadrm/FactoryLoader.h
@@ -88,7 +88,7 @@
}
// no luck, have to search
- String8 dirPath("/vendor/lib/mediacas");
+ String8 dirPath("/system/lib/mediacas");
DIR* pDir = opendir(dirPath.string());
if (pDir == NULL) {
@@ -123,7 +123,7 @@
results->clear();
- String8 dirPath("/vendor/lib/mediacas");
+ String8 dirPath("/system/lib/mediacas");
DIR* pDir = opendir(dirPath.string());
if (pDir == NULL) {
diff --git a/services/mediaextractor/seccomp_policy/mediaextractor-arm.policy b/services/mediaextractor/seccomp_policy/mediaextractor-arm.policy
index 4e4ce30..7e8af1a 100644
--- a/services/mediaextractor/seccomp_policy/mediaextractor-arm.policy
+++ b/services/mediaextractor/seccomp_policy/mediaextractor-arm.policy
@@ -20,6 +20,7 @@
lseek: 1
writev: 1
fstatat64: 1
+fstatfs64: 1
fstat64: 1
restart_syscall: 1
exit: 1
diff --git a/services/mediaextractor/seccomp_policy/mediaextractor-arm64.policy b/services/mediaextractor/seccomp_policy/mediaextractor-arm64.policy
index 1683adb..aa8be5b 100644
--- a/services/mediaextractor/seccomp_policy/mediaextractor-arm64.policy
+++ b/services/mediaextractor/seccomp_policy/mediaextractor-arm64.policy
@@ -14,6 +14,7 @@
madvise: 1
getuid: 1
fstat: 1
+fstatfs: 1
read: 1
setpriority: 1
sigaltstack: 1
diff --git a/services/mediaextractor/seccomp_policy/mediaextractor-x86.policy b/services/mediaextractor/seccomp_policy/mediaextractor-x86.policy
index 83725cd..b5a6503 100644
--- a/services/mediaextractor/seccomp_policy/mediaextractor-x86.policy
+++ b/services/mediaextractor/seccomp_policy/mediaextractor-x86.policy
@@ -22,6 +22,7 @@
setpriority: 1
sigaltstack: 1
fstatat64: 1
+fstatfs64: 1
fstat64: 1
restart_syscall: 1
exit: 1
diff --git a/services/oboeservice/AAudioEndpointManager.cpp b/services/oboeservice/AAudioEndpointManager.cpp
index 84fa227..65b17bc 100644
--- a/services/oboeservice/AAudioEndpointManager.cpp
+++ b/services/oboeservice/AAudioEndpointManager.cpp
@@ -14,6 +14,10 @@
* limitations under the License.
*/
+#define LOG_TAG "AAudioService"
+//#define LOG_NDEBUG 0
+#include <utils/Log.h>
+
#include <assert.h>
#include <map>
#include <mutex>
@@ -28,13 +32,18 @@
ANDROID_SINGLETON_STATIC_INSTANCE(AAudioEndpointManager);
AAudioEndpointManager::AAudioEndpointManager()
- : Singleton<AAudioEndpointManager>() {
+ : Singleton<AAudioEndpointManager>()
+ , mInputs()
+ , mOutputs() {
}
-AAudioServiceEndpoint *AAudioEndpointManager::findEndpoint(AAudioService &audioService, int32_t deviceId,
+AAudioServiceEndpoint *AAudioEndpointManager::openEndpoint(AAudioService &audioService, int32_t deviceId,
aaudio_direction_t direction) {
AAudioServiceEndpoint *endpoint = nullptr;
std::lock_guard<std::mutex> lock(mLock);
+
+ // Try to find an existing endpoint.
+ ALOGD("AAudioEndpointManager::openEndpoint(), device = %d, dir = %d", deviceId, direction);
switch (direction) {
case AAUDIO_DIRECTION_INPUT:
endpoint = mInputs[deviceId];
@@ -48,11 +57,11 @@
}
// If we can't find an existing one then open one.
- ALOGD("AAudioEndpointManager::findEndpoint(), found %p", endpoint);
+ ALOGD("AAudioEndpointManager::openEndpoint(), found %p", endpoint);
if (endpoint == nullptr) {
endpoint = new AAudioServiceEndpoint(audioService);
if (endpoint->open(deviceId, direction) != AAUDIO_OK) {
- ALOGD("AAudioEndpointManager::findEndpoint(), open failed");
+ ALOGE("AAudioEndpointManager::findEndpoint(), open failed");
delete endpoint;
endpoint = nullptr;
} else {
@@ -66,22 +75,37 @@
}
}
}
+
+ if (endpoint != nullptr) {
+ // Increment the reference count under this lock.
+ endpoint->setReferenceCount(endpoint->getReferenceCount() + 1);
+ }
+
return endpoint;
}
-// FIXME add reference counter for serviceEndpoints and removed on last use.
-
-void AAudioEndpointManager::removeEndpoint(AAudioServiceEndpoint *serviceEndpoint) {
- aaudio_direction_t direction = serviceEndpoint->getDirection();
- int32_t deviceId = serviceEndpoint->getDeviceId();
-
+void AAudioEndpointManager::closeEndpoint(AAudioServiceEndpoint *serviceEndpoint) {
std::lock_guard<std::mutex> lock(mLock);
- switch(direction) {
- case AAUDIO_DIRECTION_INPUT:
- mInputs.erase(deviceId);
- break;
- case AAUDIO_DIRECTION_OUTPUT:
- mOutputs.erase(deviceId);
- break;
+ if (serviceEndpoint == nullptr) {
+ return;
}
-}
\ No newline at end of file
+
+ // Decrement the reference count under this lock.
+ int32_t newRefCount = serviceEndpoint->getReferenceCount() - 1;
+ serviceEndpoint->setReferenceCount(newRefCount);
+ if (newRefCount <= 0) {
+ aaudio_direction_t direction = serviceEndpoint->getDirection();
+ int32_t deviceId = serviceEndpoint->getDeviceId();
+
+ switch (direction) {
+ case AAUDIO_DIRECTION_INPUT:
+ mInputs.erase(deviceId);
+ break;
+ case AAUDIO_DIRECTION_OUTPUT:
+ mOutputs.erase(deviceId);
+ break;
+ }
+ serviceEndpoint->close();
+ delete serviceEndpoint;
+ }
+}
diff --git a/services/oboeservice/AAudioEndpointManager.h b/services/oboeservice/AAudioEndpointManager.h
index 48b27f0..bbcfc1d 100644
--- a/services/oboeservice/AAudioEndpointManager.h
+++ b/services/oboeservice/AAudioEndpointManager.h
@@ -39,11 +39,11 @@
* @param direction
* @return endpoint or nullptr
*/
- AAudioServiceEndpoint *findEndpoint(android::AAudioService &audioService,
+ AAudioServiceEndpoint *openEndpoint(android::AAudioService &audioService,
int32_t deviceId,
aaudio_direction_t direction);
- void removeEndpoint(AAudioServiceEndpoint *serviceEndpoint);
+ void closeEndpoint(AAudioServiceEndpoint *serviceEndpoint);
private:
diff --git a/services/oboeservice/AAudioMixer.cpp b/services/oboeservice/AAudioMixer.cpp
index 70da339..43203d4 100644
--- a/services/oboeservice/AAudioMixer.cpp
+++ b/services/oboeservice/AAudioMixer.cpp
@@ -41,7 +41,7 @@
memset(mOutputBuffer, 0, mBufferSizeInBytes);
}
-void AAudioMixer::mix(FifoBuffer *fifo, float volume) {
+bool AAudioMixer::mix(FifoBuffer *fifo, float volume) {
WrappingBuffer wrappingBuffer;
float *destination = mOutputBuffer;
fifo_frames_t framesLeft = mFramesPerBurst;
@@ -67,9 +67,10 @@
}
fifo->getFifoControllerBase()->advanceReadIndex(mFramesPerBurst - framesLeft);
if (framesLeft > 0) {
- ALOGW("AAudioMixer::mix() UNDERFLOW by %d / %d frames ----- UNDERFLOW !!!!!!!!!!",
- framesLeft, mFramesPerBurst);
+ //ALOGW("AAudioMixer::mix() UNDERFLOW by %d / %d frames ----- UNDERFLOW !!!!!!!!!!",
+ // framesLeft, mFramesPerBurst);
}
+ return (framesLeft > 0); // did not get all the frames we needed, ie. "underflow"
}
void AAudioMixer::mixPart(float *destination, float *source, int32_t numFrames, float volume) {
diff --git a/services/oboeservice/AAudioMixer.h b/services/oboeservice/AAudioMixer.h
index 2191183..9155fec 100644
--- a/services/oboeservice/AAudioMixer.h
+++ b/services/oboeservice/AAudioMixer.h
@@ -31,7 +31,13 @@
void clear();
- void mix(android::FifoBuffer *fifo, float volume);
+ /**
+ * Mix from this FIFO
+ * @param fifo
+ * @param volume
+ * @return true if underflowed
+ */
+ bool mix(android::FifoBuffer *fifo, float volume);
void mixPart(float *destination, float *source, int32_t numFrames, float volume);
diff --git a/services/oboeservice/AAudioService.cpp b/services/oboeservice/AAudioService.cpp
index 723ef63..816d5ab 100644
--- a/services/oboeservice/AAudioService.cpp
+++ b/services/oboeservice/AAudioService.cpp
@@ -54,8 +54,8 @@
aaudio_result_t result = AAUDIO_OK;
AAudioServiceStreamBase *serviceStream = nullptr;
const AAudioStreamConfiguration &configurationInput = request.getConstantConfiguration();
+ bool sharingModeMatchRequired = request.isSharingModeMatchRequired();
aaudio_sharing_mode_t sharingMode = configurationInput.getSharingMode();
- ALOGE("AAudioService::openStream(): sharingMode = %d", sharingMode);
if (sharingMode != AAUDIO_SHARING_MODE_EXCLUSIVE && sharingMode != AAUDIO_SHARING_MODE_SHARED) {
ALOGE("AAudioService::openStream(): unrecognized sharing mode = %d", sharingMode);
@@ -77,8 +77,9 @@
}
// if SHARED requested or if EXCLUSIVE failed
- if (serviceStream == nullptr) {
- ALOGD("AAudioService::openStream(), sharingMode = AAUDIO_SHARING_MODE_SHARED");
+ if (sharingMode == AAUDIO_SHARING_MODE_SHARED
+ || (serviceStream == nullptr && !sharingModeMatchRequired)) {
+ ALOGD("AAudioService::openStream(), try AAUDIO_SHARING_MODE_SHARED");
serviceStream = new AAudioServiceStreamShared(*this);
result = serviceStream->open(request, configurationOutput);
configurationOutput.setSharingMode(AAUDIO_SHARING_MODE_SHARED);
@@ -126,9 +127,7 @@
ALOGE("AAudioService::getStreamDescription(), illegal stream handle = 0x%0x", streamHandle);
return AAUDIO_ERROR_INVALID_HANDLE;
}
- ALOGD("AAudioService::getStreamDescription(), handle = 0x%08x", streamHandle);
aaudio_result_t result = serviceStream->getDescription(parcelable);
- ALOGD("AAudioService::getStreamDescription(), result = %d", result);
// parcelable.dump();
return result;
}
@@ -140,7 +139,6 @@
return AAUDIO_ERROR_INVALID_HANDLE;
}
aaudio_result_t result = serviceStream->start();
- ALOGD("AAudioService::startStream(), serviceStream->start() returned %d", result);
return result;
}
@@ -154,6 +152,16 @@
return result;
}
+aaudio_result_t AAudioService::stopStream(aaudio_handle_t streamHandle) {
+ AAudioServiceStreamBase *serviceStream = convertHandleToServiceStream(streamHandle);
+ if (serviceStream == nullptr) {
+ ALOGE("AAudioService::pauseStream(), illegal stream handle = 0x%0x", streamHandle);
+ return AAUDIO_ERROR_INVALID_HANDLE;
+ }
+ aaudio_result_t result = serviceStream->stop();
+ return result;
+}
+
aaudio_result_t AAudioService::flushStream(aaudio_handle_t streamHandle) {
AAudioServiceStreamBase *serviceStream = convertHandleToServiceStream(streamHandle);
if (serviceStream == nullptr) {
@@ -168,7 +176,6 @@
pid_t clientThreadId,
int64_t periodNanoseconds) {
AAudioServiceStreamBase *serviceStream = convertHandleToServiceStream(streamHandle);
- ALOGD("AAudioService::registerAudioThread(), serviceStream = %p", serviceStream);
if (serviceStream == nullptr) {
ALOGE("AAudioService::registerAudioThread(), illegal stream handle = 0x%0x", streamHandle);
return AAUDIO_ERROR_INVALID_HANDLE;
@@ -193,7 +200,6 @@
pid_t clientProcessId,
pid_t clientThreadId) {
AAudioServiceStreamBase *serviceStream = convertHandleToServiceStream(streamHandle);
- ALOGI("AAudioService::unregisterAudioThread(), serviceStream = %p", serviceStream);
if (serviceStream == nullptr) {
ALOGE("AAudioService::unregisterAudioThread(), illegal stream handle = 0x%0x",
streamHandle);
diff --git a/services/oboeservice/AAudioService.h b/services/oboeservice/AAudioService.h
index 5a7a2b6..f5a7d2f 100644
--- a/services/oboeservice/AAudioService.h
+++ b/services/oboeservice/AAudioService.h
@@ -57,6 +57,8 @@
virtual aaudio_result_t pauseStream(aaudio_handle_t streamHandle);
+ virtual aaudio_result_t stopStream(aaudio_handle_t streamHandle);
+
virtual aaudio_result_t flushStream(aaudio_handle_t streamHandle);
virtual aaudio_result_t registerAudioThread(aaudio_handle_t streamHandle,
diff --git a/services/oboeservice/AAudioServiceEndpoint.cpp b/services/oboeservice/AAudioServiceEndpoint.cpp
index 80551c9..b197798 100644
--- a/services/oboeservice/AAudioServiceEndpoint.cpp
+++ b/services/oboeservice/AAudioServiceEndpoint.cpp
@@ -14,6 +14,17 @@
* limitations under the License.
*/
+#define LOG_TAG "AAudioService"
+//#define LOG_NDEBUG 0
+#include <utils/Log.h>
+
+#include <assert.h>
+#include <map>
+#include <mutex>
+#include <utils/Singleton.h>
+
+#include "AAudioEndpointManager.h"
+#include "AAudioServiceEndpoint.h"
#include <algorithm>
#include <mutex>
#include <vector>
@@ -30,6 +41,12 @@
// Wait at least this many times longer than the operation should take.
#define MIN_TIMEOUT_OPERATIONS 4
+// This is the maximum size in frames. The effective size can be tuned smaller at runtime.
+#define DEFAULT_BUFFER_CAPACITY (48 * 8)
+
+// Use 2 for "double buffered"
+#define BUFFER_SIZE_IN_BURSTS 2
+
// The mStreamInternal will use a service interface that does not go through Binder.
AAudioServiceEndpoint::AAudioServiceEndpoint(AAudioService &audioService)
: mStreamInternal(audioService, true)
@@ -43,11 +60,18 @@
aaudio_result_t AAudioServiceEndpoint::open(int32_t deviceId, aaudio_direction_t direction) {
AudioStreamBuilder builder;
builder.setSharingMode(AAUDIO_SHARING_MODE_EXCLUSIVE);
+ // Don't fall back to SHARED because that would cause recursion.
+ builder.setSharingModeMatchRequired(true);
builder.setDeviceId(deviceId);
builder.setDirection(direction);
+ builder.setBufferCapacity(DEFAULT_BUFFER_CAPACITY);
+
aaudio_result_t result = mStreamInternal.open(builder);
if (result == AAUDIO_OK) {
mMixer.allocate(mStreamInternal.getSamplesPerFrame(), mStreamInternal.getFramesPerBurst());
+
+ int32_t desiredBufferSize = BUFFER_SIZE_IN_BURSTS * mStreamInternal.getFramesPerBurst();
+ mStreamInternal.setBufferSize(desiredBufferSize);
}
return result;
}
@@ -58,15 +82,12 @@
// TODO, maybe use an interface to reduce exposure
aaudio_result_t AAudioServiceEndpoint::registerStream(AAudioServiceStreamShared *sharedStream) {
- ALOGD("AAudioServiceEndpoint::registerStream(%p)", sharedStream);
- // TODO use real-time technique to avoid mutex, eg. atomic command FIFO
std::lock_guard<std::mutex> lock(mLockStreams);
mRegisteredStreams.push_back(sharedStream);
return AAUDIO_OK;
}
aaudio_result_t AAudioServiceEndpoint::unregisterStream(AAudioServiceStreamShared *sharedStream) {
- ALOGD("AAudioServiceEndpoint::unregisterStream(%p)", sharedStream);
std::lock_guard<std::mutex> lock(mLockStreams);
mRegisteredStreams.erase(std::remove(mRegisteredStreams.begin(), mRegisteredStreams.end(), sharedStream),
mRegisteredStreams.end());
@@ -75,7 +96,6 @@
aaudio_result_t AAudioServiceEndpoint::startStream(AAudioServiceStreamShared *sharedStream) {
// TODO use real-time technique to avoid mutex, eg. atomic command FIFO
- ALOGD("AAudioServiceEndpoint(): startStream() entering");
std::lock_guard<std::mutex> lock(mLockStreams);
mRunningStreams.push_back(sharedStream);
if (mRunningStreams.size() == 1) {
@@ -106,13 +126,10 @@
// Render audio in the application callback and then write the data to the stream.
void *AAudioServiceEndpoint::callbackLoop() {
- aaudio_result_t result = AAUDIO_OK;
-
ALOGD("AAudioServiceEndpoint(): callbackLoop() entering");
+ int32_t underflowCount = 0;
- result = mStreamInternal.requestStart();
- ALOGD("AAudioServiceEndpoint(): callbackLoop() after requestStart() %d, isPlaying() = %d",
- result, (int) mStreamInternal.isPlaying());
+ aaudio_result_t result = mStreamInternal.requestStart();
// result might be a frame count
while (mCallbackEnabled.load() && mStreamInternal.isPlaying() && (result >= 0)) {
@@ -123,12 +140,14 @@
for(AAudioServiceStreamShared *sharedStream : mRunningStreams) {
FifoBuffer *fifo = sharedStream->getDataFifoBuffer();
float volume = 0.5; // TODO get from system
- mMixer.mix(fifo, volume);
+ bool underflowed = mMixer.mix(fifo, volume);
+ underflowCount += underflowed ? 1 : 0;
+ // TODO log underflows in each stream
+ sharedStream->markTransferTime(AudioClock::getNanoseconds());
}
}
// Write audio data to stream using a blocking write.
- ALOGD("AAudioServiceEndpoint(): callbackLoop() write(%d)", getFramesPerBurst());
int64_t timeoutNanos = calculateReasonableTimeout(mStreamInternal.getFramesPerBurst());
result = mStreamInternal.write(mMixer.getOutputBuffer(), getFramesPerBurst(), timeoutNanos);
if (result == AAUDIO_ERROR_DISCONNECTED) {
@@ -141,11 +160,9 @@
}
}
- ALOGD("AAudioServiceEndpoint(): callbackLoop() exiting, result = %d, isPlaying() = %d",
- result, (int) mStreamInternal.isPlaying());
-
result = mStreamInternal.requestStop();
+ ALOGD("AAudioServiceEndpoint(): callbackLoop() exiting, %d underflows", underflowCount);
return NULL; // TODO review
}
diff --git a/services/oboeservice/AAudioServiceEndpoint.h b/services/oboeservice/AAudioServiceEndpoint.h
index 020d38a..a4ceae6 100644
--- a/services/oboeservice/AAudioServiceEndpoint.h
+++ b/services/oboeservice/AAudioServiceEndpoint.h
@@ -56,6 +56,16 @@
void *callbackLoop();
+ // This should only be called from the AAudioEndpointManager under a mutex.
+ int32_t getReferenceCount() const {
+ return mReferenceCount;
+ }
+
+ // This should only be called from the AAudioEndpointManager under a mutex.
+ void setReferenceCount(int32_t count) {
+ mReferenceCount = count;
+ }
+
private:
aaudio_result_t startMixer_l();
aaudio_result_t stopMixer_l();
@@ -64,13 +74,14 @@
AudioStreamInternal mStreamInternal;
AAudioMixer mMixer;
- AAudioServiceStreamMMAP mStreamMMAP;
std::atomic<bool> mCallbackEnabled;
+ int32_t mReferenceCount = 0;
std::mutex mLockStreams;
std::vector<AAudioServiceStreamShared *> mRegisteredStreams;
std::vector<AAudioServiceStreamShared *> mRunningStreams;
+
};
} /* namespace aaudio */
diff --git a/services/oboeservice/AAudioServiceStreamBase.cpp b/services/oboeservice/AAudioServiceStreamBase.cpp
index b15043d..d8882c9 100644
--- a/services/oboeservice/AAudioServiceStreamBase.cpp
+++ b/services/oboeservice/AAudioServiceStreamBase.cpp
@@ -63,6 +63,7 @@
}
aaudio_result_t AAudioServiceStreamBase::start() {
+ ALOGD("AAudioServiceStreamBase::start() send AAUDIO_SERVICE_EVENT_STARTED");
sendServiceEvent(AAUDIO_SERVICE_EVENT_STARTED);
mState = AAUDIO_STREAM_STATE_STARTED;
mThreadEnabled.store(true);
@@ -78,14 +79,37 @@
processError();
return result;
}
+ ALOGD("AAudioServiceStreamBase::pause() send AAUDIO_SERVICE_EVENT_PAUSED");
sendServiceEvent(AAUDIO_SERVICE_EVENT_PAUSED);
mState = AAUDIO_STREAM_STATE_PAUSED;
return result;
}
+aaudio_result_t AAudioServiceStreamBase::stop() {
+ // TODO wait for data to be played out
+ sendCurrentTimestamp();
+ mThreadEnabled.store(false);
+ aaudio_result_t result = mAAudioThread.stop();
+ if (result != AAUDIO_OK) {
+ processError();
+ return result;
+ }
+ ALOGD("AAudioServiceStreamBase::stop() send AAUDIO_SERVICE_EVENT_STOPPED");
+ sendServiceEvent(AAUDIO_SERVICE_EVENT_STOPPED);
+ mState = AAUDIO_STREAM_STATE_STOPPED;
+ return result;
+}
+
+aaudio_result_t AAudioServiceStreamBase::flush() {
+ ALOGD("AAudioServiceStreamBase::flush() send AAUDIO_SERVICE_EVENT_FLUSHED");
+ sendServiceEvent(AAUDIO_SERVICE_EVENT_FLUSHED);
+ mState = AAUDIO_STREAM_STATE_FLUSHED;
+ return AAUDIO_OK;
+}
+
// implement Runnable
void AAudioServiceStreamBase::run() {
- ALOGD("AAudioServiceStreamMMAP::run() entering ----------------");
+ ALOGD("AAudioServiceStreamBase::run() entering ----------------");
TimestampScheduler timestampScheduler;
timestampScheduler.setBurstPeriod(mFramesPerBurst, mSampleRate);
timestampScheduler.start(AudioClock::getNanoseconds());
@@ -102,7 +126,7 @@
AudioClock::sleepUntilNanoTime(nextTime);
}
}
- ALOGD("AAudioServiceStreamMMAP::run() exiting ----------------");
+ ALOGD("AAudioServiceStreamBase::run() exiting ----------------");
}
void AAudioServiceStreamBase::processError() {
@@ -122,6 +146,10 @@
aaudio_result_t AAudioServiceStreamBase::writeUpMessageQueue(AAudioServiceMessage *command) {
std::lock_guard<std::mutex> lock(mLockUpMessageQueue);
+ if (mUpMessageQueue == nullptr) {
+ ALOGE("writeUpMessageQueue(): mUpMessageQueue null! - stream not open");
+ return AAUDIO_ERROR_NULL;
+ }
int32_t count = mUpMessageQueue->getFifoBuffer()->write(command, 1);
if (count != 1) {
ALOGE("writeUpMessageQueue(): Queue full. Did client die?");
@@ -133,9 +161,11 @@
aaudio_result_t AAudioServiceStreamBase::sendCurrentTimestamp() {
AAudioServiceMessage command;
+ //ALOGD("sendCurrentTimestamp() called");
aaudio_result_t result = getFreeRunningPosition(&command.timestamp.position,
&command.timestamp.timestamp);
if (result == AAUDIO_OK) {
+ //ALOGD("sendCurrentTimestamp(): position %d", (int) command.timestamp.position);
command.what = AAudioServiceMessage::code::TIMESTAMP;
result = writeUpMessageQueue(&command);
}
diff --git a/services/oboeservice/AAudioServiceStreamBase.h b/services/oboeservice/AAudioServiceStreamBase.h
index 91eec35..d6b6ee3 100644
--- a/services/oboeservice/AAudioServiceStreamBase.h
+++ b/services/oboeservice/AAudioServiceStreamBase.h
@@ -17,6 +17,7 @@
#ifndef AAUDIO_AAUDIO_SERVICE_STREAM_BASE_H
#define AAUDIO_AAUDIO_SERVICE_STREAM_BASE_H
+#include <assert.h>
#include <mutex>
#include "fifo/FifoBuffer.h"
@@ -60,17 +61,22 @@
/**
* Start the flow of data.
*/
- virtual aaudio_result_t start() = 0;
+ virtual aaudio_result_t start();
/**
* Stop the flow of data such that start() can resume with loss of data.
*/
- virtual aaudio_result_t pause() = 0;
+ virtual aaudio_result_t pause();
+
+ /**
+ * Stop the flow of data after data in buffer has played.
+ */
+ virtual aaudio_result_t stop();
/**
* Discard any data held by the underlying HAL or Service.
*/
- virtual aaudio_result_t flush() = 0;
+ virtual aaudio_result_t flush();
// -------------------------------------------------------------------
diff --git a/services/oboeservice/AAudioServiceStreamMMAP.cpp b/services/oboeservice/AAudioServiceStreamMMAP.cpp
index b70c625..b2e7fc9 100644
--- a/services/oboeservice/AAudioServiceStreamMMAP.cpp
+++ b/services/oboeservice/AAudioServiceStreamMMAP.cpp
@@ -55,6 +55,11 @@
aaudio_result_t AAudioServiceStreamMMAP::close() {
ALOGD("AAudioServiceStreamMMAP::close() called, %p", mMmapStream.get());
mMmapStream.clear(); // TODO review. Is that all we have to do?
+ // Apparently the above close is asynchronous. An attempt to open a new device
+ // right after a close can fail. Also some callbacks may still be in flight!
+ // FIXME Make closing synchronous.
+ AudioClock::sleepForNanos(100 * AAUDIO_NANOS_PER_MILLISECOND);
+
return AAudioServiceStreamBase::close();
}
@@ -79,8 +84,8 @@
const AAudioStreamConfiguration &configurationInput = request.getConstantConfiguration();
audio_port_handle_t deviceId = configurationInput.getDeviceId();
- ALOGI("open request dump()");
- request.dump();
+ // ALOGI("open request dump()");
+ // request.dump();
mMmapClient.clientUid = request.getUserId();
mMmapClient.clientPid = request.getProcessId();
@@ -198,16 +203,25 @@
return (result1 != AAUDIO_OK) ? result1 : result2;
}
+aaudio_result_t AAudioServiceStreamMMAP::stop() {
+ if (mMmapStream == nullptr) return AAUDIO_ERROR_NULL;
+
+ aaudio_result_t result1 = AAudioServiceStreamBase::stop();
+ aaudio_result_t result2 = mMmapStream->stop(mPortHandle);
+ mFramesRead.reset32();
+ return (result1 != AAUDIO_OK) ? result1 : result2;
+}
+
/**
* Discard any data held by the underlying HAL or Service.
*/
aaudio_result_t AAudioServiceStreamMMAP::flush() {
if (mMmapStream == nullptr) return AAUDIO_ERROR_NULL;
// TODO how do we flush an MMAP/NOIRQ buffer? sync pointers?
- ALOGD("AAudioServiceStreamMMAP::pause() send AAUDIO_SERVICE_EVENT_FLUSHED");
+ ALOGD("AAudioServiceStreamMMAP::flush() send AAUDIO_SERVICE_EVENT_FLUSHED");
sendServiceEvent(AAUDIO_SERVICE_EVENT_FLUSHED);
mState = AAUDIO_STREAM_STATE_FLUSHED;
- return AAUDIO_OK;
+ return AAudioServiceStreamBase::flush();;
}
diff --git a/services/oboeservice/AAudioServiceStreamMMAP.h b/services/oboeservice/AAudioServiceStreamMMAP.h
index f121c5c..a8e63a6 100644
--- a/services/oboeservice/AAudioServiceStreamMMAP.h
+++ b/services/oboeservice/AAudioServiceStreamMMAP.h
@@ -66,6 +66,8 @@
*/
aaudio_result_t pause() override;
+ aaudio_result_t stop() override;
+
/**
* Discard any data held by the underlying HAL or Service.
*
diff --git a/services/oboeservice/AAudioServiceStreamShared.cpp b/services/oboeservice/AAudioServiceStreamShared.cpp
index cd9336b..b5d9927 100644
--- a/services/oboeservice/AAudioServiceStreamShared.cpp
+++ b/services/oboeservice/AAudioServiceStreamShared.cpp
@@ -61,7 +61,7 @@
ALOGD("AAudioServiceStreamShared::open(), direction = %d", direction);
AAudioEndpointManager &mEndpointManager = AAudioEndpointManager::getInstance();
- mServiceEndpoint = mEndpointManager.findEndpoint(mAudioService, deviceId, direction);
+ mServiceEndpoint = mEndpointManager.openEndpoint(mAudioService, deviceId, direction);
ALOGD("AAudioServiceStreamShared::open(), mServiceEndPoint = %p", mServiceEndpoint);
if (mServiceEndpoint == nullptr) {
return AAUDIO_ERROR_UNAVAILABLE;
@@ -72,6 +72,7 @@
if (mAudioFormat == AAUDIO_FORMAT_UNSPECIFIED) {
mAudioFormat = AAUDIO_FORMAT_PCM_FLOAT;
} else if (mAudioFormat != AAUDIO_FORMAT_PCM_FLOAT) {
+ ALOGE("AAudioServiceStreamShared::open(), mAudioFormat = %d, need FLOAT", mAudioFormat);
return AAUDIO_ERROR_INVALID_FORMAT;
}
@@ -79,6 +80,8 @@
if (mSampleRate == AAUDIO_FORMAT_UNSPECIFIED) {
mSampleRate = mServiceEndpoint->getSampleRate();
} else if (mSampleRate != mServiceEndpoint->getSampleRate()) {
+ ALOGE("AAudioServiceStreamShared::open(), mAudioFormat = %d, need %d",
+ mSampleRate, mServiceEndpoint->getSampleRate());
return AAUDIO_ERROR_INVALID_RATE;
}
@@ -86,17 +89,22 @@
if (mSamplesPerFrame == AAUDIO_FORMAT_UNSPECIFIED) {
mSamplesPerFrame = mServiceEndpoint->getSamplesPerFrame();
} else if (mSamplesPerFrame != mServiceEndpoint->getSamplesPerFrame()) {
+ ALOGE("AAudioServiceStreamShared::open(), mSamplesPerFrame = %d, need %d",
+ mSamplesPerFrame, mServiceEndpoint->getSamplesPerFrame());
return AAUDIO_ERROR_OUT_OF_RANGE;
}
// Determine this stream's shared memory buffer capacity.
mFramesPerBurst = mServiceEndpoint->getFramesPerBurst();
int32_t minCapacityFrames = configurationInput.getBufferCapacity();
- int32_t numBursts = (minCapacityFrames + mFramesPerBurst - 1) / mFramesPerBurst;
- if (numBursts < MIN_BURSTS_PER_BUFFER) {
- numBursts = MIN_BURSTS_PER_BUFFER;
- } else if (numBursts > MAX_BURSTS_PER_BUFFER) {
- numBursts = MAX_BURSTS_PER_BUFFER;
+ int32_t numBursts = MAX_BURSTS_PER_BUFFER;
+ if (minCapacityFrames != AAUDIO_UNSPECIFIED) {
+ numBursts = (minCapacityFrames + mFramesPerBurst - 1) / mFramesPerBurst;
+ if (numBursts < MIN_BURSTS_PER_BUFFER) {
+ numBursts = MIN_BURSTS_PER_BUFFER;
+ } else if (numBursts > MAX_BURSTS_PER_BUFFER) {
+ numBursts = MAX_BURSTS_PER_BUFFER;
+ }
}
mCapacityInFrames = numBursts * mFramesPerBurst;
ALOGD("AAudioServiceStreamShared::open(), mCapacityInFrames = %d", mCapacityInFrames);
@@ -122,8 +130,12 @@
* An AAUDIO_SERVICE_EVENT_STARTED will be sent to the client when complete.
*/
aaudio_result_t AAudioServiceStreamShared::start() {
+ AAudioServiceEndpoint *endpoint = mServiceEndpoint;
+ if (endpoint == nullptr) {
+ return AAUDIO_ERROR_INVALID_STATE;
+ }
// Add this stream to the mixer.
- aaudio_result_t result = mServiceEndpoint->startStream(this);
+ aaudio_result_t result = endpoint->startStream(this);
if (result != AAUDIO_OK) {
ALOGE("AAudioServiceStreamShared::start() mServiceEndpoint returned %d", result);
processError();
@@ -139,15 +151,31 @@
* An AAUDIO_SERVICE_EVENT_PAUSED will be sent to the client when complete.
*/
aaudio_result_t AAudioServiceStreamShared::pause() {
+ AAudioServiceEndpoint *endpoint = mServiceEndpoint;
+ if (endpoint == nullptr) {
+ return AAUDIO_ERROR_INVALID_STATE;
+ }
// Add this stream to the mixer.
- aaudio_result_t result = mServiceEndpoint->stopStream(this);
+ aaudio_result_t result = endpoint->stopStream(this);
+ if (result != AAUDIO_OK) {
+ ALOGE("AAudioServiceStreamShared::pause() mServiceEndpoint returned %d", result);
+ processError();
+ }
+ return AAudioServiceStreamBase::pause();
+}
+
+aaudio_result_t AAudioServiceStreamShared::stop() {
+ AAudioServiceEndpoint *endpoint = mServiceEndpoint;
+ if (endpoint == nullptr) {
+ return AAUDIO_ERROR_INVALID_STATE;
+ }
+ // Add this stream to the mixer.
+ aaudio_result_t result = endpoint->stopStream(this);
if (result != AAUDIO_OK) {
ALOGE("AAudioServiceStreamShared::stop() mServiceEndpoint returned %d", result);
processError();
- } else {
- result = AAudioServiceStreamBase::start();
}
- return AAUDIO_OK;
+ return AAudioServiceStreamBase::stop();
}
/**
@@ -157,15 +185,21 @@
*/
aaudio_result_t AAudioServiceStreamShared::flush() {
// TODO make sure we are paused
- return AAUDIO_OK;
+ // TODO actually flush the data
+ return AAudioServiceStreamBase::flush() ;
}
aaudio_result_t AAudioServiceStreamShared::close() {
pause();
// TODO wait for pause() to synchronize
- mServiceEndpoint->unregisterStream(this);
- mServiceEndpoint->close();
- mServiceEndpoint = nullptr;
+ AAudioServiceEndpoint *endpoint = mServiceEndpoint;
+ if (endpoint != nullptr) {
+ endpoint->unregisterStream(this);
+
+ AAudioEndpointManager &mEndpointManager = AAudioEndpointManager::getInstance();
+ mEndpointManager.closeEndpoint(endpoint);
+ mServiceEndpoint = nullptr;
+ }
return AAudioServiceStreamBase::close();
}
@@ -189,10 +223,15 @@
mServiceEndpoint = nullptr;
}
+void AAudioServiceStreamShared::markTransferTime(int64_t nanoseconds) {
+ mMarkedPosition = mAudioDataQueue->getFifoBuffer()->getReadCounter();
+ mMarkedTime = nanoseconds;
+}
aaudio_result_t AAudioServiceStreamShared::getFreeRunningPosition(int64_t *positionFrames,
int64_t *timeNanos) {
- *positionFrames = mAudioDataQueue->getFifoBuffer()->getReadCounter();
- *timeNanos = AudioClock::getNanoseconds();
+ // TODO get these two numbers as an atomic pair
+ *positionFrames = mMarkedPosition;
+ *timeNanos = mMarkedTime;
return AAUDIO_OK;
}
diff --git a/services/oboeservice/AAudioServiceStreamShared.h b/services/oboeservice/AAudioServiceStreamShared.h
index f6df7ce..b981387 100644
--- a/services/oboeservice/AAudioServiceStreamShared.h
+++ b/services/oboeservice/AAudioServiceStreamShared.h
@@ -66,6 +66,11 @@
aaudio_result_t pause() override;
/**
+ * Stop the flow of data after data in buffer has played.
+ */
+ aaudio_result_t stop() override;
+
+ /**
* Discard any data held by the underlying HAL or Service.
*
* This is not guaranteed to be synchronous but it currently is.
@@ -77,6 +82,11 @@
android::FifoBuffer *getDataFifoBuffer() { return mAudioDataQueue->getFifoBuffer(); }
+ /* Keep a record of when a buffer transfer completed.
+ * This allows for a more accurate timing model.
+ */
+ void markTransferTime(int64_t nanoseconds);
+
void onStop();
void onDisconnect();
@@ -91,6 +101,9 @@
android::AAudioService &mAudioService;
AAudioServiceEndpoint *mServiceEndpoint = nullptr;
SharedRingBuffer *mAudioDataQueue;
+
+ int64_t mMarkedPosition = 0;
+ int64_t mMarkedTime = 0;
};
} /* namespace aaudio */