Merge "audio track: dump more data"
diff --git a/media/extractors/mp3/MP3Extractor.cpp b/media/extractors/mp3/MP3Extractor.cpp
index 2731f0f..f26ed25 100644
--- a/media/extractors/mp3/MP3Extractor.cpp
+++ b/media/extractors/mp3/MP3Extractor.cpp
@@ -678,6 +678,15 @@
off64_t pos = 0;
off64_t post_id3_pos;
uint32_t header;
+ uint8_t mpeg_header[5];
+ if (source->readAt(0, mpeg_header, sizeof(mpeg_header)) < (ssize_t)sizeof(mpeg_header)) {
+ return NULL;
+ }
+
+ if (!memcmp("\x00\x00\x01\xba", mpeg_header, 4) && (mpeg_header[4] >> 4) == 2) {
+ ALOGV("MPEG1PS container is not supported!");
+ return NULL;
+ }
if (!Resync(source, 0, &pos, &post_id3_pos, &header)) {
return NULL;
}
diff --git a/media/extractors/mp4/ItemTable.cpp b/media/extractors/mp4/ItemTable.cpp
index 9a6cb64..85c66b2 100644
--- a/media/extractors/mp4/ItemTable.cpp
+++ b/media/extractors/mp4/ItemTable.cpp
@@ -1425,7 +1425,7 @@
meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_IMAGE_ANDROID_HEIC);
if (image->itemId == mPrimaryItemId) {
- meta->setInt32(kKeyIsPrimaryImage, 1);
+ meta->setInt32(kKeyTrackIsDefault, 1);
}
ALOGV("image[%u]: size %dx%d", imageIndex, image->width, image->height);
diff --git a/media/extractors/mp4/MPEG4Extractor.cpp b/media/extractors/mp4/MPEG4Extractor.cpp
index 6671956..b411125 100644
--- a/media/extractors/mp4/MPEG4Extractor.cpp
+++ b/media/extractors/mp4/MPEG4Extractor.cpp
@@ -347,7 +347,7 @@
mHeaderTimescale(0),
mIsQT(false),
mIsHeif(false),
- mIsHeifSequence(false),
+ mHasMoovBox(false),
mPreferHeif(mime != NULL && !strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_HEIF)),
mFirstTrack(NULL),
mLastTrack(NULL),
@@ -563,9 +563,9 @@
status_t err;
bool sawMoovOrSidx = false;
- while (!((!mIsHeif && sawMoovOrSidx && (mMdatFound || mMoofFound)) ||
- (mIsHeif && (mPreferHeif || !mIsHeifSequence)
- && (mItemTable != NULL) && mItemTable->isValid()))) {
+ while (!((mHasMoovBox && sawMoovOrSidx && (mMdatFound || mMoofFound)) ||
+ (mIsHeif && (mPreferHeif || !mHasMoovBox) &&
+ (mItemTable != NULL) && mItemTable->isValid()))) {
off64_t orig_offset = offset;
err = parseChunk(&offset, 0);
@@ -582,34 +582,30 @@
}
}
- if (mIsHeif) {
- uint32_t imageCount = mItemTable->countImages();
- if (imageCount == 0) {
- ALOGE("found no image in heif!");
- } else {
- for (uint32_t imageIndex = 0; imageIndex < imageCount; imageIndex++) {
- sp<MetaData> meta = mItemTable->getImageMeta(imageIndex);
- if (meta == NULL) {
- ALOGE("heif image %u has no meta!", imageIndex);
- continue;
- }
-
- ALOGV("adding HEIF image track %u", imageIndex);
- Track *track = new Track;
- track->next = NULL;
- if (mLastTrack != NULL) {
- mLastTrack->next = track;
- } else {
- mFirstTrack = track;
- }
- mLastTrack = track;
-
- track->meta = meta;
- track->meta->setInt32(kKeyTrackID, imageIndex);
- track->includes_expensive_metadata = false;
- track->skipTrack = false;
- track->timescale = 0;
+ if (mIsHeif && (mItemTable != NULL) && (mItemTable->countImages() > 0)) {
+ for (uint32_t imageIndex = 0;
+ imageIndex < mItemTable->countImages(); imageIndex++) {
+ sp<MetaData> meta = mItemTable->getImageMeta(imageIndex);
+ if (meta == NULL) {
+ ALOGE("heif image %u has no meta!", imageIndex);
+ continue;
}
+
+ ALOGV("adding HEIF image track %u", imageIndex);
+ Track *track = new Track;
+ track->next = NULL;
+ if (mLastTrack != NULL) {
+ mLastTrack->next = track;
+ } else {
+ mFirstTrack = track;
+ }
+ mLastTrack = track;
+
+ track->meta = meta;
+ track->meta->setInt32(kKeyTrackID, imageIndex);
+ track->includes_expensive_metadata = false;
+ track->skipTrack = false;
+ track->timescale = 0;
}
}
@@ -2512,13 +2508,18 @@
} else {
if (brandSet.count(FOURCC('m', 'i', 'f', '1')) > 0
&& brandSet.count(FOURCC('h', 'e', 'i', 'c')) > 0) {
- mIsHeif = true;
ALOGV("identified HEIF image");
+
+ mIsHeif = true;
+ brandSet.erase(FOURCC('m', 'i', 'f', '1'));
+ brandSet.erase(FOURCC('h', 'e', 'i', 'c'));
}
- if (brandSet.count(FOURCC('m', 's', 'f', '1')) > 0
- && brandSet.count(FOURCC('h', 'e', 'v', 'c')) > 0) {
- mIsHeifSequence = true;
- ALOGV("identified HEIF image sequence");
+
+ if (!brandSet.empty()) {
+ // This means that the file should have moov box.
+ // It could be any iso files (mp4, heifs, etc.)
+ mHasMoovBox = true;
+ ALOGV("identified HEIF image with other tracks");
}
}
diff --git a/media/extractors/mp4/MPEG4Extractor.h b/media/extractors/mp4/MPEG4Extractor.h
index d4f17e3..76b549d 100644
--- a/media/extractors/mp4/MPEG4Extractor.h
+++ b/media/extractors/mp4/MPEG4Extractor.h
@@ -104,7 +104,7 @@
uint32_t mHeaderTimescale;
bool mIsQT;
bool mIsHeif;
- bool mIsHeifSequence;
+ bool mHasMoovBox;
bool mPreferHeif;
Track *mFirstTrack, *mLastTrack;
diff --git a/media/libaaudio/examples/utils/AAudioSimplePlayer.h b/media/libaaudio/examples/utils/AAudioSimplePlayer.h
index 1061e42..3fafecf 100644
--- a/media/libaaudio/examples/utils/AAudioSimplePlayer.h
+++ b/media/libaaudio/examples/utils/AAudioSimplePlayer.h
@@ -30,8 +30,8 @@
#define SHARING_MODE AAUDIO_SHARING_MODE_SHARED
#define PERFORMANCE_MODE AAUDIO_PERFORMANCE_MODE_NONE
-// Arbitrary period for glitches, once per second at 48000 Hz.
-#define FORCED_UNDERRUN_PERIOD_FRAMES 48000
+// Arbitrary period for glitches
+#define FORCED_UNDERRUN_PERIOD_FRAMES (2 * 48000)
// How long to sleep in a callback to cause an intentional glitch. For testing.
#define FORCED_UNDERRUN_SLEEP_MICROS (10 * 1000)
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 c2dd7af..5d41fd0 100644
--- a/media/libaaudio/examples/write_sine/src/write_sine_callback.cpp
+++ b/media/libaaudio/examples/write_sine/src/write_sine_callback.cpp
@@ -28,6 +28,7 @@
#include <aaudio/AAudio.h>
#include "AAudioExampleUtils.h"
#include "AAudioSimplePlayer.h"
+#include "AAudioArgsParser.h"
/**
* Open stream, play some sine waves, then close the stream.
@@ -37,7 +38,8 @@
*/
static aaudio_result_t testOpenPlayClose(AAudioArgsParser &argParser,
int32_t loopCount,
- int32_t prefixToneMsec)
+ int32_t prefixToneMsec,
+ bool forceUnderruns)
{
SineThreadedData_t myData;
AAudioSimplePlayer &player = myData.simplePlayer;
@@ -49,8 +51,7 @@
printf("----------------------- run complete test --------------------------\n");
myData.schedulerChecked = false;
myData.callbackCount = 0;
- // TODO add a command line option for the forceUnderruns
- myData.forceUnderruns = false; // set true to test AAudioStream_getXRunCount()
+ myData.forceUnderruns = forceUnderruns; // test AAudioStream_getXRunCount()
result = player.open(argParser,
SimplePlayerDataCallbackProc, SimplePlayerErrorCallbackProc, &myData);
@@ -202,7 +203,8 @@
static void usage() {
AAudioArgsParser::usage();
printf(" -l{count} loopCount start/stop, every other one is silent\n");
- printf(" -t{msec} play a high pitched tone at the beginning\n");
+ printf(" -t{msec} play a high pitched tone at the beginning\n");
+ printf(" -u force periodic Underruns by sleeping in callback\n");
}
int main(int argc, const char **argv)
@@ -211,6 +213,7 @@
aaudio_result_t result;
int32_t loopCount = 1;
int32_t prefixToneMsec = 0;
+ bool forceUnderruns = false;
// Make printf print immediately so that debug info is not stuck
// in a buffer if we hang or crash.
@@ -231,6 +234,9 @@
case 't':
prefixToneMsec = atoi(&arg[2]);
break;
+ case 'u':
+ forceUnderruns = true;
+ break;
default:
usage();
exit(EXIT_FAILURE);
@@ -245,7 +251,7 @@
}
// Keep looping until we can complete the test without disconnecting.
- while((result = testOpenPlayClose(argParser, loopCount, prefixToneMsec))
+ while((result = testOpenPlayClose(argParser, loopCount, prefixToneMsec, forceUnderruns))
== AAUDIO_ERROR_DISCONNECTED);
return (result) ? EXIT_FAILURE : EXIT_SUCCESS;
diff --git a/media/libaaudio/src/binding/AAudioServiceMessage.h b/media/libaaudio/src/binding/AAudioServiceMessage.h
index 54e8001..9779f24 100644
--- a/media/libaaudio/src/binding/AAudioServiceMessage.h
+++ b/media/libaaudio/src/binding/AAudioServiceMessage.h
@@ -38,13 +38,16 @@
AAUDIO_SERVICE_EVENT_FLUSHED,
AAUDIO_SERVICE_EVENT_CLOSED,
AAUDIO_SERVICE_EVENT_DISCONNECTED,
- AAUDIO_SERVICE_EVENT_VOLUME
+ AAUDIO_SERVICE_EVENT_VOLUME,
+ AAUDIO_SERVICE_EVENT_XRUN
} aaudio_service_event_t;
struct AAudioMessageEvent {
aaudio_service_event_t event;
- double dataDouble;
- int64_t dataLong;
+ union {
+ double dataDouble;
+ int64_t dataLong;
+ };
};
typedef struct AAudioServiceMessage_s {
diff --git a/media/libaaudio/src/client/AudioStreamInternal.cpp b/media/libaaudio/src/client/AudioStreamInternal.cpp
index 3a7a342..b7b4b5c 100644
--- a/media/libaaudio/src/client/AudioStreamInternal.cpp
+++ b/media/libaaudio/src/client/AudioStreamInternal.cpp
@@ -492,6 +492,9 @@
doSetVolume();
ALOGD("%s - AAUDIO_SERVICE_EVENT_VOLUME %lf", __func__, message->event.dataDouble);
break;
+ case AAUDIO_SERVICE_EVENT_XRUN:
+ mXRunCount = static_cast<int32_t>(message->event.dataLong);
+ break;
default:
ALOGE("%s - Unrecognized event = %d", __func__, (int) message->event.event);
break;
diff --git a/media/libaaudio/src/client/AudioStreamInternalCapture.cpp b/media/libaaudio/src/client/AudioStreamInternalCapture.cpp
index 77a481b..3e82a88 100644
--- a/media/libaaudio/src/client/AudioStreamInternalCapture.cpp
+++ b/media/libaaudio/src/client/AudioStreamInternalCapture.cpp
@@ -102,7 +102,8 @@
}
// If the write index passed the read index then consider it an overrun.
- if (mAudioEndpoint.getEmptyFramesAvailable() < 0) {
+ // For shared streams, the xRunCount is passed up from the service.
+ if (mAudioEndpoint.isFreeRunning() && mAudioEndpoint.getEmptyFramesAvailable() < 0) {
mXRunCount++;
if (ATRACE_ENABLED()) {
ATRACE_INT("aaOverRuns", mXRunCount);
diff --git a/media/libaaudio/src/client/AudioStreamInternalPlay.cpp b/media/libaaudio/src/client/AudioStreamInternalPlay.cpp
index 8d7a01e..b49e08c 100644
--- a/media/libaaudio/src/client/AudioStreamInternalPlay.cpp
+++ b/media/libaaudio/src/client/AudioStreamInternalPlay.cpp
@@ -140,7 +140,8 @@
}
// If the read index passed the write index then consider it an underrun.
- if (mAudioEndpoint.getFullFramesAvailable() < 0) {
+ // For shared streams, the xRunCount is passed up from the service.
+ if (mAudioEndpoint.isFreeRunning() && mAudioEndpoint.getFullFramesAvailable() < 0) {
mXRunCount++;
if (ATRACE_ENABLED()) {
ATRACE_INT("aaUnderRuns", mXRunCount);
diff --git a/media/libaaudio/tests/Android.bp b/media/libaaudio/tests/Android.bp
index 87a4273..884a2b3 100644
--- a/media/libaaudio/tests/Android.bp
+++ b/media/libaaudio/tests/Android.bp
@@ -99,3 +99,15 @@
"libutils",
],
}
+
+cc_test {
+ name: "test_various",
+ defaults: ["libaaudio_tests_defaults"],
+ srcs: ["test_various.cpp"],
+ shared_libs: [
+ "libaaudio",
+ "libbinder",
+ "libcutils",
+ "libutils",
+ ],
+}
diff --git a/media/libaaudio/tests/test_various.cpp b/media/libaaudio/tests/test_various.cpp
new file mode 100644
index 0000000..9e505d5
--- /dev/null
+++ b/media/libaaudio/tests/test_various.cpp
@@ -0,0 +1,107 @@
+/*
+ * 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.
+ */
+
+// Test various AAudio features including AAudioStream_setBufferSizeInFrames().
+
+#include <stdio.h>
+//#include <stdlib.h>
+//#include <math.h>
+
+#include <android-base/macros.h>
+#include <aaudio/AAudio.h>
+
+#include <gtest/gtest.h>
+
+// Callback function that does nothing.
+aaudio_data_callback_result_t MyDataCallbackProc(
+ AAudioStream *stream,
+ void *userData,
+ void *audioData,
+ int32_t numFrames
+) {
+ (void) stream;
+ (void) userData;
+ (void) audioData;
+ (void) numFrames;
+ return AAUDIO_CALLBACK_RESULT_CONTINUE;
+}
+
+// Test AAudioStream_setBufferSizeInFrames()
+
+//int main() { // To fix Android Studio formatting when editing.
+TEST(test_various, aaudio_set_buffer_size) {
+
+ aaudio_result_t result = AAUDIO_OK;
+ int32_t bufferCapacity;
+ int32_t framesPerBurst = 0;
+ int32_t actualSize = 0;
+
+ AAudioStreamBuilder *aaudioBuilder = nullptr;
+ AAudioStream *aaudioStream = nullptr;
+
+ // Use an AAudioStreamBuilder to contain requested parameters.
+ ASSERT_EQ(AAUDIO_OK, AAudio_createStreamBuilder(&aaudioBuilder));
+
+ // Request stream properties.
+ AAudioStreamBuilder_setDataCallback(aaudioBuilder, MyDataCallbackProc, nullptr);
+ AAudioStreamBuilder_setPerformanceMode(aaudioBuilder, AAUDIO_PERFORMANCE_MODE_LOW_LATENCY);
+
+ // Create an AAudioStream using the Builder.
+ EXPECT_EQ(AAUDIO_OK, AAudioStreamBuilder_openStream(aaudioBuilder, &aaudioStream));
+
+ // 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);
+ bufferCapacity = AAudioStream_getBufferCapacityInFrames(aaudioStream);
+ printf(" bufferCapacity = %d, remainder = %d\n",
+ bufferCapacity, bufferCapacity % framesPerBurst);
+
+ actualSize = AAudioStream_setBufferSizeInFrames(aaudioStream, 0);
+ EXPECT_GT(actualSize, 0);
+ EXPECT_LE(actualSize, bufferCapacity);
+
+ actualSize = AAudioStream_setBufferSizeInFrames(aaudioStream, 2 * framesPerBurst);
+ EXPECT_GT(actualSize, framesPerBurst);
+ EXPECT_LE(actualSize, bufferCapacity);
+
+ actualSize = AAudioStream_setBufferSizeInFrames(aaudioStream, bufferCapacity - 1);
+ EXPECT_GT(actualSize, framesPerBurst);
+ EXPECT_LE(actualSize, bufferCapacity);
+
+ actualSize = AAudioStream_setBufferSizeInFrames(aaudioStream, bufferCapacity);
+ EXPECT_GT(actualSize, framesPerBurst);
+ EXPECT_LE(actualSize, bufferCapacity);
+
+ actualSize = AAudioStream_setBufferSizeInFrames(aaudioStream, bufferCapacity + 1);
+ EXPECT_GT(actualSize, framesPerBurst);
+ EXPECT_LE(actualSize, bufferCapacity);
+
+ actualSize = AAudioStream_setBufferSizeInFrames(aaudioStream, 1234567);
+ EXPECT_GT(actualSize, framesPerBurst);
+ EXPECT_LE(actualSize, bufferCapacity);
+
+ actualSize = AAudioStream_setBufferSizeInFrames(aaudioStream, INT32_MAX);
+ EXPECT_GT(actualSize, framesPerBurst);
+ EXPECT_LE(actualSize, bufferCapacity);
+
+ actualSize = AAudioStream_setBufferSizeInFrames(aaudioStream, INT32_MIN);
+ EXPECT_GT(actualSize, 0);
+ EXPECT_LE(actualSize, bufferCapacity);
+
+ AAudioStream_close(aaudioStream);
+ AAudioStreamBuilder_delete(aaudioBuilder);
+ printf(" result = %d = %s\n", result, AAudio_convertResultToText(result));
+}
diff --git a/media/libaudiohal/EffectBufferHalHidl.h b/media/libaudiohal/EffectBufferHalHidl.h
index 66a81c2..d7a43ae 100644
--- a/media/libaudiohal/EffectBufferHalHidl.h
+++ b/media/libaudiohal/EffectBufferHalHidl.h
@@ -35,6 +35,8 @@
virtual audio_buffer_t* audioBuffer();
virtual void* externalData() const;
+ virtual size_t getSize() const override { return mBufferSize; }
+
virtual void setExternalData(void* external);
virtual void setFrameCount(size_t frameCount);
virtual bool checkFrameCountChange();
diff --git a/media/libaudiohal/include/media/audiohal/EffectBufferHalInterface.h b/media/libaudiohal/include/media/audiohal/EffectBufferHalInterface.h
index e862f6e..1cae662 100644
--- a/media/libaudiohal/include/media/audiohal/EffectBufferHalInterface.h
+++ b/media/libaudiohal/include/media/audiohal/EffectBufferHalInterface.h
@@ -37,6 +37,8 @@
return externalData() != nullptr ? externalData() : audioBuffer()->raw;
}
+ virtual size_t getSize() const = 0;
+
virtual void setExternalData(void* external) = 0;
virtual void setFrameCount(size_t frameCount) = 0;
virtual bool checkFrameCountChange() = 0; // returns whether frame count has been updated
diff --git a/media/libaudioprocessing/AudioMixer.cpp b/media/libaudioprocessing/AudioMixer.cpp
index 3e72c89..43b97a5 100644
--- a/media/libaudioprocessing/AudioMixer.cpp
+++ b/media/libaudioprocessing/AudioMixer.cpp
@@ -1947,11 +1947,10 @@
case AUDIO_FORMAT_PCM_16_BIT:
switch (mixerOutFormat) {
case AUDIO_FORMAT_PCM_FLOAT:
- memcpy_to_float_from_q4_27((float*)out, (int32_t*)in, sampleCount);
+ memcpy_to_float_from_q4_27((float*)out, (const int32_t*)in, sampleCount);
break;
case AUDIO_FORMAT_PCM_16_BIT:
- // two int16_t are produced per iteration
- ditherAndClamp((int32_t*)out, (int32_t*)in, sampleCount >> 1);
+ memcpy_to_i16_from_q4_27((int16_t*)out, (const int32_t*)in, sampleCount);
break;
default:
LOG_ALWAYS_FATAL("bad mixerOutFormat: %#x", mixerOutFormat);
diff --git a/media/libaudioprocessing/tests/test-mixer.cpp b/media/libaudioprocessing/tests/test-mixer.cpp
index 75dbf91..b67810d 100644
--- a/media/libaudioprocessing/tests/test-mixer.cpp
+++ b/media/libaudioprocessing/tests/test-mixer.cpp
@@ -316,8 +316,7 @@
outputSampleRate, outputChannels, outputFrames, useMixerFloat);
if (auxFilename) {
// Aux buffer is always in q4_27 format for now.
- // memcpy_to_i16_from_q4_27(), but with stereo frame count (not sample count)
- ditherAndClamp((int32_t*)auxAddr, (int32_t*)auxAddr, outputFrames >> 1);
+ memcpy_to_i16_from_q4_27((int16_t*)auxAddr, (const int32_t*)auxAddr, outputFrames);
writeFile(auxFilename, auxAddr, outputSampleRate, 1, outputFrames, false);
}
diff --git a/media/libeffects/lvm/lib/Common/lib/LVM_Types.h b/media/libeffects/lvm/lib/Common/lib/LVM_Types.h
index cb15b60..ea16072 100644
--- a/media/libeffects/lvm/lib/Common/lib/LVM_Types.h
+++ b/media/libeffects/lvm/lib/Common/lib/LVM_Types.h
@@ -44,9 +44,6 @@
#define LVM_MAXINT_8 127 /* Maximum positive integer size */
#define LVM_MAXINT_16 32767
-#ifdef BUILD_FLOAT
-#define LVM_MAXFLOAT 1.0f
-#endif
#define LVM_MAXINT_32 2147483647
#define LVM_MAXENUM 2147483647
@@ -99,8 +96,32 @@
typedef uint32_t LVM_UINT32; /* Unsigned 32-bit word */
#ifdef BUILD_FLOAT
-typedef float LVM_FLOAT; /* single precission floating point*/
-#endif
+
+#define LVM_MAXFLOAT 1.f
+
+typedef float LVM_FLOAT; /* single precision floating point */
+
+// If NATIVE_FLOAT_BUFFER is defined, we expose effects as floating point format;
+// otherwise we expose as integer 16 bit and translate to float for the effect libraries.
+// Hence, NATIVE_FLOAT_BUFFER should only be enabled under BUILD_FLOAT compilation.
+
+#define NATIVE_FLOAT_BUFFER
+
+#endif // BUILD_FLOAT
+
+// Select whether we expose int16_t or float buffers.
+#ifdef NATIVE_FLOAT_BUFFER
+
+#define EFFECT_BUFFER_FORMAT AUDIO_FORMAT_PCM_FLOAT
+typedef float effect_buffer_t;
+
+#else // NATIVE_FLOAT_BUFFER
+
+#define EFFECT_BUFFER_FORMAT AUDIO_FORMAT_PCM_16_BIT
+typedef int16_t effect_buffer_t;
+
+#endif // NATIVE_FLOAT_BUFFER
+
/****************************************************************************************/
/* */
/* Standard Enumerated types */
diff --git a/media/libeffects/lvm/wrapper/Android.mk b/media/libeffects/lvm/wrapper/Android.mk
index 91e2246..341dbc2 100644
--- a/media/libeffects/lvm/wrapper/Android.mk
+++ b/media/libeffects/lvm/wrapper/Android.mk
@@ -1,5 +1,8 @@
LOCAL_PATH:= $(call my-dir)
+# The wrapper -DBUILD_FLOAT needs to match
+# the lvm library -DBUILD_FLOAT.
+
# music bundle wrapper
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
@@ -20,15 +23,17 @@
LOCAL_STATIC_LIBRARIES += libmusicbundle
LOCAL_SHARED_LIBRARIES := \
- liblog \
+ libaudioutils \
libcutils \
- libdl
+ libdl \
+ liblog \
LOCAL_C_INCLUDES += \
$(LOCAL_PATH)/Bundle \
$(LOCAL_PATH)/../lib/Common/lib/ \
$(LOCAL_PATH)/../lib/Bundle/lib/ \
- $(call include-path-for, audio-effects)
+ $(call include-path-for, audio-effects) \
+ $(call include-path-for, audio-utils) \
LOCAL_HEADER_LIBRARIES += libhardware_headers
include $(BUILD_SHARED_LIBRARY)
@@ -53,15 +58,17 @@
LOCAL_STATIC_LIBRARIES += libreverb
LOCAL_SHARED_LIBRARIES := \
- liblog \
+ libaudioutils \
libcutils \
- libdl
+ libdl \
+ liblog \
LOCAL_C_INCLUDES += \
$(LOCAL_PATH)/Reverb \
$(LOCAL_PATH)/../lib/Common/lib/ \
$(LOCAL_PATH)/../lib/Reverb/lib/ \
- $(call include-path-for, audio-effects)
+ $(call include-path-for, audio-effects) \
+ $(call include-path-for, audio-utils) \
LOCAL_HEADER_LIBRARIES += libhardware_headers
diff --git a/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp b/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp
index aae80b6..146e9e8 100644
--- a/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp
+++ b/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp
@@ -27,6 +27,7 @@
#include <stdlib.h>
#include <string.h>
+#include <audio_utils/primitives.h>
#include <log/log.h>
#include "EffectBundle.h"
@@ -63,16 +64,6 @@
}\
}
-
-static inline int16_t clamp16(int32_t sample)
-{
- // check overflow for both positive and negative values:
- // all bits above short range must me equal to sign bit
- if ((sample>>15) ^ (sample>>31))
- sample = 0x7FFF ^ (sample>>31);
- return sample;
-}
-
// Namespaces
namespace android {
namespace {
@@ -299,7 +290,7 @@
pContext->pBundledContext->SamplesToExitCountVirt = 0;
pContext->pBundledContext->SamplesToExitCountBb = 0;
pContext->pBundledContext->SamplesToExitCountEq = 0;
-#ifdef BUILD_FLOAT
+#if defined(BUILD_FLOAT) && !defined(NATIVE_FLOAT_BUFFER)
pContext->pBundledContext->pInputBuffer = NULL;
pContext->pBundledContext->pOutputBuffer = NULL;
#endif
@@ -470,13 +461,9 @@
if (pContext->pBundledContext->workBuffer != NULL) {
free(pContext->pBundledContext->workBuffer);
}
-#ifdef BUILD_FLOAT
- if (pContext->pBundledContext->pInputBuffer != NULL) {
- free(pContext->pBundledContext->pInputBuffer);
- }
- if (pContext->pBundledContext->pOutputBuffer != NULL) {
- free(pContext->pBundledContext->pOutputBuffer);
- }
+#if defined(BUILD_FLOAT) && !defined(NATIVE_FLOAT_BUFFER)
+ free(pContext->pBundledContext->pInputBuffer);
+ free(pContext->pBundledContext->pOutputBuffer);
#endif
delete pContext->pBundledContext;
pContext->pBundledContext = LVM_NULL;
@@ -549,7 +536,7 @@
pContext->config.inputCfg.accessMode = EFFECT_BUFFER_ACCESS_READ;
pContext->config.inputCfg.channels = AUDIO_CHANNEL_OUT_STEREO;
- pContext->config.inputCfg.format = AUDIO_FORMAT_PCM_16_BIT;
+ pContext->config.inputCfg.format = EFFECT_BUFFER_FORMAT;
pContext->config.inputCfg.samplingRate = 44100;
pContext->config.inputCfg.bufferProvider.getBuffer = NULL;
pContext->config.inputCfg.bufferProvider.releaseBuffer = NULL;
@@ -557,7 +544,7 @@
pContext->config.inputCfg.mask = EFFECT_CONFIG_ALL;
pContext->config.outputCfg.accessMode = EFFECT_BUFFER_ACCESS_ACCUMULATE;
pContext->config.outputCfg.channels = AUDIO_CHANNEL_OUT_STEREO;
- pContext->config.outputCfg.format = AUDIO_FORMAT_PCM_16_BIT;
+ pContext->config.outputCfg.format = EFFECT_BUFFER_FORMAT;
pContext->config.outputCfg.samplingRate = 44100;
pContext->config.outputCfg.bufferProvider.getBuffer = NULL;
pContext->config.outputCfg.bufferProvider.releaseBuffer = NULL;
@@ -734,47 +721,6 @@
return 0;
} /* end LvmBundle_init */
-#ifdef BUILD_FLOAT
-/**********************************************************************************
- FUNCTION INT16LTOFLOAT
-***********************************************************************************/
-// Todo: need to write function descriptor
-static void Int16ToFloat(const LVM_INT16 *src, LVM_FLOAT *dst, size_t n) {
- size_t ii;
- src += n-1;
- dst += n-1;
- for (ii = n; ii != 0; ii--) {
- *dst = ((LVM_FLOAT)((LVM_INT16)*src)) / 32768.0f;
- src--;
- dst--;
- }
- return;
-}
-/**********************************************************************************
- FUNCTION FLOATTOINT16_SAT
-***********************************************************************************/
-// Todo : Need to write function descriptor
-static void FloatToInt16_SAT(const LVM_FLOAT *src, LVM_INT16 *dst, size_t n) {
- size_t ii;
- LVM_INT32 temp;
-
- src += n-1;
- dst += n-1;
- for (ii = n; ii != 0; ii--) {
- temp = (LVM_INT32)((*src) * 32768.0f);
- if (temp >= 32767) {
- *dst = 32767;
- } else if (temp <= -32768) {
- *dst = -32768;
- } else {
- *dst = (LVM_INT16)temp;
- }
- src--;
- dst--;
- }
- return;
-}
-#endif
//----------------------------------------------------------------------------
// LvmBundle_process()
//----------------------------------------------------------------------------
@@ -782,8 +728,8 @@
// Apply LVM Bundle effects
//
// Inputs:
-// pIn: pointer to stereo 16 bit input data
-// pOut: pointer to stereo 16 bit output data
+// pIn: pointer to stereo float or 16 bit input data
+// pOut: pointer to stereo float or 16 bit output data
// frameCount: Frames to process
// pContext: effect engine context
// strength strength to be applied
@@ -793,44 +739,37 @@
//
//----------------------------------------------------------------------------
#ifdef BUILD_FLOAT
-int LvmBundle_process(LVM_INT16 *pIn,
- LVM_INT16 *pOut,
+int LvmBundle_process(effect_buffer_t *pIn,
+ effect_buffer_t *pOut,
int frameCount,
EffectContext *pContext){
-
- //LVM_ControlParams_t ActiveParams; /* Current control Parameters */
LVM_ReturnStatus_en LvmStatus = LVM_SUCCESS; /* Function call status */
- LVM_INT16 *pOutTmp;
- LVM_FLOAT *pInputBuff;
- LVM_FLOAT *pOutputBuff;
-
- if (pContext->pBundledContext->pInputBuffer == NULL ||
+ effect_buffer_t *pOutTmp;
+#ifndef NATIVE_FLOAT_BUFFER
+ if (pContext->pBundledContext->pInputBuffer == nullptr ||
pContext->pBundledContext->frameCount < frameCount) {
- if (pContext->pBundledContext->pInputBuffer != NULL) {
- free(pContext->pBundledContext->pInputBuffer);
- }
- pContext->pBundledContext->pInputBuffer = (LVM_FLOAT *)malloc(frameCount * \
- sizeof(LVM_FLOAT) * FCC_2);
+ free(pContext->pBundledContext->pInputBuffer);
+ pContext->pBundledContext->pInputBuffer =
+ (LVM_FLOAT *)calloc(frameCount, sizeof(LVM_FLOAT) * FCC_2);
}
- if (pContext->pBundledContext->pOutputBuffer == NULL ||
+ if (pContext->pBundledContext->pOutputBuffer == nullptr ||
pContext->pBundledContext->frameCount < frameCount) {
- if (pContext->pBundledContext->pOutputBuffer != NULL) {
- free(pContext->pBundledContext->pOutputBuffer);
- }
- pContext->pBundledContext->pOutputBuffer = (LVM_FLOAT *)malloc(frameCount * \
- sizeof(LVM_FLOAT) * FCC_2);
+ free(pContext->pBundledContext->pOutputBuffer);
+ pContext->pBundledContext->pOutputBuffer =
+ (LVM_FLOAT *)calloc(frameCount, sizeof(LVM_FLOAT) * FCC_2);
}
- if ((pContext->pBundledContext->pInputBuffer == NULL) ||
- (pContext->pBundledContext->pOutputBuffer == NULL)) {
- ALOGV("LVM_ERROR : LvmBundle_process memory allocation for float buffer's failed");
+ if (pContext->pBundledContext->pInputBuffer == nullptr ||
+ pContext->pBundledContext->pOutputBuffer == nullptr) {
+ ALOGE("LVM_ERROR : LvmBundle_process memory allocation for float buffer's failed");
return -EINVAL;
}
- pInputBuff = pContext->pBundledContext->pInputBuffer;
- pOutputBuff = pContext->pBundledContext->pOutputBuffer;
+ LVM_FLOAT * const pInputBuff = pContext->pBundledContext->pInputBuffer;
+ LVM_FLOAT * const pOutputBuff = pContext->pBundledContext->pOutputBuffer;
+#endif
if (pContext->config.outputCfg.accessMode == EFFECT_BUFFER_ACCESS_WRITE){
pOutTmp = pOut;
@@ -840,7 +779,7 @@
free(pContext->pBundledContext->workBuffer);
}
pContext->pBundledContext->workBuffer =
- (LVM_INT16 *)calloc(frameCount, sizeof(LVM_INT16) * FCC_2);
+ (effect_buffer_t *)calloc(frameCount, sizeof(effect_buffer_t) * FCC_2);
if (pContext->pBundledContext->workBuffer == NULL) {
return -ENOMEM;
}
@@ -852,43 +791,61 @@
return -EINVAL;
}
- #ifdef LVM_PCM
- fwrite(pIn, frameCount*sizeof(LVM_INT16) * FCC_2, 1, pContext->pBundledContext->PcmInPtr);
+#ifdef LVM_PCM
+ fwrite(pIn,
+ frameCount*sizeof(effect_buffer_t) * FCC_2, 1, pContext->pBundledContext->PcmInPtr);
fflush(pContext->pBundledContext->PcmInPtr);
- #endif
+#endif
+#ifndef NATIVE_FLOAT_BUFFER
/* Converting input data from fixed point to float point */
- Int16ToFloat(pIn, pInputBuff, frameCount * 2);
+ memcpy_to_float_from_i16(pInputBuff, pIn, frameCount * FCC_2);
/* Process the samples */
LvmStatus = LVM_Process(pContext->pBundledContext->hInstance, /* Instance handle */
pInputBuff, /* Input buffer */
pOutputBuff, /* Output buffer */
(LVM_UINT16)frameCount, /* Number of samples to read */
- 0); /* Audo Time */
+ 0); /* Audio Time */
+ /* Converting output data from float point to fixed point */
+ memcpy_to_i16_from_float(pOutTmp, pOutputBuff, frameCount * FCC_2);
+
+#else
+ /* Process the samples */
+ LvmStatus = LVM_Process(pContext->pBundledContext->hInstance, /* Instance handle */
+ pIn, /* Input buffer */
+ pOutTmp, /* Output buffer */
+ (LVM_UINT16)frameCount, /* Number of samples to read */
+ 0); /* Audio Time */
+#endif
LVM_ERROR_CHECK(LvmStatus, "LVM_Process", "LvmBundle_process")
if(LvmStatus != LVM_SUCCESS) return -EINVAL;
- /* Converting output data from float point to fixed point */
- FloatToInt16_SAT(pOutputBuff, pOutTmp, (LVM_UINT16)frameCount * 2);
- #ifdef LVM_PCM
- fwrite(pOutTmp, frameCount*sizeof(LVM_INT16) * FCC_2, 1, pContext->pBundledContext->PcmOutPtr);
+#ifdef LVM_PCM
+ fwrite(pOutTmp,
+ frameCount*sizeof(effect_buffer_t) * FCC_2, 1, pContext->pBundledContext->PcmOutPtr);
fflush(pContext->pBundledContext->PcmOutPtr);
- #endif
+#endif
if (pContext->config.outputCfg.accessMode == EFFECT_BUFFER_ACCESS_ACCUMULATE){
- for (int i = 0; i < frameCount * 2; i++){
+ for (int i = 0; i < frameCount * FCC_2; i++) {
+#ifndef NATIVE_FLOAT_BUFFER
pOut[i] = clamp16((LVM_INT32)pOut[i] + (LVM_INT32)pOutTmp[i]);
+#else
+ pOut[i] = pOut[i] + pOutTmp[i];
+#endif
}
}
return 0;
} /* end LvmBundle_process */
-#else
+
+#else // BUILD_FLOAT
+
int LvmBundle_process(LVM_INT16 *pIn,
LVM_INT16 *pOut,
int frameCount,
- EffectContext *pContext){
+ EffectContext *pContext) {
LVM_ReturnStatus_en LvmStatus = LVM_SUCCESS; /* Function call status */
LVM_INT16 *pOutTmp;
@@ -901,7 +858,7 @@
free(pContext->pBundledContext->workBuffer);
}
pContext->pBundledContext->workBuffer =
- (LVM_INT16 *)calloc(frameCount, sizeof(LVM_INT16) * 2);
+ (effect_buffer_t *)calloc(frameCount, sizeof(effect_buffer_t) * FCC_2);
if (pContext->pBundledContext->workBuffer == NULL) {
return -ENOMEM;
}
@@ -913,10 +870,11 @@
return -EINVAL;
}
- #ifdef LVM_PCM
- fwrite(pIn, frameCount*sizeof(LVM_INT16)*2, 1, pContext->pBundledContext->PcmInPtr);
+#ifdef LVM_PCM
+ fwrite(pIn, frameCount * sizeof(*pIn) * FCC_2,
+ 1 /* nmemb */, pContext->pBundledContext->PcmInPtr);
fflush(pContext->pBundledContext->PcmInPtr);
- #endif
+#endif
//ALOGV("Calling LVM_Process");
@@ -925,15 +883,16 @@
pIn, /* Input buffer */
pOutTmp, /* Output buffer */
(LVM_UINT16)frameCount, /* Number of samples to read */
- 0); /* Audo Time */
+ 0); /* Audio Time */
LVM_ERROR_CHECK(LvmStatus, "LVM_Process", "LvmBundle_process")
if(LvmStatus != LVM_SUCCESS) return -EINVAL;
- #ifdef LVM_PCM
- fwrite(pOutTmp, frameCount*sizeof(LVM_INT16)*2, 1, pContext->pBundledContext->PcmOutPtr);
+#ifdef LVM_PCM
+ fwrite(pOutTmp, frameCount * sizeof(*pOutTmp) * FCC_2,
+ 1 /* nmemb */, pContext->pBundledContext->PcmOutPtr);
fflush(pContext->pBundledContext->PcmOutPtr);
- #endif
+#endif
if (pContext->config.outputCfg.accessMode == EFFECT_BUFFER_ACCESS_ACCUMULATE){
for (int i=0; i<frameCount*2; i++){
@@ -942,7 +901,8 @@
}
return 0;
} /* end LvmBundle_process */
-#endif
+
+#endif // BUILD_FLOAT
//----------------------------------------------------------------------------
// EqualizerUpdateActiveParams()
@@ -1276,8 +1236,7 @@
CHECK_ARG(pConfig->inputCfg.channels == AUDIO_CHANNEL_OUT_STEREO);
CHECK_ARG(pConfig->outputCfg.accessMode == EFFECT_BUFFER_ACCESS_WRITE
|| pConfig->outputCfg.accessMode == EFFECT_BUFFER_ACCESS_ACCUMULATE);
- CHECK_ARG(pConfig->inputCfg.format == AUDIO_FORMAT_PCM_16_BIT);
-
+ CHECK_ARG(pConfig->inputCfg.format == EFFECT_BUFFER_FORMAT);
pContext->config = *pConfig;
switch (pConfig->inputCfg.samplingRate) {
@@ -3349,10 +3308,17 @@
pContext->pBundledContext->NumberEffectsCalled = 0;
/* Process all the available frames, block processing is
handled internalLY by the LVM bundle */
- processStatus = android::LvmBundle_process( (LVM_INT16 *)inBuffer->raw,
- (LVM_INT16 *)outBuffer->raw,
- outBuffer->frameCount,
- pContext);
+#ifdef NATIVE_FLOAT_BUFFER
+ processStatus = android::LvmBundle_process(inBuffer->f32,
+ outBuffer->f32,
+ outBuffer->frameCount,
+ pContext);
+#else
+ processStatus = android::LvmBundle_process(inBuffer->s16,
+ outBuffer->s16,
+ outBuffer->frameCount,
+ pContext);
+#endif
if (processStatus != 0){
ALOGV("\tLVM_ERROR : LvmBundle_process returned error %d", processStatus);
if (status == 0) {
diff --git a/media/libeffects/lvm/wrapper/Bundle/EffectBundle.h b/media/libeffects/lvm/wrapper/Bundle/EffectBundle.h
index 291383a..6bf045d 100644
--- a/media/libeffects/lvm/wrapper/Bundle/EffectBundle.h
+++ b/media/libeffects/lvm/wrapper/Bundle/EffectBundle.h
@@ -95,7 +95,7 @@
int SamplesToExitCountEq;
int SamplesToExitCountBb;
int SamplesToExitCountVirt;
- LVM_INT16 *workBuffer;
+ effect_buffer_t *workBuffer;
int frameCount;
int32_t bandGaindB[FIVEBAND_NUMBANDS];
int volume;
@@ -103,10 +103,10 @@
FILE *PcmInPtr;
FILE *PcmOutPtr;
#endif
- #ifdef BUILD_FLOAT
+#if defined(BUILD_FLOAT) && !defined(NATIVE_FLOAT_BUFFER)
LVM_FLOAT *pInputBuffer;
LVM_FLOAT *pOutputBuffer;
- #endif
+#endif
};
/* SessionContext : One session */
diff --git a/media/libeffects/lvm/wrapper/Reverb/EffectReverb.cpp b/media/libeffects/lvm/wrapper/Reverb/EffectReverb.cpp
index 3d8e982..0630285 100644
--- a/media/libeffects/lvm/wrapper/Reverb/EffectReverb.cpp
+++ b/media/libeffects/lvm/wrapper/Reverb/EffectReverb.cpp
@@ -27,6 +27,7 @@
#include <stdlib.h>
#include <string.h>
+#include <audio_utils/primitives.h>
#include <log/log.h>
#include "EffectReverb.h"
@@ -135,6 +136,12 @@
&gInsertPresetReverbDescriptor
};
+#ifdef BUILD_FLOAT
+typedef float process_buffer_t; // process in float
+#else
+typedef int32_t process_buffer_t; // process in Q4_27
+#endif // BUILD_FLOAT
+
struct ReverbContext{
const struct effect_interface_s *itfe;
effect_config_t config;
@@ -152,8 +159,8 @@
FILE *PcmOutPtr;
#endif
LVM_Fs_en SampleRate;
- LVM_INT32 *InFrames32;
- LVM_INT32 *OutFrames32;
+ process_buffer_t *InFrames;
+ process_buffer_t *OutFrames;
size_t bufferSizeIn;
size_t bufferSizeOut;
bool auxiliary;
@@ -262,7 +269,7 @@
*pHandle = (effect_handle_t)pContext;
- #ifdef LVM_PCM
+#ifdef LVM_PCM
pContext->PcmInPtr = NULL;
pContext->PcmOutPtr = NULL;
@@ -273,19 +280,15 @@
(pContext->PcmOutPtr == NULL)){
return -EINVAL;
}
- #endif
+#endif
+ int channels = audio_channel_count_from_out_mask(pContext->config.inputCfg.channels);
// Allocate memory for reverb process (*2 is for STEREO)
-#ifdef BUILD_FLOAT
- pContext->bufferSizeIn = LVREV_MAX_FRAME_SIZE * sizeof(float) * 2;
- pContext->bufferSizeOut = pContext->bufferSizeIn;
-#else
- pContext->bufferSizeIn = LVREV_MAX_FRAME_SIZE * sizeof(LVM_INT32) * 2;
- pContext->bufferSizeOut = pContext->bufferSizeIn;
-#endif
- pContext->InFrames32 = (LVM_INT32 *)malloc(pContext->bufferSizeIn);
- pContext->OutFrames32 = (LVM_INT32 *)malloc(pContext->bufferSizeOut);
+ pContext->bufferSizeIn = LVREV_MAX_FRAME_SIZE * sizeof(process_buffer_t) * channels;
+ pContext->bufferSizeOut = LVREV_MAX_FRAME_SIZE * sizeof(process_buffer_t) * FCC_2;
+ pContext->InFrames = (process_buffer_t *)calloc(pContext->bufferSizeIn, 1 /* size */);
+ pContext->OutFrames = (process_buffer_t *)calloc(pContext->bufferSizeOut, 1 /* size */);
ALOGV("\tEffectCreate %p, size %zu", pContext, sizeof(ReverbContext));
ALOGV("\tEffectCreate end\n");
@@ -305,8 +308,8 @@
fclose(pContext->PcmInPtr);
fclose(pContext->PcmOutPtr);
#endif
- free(pContext->InFrames32);
- free(pContext->OutFrames32);
+ free(pContext->InFrames);
+ free(pContext->OutFrames);
pContext->bufferSizeIn = 0;
pContext->bufferSizeOut = 0;
Reverb_free(pContext);
@@ -344,114 +347,6 @@
} \
}
-#if 0
-//----------------------------------------------------------------------------
-// MonoTo2I_32()
-//----------------------------------------------------------------------------
-// Purpose:
-// Convert MONO to STEREO
-//
-//----------------------------------------------------------------------------
-
-void MonoTo2I_32( const LVM_INT32 *src,
- LVM_INT32 *dst,
- LVM_INT16 n)
-{
- LVM_INT16 ii;
- src += (n-1);
- dst += ((n*2)-1);
-
- for (ii = n; ii != 0; ii--)
- {
- *dst = *src;
- dst--;
-
- *dst = *src;
- dst--;
- src--;
- }
-
- return;
-}
-
-//----------------------------------------------------------------------------
-// From2iToMono_32()
-//----------------------------------------------------------------------------
-// Purpose:
-// Convert STEREO to MONO
-//
-//----------------------------------------------------------------------------
-
-void From2iToMono_32( const LVM_INT32 *src,
- LVM_INT32 *dst,
- LVM_INT16 n)
-{
- LVM_INT16 ii;
- LVM_INT32 Temp;
-
- for (ii = n; ii != 0; ii--)
- {
- Temp = (*src>>1);
- src++;
-
- Temp +=(*src>>1);
- src++;
-
- *dst = Temp;
- dst++;
- }
-
- return;
-}
-#endif
-
-#ifdef BUILD_FLOAT
-/**********************************************************************************
- FUNCTION INT16LTOFLOAT
-***********************************************************************************/
-// Todo: need to write function descriptor
-static void Int16ToFloat(const LVM_INT16 *src, LVM_FLOAT *dst, size_t n) {
- size_t ii;
- src += n-1;
- dst += n-1;
- for (ii = n; ii != 0; ii--) {
- *dst = ((LVM_FLOAT)((LVM_INT16)*src)) / 32768.0f;
- src--;
- dst--;
- }
- return;
-}
-/**********************************************************************************
- FUNCTION FLOATTOINT16_SAT
-***********************************************************************************/
-// Todo : Need to write function descriptor
-static void FloatToInt16_SAT(const LVM_FLOAT *src, LVM_INT16 *dst, size_t n) {
- size_t ii;
- LVM_INT32 temp;
-
- for (ii = 0; ii < n; ii++) {
- temp = (LVM_INT32)((*src) * 32768.0f);
- if (temp >= 32767) {
- *dst = 32767;
- } else if (temp <= -32768) {
- *dst = -32768;
- } else {
- *dst = (LVM_INT16)temp;
- }
- src++;
- dst++;
- }
- return;
-}
-#endif
-
-static inline int16_t clamp16(int32_t sample)
-{
- if ((sample>>15) ^ (sample>>31))
- sample = 0x7FFF ^ (sample>>31);
- return sample;
-}
-
//----------------------------------------------------------------------------
// process()
//----------------------------------------------------------------------------
@@ -459,8 +354,8 @@
// Apply the Reverb
//
// Inputs:
-// pIn: pointer to stereo/mono 16 bit input data
-// pOut: pointer to stereo 16 bit output data
+// pIn: pointer to stereo/mono float or 16 bit input data
+// pOut: pointer to stereo float or 16 bit output data
// frameCount: Frames to process
// pContext: effect engine context
// strength strength to be applied
@@ -469,116 +364,107 @@
// pOut: pointer to updated stereo 16 bit output data
//
//----------------------------------------------------------------------------
-
-int process( LVM_INT16 *pIn,
- LVM_INT16 *pOut,
+int process( effect_buffer_t *pIn,
+ effect_buffer_t *pOut,
int frameCount,
ReverbContext *pContext){
- LVM_INT16 samplesPerFrame = 1;
+ int channels = audio_channel_count_from_out_mask(pContext->config.inputCfg.channels);
LVREV_ReturnStatus_en LvmStatus = LVREV_SUCCESS; /* Function call status */
- LVM_INT16 *OutFrames16;
-#ifdef BUILD_FLOAT
- LVM_FLOAT *pInputBuff;
- LVM_FLOAT *pOutputBuff;
-#endif
-#ifdef BUILD_FLOAT
- if (pContext->InFrames32 == NULL ||
- pContext->bufferSizeIn < frameCount * sizeof(float) * 2) {
- if (pContext->InFrames32 != NULL) {
- free(pContext->InFrames32);
- }
- pContext->bufferSizeIn = frameCount * sizeof(float) * 2;
- pContext->InFrames32 = (LVM_INT32 *)malloc(pContext->bufferSizeIn);
- }
- if (pContext->OutFrames32 == NULL ||
- pContext->bufferSizeOut < frameCount * sizeof(float) * 2) {
- if (pContext->OutFrames32 != NULL) {
- free(pContext->OutFrames32);
- }
- pContext->bufferSizeOut = frameCount * sizeof(float) * 2;
- pContext->OutFrames32 = (LVM_INT32 *)malloc(pContext->bufferSizeOut);
- }
- pInputBuff = (float *)pContext->InFrames32;
- pOutputBuff = (float *)pContext->OutFrames32;
-#endif
// Check that the input is either mono or stereo
- if (pContext->config.inputCfg.channels == AUDIO_CHANNEL_OUT_STEREO) {
- samplesPerFrame = 2;
- } else if (pContext->config.inputCfg.channels != AUDIO_CHANNEL_OUT_MONO) {
- ALOGV("\tLVREV_ERROR : process invalid PCM format");
+ if (!(channels == 1 || channels == FCC_2) ) {
+ ALOGE("\tLVREV_ERROR : process invalid PCM format");
return -EINVAL;
}
- OutFrames16 = (LVM_INT16 *)pContext->OutFrames32;
+#ifdef BUILD_FLOAT
+ size_t inSize = frameCount * sizeof(process_buffer_t) * channels;
+ size_t outSize = frameCount * sizeof(process_buffer_t) * FCC_2;
+ if (pContext->InFrames == NULL ||
+ pContext->bufferSizeIn < inSize) {
+ free(pContext->InFrames);
+ pContext->bufferSizeIn = inSize;
+ pContext->InFrames = (process_buffer_t *)calloc(1, pContext->bufferSizeIn);
+ }
+ if (pContext->OutFrames == NULL ||
+ pContext->bufferSizeOut < outSize) {
+ free(pContext->OutFrames);
+ pContext->bufferSizeOut = outSize;
+ pContext->OutFrames = (process_buffer_t *)calloc(1, pContext->bufferSizeOut);
+ }
+
+#ifndef NATIVE_FLOAT_BUFFER
+ effect_buffer_t * const OutFrames16 = (effect_buffer_t *)pContext->OutFrames;
+#endif
+#endif
// Check for NULL pointers
- if((pContext->InFrames32 == NULL)||(pContext->OutFrames32 == NULL)){
- ALOGV("\tLVREV_ERROR : process failed to allocate memory for temporary buffers ");
+ if ((pContext->InFrames == NULL) || (pContext->OutFrames == NULL)) {
+ ALOGE("\tLVREV_ERROR : process failed to allocate memory for temporary buffers ");
return -EINVAL;
}
- #ifdef LVM_PCM
- fwrite(pIn, frameCount*sizeof(LVM_INT16)*samplesPerFrame, 1, pContext->PcmInPtr);
+#ifdef LVM_PCM
+ fwrite(pIn, frameCount * sizeof(*pIn) * channels, 1 /* nmemb */, pContext->PcmInPtr);
fflush(pContext->PcmInPtr);
- #endif
+#endif
if (pContext->preset && pContext->nextPreset != pContext->curPreset) {
Reverb_LoadPreset(pContext);
}
- // Convert to Input 32 bits
if (pContext->auxiliary) {
#ifdef BUILD_FLOAT
- Int16ToFloat(pIn, pInputBuff, frameCount * samplesPerFrame);
+#ifdef NATIVE_FLOAT_BUFFER
+ static_assert(std::is_same<decltype(*pIn), decltype(*pContext->InFrames)>::value,
+ "pIn and InFrames must be same type");
+ memcpy(pContext->InFrames, pIn, frameCount * channels * sizeof(*pIn));
#else
- for(int i=0; i<frameCount*samplesPerFrame; i++){
- pContext->InFrames32[i] = (LVM_INT32)pIn[i]<<8;
+ memcpy_to_float_from_i16(
+ pContext->InFrames, pIn, frameCount * channels);
+#endif
+#else //no BUILD_FLOAT
+ for (int i = 0; i < frameCount * channels; i++) {
+ pContext->InFrames[i] = (process_buffer_t)pIn[i]<<8;
}
#endif
} else {
// insert reverb input is always stereo
for (int i = 0; i < frameCount; i++) {
-#ifndef BUILD_FLOAT
- pContext->InFrames32[2*i] = (pIn[2*i] * REVERB_SEND_LEVEL) >> 4; // <<8 + >>12
- pContext->InFrames32[2*i+1] = (pIn[2*i+1] * REVERB_SEND_LEVEL) >> 4; // <<8 + >>12
+#ifdef BUILD_FLOAT
+#ifdef NATIVE_FLOAT_BUFFER
+ pContext->InFrames[2 * i] = (process_buffer_t)pIn[2 * i] * REVERB_SEND_LEVEL;
+ pContext->InFrames[2 * i + 1] = (process_buffer_t)pIn[2 * i + 1] * REVERB_SEND_LEVEL;
#else
- pInputBuff[2 * i] = (LVM_FLOAT)pIn[2 * i] * REVERB_SEND_LEVEL / 32768.0f;
- pInputBuff[2 * i + 1] = (LVM_FLOAT)pIn[2 * i + 1] * REVERB_SEND_LEVEL / 32768.0f;
+ pContext->InFrames[2 * i] =
+ (process_buffer_t)pIn[2 * i] * REVERB_SEND_LEVEL / 32768.0f;
+ pContext->InFrames[2 * i + 1] =
+ (process_buffer_t)pIn[2 * i + 1] * REVERB_SEND_LEVEL / 32768.0f;
+#endif
+#else
+ pContext->InFrames[2*i] = (pIn[2*i] * REVERB_SEND_LEVEL) >> 4; // <<8 + >>12
+ pContext->InFrames[2*i+1] = (pIn[2*i+1] * REVERB_SEND_LEVEL) >> 4; // <<8 + >>12
#endif
}
}
if (pContext->preset && pContext->curPreset == REVERB_PRESET_NONE) {
-#ifdef BUILD_FLOAT
- memset(pOutputBuff, 0, frameCount * sizeof(LVM_FLOAT) * 2); //always stereo here
-#else
- memset(pContext->OutFrames32, 0, frameCount * sizeof(LVM_INT32) * 2); //always stereo here
-#endif
+ memset(pContext->OutFrames, 0,
+ frameCount * sizeof(*pContext->OutFrames) * FCC_2); //always stereo here
} else {
if(pContext->bEnabled == LVM_FALSE && pContext->SamplesToExitCount > 0) {
-#ifdef BUILD_FLOAT
- memset(pInputBuff, 0, frameCount * sizeof(LVM_FLOAT) * samplesPerFrame);
-#else
- memset(pContext->InFrames32,0,frameCount * sizeof(LVM_INT32) * samplesPerFrame);
-#endif
- ALOGV("\tZeroing %d samples per frame at the end of call", samplesPerFrame);
+ memset(pContext->InFrames, 0,
+ frameCount * sizeof(*pContext->OutFrames) * channels);
+ ALOGV("\tZeroing %d samples per frame at the end of call", channels);
}
/* Process the samples, producing a stereo output */
-#ifdef BUILD_FLOAT
LvmStatus = LVREV_Process(pContext->hInstance, /* Instance handle */
- pInputBuff, /* Input buffer */
- pOutputBuff, /* Output buffer */
+ pContext->InFrames, /* Input buffer */
+ pContext->OutFrames, /* Output buffer */
frameCount); /* Number of samples to read */
-#else
- LvmStatus = LVREV_Process(pContext->hInstance, /* Instance handle */
- pContext->InFrames32, /* Input buffer */
- pContext->OutFrames32, /* Output buffer */
- frameCount); /* Number of samples to read */
-#endif
- }
+ }
LVM_ERROR_CHECK(LvmStatus, "LVREV_Process", "process")
if(LvmStatus != LVREV_SUCCESS) return -EINVAL;
@@ -586,55 +472,87 @@
// Convert to 16 bits
if (pContext->auxiliary) {
#ifdef BUILD_FLOAT
- FloatToInt16_SAT(pOutputBuff, OutFrames16, (size_t)frameCount * 2);
-#else
- for (int i=0; i < frameCount*2; i++) { //always stereo here
- OutFrames16[i] = clamp16(pContext->OutFrames32[i]>>8);
- }
+ // nothing to do here
+#ifndef NATIVE_FLOAT_BUFFER
+ // pContext->OutFrames and OutFrames16 point to the same buffer
+ // make sure the float to int conversion happens in the right order.
+ memcpy_to_i16_from_float(OutFrames16, pContext->OutFrames,
+ (size_t)frameCount * FCC_2);
#endif
- } else {
-#ifdef BUILD_FLOAT
- for (int i = 0; i < frameCount * 2; i++) {//always stereo here
- //pOutputBuff and OutFrames16 point to the same buffer, so better to
- //accumulate in pInputBuff, which is available
- pInputBuff[i] = pOutputBuff[i] + (LVM_FLOAT)pIn[i] / 32768.0f;
- }
-
- FloatToInt16_SAT(pInputBuff, OutFrames16, (size_t)frameCount * 2);
#else
- for (int i=0; i < frameCount*2; i++) { //always stereo here
- OutFrames16[i] = clamp16((pContext->OutFrames32[i]>>8) + (LVM_INT32)pIn[i]);
- }
+ memcpy_to_i16_from_q4_27(OutFrames16, pContext->OutFrames, (size_t)frameCount * FCC_2);
+#endif
+ } else {
+#ifdef BUILD_FLOAT
+#ifdef NATIVE_FLOAT_BUFFER
+ for (int i = 0; i < frameCount * FCC_2; i++) { // always stereo here
+ // Mix with dry input
+ pContext->OutFrames[i] += pIn[i];
+ }
+#else
+ for (int i = 0; i < frameCount * FCC_2; i++) { // always stereo here
+ // pOutputBuff and OutFrames16 point to the same buffer
+ // make sure the float to int conversion happens in the right order.
+ pContext->OutFrames[i] += (process_buffer_t)pIn[i] / 32768.0f;
+ }
+ memcpy_to_i16_from_float(OutFrames16, pContext->OutFrames,
+ (size_t)frameCount * FCC_2);
+#endif
+#else
+ for (int i=0; i < frameCount * FCC_2; i++) { // always stereo here
+ OutFrames16[i] = clamp16((pContext->OutFrames[i]>>8) + (process_buffer_t)pIn[i]);
+ }
#endif
// apply volume with ramp if needed
if ((pContext->leftVolume != pContext->prevLeftVolume ||
pContext->rightVolume != pContext->prevRightVolume) &&
pContext->volumeMode == REVERB_VOLUME_RAMP) {
+#if defined (BUILD_FLOAT) && defined (NATIVE_FLOAT_BUFFER)
+ // FIXME: still using int16 volumes.
+ // For reference: REVERB_UNIT_VOLUME (0x1000) // 1.0 in 4.12 format
+ float vl = (float)pContext->prevLeftVolume / 4096;
+ float incl = (((float)pContext->leftVolume / 4096) - vl) / frameCount;
+ float vr = (float)pContext->prevRightVolume / 4096;
+ float incr = (((float)pContext->rightVolume / 4096) - vr) / frameCount;
+
+ for (int i = 0; i < frameCount; i++) {
+ pContext->OutFrames[FCC_2 * i] *= vl;
+ pContext->OutFrames[FCC_2 * i + 1] *= vr;
+
+ vl += incl;
+ vr += incr;
+ }
+#else
LVM_INT32 vl = (LVM_INT32)pContext->prevLeftVolume << 16;
LVM_INT32 incl = (((LVM_INT32)pContext->leftVolume << 16) - vl) / frameCount;
LVM_INT32 vr = (LVM_INT32)pContext->prevRightVolume << 16;
LVM_INT32 incr = (((LVM_INT32)pContext->rightVolume << 16) - vr) / frameCount;
for (int i = 0; i < frameCount; i++) {
- OutFrames16[2*i] =
+ OutFrames16[FCC_2 * i] =
clamp16((LVM_INT32)((vl >> 16) * OutFrames16[2*i]) >> 12);
- OutFrames16[2*i+1] =
+ OutFrames16[FCC_2 * i + 1] =
clamp16((LVM_INT32)((vr >> 16) * OutFrames16[2*i+1]) >> 12);
vl += incl;
vr += incr;
}
-
+#endif
pContext->prevLeftVolume = pContext->leftVolume;
pContext->prevRightVolume = pContext->rightVolume;
} else if (pContext->volumeMode != REVERB_VOLUME_OFF) {
if (pContext->leftVolume != REVERB_UNIT_VOLUME ||
pContext->rightVolume != REVERB_UNIT_VOLUME) {
for (int i = 0; i < frameCount; i++) {
- OutFrames16[2*i] =
+#if defined(BUILD_FLOAT) && defined(NATIVE_FLOAT_BUFFER)
+ pContext->OutFrames[FCC_2 * i] *= ((float)pContext->leftVolume / 4096);
+ pContext->OutFrames[FCC_2 * i + 1] *= ((float)pContext->rightVolume / 4096);
+#else
+ OutFrames16[FCC_2 * i] =
clamp16((LVM_INT32)(pContext->leftVolume * OutFrames16[2*i]) >> 12);
- OutFrames16[2*i+1] =
+ OutFrames16[FCC_2 * i + 1] =
clamp16((LVM_INT32)(pContext->rightVolume * OutFrames16[2*i+1]) >> 12);
+#endif
}
}
pContext->prevLeftVolume = pContext->leftVolume;
@@ -643,20 +561,25 @@
}
}
- #ifdef LVM_PCM
- fwrite(OutFrames16, frameCount*sizeof(LVM_INT16)*2, 1, pContext->PcmOutPtr);
+#ifdef LVM_PCM
+ fwrite(pContext->OutFrames, frameCount * sizeof(*pContext->OutFrames) * FCC_2,
+ 1 /* nmemb */, pContext->PcmOutPtr);
fflush(pContext->PcmOutPtr);
- #endif
+#endif
// Accumulate if required
if (pContext->config.outputCfg.accessMode == EFFECT_BUFFER_ACCESS_ACCUMULATE){
//ALOGV("\tBuffer access is ACCUMULATE");
- for (int i=0; i<frameCount*2; i++){ //always stereo here
+ for (int i = 0; i < frameCount * FCC_2; i++) { // always stereo here
+#ifndef NATIVE_FLOAT_BUFFER
pOut[i] = clamp16((int32_t)pOut[i] + (int32_t)OutFrames16[i]);
+#else
+ pOut[i] += pContext->OutFrames[i];
+#endif
}
}else{
//ALOGV("\tBuffer access is WRITE");
- memcpy(pOut, OutFrames16, frameCount*sizeof(LVM_INT16)*2);
+ memcpy(pOut, pContext->OutFrames, frameCount * sizeof(*pOut) * FCC_2);
}
return 0;
@@ -733,8 +656,7 @@
CHECK_ARG(pConfig->outputCfg.channels == AUDIO_CHANNEL_OUT_STEREO);
CHECK_ARG(pConfig->outputCfg.accessMode == EFFECT_BUFFER_ACCESS_WRITE
|| pConfig->outputCfg.accessMode == EFFECT_BUFFER_ACCESS_ACCUMULATE);
- CHECK_ARG(pConfig->inputCfg.format == AUDIO_FORMAT_PCM_16_BIT);
-
+ CHECK_ARG(pConfig->inputCfg.format == EFFECT_BUFFER_FORMAT);
//ALOGV("\tReverb_setConfig calling memcpy");
pContext->config = *pConfig;
@@ -847,8 +769,7 @@
} else {
pContext->config.inputCfg.channels = AUDIO_CHANNEL_OUT_STEREO;
}
-
- pContext->config.inputCfg.format = AUDIO_FORMAT_PCM_16_BIT;
+ pContext->config.inputCfg.format = EFFECT_BUFFER_FORMAT;
pContext->config.inputCfg.samplingRate = 44100;
pContext->config.inputCfg.bufferProvider.getBuffer = NULL;
pContext->config.inputCfg.bufferProvider.releaseBuffer = NULL;
@@ -856,7 +777,7 @@
pContext->config.inputCfg.mask = EFFECT_CONFIG_ALL;
pContext->config.outputCfg.accessMode = EFFECT_BUFFER_ACCESS_ACCUMULATE;
pContext->config.outputCfg.channels = AUDIO_CHANNEL_OUT_STEREO;
- pContext->config.outputCfg.format = AUDIO_FORMAT_PCM_16_BIT;
+ pContext->config.outputCfg.format = EFFECT_BUFFER_FORMAT;
pContext->config.outputCfg.samplingRate = 44100;
pContext->config.outputCfg.bufferProvider.getBuffer = NULL;
pContext->config.outputCfg.bufferProvider.releaseBuffer = NULL;
@@ -2031,10 +1952,17 @@
}
//ALOGV("\tReverb_process() Calling process with %d frames", outBuffer->frameCount);
/* Process all the available frames, block processing is handled internalLY by the LVM bundle */
- status = process( (LVM_INT16 *)inBuffer->raw,
- (LVM_INT16 *)outBuffer->raw,
- outBuffer->frameCount,
- pContext);
+#if defined (BUILD_FLOAT) && defined (NATIVE_FLOAT_BUFFER)
+ status = process( inBuffer->f32,
+ outBuffer->f32,
+ outBuffer->frameCount,
+ pContext);
+#else
+ status = process( inBuffer->s16,
+ outBuffer->s16,
+ outBuffer->frameCount,
+ pContext);
+#endif
if (pContext->bEnabled == LVM_FALSE) {
if (pContext->SamplesToExitCount > 0) {
diff --git a/media/libmedia/include/media/mediarecorder.h b/media/libmedia/include/media/mediarecorder.h
index 071e7a1..b9717ea 100644
--- a/media/libmedia/include/media/mediarecorder.h
+++ b/media/libmedia/include/media/mediarecorder.h
@@ -77,6 +77,9 @@
/* VP8/VORBIS data in a WEBM container */
OUTPUT_FORMAT_WEBM = 9,
+ /* HEIC data in a HEIF container */
+ OUTPUT_FORMAT_HEIF = 10,
+
OUTPUT_FORMAT_LIST_END // must be last - used to validate format type
};
diff --git a/media/libstagefright/MPEG4Writer.cpp b/media/libstagefright/MPEG4Writer.cpp
index a132873..1fe5f60 100644
--- a/media/libstagefright/MPEG4Writer.cpp
+++ b/media/libstagefright/MPEG4Writer.cpp
@@ -112,14 +112,18 @@
int64_t getDurationUs() const;
int64_t getEstimatedTrackSizeBytes() const;
+ int32_t getMetaSizeIncrease() const;
void writeTrackHeader(bool use32BitOffset = true);
int64_t getMinCttsOffsetTimeUs();
void bufferChunk(int64_t timestampUs);
bool isAvc() const { return mIsAvc; }
bool isHevc() const { return mIsHevc; }
+ bool isHeic() const { return mIsHeic; }
bool isAudio() const { return mIsAudio; }
bool isMPEG4() const { return mIsMPEG4; }
+ bool usePrefix() const { return mIsAvc || mIsHevc || mIsHeic; }
void addChunkOffset(off64_t offset);
+ void addItemOffsetAndSize(off64_t offset, size_t size);
int32_t getTrackId() const { return mTrackId; }
status_t dump(int fd, const Vector<String16>& args) const;
static const char *getFourCCForMime(const char *mime);
@@ -281,6 +285,7 @@
bool mIsHevc;
bool mIsAudio;
bool mIsVideo;
+ bool mIsHeic;
bool mIsMPEG4;
bool mGotStartKeyFrame;
bool mIsMalformed;
@@ -347,6 +352,16 @@
int64_t mPreviousTrackTimeUs;
int64_t mTrackEveryTimeDurationUs;
+ int32_t mRotation;
+
+ Vector<uint16_t> mProperties;
+ Vector<uint16_t> mDimgRefs;
+ int32_t mIsPrimary;
+ int32_t mWidth, mHeight;
+ int32_t mGridWidth, mGridHeight;
+ int32_t mGridRows, mGridCols;
+ size_t mNumTiles, mTileIndex;
+
// Update the audio track's drift information.
void updateDriftTime(const sp<MetaData>& meta);
@@ -386,7 +401,6 @@
// Simple validation on the codec specific data
status_t checkCodecSpecificData() const;
- int32_t mRotation;
void updateTrackSizeEstimate();
void addOneStscTableEntry(size_t chunkId, size_t sampleId);
@@ -474,13 +488,18 @@
mUse32BitOffset = true;
mOffset = 0;
mMdatOffset = 0;
- mMoovBoxBuffer = NULL;
- mMoovBoxBufferOffset = 0;
- mWriteMoovBoxToMemory = false;
+ mInMemoryCache = NULL;
+ mInMemoryCacheOffset = 0;
+ mInMemoryCacheSize = 0;
+ mWriteBoxToMemory = false;
mFreeBoxOffset = 0;
mStreamableFile = false;
- mEstimatedMoovBoxSize = 0;
mTimeScale = -1;
+ mHasFileLevelMeta = false;
+ mHasMoovBox = false;
+ mPrimaryItemId = 0;
+ mAssociationEntryCount = 0;
+ mNumGrids = 0;
// Following variables only need to be set for the first recording session.
// And they will stay the same for all the recording sessions.
@@ -567,6 +586,8 @@
}
} else if (!strncasecmp(mime, "application/", 12)) {
return "mett";
+ } else if (!strcasecmp(MEDIA_MIMETYPE_IMAGE_ANDROID_HEIC, mime)) {
+ return "heic";
} else {
ALOGE("Track (%s) other than video/audio/metadata is not supported", mime);
}
@@ -595,6 +616,9 @@
Track *track = new Track(this, source, 1 + mTracks.size());
mTracks.push_back(track);
+ mHasMoovBox |= !track->isHeic();
+ mHasFileLevelMeta |= track->isHeic();
+
return OK;
}
@@ -656,6 +680,32 @@
#endif
}
+int64_t MPEG4Writer::estimateFileLevelMetaSize() {
+ // base meta size
+ int64_t metaSize = 12 // meta fullbox header
+ + 33 // hdlr box
+ + 14 // pitm box
+ + 16 // iloc box (fixed size portion)
+ + 14 // iinf box (fixed size portion)
+ + 32 // iprp box (fixed size protion)
+ + 8 // idat box (when empty)
+ + 12 // iref box (when empty)
+ ;
+
+ for (List<Track *>::iterator it = mTracks.begin();
+ it != mTracks.end(); ++it) {
+ if ((*it)->isHeic()) {
+ metaSize += (*it)->getMetaSizeIncrease();
+ }
+ }
+
+ ALOGV("estimated meta size: %lld", (long long) metaSize);
+
+ // Need at least 8-byte padding at the end, otherwise the left-over
+ // freebox may become malformed
+ return metaSize + 8;
+}
+
int64_t MPEG4Writer::estimateMoovBoxSize(int32_t bitRate) {
// This implementation is highly experimental/heurisitic.
//
@@ -715,7 +765,11 @@
ALOGI("limits: %" PRId64 "/%" PRId64 " bytes/us, bit rate: %d bps and the"
" estimated moov size %" PRId64 " bytes",
mMaxFileSizeLimitBytes, mMaxFileDurationLimitUs, bitRate, size);
- return factor * size;
+
+ int64_t estimatedSize = factor * size;
+ CHECK_GE(estimatedSize, 8);
+
+ return estimatedSize;
}
status_t MPEG4Writer::start(MetaData *param) {
@@ -797,63 +851,70 @@
mMaxFileSizeLimitBytes >= kMinStreamableFileSizeInBytes);
/*
- * mWriteMoovBoxToMemory is true if the amount of data in moov box is
- * smaller than the reserved free space at the beginning of a file, AND
- * when the content of moov box is constructed. Note that video/audio
- * frame data is always written to the file but not in the memory.
+ * mWriteBoxToMemory is true if the amount of data in a file-level meta or
+ * moov box is smaller than the reserved free space at the beginning of a
+ * file, AND when the content of the box is constructed. Note that video/
+ * audio frame data is always written to the file but not in the memory.
*
- * Before stop()/reset() is called, mWriteMoovBoxToMemory is always
+ * Before stop()/reset() is called, mWriteBoxToMemory is always
* false. When reset() is called at the end of a recording session,
- * Moov box needs to be constructed.
+ * file-level meta and/or moov box needs to be constructed.
*
- * 1) Right before a moov box is constructed, mWriteMoovBoxToMemory
- * to set to mStreamableFile so that if
- * the file is intended to be streamable, it is set to true;
- * otherwise, it is set to false. When the value is set to false,
- * all the content of the moov box is written immediately to
+ * 1) Right before the box is constructed, mWriteBoxToMemory to set to
+ * mStreamableFile so that if the file is intended to be streamable, it
+ * is set to true; otherwise, it is set to false. When the value is set
+ * to false, all the content of that box is written immediately to
* the end of the file. When the value is set to true, all the
- * content of the moov box is written to an in-memory cache,
- * mMoovBoxBuffer, util the following condition happens. Note
+ * content of that box is written to an in-memory cache,
+ * mInMemoryCache, util the following condition happens. Note
* that the size of the in-memory cache is the same as the
* reserved free space at the beginning of the file.
*
- * 2) While the data of the moov box is written to an in-memory
+ * 2) While the data of the box is written to an in-memory
* cache, the data size is checked against the reserved space.
- * If the data size surpasses the reserved space, subsequent moov
- * data could no longer be hold in the in-memory cache. This also
+ * If the data size surpasses the reserved space, subsequent box data
+ * could no longer be hold in the in-memory cache. This also
* indicates that the reserved space was too small. At this point,
- * _all_ moov data must be written to the end of the file.
- * mWriteMoovBoxToMemory must be set to false to direct the write
+ * _all_ subsequent box data must be written to the end of the file.
+ * mWriteBoxToMemory must be set to false to direct the write
* to the file.
*
- * 3) If the data size in moov box is smaller than the reserved
- * space after moov box is completely constructed, the in-memory
- * cache copy of the moov box is written to the reserved free
- * space. Thus, immediately after the moov is completedly
- * constructed, mWriteMoovBoxToMemory is always set to false.
+ * 3) If the data size in the box is smaller than the reserved
+ * space after the box is completely constructed, the in-memory
+ * cache copy of the box is written to the reserved free space.
+ * mWriteBoxToMemory is always set to false after all boxes that
+ * using the in-memory cache have been constructed.
*/
- mWriteMoovBoxToMemory = false;
- mMoovBoxBuffer = NULL;
- mMoovBoxBufferOffset = 0;
+ mWriteBoxToMemory = false;
+ mInMemoryCache = NULL;
+ mInMemoryCacheOffset = 0;
+
+
+ ALOGV("muxer starting: mHasMoovBox %d, mHasFileLevelMeta %d",
+ mHasMoovBox, mHasFileLevelMeta);
writeFtypBox(param);
mFreeBoxOffset = mOffset;
- if (mEstimatedMoovBoxSize == 0) {
+ if (mInMemoryCacheSize == 0) {
int32_t bitRate = -1;
- if (param) {
- param->findInt32(kKeyBitRate, &bitRate);
+ if (mHasFileLevelMeta) {
+ mInMemoryCacheSize += estimateFileLevelMetaSize();
}
- mEstimatedMoovBoxSize = estimateMoovBoxSize(bitRate);
+ if (mHasMoovBox) {
+ if (param) {
+ param->findInt32(kKeyBitRate, &bitRate);
+ }
+ mInMemoryCacheSize += estimateMoovBoxSize(bitRate);
+ }
}
- CHECK_GE(mEstimatedMoovBoxSize, 8);
if (mStreamableFile) {
// Reserve a 'free' box only for streamable file
lseek64(mFd, mFreeBoxOffset, SEEK_SET);
- writeInt32(mEstimatedMoovBoxSize);
+ writeInt32(mInMemoryCacheSize);
write("free", 4);
- mMdatOffset = mFreeBoxOffset + mEstimatedMoovBoxSize;
+ mMdatOffset = mFreeBoxOffset + mInMemoryCacheSize;
} else {
mMdatOffset = mOffset;
}
@@ -965,8 +1026,8 @@
mFd = -1;
mInitCheck = NO_INIT;
mStarted = false;
- free(mMoovBoxBuffer);
- mMoovBoxBuffer = NULL;
+ free(mInMemoryCache);
+ mInMemoryCache = NULL;
}
void MPEG4Writer::finishCurrentSession() {
@@ -1009,13 +1070,18 @@
status_t err = OK;
int64_t maxDurationUs = 0;
int64_t minDurationUs = 0x7fffffffffffffffLL;
+ int32_t nonImageTrackCount = 0;
for (List<Track *>::iterator it = mTracks.begin();
- it != mTracks.end(); ++it) {
+ it != mTracks.end(); ++it) {
status_t status = (*it)->stop(stopSource);
if (err == OK && status != OK) {
err = status;
}
+ // skip image tracks
+ if ((*it)->isHeic()) continue;
+ nonImageTrackCount++;
+
int64_t durationUs = (*it)->getDurationUs();
if (durationUs > maxDurationUs) {
maxDurationUs = durationUs;
@@ -1025,7 +1091,7 @@
}
}
- if (mTracks.size() > 1) {
+ if (nonImageTrackCount > 1) {
ALOGD("Duration from tracks range is [%" PRId64 ", %" PRId64 "] us",
minDurationUs, maxDurationUs);
}
@@ -1051,45 +1117,43 @@
}
lseek64(mFd, mOffset, SEEK_SET);
- // Construct moov box now
- mMoovBoxBufferOffset = 0;
- mWriteMoovBoxToMemory = mStreamableFile;
- if (mWriteMoovBoxToMemory) {
+ // Construct file-level meta and moov box now
+ mInMemoryCacheOffset = 0;
+ mWriteBoxToMemory = mStreamableFile;
+ if (mWriteBoxToMemory) {
// There is no need to allocate in-memory cache
- // for moov box if the file is not streamable.
+ // if the file is not streamable.
- mMoovBoxBuffer = (uint8_t *) malloc(mEstimatedMoovBoxSize);
- CHECK(mMoovBoxBuffer != NULL);
- }
- writeMoovBox(maxDurationUs);
-
- // mWriteMoovBoxToMemory could be set to false in
- // MPEG4Writer::write() method
- if (mWriteMoovBoxToMemory) {
- mWriteMoovBoxToMemory = false;
- // Content of the moov box is saved in the cache, and the in-memory
- // moov box needs to be written to the file in a single shot.
-
- CHECK_LE(mMoovBoxBufferOffset + 8, mEstimatedMoovBoxSize);
-
- // Moov box
- lseek64(mFd, mFreeBoxOffset, SEEK_SET);
- mOffset = mFreeBoxOffset;
- write(mMoovBoxBuffer, 1, mMoovBoxBufferOffset);
-
- // Free box
- lseek64(mFd, mOffset, SEEK_SET);
- writeInt32(mEstimatedMoovBoxSize - mMoovBoxBufferOffset);
- write("free", 4);
- } else {
- ALOGI("The mp4 file will not be streamable.");
+ mInMemoryCache = (uint8_t *) malloc(mInMemoryCacheSize);
+ CHECK(mInMemoryCache != NULL);
}
- // Free in-memory cache for moov box
- if (mMoovBoxBuffer != NULL) {
- free(mMoovBoxBuffer);
- mMoovBoxBuffer = NULL;
- mMoovBoxBufferOffset = 0;
+ if (mHasFileLevelMeta) {
+ writeFileLevelMetaBox();
+ if (mWriteBoxToMemory) {
+ writeCachedBoxToFile("meta");
+ } else {
+ ALOGI("The file meta box is written at the end.");
+ }
+ }
+
+ if (mHasMoovBox) {
+ writeMoovBox(maxDurationUs);
+ // mWriteBoxToMemory could be set to false in
+ // MPEG4Writer::write() method
+ if (mWriteBoxToMemory) {
+ writeCachedBoxToFile("moov");
+ } else {
+ ALOGI("The mp4 file will not be streamable.");
+ }
+ }
+ mWriteBoxToMemory = false;
+
+ // Free in-memory cache for box writing
+ if (mInMemoryCache != NULL) {
+ free(mInMemoryCache);
+ mInMemoryCache = NULL;
+ mInMemoryCacheOffset = 0;
}
CHECK(mBoxes.empty());
@@ -1098,6 +1162,42 @@
return err;
}
+/*
+ * Writes currently cached box into file.
+ *
+ * Must be called while mWriteBoxToMemory is true, and will not modify
+ * mWriteBoxToMemory. After the call, remaining cache size will be
+ * reduced and buffer offset will be set to the beginning of the cache.
+ */
+void MPEG4Writer::writeCachedBoxToFile(const char *type) {
+ CHECK(mWriteBoxToMemory);
+
+ mWriteBoxToMemory = false;
+ // Content of the box is saved in the cache, and the in-memory
+ // box needs to be written to the file in a single shot.
+
+ CHECK_LE(mInMemoryCacheOffset + 8, mInMemoryCacheSize);
+
+ // Cached box
+ lseek64(mFd, mFreeBoxOffset, SEEK_SET);
+ mOffset = mFreeBoxOffset;
+ write(mInMemoryCache, 1, mInMemoryCacheOffset);
+
+ // Free box
+ lseek64(mFd, mOffset, SEEK_SET);
+ mFreeBoxOffset = mOffset;
+ writeInt32(mInMemoryCacheSize - mInMemoryCacheOffset);
+ write("free", 4);
+
+ // Rewind buffering to the beginning, and restore mWriteBoxToMemory flag
+ mInMemoryCacheSize -= mInMemoryCacheOffset;
+ mInMemoryCacheOffset = 0;
+ mWriteBoxToMemory = true;
+
+ ALOGV("dumped out %s box, estimated size remaining %lld",
+ type, (long long)mInMemoryCacheSize);
+}
+
uint32_t MPEG4Writer::getMpeg4Time() {
time_t now = time(NULL);
// MP4 file uses time counting seconds since midnight, Jan. 1, 1904
@@ -1142,14 +1242,16 @@
if (mAreGeoTagsAvailable) {
writeUdtaBox();
}
- writeMetaBox();
+ writeMoovLevelMetaBox();
// Loop through all the tracks to get the global time offset if there is
// any ctts table appears in a video track.
int64_t minCttsOffsetTimeUs = kMaxCttsOffsetTimeUs;
for (List<Track *>::iterator it = mTracks.begin();
it != mTracks.end(); ++it) {
- minCttsOffsetTimeUs =
- std::min(minCttsOffsetTimeUs, (*it)->getMinCttsOffsetTimeUs());
+ if (!(*it)->isHeic()) {
+ minCttsOffsetTimeUs =
+ std::min(minCttsOffsetTimeUs, (*it)->getMinCttsOffsetTimeUs());
+ }
}
ALOGI("Ajust the moov start time from %lld us -> %lld us",
(long long)mStartTimestampUs,
@@ -1159,7 +1261,9 @@
for (List<Track *>::iterator it = mTracks.begin();
it != mTracks.end(); ++it) {
- (*it)->writeTrackHeader(mUse32BitOffset);
+ if (!(*it)->isHeic()) {
+ (*it)->writeTrackHeader(mUse32BitOffset);
+ }
}
endBox(); // moov
}
@@ -1168,17 +1272,31 @@
beginBox("ftyp");
int32_t fileType;
- if (param && param->findInt32(kKeyFileType, &fileType) &&
- fileType != OUTPUT_FORMAT_MPEG_4) {
+ if (!param || !param->findInt32(kKeyFileType, &fileType)) {
+ fileType = OUTPUT_FORMAT_MPEG_4;
+ }
+ if (fileType != OUTPUT_FORMAT_MPEG_4 && fileType != OUTPUT_FORMAT_HEIF) {
writeFourcc("3gp4");
writeInt32(0);
writeFourcc("isom");
writeFourcc("3gp4");
} else {
- writeFourcc("mp42");
+ // Only write "heic" as major brand if the client specified HEIF
+ // AND we indeed receive some image heic tracks.
+ if (fileType == OUTPUT_FORMAT_HEIF && mHasFileLevelMeta) {
+ writeFourcc("heic");
+ } else {
+ writeFourcc("mp42");
+ }
writeInt32(0);
- writeFourcc("isom");
- writeFourcc("mp42");
+ if (mHasFileLevelMeta) {
+ writeFourcc("mif1");
+ writeFourcc("heic");
+ }
+ if (mHasMoovBox) {
+ writeFourcc("isom");
+ writeFourcc("mp42");
+ }
}
endBox();
@@ -1225,15 +1343,21 @@
mLock.unlock();
}
-off64_t MPEG4Writer::addSample_l(MediaBuffer *buffer) {
+off64_t MPEG4Writer::addSample_l(
+ MediaBuffer *buffer, bool usePrefix, size_t *bytesWritten) {
off64_t old_offset = mOffset;
- ::write(mFd,
- (const uint8_t *)buffer->data() + buffer->range_offset(),
- buffer->range_length());
+ if (usePrefix) {
+ addMultipleLengthPrefixedSamples_l(buffer);
+ } else {
+ ::write(mFd,
+ (const uint8_t *)buffer->data() + buffer->range_offset(),
+ buffer->range_length());
- mOffset += buffer->range_length();
+ mOffset += buffer->range_length();
+ }
+ *bytesWritten = mOffset - old_offset;
return old_offset;
}
@@ -1251,9 +1375,7 @@
}
}
-off64_t MPEG4Writer::addMultipleLengthPrefixedSamples_l(MediaBuffer *buffer) {
- off64_t old_offset = mOffset;
-
+void MPEG4Writer::addMultipleLengthPrefixedSamples_l(MediaBuffer *buffer) {
const size_t kExtensionNALSearchRange = 64; // bytes to look for non-VCL NALUs
const uint8_t *dataStart = (const uint8_t *)buffer->data() + buffer->range_offset();
@@ -1278,13 +1400,9 @@
buffer->set_range(buffer->range_offset() + currentNalOffset,
buffer->range_length() - currentNalOffset);
addLengthPrefixedSample_l(buffer);
-
- return old_offset;
}
-off64_t MPEG4Writer::addLengthPrefixedSample_l(MediaBuffer *buffer) {
- off64_t old_offset = mOffset;
-
+void MPEG4Writer::addLengthPrefixedSample_l(MediaBuffer *buffer) {
size_t length = buffer->range_length();
if (mUse4ByteNalLength) {
@@ -1312,40 +1430,35 @@
::write(mFd, (const uint8_t *)buffer->data() + buffer->range_offset(), length);
mOffset += length + 2;
}
-
- return old_offset;
}
size_t MPEG4Writer::write(
const void *ptr, size_t size, size_t nmemb) {
const size_t bytes = size * nmemb;
- if (mWriteMoovBoxToMemory) {
+ if (mWriteBoxToMemory) {
- off64_t moovBoxSize = 8 + mMoovBoxBufferOffset + bytes;
- if (moovBoxSize > mEstimatedMoovBoxSize) {
- // The reserved moov box at the beginning of the file
- // is not big enough. Moov box should be written to
- // the end of the file from now on, but not to the
- // in-memory cache.
+ off64_t boxSize = 8 + mInMemoryCacheOffset + bytes;
+ if (boxSize > mInMemoryCacheSize) {
+ // The reserved free space at the beginning of the file is not big
+ // enough. Boxes should be written to the end of the file from now
+ // on, but not to the in-memory cache.
- // We write partial moov box that is in the memory to
- // the file first.
+ // We write partial box that is in the memory to the file first.
for (List<off64_t>::iterator it = mBoxes.begin();
it != mBoxes.end(); ++it) {
(*it) += mOffset;
}
lseek64(mFd, mOffset, SEEK_SET);
- ::write(mFd, mMoovBoxBuffer, mMoovBoxBufferOffset);
+ ::write(mFd, mInMemoryCache, mInMemoryCacheOffset);
::write(mFd, ptr, bytes);
- mOffset += (bytes + mMoovBoxBufferOffset);
+ mOffset += (bytes + mInMemoryCacheOffset);
- // All subsequent moov box content will be written
- // to the end of the file.
- mWriteMoovBoxToMemory = false;
+ // All subsequent boxes will be written to the end of the file.
+ mWriteBoxToMemory = false;
} else {
- memcpy(mMoovBoxBuffer + mMoovBoxBufferOffset, ptr, bytes);
- mMoovBoxBufferOffset += bytes;
+ memcpy(mInMemoryCache + mInMemoryCacheOffset, ptr, bytes);
+ mInMemoryCacheOffset += bytes;
}
} else {
::write(mFd, ptr, size * nmemb);
@@ -1355,8 +1468,8 @@
}
void MPEG4Writer::beginBox(uint32_t id) {
- mBoxes.push_back(mWriteMoovBoxToMemory?
- mMoovBoxBufferOffset: mOffset);
+ mBoxes.push_back(mWriteBoxToMemory?
+ mInMemoryCacheOffset: mOffset);
writeInt32(0);
writeInt32(id);
@@ -1365,8 +1478,8 @@
void MPEG4Writer::beginBox(const char *fourcc) {
CHECK_EQ(strlen(fourcc), 4u);
- mBoxes.push_back(mWriteMoovBoxToMemory?
- mMoovBoxBufferOffset: mOffset);
+ mBoxes.push_back(mWriteBoxToMemory?
+ mInMemoryCacheOffset: mOffset);
writeInt32(0);
writeFourcc(fourcc);
@@ -1378,9 +1491,9 @@
off64_t offset = *--mBoxes.end();
mBoxes.erase(--mBoxes.end());
- if (mWriteMoovBoxToMemory) {
- int32_t x = htonl(mMoovBoxBufferOffset - offset);
- memcpy(mMoovBoxBuffer + offset, &x, 4);
+ if (mWriteBoxToMemory) {
+ int32_t x = htonl(mInMemoryCacheOffset - offset);
+ memcpy(mInMemoryCache + offset, &x, 4);
} else {
lseek64(mFd, offset, SEEK_SET);
writeInt32(mOffset - offset);
@@ -1539,7 +1652,7 @@
if (mMaxFileSizeLimitBytes == 0) {
return false;
}
- int64_t nTotalBytesEstimate = static_cast<int64_t>(mEstimatedMoovBoxSize);
+ int64_t nTotalBytesEstimate = static_cast<int64_t>(mInMemoryCacheSize);
for (List<Track *>::iterator it = mTracks.begin();
it != mTracks.end(); ++it) {
nTotalBytesEstimate += (*it)->getEstimatedTrackSizeBytes();
@@ -1562,7 +1675,7 @@
return false;
}
- int64_t nTotalBytesEstimate = static_cast<int64_t>(mEstimatedMoovBoxSize);
+ int64_t nTotalBytesEstimate = static_cast<int64_t>(mInMemoryCacheSize);
for (List<Track *>::iterator it = mTracks.begin();
it != mTracks.end(); ++it) {
nTotalBytesEstimate += (*it)->getEstimatedTrackSizeBytes();
@@ -1584,7 +1697,7 @@
for (List<Track *>::iterator it = mTracks.begin();
it != mTracks.end(); ++it) {
- if ((*it)->getDurationUs() >= mMaxFileDurationLimitUs) {
+ if (!(*it)->isHeic() && (*it)->getDurationUs() >= mMaxFileDurationLimitUs) {
return true;
}
}
@@ -1656,7 +1769,16 @@
mGotAllCodecSpecificData(false),
mReachedEOS(false),
mStartTimestampUs(-1),
- mRotation(0) {
+ mRotation(0),
+ mIsPrimary(0),
+ mWidth(0),
+ mHeight(0),
+ mGridWidth(0),
+ mGridHeight(0),
+ mGridRows(0),
+ mGridCols(0),
+ mNumTiles(1),
+ mTileIndex(0) {
getCodecSpecificDataFromInputFormatIfPossible();
const char *mime;
@@ -1665,6 +1787,7 @@
mIsHevc = !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_HEVC);
mIsAudio = !strncasecmp(mime, "audio/", 6);
mIsVideo = !strncasecmp(mime, "video/", 6);
+ mIsHeic = !strcasecmp(mime, MEDIA_MIMETYPE_IMAGE_ANDROID_HEIC);
mIsMPEG4 = !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_MPEG4) ||
!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AAC);
@@ -1676,7 +1799,27 @@
}
}
- setTimeScale();
+ if (!mIsHeic) {
+ setTimeScale();
+ } else {
+ CHECK(mMeta->findInt32(kKeyWidth, &mWidth) && (mWidth > 0));
+ CHECK(mMeta->findInt32(kKeyHeight, &mHeight) && (mHeight > 0));
+
+ int32_t gridWidth, gridHeight, gridRows, gridCols;
+ if (mMeta->findInt32(kKeyGridWidth, &gridWidth) && (gridWidth > 0) &&
+ mMeta->findInt32(kKeyGridHeight, &gridHeight) && (gridHeight > 0) &&
+ mMeta->findInt32(kKeyGridRows, &gridRows) && (gridRows > 0) &&
+ mMeta->findInt32(kKeyGridCols, &gridCols) && (gridCols > 0)) {
+ mGridWidth = gridWidth;
+ mGridHeight = gridHeight;
+ mGridRows = gridRows;
+ mGridCols = gridCols;
+ mNumTiles = gridRows * gridCols;
+ }
+ if (!mMeta->findInt32(kKeyTrackIsDefault, &mIsPrimary)) {
+ mIsPrimary = false;
+ }
+ }
}
// Clear all the internal states except the CSD data.
@@ -1724,15 +1867,15 @@
}
void MPEG4Writer::Track::updateTrackSizeEstimate() {
-
- uint32_t stcoBoxCount = (mOwner->use32BitFileOffset()
- ? mStcoTableEntries->count()
- : mCo64TableEntries->count());
- int64_t stcoBoxSizeBytes = stcoBoxCount * 4;
- int64_t stszBoxSizeBytes = mSamplesHaveSameSize? 4: (mStszTableEntries->count() * 4);
-
mEstimatedTrackSizeBytes = mMdatSizeBytes; // media data size
- if (!mOwner->isFileStreamable()) {
+
+ if (!isHeic() && !mOwner->isFileStreamable()) {
+ uint32_t stcoBoxCount = (mOwner->use32BitFileOffset()
+ ? mStcoTableEntries->count()
+ : mCo64TableEntries->count());
+ int64_t stcoBoxSizeBytes = stcoBoxCount * 4;
+ int64_t stszBoxSizeBytes = mSamplesHaveSameSize? 4: (mStszTableEntries->count() * 4);
+
// Reserved free space is not large enough to hold
// all meta data and thus wasted.
mEstimatedTrackSizeBytes += mStscTableEntries->count() * 12 + // stsc box size
@@ -1746,10 +1889,9 @@
void MPEG4Writer::Track::addOneStscTableEntry(
size_t chunkId, size_t sampleId) {
-
- mStscTableEntries->add(htonl(chunkId));
- mStscTableEntries->add(htonl(sampleId));
- mStscTableEntries->add(htonl(1));
+ mStscTableEntries->add(htonl(chunkId));
+ mStscTableEntries->add(htonl(sampleId));
+ mStscTableEntries->add(htonl(1));
}
void MPEG4Writer::Track::addOneStssTableEntry(size_t sampleId) {
@@ -1795,6 +1937,7 @@
}
void MPEG4Writer::Track::addChunkOffset(off64_t offset) {
+ CHECK(!mIsHeic);
if (mOwner->use32BitFileOffset()) {
uint32_t value = offset;
mStcoTableEntries->add(htonl(value));
@@ -1803,6 +1946,70 @@
}
}
+void MPEG4Writer::Track::addItemOffsetAndSize(off64_t offset, size_t size) {
+ CHECK(mIsHeic);
+
+ if (offset > UINT32_MAX || size > UINT32_MAX) {
+ ALOGE("offset or size is out of range: %lld, %lld",
+ (long long) offset, (long long) size);
+ mIsMalformed = true;
+ }
+ if (mIsMalformed) {
+ return;
+ }
+ if (mTileIndex >= mNumTiles) {
+ ALOGW("Ignoring excess tiles!");
+ return;
+ }
+
+ if (mProperties.empty()) {
+ mProperties.push_back(mOwner->addProperty_l({
+ .type = FOURCC('h', 'v', 'c', 'C'),
+ .hvcc = ABuffer::CreateAsCopy(mCodecSpecificData, mCodecSpecificDataSize)
+ }));
+
+ mProperties.push_back(mOwner->addProperty_l({
+ .type = FOURCC('i', 's', 'p', 'e'),
+ .width = (mNumTiles > 1) ? mGridWidth : mWidth,
+ .height = (mNumTiles > 1) ? mGridHeight : mHeight,
+ }));
+ }
+
+ uint16_t itemId = mOwner->addItem_l({
+ .itemType = "hvc1",
+ .isPrimary = (mNumTiles > 1) ? false : (mIsPrimary != 0),
+ .isHidden = (mNumTiles > 1),
+ .offset = (uint32_t)offset,
+ .size = (uint32_t)size,
+ .properties = mProperties,
+ });
+
+ mTileIndex++;
+ if (mNumTiles > 1) {
+ mDimgRefs.push_back(itemId);
+
+ if (mTileIndex == mNumTiles) {
+ mProperties.clear();
+ mProperties.push_back(mOwner->addProperty_l({
+ .type = FOURCC('i', 's', 'p', 'e'),
+ .width = mWidth,
+ .height = mHeight,
+ }));
+ mOwner->addItem_l({
+ .itemType = "grid",
+ .isPrimary = (mIsPrimary != 0),
+ .isHidden = false,
+ .rows = (uint32_t)mGridRows,
+ .cols = (uint32_t)mGridCols,
+ .width = (uint32_t)mWidth,
+ .height = (uint32_t)mHeight,
+ .properties = mProperties,
+ .dimgRefs = mDimgRefs,
+ });
+ }
+ }
+}
+
void MPEG4Writer::Track::setTimeScale() {
ALOGV("setTimeScale");
// Default time scale
@@ -1855,7 +2062,8 @@
size_t size = 0;
if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AVC)) {
mMeta->findData(kKeyAVCC, &type, &data, &size);
- } else if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_HEVC)) {
+ } else if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_HEVC) ||
+ !strcasecmp(mime, MEDIA_MIMETYPE_IMAGE_ANDROID_HEIC)) {
mMeta->findData(kKeyHVCC, &type, &data, &size);
} else if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_MPEG4)
|| !strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AAC)) {
@@ -1945,14 +2153,16 @@
chunk->mTimeStampUs, chunk->mTrack->getTrackType());
int32_t isFirstSample = true;
+ bool usePrefix = chunk->mTrack->usePrefix();
while (!chunk->mSamples.empty()) {
List<MediaBuffer *>::iterator it = chunk->mSamples.begin();
- off64_t offset = (chunk->mTrack->isAvc() || chunk->mTrack->isHevc())
- ? addMultipleLengthPrefixedSamples_l(*it)
- : addSample_l(*it);
+ size_t bytesWritten;
+ off64_t offset = addSample_l(*it, usePrefix, &bytesWritten);
- if (isFirstSample) {
+ if (chunk->mTrack->isHeic()) {
+ chunk->mTrack->addItemOffsetAndSize(offset, bytesWritten);
+ } else if (isFirstSample) {
chunk->mTrack->addChunkOffset(offset);
isFirstSample = false;
}
@@ -2637,7 +2847,7 @@
(const uint8_t *)buffer->data()
+ buffer->range_offset(),
buffer->range_length());
- } else if (mIsHevc) {
+ } else if (mIsHevc || mIsHeic) {
err = makeHEVCCodecSpecificData(
(const uint8_t *)buffer->data()
+ buffer->range_offset(),
@@ -2662,7 +2872,8 @@
}
// Per-frame metadata sample's size must be smaller than max allowed.
- if (!mIsVideo && !mIsAudio && buffer->range_length() >= kMaxMetadataSize) {
+ if (!mIsVideo && !mIsAudio && !mIsHeic &&
+ buffer->range_length() >= kMaxMetadataSize) {
ALOGW("Buffer size is %zu. Maximum metadata buffer size is %lld for %s track",
buffer->range_length(), (long long)kMaxMetadataSize, trackName);
buffer->release();
@@ -2683,10 +2894,10 @@
buffer->release();
buffer = NULL;
- if (mIsAvc || mIsHevc) StripStartcode(copy);
+ if (usePrefix()) StripStartcode(copy);
size_t sampleSize = copy->range_length();
- if (mIsAvc || mIsHevc) {
+ if (usePrefix()) {
if (mOwner->useNalLengthFour()) {
sampleSize += 4;
} else {
@@ -2948,15 +3159,19 @@
trackProgressStatus(timestampUs);
}
if (!hasMultipleTracks) {
- off64_t offset = (mIsAvc || mIsHevc) ? mOwner->addMultipleLengthPrefixedSamples_l(copy)
- : mOwner->addSample_l(copy);
+ size_t bytesWritten;
+ off64_t offset = mOwner->addSample_l(copy, usePrefix(), &bytesWritten);
- uint32_t count = (mOwner->use32BitFileOffset()
- ? mStcoTableEntries->count()
- : mCo64TableEntries->count());
+ if (mIsHeic) {
+ addItemOffsetAndSize(offset, bytesWritten);
+ } else {
+ uint32_t count = (mOwner->use32BitFileOffset()
+ ? mStcoTableEntries->count()
+ : mCo64TableEntries->count());
- if (count == 0) {
- addChunkOffset(offset);
+ if (count == 0) {
+ addChunkOffset(offset);
+ }
}
copy->release();
copy = NULL;
@@ -2964,7 +3179,10 @@
}
mChunkSamples.push_back(copy);
- if (interleaveDurationUs == 0) {
+ if (mIsHeic) {
+ bufferChunk(0 /*timestampUs*/);
+ ++nChunks;
+ } else if (interleaveDurationUs == 0) {
addOneStscTableEntry(++nChunks, 1);
bufferChunk(timestampUs);
} else {
@@ -2997,42 +3215,49 @@
mOwner->trackProgressStatus(mTrackId, -1, err);
- // Last chunk
- if (!hasMultipleTracks) {
- addOneStscTableEntry(1, mStszTableEntries->count());
- } else if (!mChunkSamples.empty()) {
- addOneStscTableEntry(++nChunks, mChunkSamples.size());
- bufferChunk(timestampUs);
- }
-
- // We don't really know how long the last frame lasts, since
- // there is no frame time after it, just repeat the previous
- // frame's duration.
- if (mStszTableEntries->count() == 1) {
- lastDurationUs = 0; // A single sample's duration
- lastDurationTicks = 0;
- } else {
- ++sampleCount; // Count for the last sample
- }
-
- if (mStszTableEntries->count() <= 2) {
- addOneSttsTableEntry(1, lastDurationTicks);
- if (sampleCount - 1 > 0) {
- addOneSttsTableEntry(sampleCount - 1, lastDurationTicks);
+ if (mIsHeic) {
+ if (!mChunkSamples.empty()) {
+ bufferChunk(0);
+ ++nChunks;
}
} else {
- addOneSttsTableEntry(sampleCount, lastDurationTicks);
- }
-
- // The last ctts box may not have been written yet, and this
- // is to make sure that we write out the last ctts box.
- if (currCttsOffsetTimeTicks == lastCttsOffsetTimeTicks) {
- if (cttsSampleCount > 0) {
- addOneCttsTableEntry(cttsSampleCount, lastCttsOffsetTimeTicks);
+ // Last chunk
+ if (!hasMultipleTracks) {
+ addOneStscTableEntry(1, mStszTableEntries->count());
+ } else if (!mChunkSamples.empty()) {
+ addOneStscTableEntry(++nChunks, mChunkSamples.size());
+ bufferChunk(timestampUs);
}
- }
- mTrackDurationUs += lastDurationUs;
+ // We don't really know how long the last frame lasts, since
+ // there is no frame time after it, just repeat the previous
+ // frame's duration.
+ if (mStszTableEntries->count() == 1) {
+ lastDurationUs = 0; // A single sample's duration
+ lastDurationTicks = 0;
+ } else {
+ ++sampleCount; // Count for the last sample
+ }
+
+ if (mStszTableEntries->count() <= 2) {
+ addOneSttsTableEntry(1, lastDurationTicks);
+ if (sampleCount - 1 > 0) {
+ addOneSttsTableEntry(sampleCount - 1, lastDurationTicks);
+ }
+ } else {
+ addOneSttsTableEntry(sampleCount, lastDurationTicks);
+ }
+
+ // The last ctts box may not have been written yet, and this
+ // is to make sure that we write out the last ctts box.
+ if (currCttsOffsetTimeTicks == lastCttsOffsetTimeTicks) {
+ if (cttsSampleCount > 0) {
+ addOneCttsTableEntry(cttsSampleCount, lastCttsOffsetTimeTicks);
+ }
+ }
+
+ mTrackDurationUs += lastDurationUs;
+ }
mReachedEOS = true;
sendTrackSummary(hasMultipleTracks);
@@ -3054,7 +3279,7 @@
return true;
}
- if (mStszTableEntries->count() == 0) { // no samples written
+ if (!mIsHeic && mStszTableEntries->count() == 0) { // no samples written
ALOGE("The number of recorded samples is 0");
return true;
}
@@ -3200,13 +3425,28 @@
return mEstimatedTrackSizeBytes;
}
+int32_t MPEG4Writer::Track::getMetaSizeIncrease() const {
+ CHECK(mIsHeic);
+ return 20 // 1. 'ispe' property
+ + (8 + mCodecSpecificDataSize) // 2. 'hvcC' property
+ + (20 // 3. extra 'ispe'
+ + (8 + 2 + 2 + mNumTiles * 2) // 4. 'dimg' ref
+ + 12) // 5. ImageGrid in 'idat' (worst case)
+ * (mNumTiles > 1) // - (3~5: applicable only if grid)
+ + (16 // 6. increase to 'iloc'
+ + 21 // 7. increase to 'iinf'
+ + (3 + 2 * 2)) // 8. increase to 'ipma' (worst case)
+ * (mNumTiles + 1); // - (6~8: are per-item)
+}
+
status_t MPEG4Writer::Track::checkCodecSpecificData() const {
const char *mime;
CHECK(mMeta->findCString(kKeyMIMEType, &mime));
if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AAC, mime) ||
!strcasecmp(MEDIA_MIMETYPE_VIDEO_MPEG4, mime) ||
!strcasecmp(MEDIA_MIMETYPE_VIDEO_AVC, mime) ||
- !strcasecmp(MEDIA_MIMETYPE_VIDEO_HEVC, mime)) {
+ !strcasecmp(MEDIA_MIMETYPE_VIDEO_HEVC, mime) ||
+ !strcasecmp(MEDIA_MIMETYPE_IMAGE_ANDROID_HEIC, mime)) {
if (!mCodecSpecificData ||
mCodecSpecificDataSize <= 0) {
ALOGE("Missing codec specific data");
@@ -3223,7 +3463,10 @@
}
const char *MPEG4Writer::Track::getTrackType() const {
- return mIsAudio ? "Audio" : (mIsVideo ? "Video" : "Metadata");
+ return mIsAudio ? "Audio" :
+ mIsVideo ? "Video" :
+ mIsHeic ? "Image" :
+ "Metadata";
}
void MPEG4Writer::Track::writeTrackHeader(bool use32BitOffset) {
@@ -3793,11 +4036,11 @@
endBox();
}
-void MPEG4Writer::writeHdlr() {
+void MPEG4Writer::writeHdlr(const char *handlerType) {
beginBox("hdlr");
writeInt32(0); // Version, Flags
writeInt32(0); // Predefined
- writeFourcc("mdta");
+ writeFourcc(handlerType);
writeInt32(0); // Reserved[0]
writeInt32(0); // Reserved[1]
writeInt32(0); // Reserved[2]
@@ -3877,19 +4120,283 @@
endBox(); // ilst
}
-void MPEG4Writer::writeMetaBox() {
+void MPEG4Writer::writeMoovLevelMetaBox() {
size_t count = mMetaKeys->countEntries();
if (count == 0) {
return;
}
beginBox("meta");
- writeHdlr();
+ writeHdlr("mdta");
writeKeys();
writeIlst();
endBox();
}
+void MPEG4Writer::writeIlocBox() {
+ beginBox("iloc");
+ // Use version 1 to allow construction method 1 that refers to
+ // data in idat box inside meta box.
+ writeInt32(0x01000000); // Version = 1, Flags = 0
+ writeInt16(0x4400); // offset_size = length_size = 4
+ // base_offset_size = index_size = 0
+
+ // 16-bit item_count
+ size_t itemCount = mItems.size();
+ if (itemCount > 65535) {
+ ALOGW("Dropping excess items: itemCount %zu", itemCount);
+ itemCount = 65535;
+ }
+ writeInt16((uint16_t)itemCount);
+
+ for (size_t i = 0; i < itemCount; i++) {
+ writeInt16(mItems[i].itemId);
+ bool isGrid = mItems[i].isGrid();
+
+ writeInt16(isGrid ? 1 : 0); // construction_method
+ writeInt16(0); // data_reference_index = 0
+ writeInt16(1); // extent_count = 1
+
+ if (isGrid) {
+ // offset into the 'idat' box
+ writeInt32(mNumGrids++ * 8);
+ writeInt32(8);
+ } else {
+ writeInt32(mItems[i].offset);
+ writeInt32(mItems[i].size);
+ }
+ }
+ endBox();
+}
+
+void MPEG4Writer::writeInfeBox(
+ uint16_t itemId, const char *itemType, uint32_t flags) {
+ beginBox("infe");
+ writeInt32(0x02000000 | flags); // Version = 2, Flags = 0
+ writeInt16(itemId);
+ writeInt16(0); //item_protection_index = 0
+ writeFourcc(itemType);
+ writeCString(""); // item_name
+ endBox();
+}
+
+void MPEG4Writer::writeIinfBox() {
+ beginBox("iinf");
+ writeInt32(0); // Version = 0, Flags = 0
+
+ // 16-bit item_count
+ size_t itemCount = mItems.size();
+ if (itemCount > 65535) {
+ ALOGW("Dropping excess items: itemCount %zu", itemCount);
+ itemCount = 65535;
+ }
+
+ writeInt16((uint16_t)itemCount);
+ for (size_t i = 0; i < itemCount; i++) {
+ writeInfeBox(mItems[i].itemId, mItems[i].itemType,
+ mItems[i].isHidden ? 1 : 0);
+ }
+
+ endBox();
+}
+
+void MPEG4Writer::writeIdatBox() {
+ beginBox("idat");
+
+ for (size_t i = 0; i < mItems.size(); i++) {
+ if (mItems[i].isGrid()) {
+ writeInt8(0); // version
+ // flags == 1 means 32-bit width,height
+ int8_t flags = (mItems[i].width > 65535 || mItems[i].height > 65535);
+ writeInt8(flags);
+ writeInt8(mItems[i].rows - 1);
+ writeInt8(mItems[i].cols - 1);
+ if (flags) {
+ writeInt32(mItems[i].width);
+ writeInt32(mItems[i].height);
+ } else {
+ writeInt16((uint16_t)mItems[i].width);
+ writeInt16((uint16_t)mItems[i].height);
+ }
+ }
+ }
+
+ endBox();
+}
+
+void MPEG4Writer::writeIrefBox() {
+ beginBox("iref");
+ writeInt32(0); // Version = 0, Flags = 0
+ {
+ for (size_t i = 0; i < mItems.size(); i++) {
+ if (!mItems[i].isGrid()) {
+ continue;
+ }
+ beginBox("dimg");
+ writeInt16(mItems[i].itemId);
+ size_t refCount = mItems[i].dimgRefs.size();
+ if (refCount > 65535) {
+ ALOGW("too many entries in dimg");
+ refCount = 65535;
+ }
+ writeInt16((uint16_t)refCount);
+ for (size_t refIndex = 0; refIndex < refCount; refIndex++) {
+ writeInt16(mItems[i].dimgRefs[refIndex]);
+ }
+ endBox();
+ }
+ }
+ endBox();
+}
+
+void MPEG4Writer::writePitmBox() {
+ beginBox("pitm");
+ writeInt32(0); // Version = 0, Flags = 0
+ writeInt16(mPrimaryItemId);
+ endBox();
+}
+
+void MPEG4Writer::writeIpcoBox() {
+ beginBox("ipco");
+ size_t numProperties = mProperties.size();
+ if (numProperties > 32767) {
+ ALOGW("Dropping excess properties: numProperties %zu", numProperties);
+ numProperties = 32767;
+ }
+ for (size_t propIndex = 0; propIndex < numProperties; propIndex++) {
+ if (mProperties[propIndex].type == FOURCC('h', 'v', 'c', 'C')) {
+ beginBox("hvcC");
+ sp<ABuffer> hvcc = mProperties[propIndex].hvcc;
+ // Patch avcc's lengthSize field to match the number
+ // of bytes we use to indicate the size of a nal unit.
+ uint8_t *ptr = (uint8_t *)hvcc->data();
+ ptr[21] = (ptr[21] & 0xfc) | (useNalLengthFour() ? 3 : 1);
+ write(hvcc->data(), hvcc->size());
+ endBox();
+ } else if (mProperties[propIndex].type == FOURCC('i', 's', 'p', 'e')) {
+ beginBox("ispe");
+ writeInt32(0); // Version = 0, Flags = 0
+ writeInt32(mProperties[propIndex].width);
+ writeInt32(mProperties[propIndex].height);
+ endBox();
+ } else {
+ ALOGW("Skipping unrecognized property: type 0x%08x",
+ mProperties[propIndex].type);
+ }
+ }
+ endBox();
+}
+
+void MPEG4Writer::writeIpmaBox() {
+ beginBox("ipma");
+ uint32_t flags = (mProperties.size() > 127) ? 1 : 0;
+ writeInt32(flags); // Version = 0
+
+ writeInt32(mAssociationEntryCount);
+ for (size_t itemIndex = 0; itemIndex < mItems.size(); itemIndex++) {
+ const Vector<uint16_t> &properties = mItems[itemIndex].properties;
+ if (properties.empty()) {
+ continue;
+ }
+ writeInt16(mItems[itemIndex].itemId);
+
+ size_t entryCount = properties.size();
+ if (entryCount > 255) {
+ ALOGW("Dropping excess associations: entryCount %zu", entryCount);
+ entryCount = 255;
+ }
+ writeInt8((uint8_t)entryCount);
+ for (size_t propIndex = 0; propIndex < entryCount; propIndex++) {
+ if (flags & 1) {
+ writeInt16((1 << 15) | properties[propIndex]);
+ } else {
+ writeInt8((1 << 7) | properties[propIndex]);
+ }
+ }
+ }
+ endBox();
+}
+
+void MPEG4Writer::writeIprpBox() {
+ beginBox("iprp");
+ writeIpcoBox();
+ writeIpmaBox();
+ endBox();
+}
+
+void MPEG4Writer::writeFileLevelMetaBox() {
+ if (mItems.empty()) {
+ ALOGE("no valid item was found");
+ return;
+ }
+
+ // patch up the mPrimaryItemId and count items with prop associations
+ for (size_t index = 0; index < mItems.size(); index++) {
+ if (mItems[index].isPrimary) {
+ mPrimaryItemId = mItems[index].itemId;
+ }
+
+ if (!mItems[index].properties.empty()) {
+ mAssociationEntryCount++;
+ }
+ }
+
+ if (mPrimaryItemId == 0) {
+ ALOGW("didn't find primary, using first item");
+ mPrimaryItemId = mItems[0].itemId;
+ }
+
+ beginBox("meta");
+ writeInt32(0); // Version = 0, Flags = 0
+ writeHdlr("pict");
+ writeIlocBox();
+ writeIinfBox();
+ writePitmBox();
+ writeIprpBox();
+ if (mNumGrids > 0) {
+ writeIdatBox();
+ writeIrefBox();
+ }
+ endBox();
+}
+
+uint16_t MPEG4Writer::addProperty_l(const ItemProperty &prop) {
+ char typeStr[5];
+ MakeFourCCString(prop.type, typeStr);
+ ALOGV("addProperty_l: %s", typeStr);
+
+ mProperties.push_back(prop);
+
+ // returning 1-based property index
+ return mProperties.size();
+}
+
+uint16_t MPEG4Writer::addItem_l(const ItemInfo &info) {
+ ALOGV("addItem_l: type %s, offset %u, size %u",
+ info.itemType, info.offset, info.size);
+
+ size_t index = mItems.size();
+ mItems.push_back(info);
+
+ // make the item id start at 10000
+ mItems.editItemAt(index).itemId = index + 10000;
+
+#if (LOG_NDEBUG==0)
+ if (!info.properties.empty()) {
+ AString str;
+ for (size_t i = 0; i < info.properties.size(); i++) {
+ if (i > 0) {
+ str.append(", ");
+ }
+ str.append(info.properties[i]);
+ }
+ ALOGV("addItem_l: id %d, properties: %s", mItems[index].itemId, str.c_str());
+ }
+#endif // (LOG_NDEBUG==0)
+
+ return mItems[index].itemId;
+}
+
/*
* Geodata is stored according to ISO-6709 standard.
*/
diff --git a/media/libstagefright/MediaMuxer.cpp b/media/libstagefright/MediaMuxer.cpp
index fb048fe..62daac8 100644
--- a/media/libstagefright/MediaMuxer.cpp
+++ b/media/libstagefright/MediaMuxer.cpp
@@ -23,6 +23,7 @@
#include <media/stagefright/MediaMuxer.h>
+#include <media/mediarecorder.h>
#include <media/MediaSource.h>
#include <media/stagefright/foundation/ABuffer.h>
#include <media/stagefright/foundation/ADebug.h>
@@ -38,10 +39,16 @@
namespace android {
+static bool isMp4Format(MediaMuxer::OutputFormat format) {
+ return format == MediaMuxer::OUTPUT_FORMAT_MPEG_4 ||
+ format == MediaMuxer::OUTPUT_FORMAT_THREE_GPP ||
+ format == MediaMuxer::OUTPUT_FORMAT_HEIF;
+}
+
MediaMuxer::MediaMuxer(int fd, OutputFormat format)
: mFormat(format),
mState(UNINITIALIZED) {
- if (format == OUTPUT_FORMAT_MPEG_4 || format == OUTPUT_FORMAT_THREE_GPP) {
+ if (isMp4Format(format)) {
mWriter = new MPEG4Writer(fd);
} else if (format == OUTPUT_FORMAT_WEBM) {
mWriter = new WebmWriter(fd);
@@ -49,6 +56,10 @@
if (mWriter != NULL) {
mFileMeta = new MetaData;
+ if (format == OUTPUT_FORMAT_HEIF) {
+ // Note that the key uses recorder file types.
+ mFileMeta->setInt32(kKeyFileType, output_format::OUTPUT_FORMAT_HEIF);
+ }
mState = INITIALIZED;
}
}
@@ -108,8 +119,8 @@
ALOGE("setLocation() must be called before start().");
return INVALID_OPERATION;
}
- if (mFormat != OUTPUT_FORMAT_MPEG_4 && mFormat != OUTPUT_FORMAT_THREE_GPP) {
- ALOGE("setLocation() is only supported for .mp4 pr .3gp output.");
+ if (!isMp4Format(mFormat)) {
+ ALOGE("setLocation() is only supported for .mp4, .3gp or .heic output.");
return INVALID_OPERATION;
}
diff --git a/media/libstagefright/StagefrightMetadataRetriever.cpp b/media/libstagefright/StagefrightMetadataRetriever.cpp
index dfaa8b6..e2db0f5 100644
--- a/media/libstagefright/StagefrightMetadataRetriever.cpp
+++ b/media/libstagefright/StagefrightMetadataRetriever.cpp
@@ -154,7 +154,8 @@
if (!strncasecmp(mime, "image/", 6)) {
int32_t isPrimary;
- if ((index < 0 && meta->findInt32(kKeyIsPrimaryImage, &isPrimary) && isPrimary)
+ if ((index < 0 && meta->findInt32(
+ kKeyTrackIsDefault, &isPrimary) && isPrimary)
|| (index == imageCount++)) {
break;
}
@@ -490,7 +491,8 @@
}
} else if (!strncasecmp("image/", mime, 6)) {
int32_t isPrimary;
- if (trackMeta->findInt32(kKeyIsPrimaryImage, &isPrimary) && isPrimary) {
+ if (trackMeta->findInt32(
+ kKeyTrackIsDefault, &isPrimary) && isPrimary) {
imagePrimary = imageCount;
CHECK(trackMeta->findInt32(kKeyWidth, &imageWidth));
CHECK(trackMeta->findInt32(kKeyHeight, &imageHeight));
diff --git a/media/libstagefright/Utils.cpp b/media/libstagefright/Utils.cpp
index 68bbd18..53699ef 100644
--- a/media/libstagefright/Utils.cpp
+++ b/media/libstagefright/Utils.cpp
@@ -675,6 +675,10 @@
msg->setInt32("grid-rows", gridRows);
msg->setInt32("grid-cols", gridCols);
}
+ int32_t isPrimary;
+ if (meta->findInt32(kKeyTrackIsDefault, &isPrimary) && isPrimary) {
+ msg->setInt32("is-default", 1);
+ }
}
int32_t colorFormat;
@@ -1308,7 +1312,7 @@
meta->setCString(kKeyMediaLanguage, lang.c_str());
}
- if (mime.startsWith("video/")) {
+ if (mime.startsWith("video/") || mime.startsWith("image/")) {
int32_t width;
int32_t height;
if (msg->findInt32("width", &width) && msg->findInt32("height", &height)) {
@@ -1332,6 +1336,26 @@
meta->setInt32(kKeyDisplayHeight, displayHeight);
}
+ if (mime.startsWith("image/")){
+ int32_t isPrimary;
+ if (msg->findInt32("is-default", &isPrimary) && isPrimary) {
+ meta->setInt32(kKeyTrackIsDefault, 1);
+ }
+ int32_t gridWidth, gridHeight, gridRows, gridCols;
+ if (msg->findInt32("grid-width", &gridWidth)) {
+ meta->setInt32(kKeyGridWidth, gridWidth);
+ }
+ if (msg->findInt32("grid-height", &gridHeight)) {
+ meta->setInt32(kKeyGridHeight, gridHeight);
+ }
+ if (msg->findInt32("grid-rows", &gridRows)) {
+ meta->setInt32(kKeyGridRows, gridRows);
+ }
+ if (msg->findInt32("grid-cols", &gridCols)) {
+ meta->setInt32(kKeyGridCols, gridCols);
+ }
+ }
+
int32_t colorFormat;
if (msg->findInt32("color-format", &colorFormat)) {
meta->setInt32(kKeyColorFormat, colorFormat);
@@ -1448,7 +1472,8 @@
// for transporting the CSD to muxers.
reassembleESDS(csd0, esds.data());
meta->setData(kKeyESDS, kKeyESDS, esds.data(), esds.size());
- } else if (mime == MEDIA_MIMETYPE_VIDEO_HEVC) {
+ } else if (mime == MEDIA_MIMETYPE_VIDEO_HEVC ||
+ mime == MEDIA_MIMETYPE_IMAGE_ANDROID_HEIC) {
std::vector<uint8_t> hvcc(csd0size + 1024);
size_t outsize = reassembleHVCC(csd0, hvcc.data(), hvcc.size(), 4);
meta->setData(kKeyHVCC, kKeyHVCC, hvcc.data(), outsize);
diff --git a/media/libstagefright/include/media/stagefright/MPEG4Writer.h b/media/libstagefright/include/media/stagefright/MPEG4Writer.h
index eba3b32..5d2c120 100644
--- a/media/libstagefright/include/media/stagefright/MPEG4Writer.h
+++ b/media/libstagefright/include/media/stagefright/MPEG4Writer.h
@@ -31,6 +31,7 @@
struct AMessage;
class MediaBuffer;
class MetaData;
+struct ABuffer;
class MPEG4Writer : public MediaWriter {
public:
@@ -100,12 +101,12 @@
bool mSendNotify;
off64_t mOffset;
off_t mMdatOffset;
- uint8_t *mMoovBoxBuffer;
- off64_t mMoovBoxBufferOffset;
- bool mWriteMoovBoxToMemory;
+ uint8_t *mInMemoryCache;
+ off64_t mInMemoryCacheOffset;
+ off64_t mInMemoryCacheSize;
+ bool mWriteBoxToMemory;
off64_t mFreeBoxOffset;
bool mStreamableFile;
- off64_t mEstimatedMoovBoxSize;
off64_t mMoovExtraSize;
uint32_t mInterleaveDurationUs;
int32_t mTimeScale;
@@ -132,6 +133,8 @@
status_t startTracks(MetaData *params);
size_t numTracks();
int64_t estimateMoovBoxSize(int32_t bitRate);
+ int64_t estimateFileLevelMetaSize();
+ void writeCachedBoxToFile(const char *type);
struct Chunk {
Track *mTrack; // Owner
@@ -164,6 +167,46 @@
List<ChunkInfo> mChunkInfos; // Chunk infos
Condition mChunkReadyCondition; // Signal that chunks are available
+ // HEIF writing
+ typedef struct _ItemInfo {
+ bool isGrid() const { return !strcmp("grid", itemType); }
+ const char *itemType;
+ uint16_t itemId;
+ bool isPrimary;
+ bool isHidden;
+ union {
+ // image item
+ struct {
+ uint32_t offset;
+ uint32_t size;
+ };
+ // grid item
+ struct {
+ uint32_t rows;
+ uint32_t cols;
+ uint32_t width;
+ uint32_t height;
+ };
+ };
+ Vector<uint16_t> properties;
+ Vector<uint16_t> dimgRefs;
+ } ItemInfo;
+
+ typedef struct _ItemProperty {
+ uint32_t type;
+ int32_t width;
+ int32_t height;
+ sp<ABuffer> hvcc;
+ } ItemProperty;
+
+ bool mHasFileLevelMeta;
+ bool mHasMoovBox;
+ uint32_t mPrimaryItemId;
+ uint32_t mAssociationEntryCount;
+ uint32_t mNumGrids;
+ Vector<ItemInfo> mItems;
+ Vector<ItemProperty> mProperties;
+
// Writer thread handling
status_t startWriterThread();
void stopWriterThread();
@@ -209,9 +252,11 @@
void initInternal(int fd, bool isFirstSession);
// Acquire lock before calling these methods
- off64_t addSample_l(MediaBuffer *buffer);
- off64_t addLengthPrefixedSample_l(MediaBuffer *buffer);
- off64_t addMultipleLengthPrefixedSamples_l(MediaBuffer *buffer);
+ off64_t addSample_l(MediaBuffer *buffer, bool usePrefix, size_t *bytesWritten);
+ void addLengthPrefixedSample_l(MediaBuffer *buffer);
+ void addMultipleLengthPrefixedSamples_l(MediaBuffer *buffer);
+ uint16_t addProperty_l(const ItemProperty &);
+ uint16_t addItem_l(const ItemInfo &);
bool exceedsFileSizeLimit();
bool use32BitFileOffset() const;
@@ -230,10 +275,23 @@
void finishCurrentSession();
void addDeviceMeta();
- void writeHdlr();
+ void writeHdlr(const char *handlerType);
void writeKeys();
void writeIlst();
- void writeMetaBox();
+ void writeMoovLevelMetaBox();
+
+ // HEIF writing
+ void writeIlocBox();
+ void writeInfeBox(uint16_t itemId, const char *type, uint32_t flags);
+ void writeIinfBox();
+ void writeIpcoBox();
+ void writeIpmaBox();
+ void writeIprpBox();
+ void writeIdatBox();
+ void writeIrefBox();
+ void writePitmBox();
+ void writeFileLevelMetaBox();
+
void sendSessionSummary();
void release();
status_t switchFd();
diff --git a/media/libstagefright/include/media/stagefright/MediaMuxer.h b/media/libstagefright/include/media/stagefright/MediaMuxer.h
index 63c3ca5..66f4d72 100644
--- a/media/libstagefright/include/media/stagefright/MediaMuxer.h
+++ b/media/libstagefright/include/media/stagefright/MediaMuxer.h
@@ -48,6 +48,7 @@
OUTPUT_FORMAT_MPEG_4 = 0,
OUTPUT_FORMAT_WEBM = 1,
OUTPUT_FORMAT_THREE_GPP = 2,
+ OUTPUT_FORMAT_HEIF = 3,
OUTPUT_FORMAT_LIST_END // must be last - used to validate format type
};
diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h
index 7e9ef26..506420c 100644
--- a/services/audioflinger/AudioFlinger.h
+++ b/services/audioflinger/AudioFlinger.h
@@ -526,6 +526,13 @@
};
// --- PlaybackThread ---
+#ifdef FLOAT_EFFECT_CHAIN
+#define EFFECT_BUFFER_FORMAT AUDIO_FORMAT_PCM_FLOAT
+using effect_buffer_t = float;
+#else
+#define EFFECT_BUFFER_FORMAT AUDIO_FORMAT_PCM_16_BIT
+using effect_buffer_t = int16_t;
+#endif
#include "Threads.h"
diff --git a/services/audioflinger/Configuration.h b/services/audioflinger/Configuration.h
index 845697a..bad46dc 100644
--- a/services/audioflinger/Configuration.h
+++ b/services/audioflinger/Configuration.h
@@ -41,4 +41,7 @@
// uncomment to log CPU statistics every n wall clock seconds
//#define DEBUG_CPU_USAGE 10
+// define FLOAT_EFFECT_CHAIN to request float effects (falls back to int16_t if unavailable)
+#define FLOAT_EFFECT_CHAIN
+
#endif // ANDROID_AUDIOFLINGER_CONFIGURATION_H
diff --git a/services/audioflinger/Effects.cpp b/services/audioflinger/Effects.cpp
index bd5f146..e77907a 100644
--- a/services/audioflinger/Effects.cpp
+++ b/services/audioflinger/Effects.cpp
@@ -19,6 +19,8 @@
#define LOG_TAG "AudioFlinger"
//#define LOG_NDEBUG 0
+#include <algorithm>
+
#include "Configuration.h"
#include <utils/Log.h>
#include <system/audio_effects/effect_aec.h>
@@ -47,8 +49,6 @@
#define ALOGVV(a...) do { } while(0)
#endif
-#define min(a, b) ((a) < (b) ? (a) : (b))
-
namespace android {
// ----------------------------------------------------------------------------
@@ -73,6 +73,9 @@
// mDisableWaitCnt is set by process() and updateState() and not used before then
mSuspended(false),
mAudioFlinger(thread->mAudioFlinger)
+#ifdef FLOAT_EFFECT_CHAIN
+ , mSupportsFloat(false)
+#endif
{
ALOGV("Constructor %p pinned %d", this, pinned);
int lStatus;
@@ -285,31 +288,114 @@
return;
}
+ // TODO: Implement multichannel effects; here outChannelCount == FCC_2 == 2
+ const uint32_t inChannelCount =
+ audio_channel_count_from_out_mask(mConfig.inputCfg.channels);
+ const uint32_t outChannelCount =
+ audio_channel_count_from_out_mask(mConfig.outputCfg.channels);
+ const bool auxType =
+ (mDescriptor.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_AUXILIARY;
+
if (isProcessEnabled()) {
- // do 32 bit to 16 bit conversion for auxiliary effect input buffer
- if ((mDescriptor.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_AUXILIARY) {
- ditherAndClamp(mConfig.inputCfg.buffer.s32,
- mConfig.inputCfg.buffer.s32,
- mConfig.inputCfg.buffer.frameCount/2);
- }
int ret;
if (isProcessImplemented()) {
- // do the actual processing in the effect engine
+ if (auxType) {
+ // We overwrite the aux input buffer here and clear after processing.
+ // Note that aux input buffers are format q4_27.
+#ifdef FLOAT_EFFECT_CHAIN
+ if (mSupportsFloat) {
+ // Do in-place float conversion for auxiliary effect input buffer.
+ static_assert(sizeof(float) <= sizeof(int32_t),
+ "in-place conversion requires sizeof(float) <= sizeof(int32_t)");
+
+ const int32_t * const p32 = mConfig.inputCfg.buffer.s32;
+ float * const pFloat = mConfig.inputCfg.buffer.f32;
+ memcpy_to_float_from_q4_27(pFloat, p32, mConfig.inputCfg.buffer.frameCount);
+ } else {
+ memcpy_to_i16_from_q4_27(mConfig.inputCfg.buffer.s16,
+ mConfig.inputCfg.buffer.s32,
+ mConfig.inputCfg.buffer.frameCount);
+ }
+#else
+ memcpy_to_i16_from_q4_27(mConfig.inputCfg.buffer.s16,
+ mConfig.inputCfg.buffer.s32,
+ mConfig.inputCfg.buffer.frameCount);
+#endif
+ }
+#ifdef FLOAT_EFFECT_CHAIN
+ if (mSupportsFloat) {
+ ret = mEffectInterface->process();
+ } else {
+ { // convert input to int16_t as effect doesn't support float.
+ if (!auxType) {
+ if (mInBuffer16.get() == nullptr) {
+ ALOGW("%s: mInBuffer16 is null, bypassing", __func__);
+ goto data_bypass;
+ }
+ const float * const pIn = mInBuffer->audioBuffer()->f32;
+ int16_t * const pIn16 = mInBuffer16->audioBuffer()->s16;
+ memcpy_to_i16_from_float(
+ pIn16, pIn, inChannelCount * mConfig.inputCfg.buffer.frameCount);
+ }
+ if (mConfig.outputCfg.accessMode == EFFECT_BUFFER_ACCESS_ACCUMULATE) {
+ if (mOutBuffer16.get() == nullptr) {
+ ALOGW("%s: mOutBuffer16 is null, bypassing", __func__);
+ goto data_bypass;
+ }
+ int16_t * const pOut16 = mOutBuffer16->audioBuffer()->s16;
+ const float * const pOut = mOutBuffer->audioBuffer()->f32;
+ memcpy_to_i16_from_float(
+ pOut16,
+ pOut,
+ outChannelCount * mConfig.outputCfg.buffer.frameCount);
+ }
+ }
+
+ ret = mEffectInterface->process();
+
+ { // convert output back to float.
+ const int16_t * const pOut16 = mOutBuffer16->audioBuffer()->s16;
+ float * const pOut = mOutBuffer->audioBuffer()->f32;
+ memcpy_to_float_from_i16(
+ pOut, pOut16, outChannelCount * mConfig.outputCfg.buffer.frameCount);
+ }
+ }
+#else
ret = mEffectInterface->process();
+#endif
} else {
- if (mConfig.inputCfg.buffer.raw != mConfig.outputCfg.buffer.raw) {
- size_t frameCnt = mConfig.inputCfg.buffer.frameCount * FCC_2; //always stereo here
- int16_t *in = mConfig.inputCfg.buffer.s16;
- int16_t *out = mConfig.outputCfg.buffer.s16;
+#ifdef FLOAT_EFFECT_CHAIN
+ data_bypass:
+#endif
+ if (!auxType /* aux effects do not require data bypass */
+ && mConfig.inputCfg.buffer.raw != mConfig.outputCfg.buffer.raw
+ && inChannelCount == outChannelCount) {
+ const size_t sampleCount = std::min(
+ mConfig.inputCfg.buffer.frameCount,
+ mConfig.outputCfg.buffer.frameCount) * outChannelCount;
+
+#ifdef FLOAT_EFFECT_CHAIN
+ const float * const in = mConfig.inputCfg.buffer.f32;
+ float * const out = mConfig.outputCfg.buffer.f32;
if (mConfig.outputCfg.accessMode == EFFECT_BUFFER_ACCESS_ACCUMULATE) {
- for (size_t i = 0; i < frameCnt; i++) {
- out[i] = clamp16((int32_t)out[i] + (int32_t)in[i]);
- }
+ accumulate_float(out, in, sampleCount);
} else {
- memcpy(mConfig.outputCfg.buffer.raw, mConfig.inputCfg.buffer.raw,
- frameCnt * sizeof(int16_t));
+ memcpy(mConfig.outputCfg.buffer.f32, mConfig.inputCfg.buffer.f32,
+ sampleCount * sizeof(*mConfig.outputCfg.buffer.f32));
}
+
+#else
+ const int16_t * const in = mConfig.inputCfg.buffer.s16;
+ int16_t * const out = mConfig.outputCfg.buffer.s16;
+
+ if (mConfig.outputCfg.accessMode == EFFECT_BUFFER_ACCESS_ACCUMULATE) {
+ accumulate_i16(out, in, sampleCount);
+ } else {
+ memcpy(mConfig.outputCfg.buffer.s16, mConfig.inputCfg.buffer.s16,
+ sampleCount * sizeof(*mConfig.outputCfg.buffer.s16));
+ }
+#endif
}
ret = -ENODATA;
}
@@ -319,22 +405,33 @@
}
// clear auxiliary effect input buffer for next accumulation
- if ((mDescriptor.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_AUXILIARY) {
- memset(mConfig.inputCfg.buffer.raw, 0,
- mConfig.inputCfg.buffer.frameCount*sizeof(int32_t));
+ if (auxType) {
+ // input always q4_27 regardless of FLOAT_EFFECT_CHAIN.
+ const size_t size =
+ mConfig.inputCfg.buffer.frameCount * inChannelCount * sizeof(int32_t);
+ memset(mConfig.inputCfg.buffer.raw, 0, size);
}
} else if ((mDescriptor.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_INSERT &&
+ // mInBuffer->audioBuffer()->raw != mOutBuffer->audioBuffer()->raw
mConfig.inputCfg.buffer.raw != mConfig.outputCfg.buffer.raw) {
// If an insert effect is idle and input buffer is different from output buffer,
// accumulate input onto output
sp<EffectChain> chain = mChain.promote();
- if (chain != 0 && chain->activeTrackCnt() != 0) {
- size_t frameCnt = mConfig.inputCfg.buffer.frameCount * FCC_2; //always stereo here
- int16_t *in = mConfig.inputCfg.buffer.s16;
- int16_t *out = mConfig.outputCfg.buffer.s16;
- for (size_t i = 0; i < frameCnt; i++) {
- out[i] = clamp16((int32_t)out[i] + (int32_t)in[i]);
- }
+ if (chain != 0
+ && chain->activeTrackCnt() != 0
+ && inChannelCount == outChannelCount) {
+ const size_t sampleCount = std::min(
+ mConfig.inputCfg.buffer.frameCount,
+ mConfig.outputCfg.buffer.frameCount) * outChannelCount;
+#ifdef FLOAT_EFFECT_CHAIN
+ const float * const in = mConfig.inputCfg.buffer.f32;
+ float * const out = mConfig.outputCfg.buffer.f32;
+ accumulate_float(out, in, sampleCount);
+#else
+ const int16_t * const in = mConfig.inputCfg.buffer.s16;
+ int16_t * const out = mConfig.outputCfg.buffer.s16;
+ accumulate_i16(out, in, sampleCount);
+#endif
}
}
}
@@ -349,6 +446,7 @@
status_t AudioFlinger::EffectModule::configure()
{
+ ALOGVV("configure() started");
status_t status;
sp<ThreadBase> thread;
uint32_t size;
@@ -384,8 +482,8 @@
}
}
- mConfig.inputCfg.format = AUDIO_FORMAT_PCM_16_BIT;
- mConfig.outputCfg.format = AUDIO_FORMAT_PCM_16_BIT;
+ mConfig.inputCfg.format = EFFECT_BUFFER_FORMAT;
+ mConfig.outputCfg.format = EFFECT_BUFFER_FORMAT;
mConfig.inputCfg.samplingRate = thread->sampleRate();
mConfig.outputCfg.samplingRate = mConfig.inputCfg.samplingRate;
mConfig.inputCfg.bufferProvider.cookie = NULL;
@@ -413,12 +511,6 @@
mConfig.outputCfg.mask = EFFECT_CONFIG_ALL;
mConfig.inputCfg.buffer.frameCount = thread->frameCount();
mConfig.outputCfg.buffer.frameCount = mConfig.inputCfg.buffer.frameCount;
- if (mInBuffer != 0) {
- mInBuffer->setFrameCount(mConfig.inputCfg.buffer.frameCount);
- }
- if (mOutBuffer != 0) {
- mOutBuffer->setFrameCount(mConfig.outputCfg.buffer.frameCount);
- }
ALOGV("configure() %p thread %p buffer %p framecount %zu",
this, thread.get(), mConfig.inputCfg.buffer.raw, mConfig.inputCfg.buffer.frameCount);
@@ -430,32 +522,60 @@
&mConfig,
&size,
&cmdStatus);
- if (status == 0) {
+ if (status == NO_ERROR) {
status = cmdStatus;
+#ifdef FLOAT_EFFECT_CHAIN
+ mSupportsFloat = true;
+#endif
}
-
- if (status == 0 &&
- (memcmp(&mDescriptor.type, SL_IID_VISUALIZATION, sizeof(effect_uuid_t)) == 0)) {
- uint32_t buf32[sizeof(effect_param_t) / sizeof(uint32_t) + 2];
- effect_param_t *p = (effect_param_t *)buf32;
-
- p->psize = sizeof(uint32_t);
- p->vsize = sizeof(uint32_t);
- size = sizeof(int);
- *(int32_t *)p->data = VISUALIZER_PARAM_LATENCY;
-
- uint32_t latency = 0;
- PlaybackThread *pbt = thread->mAudioFlinger->checkPlaybackThread_l(thread->mId);
- if (pbt != NULL) {
- latency = pbt->latency_l();
+#ifdef FLOAT_EFFECT_CHAIN
+ else {
+ ALOGV("EFFECT_CMD_SET_CONFIG failed with float format, retry with int16_t.");
+ mConfig.inputCfg.format = AUDIO_FORMAT_PCM_16_BIT;
+ mConfig.outputCfg.format = AUDIO_FORMAT_PCM_16_BIT;
+ status = mEffectInterface->command(EFFECT_CMD_SET_CONFIG,
+ sizeof(effect_config_t),
+ &mConfig,
+ &size,
+ &cmdStatus);
+ if (status == NO_ERROR) {
+ status = cmdStatus;
+ mSupportsFloat = false;
+ ALOGVV("config worked with 16 bit");
+ } else {
+ ALOGE("%s failed %d with int16_t (as well as float)", __func__, status);
}
+ }
+#endif
- *((int32_t *)p->data + 1)= latency;
- mEffectInterface->command(EFFECT_CMD_SET_PARAM,
- sizeof(effect_param_t) + 8,
- &buf32,
- &size,
- &cmdStatus);
+ if (status == NO_ERROR) {
+ // Establish Buffer strategy
+ setInBuffer(mInBuffer);
+ setOutBuffer(mOutBuffer);
+
+ // Update visualizer latency
+ if (memcmp(&mDescriptor.type, SL_IID_VISUALIZATION, sizeof(effect_uuid_t)) == 0) {
+ uint32_t buf32[sizeof(effect_param_t) / sizeof(uint32_t) + 2];
+ effect_param_t *p = (effect_param_t *)buf32;
+
+ p->psize = sizeof(uint32_t);
+ p->vsize = sizeof(uint32_t);
+ size = sizeof(int);
+ *(int32_t *)p->data = VISUALIZER_PARAM_LATENCY;
+
+ uint32_t latency = 0;
+ PlaybackThread *pbt = thread->mAudioFlinger->checkPlaybackThread_l(thread->mId);
+ if (pbt != NULL) {
+ latency = pbt->latency_l();
+ }
+
+ *((int32_t *)p->data + 1)= latency;
+ mEffectInterface->command(EFFECT_CMD_SET_PARAM,
+ sizeof(effect_param_t) + 8,
+ &buf32,
+ &size,
+ &cmdStatus);
+ }
}
mMaxDisableWaitCnt = (MAX_DISABLE_TIME_MS * mConfig.outputCfg.samplingRate) /
@@ -463,6 +583,7 @@
exit:
mStatus = status;
+ ALOGVV("configure ended");
return status;
}
@@ -774,6 +895,7 @@
}
void AudioFlinger::EffectModule::setInBuffer(const sp<EffectBufferHalInterface>& buffer) {
+ ALOGVV("setInBuffer %p",(&buffer));
if (buffer != 0) {
mConfig.inputCfg.buffer.raw = buffer->audioBuffer()->raw;
buffer->setFrameCount(mConfig.inputCfg.buffer.frameCount);
@@ -781,10 +903,43 @@
mConfig.inputCfg.buffer.raw = NULL;
}
mInBuffer = buffer;
- mEffectInterface->setInBuffer(buffer);
+ if (buffer != nullptr) { // FIXME: EffectHalHidl::setInBuffer should accept null input.
+ mEffectInterface->setInBuffer(buffer);
+ }
+
+#ifdef FLOAT_EFFECT_CHAIN
+ // aux effects do in place conversion to float - we don't allocate mInBuffer16 for them.
+ // Theoretically insert effects can also do in-place conversions (destroying
+ // the original buffer) when the output buffer is identical to the input buffer,
+ // but we don't optimize for it here.
+ const bool auxType = (mDescriptor.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_AUXILIARY;
+ if (!auxType && !mSupportsFloat && mInBuffer.get() != nullptr) {
+ // we need to translate - create hidl shared buffer and intercept
+ const size_t inFrameCount = mConfig.inputCfg.buffer.frameCount;
+ const int inChannels = audio_channel_count_from_out_mask(mConfig.inputCfg.channels);
+ const size_t size = inChannels * inFrameCount * sizeof(int16_t);
+
+ ALOGV("%s: setInBuffer updating for inChannels:%d inFrameCount:%zu total size:%zu",
+ __func__, inChannels, inFrameCount, size);
+
+ if (size > 0 && (mInBuffer16.get() == nullptr || size > mInBuffer16->getSize())) {
+ mInBuffer16.clear();
+ ALOGV("%s: allocating mInBuffer16 %zu", __func__, size);
+ (void)EffectBufferHalInterface::allocate(size, &mInBuffer16);
+ }
+ if (mInBuffer16.get() != nullptr) {
+ // FIXME: confirm buffer has enough size.
+ mInBuffer16->setFrameCount(inFrameCount);
+ mEffectInterface->setInBuffer(mInBuffer16);
+ } else if (size > 0) {
+ ALOGE("%s cannot create mInBuffer16", __func__);
+ }
+ }
+#endif
}
void AudioFlinger::EffectModule::setOutBuffer(const sp<EffectBufferHalInterface>& buffer) {
+ ALOGVV("setOutBuffer %p",(&buffer));
if (buffer != 0) {
mConfig.outputCfg.buffer.raw = buffer->audioBuffer()->raw;
buffer->setFrameCount(mConfig.outputCfg.buffer.frameCount);
@@ -792,7 +947,34 @@
mConfig.outputCfg.buffer.raw = NULL;
}
mOutBuffer = buffer;
- mEffectInterface->setOutBuffer(buffer);
+ if (buffer != nullptr) {
+ mEffectInterface->setOutBuffer(buffer);
+ }
+
+#ifdef FLOAT_EFFECT_CHAIN
+ // Note: Any effect that does not accumulate does not need mOutBuffer16 and
+ // can do in-place conversion from int16_t to float. We don't optimize here.
+ if (!mSupportsFloat && mOutBuffer.get() != nullptr) {
+ const size_t outFrameCount = mConfig.outputCfg.buffer.frameCount;
+ const int outChannels = audio_channel_count_from_out_mask(mConfig.outputCfg.channels);
+ const size_t size = outChannels * outFrameCount * sizeof(int16_t);
+
+ ALOGV("%s: setOutBuffer updating for outChannels:%d outFrameCount:%zu total size:%zu",
+ __func__, outChannels, outFrameCount, size);
+
+ if (size > 0 && (mOutBuffer16.get() == nullptr || size > mOutBuffer16->getSize())) {
+ mOutBuffer16.clear();
+ ALOGV("%s: allocating mOutBuffer16 %zu", __func__, size);
+ (void)EffectBufferHalInterface::allocate(size, &mOutBuffer16);
+ }
+ if (mOutBuffer16.get() != nullptr) {
+ mOutBuffer16->setFrameCount(outFrameCount);
+ mEffectInterface->setOutBuffer(mOutBuffer16);
+ } else if (size > 0) {
+ ALOGE("%s cannot create mOutBuffer16", __func__);
+ }
+ }
+#endif
}
status_t AudioFlinger::EffectModule::setVolume(uint32_t *left, uint32_t *right, bool controller)
@@ -1126,6 +1308,22 @@
formatToString((audio_format_t)mConfig.outputCfg.format).c_str());
result.append(buffer);
+#ifdef FLOAT_EFFECT_CHAIN
+ if (!mSupportsFloat) {
+ int16_t* pIn16 = mInBuffer16 != 0 ? mInBuffer16->audioBuffer()->s16 : NULL;
+ int16_t* pOut16 = mOutBuffer16 != 0 ? mOutBuffer16->audioBuffer()->s16 : NULL;
+
+ result.append("\t\t- Float and int16 buffers\n");
+ result.append("\t\t\tIn_float In_int16 Out_float Out_int16\n");
+ snprintf(buffer, SIZE,"\t\t\t%p %p %p %p\n",
+ mConfig.inputCfg.buffer.raw,
+ pIn16,
+ pOut16,
+ mConfig.outputCfg.buffer.raw);
+ result.append(buffer);
+ }
+#endif
+
snprintf(buffer, SIZE, "\t\t%zu Clients:\n", mHandles.size());
result.append(buffer);
result.append("\t\t\t Pid Priority Ctrl Locked client server\n");
@@ -1602,8 +1800,11 @@
// and sample format changes for effects.
// Currently effects processing is only available for stereo, AUDIO_FORMAT_PCM_16_BIT
// (4 bytes frame size)
+
const size_t frameSize =
- audio_bytes_per_sample(AUDIO_FORMAT_PCM_16_BIT) * min(FCC_2, thread->channelCount());
+ audio_bytes_per_sample(EFFECT_BUFFER_FORMAT)
+ * std::min((uint32_t)FCC_2, thread->channelCount());
+
memset(mInBuffer->audioBuffer()->raw, 0, thread->frameCount() * frameSize);
mInBuffer->commit();
}
@@ -1718,8 +1919,13 @@
// calling the process in effect engine
size_t numSamples = thread->frameCount();
sp<EffectBufferHalInterface> halBuffer;
+#ifdef FLOAT_EFFECT_CHAIN
+ status_t result = EffectBufferHalInterface::allocate(
+ numSamples * sizeof(float), &halBuffer);
+#else
status_t result = EffectBufferHalInterface::allocate(
numSamples * sizeof(int32_t), &halBuffer);
+#endif
if (result != OK) return result;
effect->setInBuffer(halBuffer);
// auxiliary effects output samples to chain input buffer for further processing
diff --git a/services/audioflinger/Effects.h b/services/audioflinger/Effects.h
index e29798b..1864e0f 100644
--- a/services/audioflinger/Effects.h
+++ b/services/audioflinger/Effects.h
@@ -168,6 +168,12 @@
bool mSuspended; // effect is suspended: temporarily disabled by framework
bool mOffloaded; // effect is currently offloaded to the audio DSP
wp<AudioFlinger> mAudioFlinger;
+
+#ifdef FLOAT_EFFECT_CHAIN
+ bool mSupportsFloat; // effect supports float processing
+ sp<EffectBufferHalInterface> mInBuffer16; // Buffers for interacting with HAL at 16 bits
+ sp<EffectBufferHalInterface> mOutBuffer16;
+#endif
};
// The EffectHandle class implements the IEffect interface. It provides resources
@@ -308,14 +314,14 @@
void setInBuffer(const sp<EffectBufferHalInterface>& buffer) {
mInBuffer = buffer;
}
- int16_t *inBuffer() const {
- return mInBuffer != 0 ? reinterpret_cast<int16_t*>(mInBuffer->ptr()) : NULL;
+ effect_buffer_t *inBuffer() const {
+ return mInBuffer != 0 ? reinterpret_cast<effect_buffer_t*>(mInBuffer->ptr()) : NULL;
}
void setOutBuffer(const sp<EffectBufferHalInterface>& buffer) {
mOutBuffer = buffer;
}
- int16_t *outBuffer() const {
- return mOutBuffer != 0 ? reinterpret_cast<int16_t*>(mOutBuffer->ptr()) : NULL;
+ effect_buffer_t *outBuffer() const {
+ return mOutBuffer != 0 ? reinterpret_cast<effect_buffer_t*>(mOutBuffer->ptr()) : NULL;
}
void incTrackCnt() { android_atomic_inc(&mTrackCnt); }
diff --git a/services/audioflinger/PlaybackTracks.h b/services/audioflinger/PlaybackTracks.h
index 946d88f..e97bb06 100644
--- a/services/audioflinger/PlaybackTracks.h
+++ b/services/audioflinger/PlaybackTracks.h
@@ -68,8 +68,8 @@
status_t attachAuxEffect(int EffectId);
void setAuxBuffer(int EffectId, int32_t *buffer);
int32_t *auxBuffer() const { return mAuxBuffer; }
- void setMainBuffer(int16_t *buffer) { mMainBuffer = buffer; }
- int16_t *mainBuffer() const { return mMainBuffer; }
+ void setMainBuffer(effect_buffer_t *buffer) { mMainBuffer = buffer; }
+ effect_buffer_t *mainBuffer() const { return mMainBuffer; }
int auxEffectId() const { return mAuxEffectId; }
virtual status_t getTimestamp(AudioTimestamp& timestamp);
void signal();
@@ -150,7 +150,8 @@
// allocated statically at track creation time,
// and is even allocated (though unused) for fast tracks
// FIXME don't allocate track name for fast tracks
- int16_t *mMainBuffer;
+ effect_buffer_t *mMainBuffer;
+
int32_t *mAuxBuffer;
int mAuxEffectId;
bool mHasVolumeController;
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index 8e6c720..b2a1e18 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -2537,7 +2537,7 @@
free(mEffectBuffer);
mEffectBuffer = NULL;
if (mEffectBufferEnabled) {
- mEffectBufferFormat = AUDIO_FORMAT_PCM_16_BIT; // Note: Effects support 16b only
+ mEffectBufferFormat = EFFECT_BUFFER_FORMAT;
mEffectBufferSize = mNormalFrameCount * mChannelCount
* audio_bytes_per_sample(mEffectBufferFormat);
(void)posix_memalign(&mEffectBuffer, 32, mEffectBufferSize);
@@ -2884,8 +2884,7 @@
&halInBuffer);
if (result != OK) return result;
halOutBuffer = halInBuffer;
- int16_t *buffer = reinterpret_cast<int16_t*>(halInBuffer->externalData());
-
+ effect_buffer_t *buffer = reinterpret_cast<effect_buffer_t*>(halInBuffer->externalData());
ALOGV("addEffectChain_l() %p on thread %p for session %d", chain.get(), this, session);
if (session > AUDIO_SESSION_OUTPUT_MIX) {
// Only one effect chain can be present in direct output thread and it uses
@@ -2893,10 +2892,14 @@
if (mType != DIRECT) {
size_t numSamples = mNormalFrameCount * mChannelCount;
status_t result = EffectBufferHalInterface::allocate(
- numSamples * sizeof(int16_t),
+ numSamples * sizeof(effect_buffer_t),
&halInBuffer);
if (result != OK) return result;
+#ifdef FLOAT_EFFECT_CHAIN
+ buffer = halInBuffer->audioBuffer()->f32;
+#else
buffer = halInBuffer->audioBuffer()->s16;
+#endif
ALOGV("addEffectChain_l() creating new input buffer %p session %d",
buffer, session);
}
@@ -2971,7 +2974,7 @@
for (size_t i = 0; i < mTracks.size(); ++i) {
sp<Track> track = mTracks[i];
if (session == track->sessionId()) {
- track->setMainBuffer(reinterpret_cast<int16_t*>(mSinkBuffer));
+ track->setMainBuffer(reinterpret_cast<effect_buffer_t*>(mSinkBuffer));
chain->decTrackCnt();
}
}
@@ -4554,7 +4557,7 @@
mAudioMixer->setParameter(
name,
AudioMixer::TRACK,
- AudioMixer::MIXER_FORMAT, (void *)AUDIO_FORMAT_PCM_16_BIT);
+ AudioMixer::MIXER_FORMAT, (void *)EFFECT_BUFFER_FORMAT);
mAudioMixer->setParameter(
name,
AudioMixer::TRACK,
diff --git a/services/audioflinger/Threads.h b/services/audioflinger/Threads.h
index 2ca273f..c7b60d6 100644
--- a/services/audioflinger/Threads.h
+++ b/services/audioflinger/Threads.h
@@ -741,11 +741,10 @@
virtual String8 getParameters(const String8& keys);
virtual void ioConfigChanged(audio_io_config_event event, pid_t pid = 0);
status_t getRenderPosition(uint32_t *halFrames, uint32_t *dspFrames);
- // FIXME rename mixBuffer() to sinkBuffer() and remove int16_t* dependency.
// Consider also removing and passing an explicit mMainBuffer initialization
// parameter to AF::PlaybackThread::Track::Track().
- int16_t *mixBuffer() const {
- return reinterpret_cast<int16_t *>(mSinkBuffer); };
+ effect_buffer_t *sinkBuffer() const {
+ return reinterpret_cast<effect_buffer_t *>(mSinkBuffer); };
virtual void detachAuxEffect_l(int effectId);
status_t attachAuxEffect(const sp<AudioFlinger::PlaybackThread::Track>& track,
diff --git a/services/audioflinger/Tracks.cpp b/services/audioflinger/Tracks.cpp
index 50c0e23..1445572 100644
--- a/services/audioflinger/Tracks.cpp
+++ b/services/audioflinger/Tracks.cpp
@@ -395,7 +395,7 @@
mSharedBuffer(sharedBuffer),
mStreamType(streamType),
mName(-1), // see note below
- mMainBuffer(thread->mixBuffer()),
+ mMainBuffer(thread->sinkBuffer()),
mAuxBuffer(NULL),
mAuxEffectId(0), mHasVolumeController(false),
mPresentationCompleteFrames(0),
diff --git a/services/mediaanalytics/MediaAnalyticsService.cpp b/services/mediaanalytics/MediaAnalyticsService.cpp
index 83992aa..8444444 100644
--- a/services/mediaanalytics/MediaAnalyticsService.cpp
+++ b/services/mediaanalytics/MediaAnalyticsService.cpp
@@ -159,7 +159,8 @@
mMaxRecordAgeNs(kMaxRecordAgeNs),
mMaxRecordSets(kMaxRecordSets),
mNewSetInterval(kNewSetIntervalNs),
- mDumpProto(MediaAnalyticsItem::PROTO_V0) {
+ mDumpProto(MediaAnalyticsItem::PROTO_V1),
+ mDumpProtoDefault(MediaAnalyticsItem::PROTO_V1) {
ALOGD("MediaAnalyticsService created");
// clear our queues
@@ -381,6 +382,7 @@
String16 summaryOption("-summary");
bool summary = false;
String16 protoOption("-proto");
+ int chosenProto = mDumpProtoDefault;
String16 clearOption("-clear");
bool clear = false;
String16 sinceOption("-since");
@@ -400,7 +402,7 @@
i++;
if (i < n) {
String8 value(args[i]);
- int proto = MediaAnalyticsItem::PROTO_V0; // default to original
+ int proto = MediaAnalyticsItem::PROTO_V0;
char *endp;
const char *p = value.string();
proto = strtol(p, &endp, 10);
@@ -410,8 +412,12 @@
} else if (proto > MediaAnalyticsItem::PROTO_LAST) {
proto = MediaAnalyticsItem::PROTO_LAST;
}
- mDumpProto = proto;
+ chosenProto = proto;
+ } else {
+ result.append("unable to parse value for -proto\n\n");
}
+ } else {
+ result.append("missing value for -proto\n\n");
}
} else if (args[i] == sinceOption) {
i++;
@@ -437,7 +443,7 @@
} else if (args[i] == helpOption) {
result.append("Recognized parameters:\n");
result.append("-help this help message\n");
- result.append("-proto X dump using protocol X (defaults to 1)");
+ result.append("-proto # dump using protocol #");
result.append("-summary show summary info\n");
result.append("-clear clears out saved records\n");
result.append("-only X process records for component X\n");
@@ -450,6 +456,8 @@
Mutex::Autolock _l(mLock);
+ mDumpProto = chosenProto;
+
// we ALWAYS dump this piece
snprintf(buffer, SIZE, "Dump of the %s process:\n", kServiceName);
result.append(buffer);
diff --git a/services/mediaanalytics/MediaAnalyticsService.h b/services/mediaanalytics/MediaAnalyticsService.h
index 52e4631..3b34f44 100644
--- a/services/mediaanalytics/MediaAnalyticsService.h
+++ b/services/mediaanalytics/MediaAnalyticsService.h
@@ -125,6 +125,7 @@
// support for generating output
int mDumpProto;
+ int mDumpProtoDefault;
String8 dumpQueue(List<MediaAnalyticsItem*> *);
String8 dumpQueue(List<MediaAnalyticsItem*> *, nsecs_t, const char *only);
diff --git a/services/oboeservice/AAudioMixer.cpp b/services/oboeservice/AAudioMixer.cpp
index 57241a1..b031888 100644
--- a/services/oboeservice/AAudioMixer.cpp
+++ b/services/oboeservice/AAudioMixer.cpp
@@ -49,7 +49,7 @@
memset(mOutputBuffer, 0, mBufferSizeInBytes);
}
-bool AAudioMixer::mix(int streamIndex, FifoBuffer *fifo, bool allowUnderflow) {
+int32_t AAudioMixer::mix(int streamIndex, FifoBuffer *fifo, bool allowUnderflow) {
WrappingBuffer wrappingBuffer;
float *destination = mOutputBuffer;
@@ -105,7 +105,7 @@
ATRACE_END();
#endif /* AAUDIO_MIXER_ATRACE_ENABLED */
- return (framesLeft > 0); // did not get all the frames we needed, ie. "underflow"
+ return (framesDesired - framesLeft); // framesRead
}
void AAudioMixer::mixPart(float *destination, float *source, int32_t numFrames) {
diff --git a/services/oboeservice/AAudioMixer.h b/services/oboeservice/AAudioMixer.h
index 5625d4d..d5abc5b 100644
--- a/services/oboeservice/AAudioMixer.h
+++ b/services/oboeservice/AAudioMixer.h
@@ -36,15 +36,17 @@
* @param streamIndex for marking stream variables in systrace
* @param fifo to read from
* @param allowUnderflow if true then allow mixer to advance read index past the write index
- * @return true if actually underflowed
+ * @return frames read from this stream
*/
- bool mix(int streamIndex, android::FifoBuffer *fifo, bool allowUnderflow);
-
- void mixPart(float *destination, float *source, int32_t numFrames);
+ int32_t mix(int streamIndex, android::FifoBuffer *fifo, bool allowUnderflow);
float *getOutputBuffer();
+ int32_t getFramesPerBurst() const { return mFramesPerBurst; }
+
private:
+ void mixPart(float *destination, float *source, int32_t numFrames);
+
float *mOutputBuffer = nullptr;
int32_t mSamplesPerFrame = 0;
int32_t mFramesPerBurst = 0;
diff --git a/services/oboeservice/AAudioServiceEndpointCapture.cpp b/services/oboeservice/AAudioServiceEndpointCapture.cpp
index f902bef..efac788 100644
--- a/services/oboeservice/AAudioServiceEndpointCapture.cpp
+++ b/services/oboeservice/AAudioServiceEndpointCapture.cpp
@@ -58,7 +58,6 @@
// Read data from the shared MMAP stream and then distribute it to the client streams.
void *AAudioServiceEndpointCapture::callbackLoop() {
ALOGD("callbackLoop() entering");
- int32_t underflowCount = 0;
aaudio_result_t result = AAUDIO_OK;
int64_t timeoutNanos = getStreamInternal()->calculateReasonableTimeout();
@@ -102,9 +101,10 @@
int64_t positionOffset = mmapFramesRead - clientFramesWritten;
streamShared->setTimestampPositionOffset(positionOffset);
+ // Is the buffer too full to write a burst?
if (fifo->getFifoControllerBase()->getEmptyFramesAvailable() <
- getFramesPerBurst()) {
- underflowCount++;
+ getFramesPerBurst()) {
+ streamShared->incrementXRunCount();
} else {
fifo->write(mDistributionBuffer, getFramesPerBurst());
}
@@ -125,6 +125,6 @@
}
}
- ALOGD("callbackLoop() exiting, %d underflows", underflowCount);
+ ALOGD("callbackLoop() exiting");
return NULL; // TODO review
}
diff --git a/services/oboeservice/AAudioServiceEndpointPlay.cpp b/services/oboeservice/AAudioServiceEndpointPlay.cpp
index c2feb6b..2601f3f 100644
--- a/services/oboeservice/AAudioServiceEndpointPlay.cpp
+++ b/services/oboeservice/AAudioServiceEndpointPlay.cpp
@@ -34,6 +34,7 @@
#include "AAudioServiceStreamShared.h"
#include "AAudioServiceEndpointPlay.h"
#include "AAudioServiceEndpointShared.h"
+#include "AAudioServiceStreamBase.h"
using namespace android; // TODO just import names needed
using namespace aaudio; // TODO just import names needed
@@ -108,9 +109,19 @@
int64_t positionOffset = mmapFramesWritten - clientFramesRead;
streamShared->setTimestampPositionOffset(positionOffset);
- bool underflowed = mMixer.mix(index, fifo, allowUnderflow);
- if (underflowed) {
- streamShared->incrementXRunCount();
+ int32_t framesMixed = mMixer.mix(index, fifo, allowUnderflow);
+
+ if (streamShared->isFlowing()) {
+ // Consider it an underflow if we got less than a burst
+ // after the data started flowing.
+ bool underflowed = allowUnderflow
+ && framesMixed < mMixer.getFramesPerBurst();
+ if (underflowed) {
+ streamShared->incrementXRunCount();
+ }
+ } else if (framesMixed > 0) {
+ // Mark beginning of data flow after a start.
+ streamShared->setFlowing(true);
}
clientFramesRead = fifo->getReadCounter();
}
diff --git a/services/oboeservice/AAudioServiceEndpointShared.cpp b/services/oboeservice/AAudioServiceEndpointShared.cpp
index 820ed28..6af9e7e 100644
--- a/services/oboeservice/AAudioServiceEndpointShared.cpp
+++ b/services/oboeservice/AAudioServiceEndpointShared.cpp
@@ -47,6 +47,7 @@
<< std::setfill('0') << std::setw(8)
<< std::hex << mStreamInternal->getServiceHandle()
<< std::dec << std::setfill(' ');
+ result << ", XRuns = " << mStreamInternal->getXRunCount();
result << "\n";
result << " Running Stream Count: " << mRunningStreamCount << "\n";
diff --git a/services/oboeservice/AAudioServiceStreamBase.cpp b/services/oboeservice/AAudioServiceStreamBase.cpp
index 6652cc9..635b45c 100644
--- a/services/oboeservice/AAudioServiceStreamBase.cpp
+++ b/services/oboeservice/AAudioServiceStreamBase.cpp
@@ -172,6 +172,8 @@
goto error;
}
+ setFlowing(false);
+
// Start with fresh presentation timestamps.
mAtomicTimestamp.clear();
@@ -311,12 +313,19 @@
}
aaudio_result_t AAudioServiceStreamBase::sendServiceEvent(aaudio_service_event_t event,
- double dataDouble,
- int64_t dataLong) {
+ double dataDouble) {
AAudioServiceMessage command;
command.what = AAudioServiceMessage::code::EVENT;
command.event.event = event;
command.event.dataDouble = dataDouble;
+ return writeUpMessageQueue(&command);
+}
+
+aaudio_result_t AAudioServiceStreamBase::sendServiceEvent(aaudio_service_event_t event,
+ int64_t dataLong) {
+ AAudioServiceMessage command;
+ command.what = AAudioServiceMessage::code::EVENT;
+ command.event.event = event;
command.event.dataLong = dataLong;
return writeUpMessageQueue(&command);
}
@@ -336,6 +345,10 @@
}
}
+aaudio_result_t AAudioServiceStreamBase::sendXRunCount(int32_t xRunCount) {
+ return sendServiceEvent(AAUDIO_SERVICE_EVENT_XRUN, (int64_t) xRunCount);
+}
+
aaudio_result_t AAudioServiceStreamBase::sendCurrentTimestamp() {
AAudioServiceMessage command;
// Send a timestamp for the clock model.
diff --git a/services/oboeservice/AAudioServiceStreamBase.h b/services/oboeservice/AAudioServiceStreamBase.h
index af435b4..29987f6 100644
--- a/services/oboeservice/AAudioServiceStreamBase.h
+++ b/services/oboeservice/AAudioServiceStreamBase.h
@@ -129,11 +129,15 @@
// -------------------------------------------------------------------
/**
- * Send a message to the client.
+ * Send a message to the client with an int64_t data value.
*/
aaudio_result_t sendServiceEvent(aaudio_service_event_t event,
- double dataDouble = 0.0,
int64_t dataLong = 0);
+ /**
+ * Send a message to the client with an double data value.
+ */
+ aaudio_result_t sendServiceEvent(aaudio_service_event_t event,
+ double dataDouble);
/**
* Fill in a parcelable description of stream.
@@ -182,6 +186,19 @@
void onVolumeChanged(float volume);
+ /**
+ * Set false when the stream is started.
+ * Set true when data is first read from the stream.
+ * @param b
+ */
+ void setFlowing(bool b) {
+ mFlowing = b;
+ }
+
+ bool isFlowing() const {
+ return mFlowing;
+ }
+
protected:
/**
@@ -204,6 +221,8 @@
aaudio_result_t sendCurrentTimestamp();
+ aaudio_result_t sendXRunCount(int32_t xRunCount);
+
/**
* @param positionFrames
* @param timeNanos
@@ -237,6 +256,8 @@
private:
aaudio_handle_t mHandle = -1;
+
+ bool mFlowing = false;
};
} /* namespace aaudio */
diff --git a/services/oboeservice/AAudioServiceStreamShared.h b/services/oboeservice/AAudioServiceStreamShared.h
index 8499ea5..3b12e61 100644
--- a/services/oboeservice/AAudioServiceStreamShared.h
+++ b/services/oboeservice/AAudioServiceStreamShared.h
@@ -1,4 +1,4 @@
-/*
+ /*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -80,7 +80,7 @@
}
void incrementXRunCount() {
- mXRunCount++;
+ sendXRunCount(++mXRunCount);
}
int32_t getXRunCount() const {