summaryrefslogtreecommitdiffstats
path: root/media/libstagefright
diff options
context:
space:
mode:
Diffstat (limited to 'media/libstagefright')
-rw-r--r--media/libstagefright/additional_headers55
-rw-r--r--media/libstagefright/binding/Adts.cpp75
-rw-r--r--media/libstagefright/binding/AnnexB.cpp410
-rw-r--r--media/libstagefright/binding/BitReader.cpp104
-rw-r--r--media/libstagefright/binding/Box.cpp176
-rw-r--r--media/libstagefright/binding/BufferStream.cpp77
-rw-r--r--media/libstagefright/binding/DecoderData.cpp217
-rw-r--r--media/libstagefright/binding/H264.cpp528
-rw-r--r--media/libstagefright/binding/Index.cpp533
-rw-r--r--media/libstagefright/binding/MP4Metadata.cpp880
-rw-r--r--media/libstagefright/binding/MoofParser.cpp925
-rw-r--r--media/libstagefright/binding/ResourceStream.cpp67
-rw-r--r--media/libstagefright/binding/SinfParser.cpp72
-rw-r--r--media/libstagefright/binding/include/demuxer/TrackDemuxer.h34
-rw-r--r--media/libstagefright/binding/include/mp4_demuxer/Adts.h26
-rw-r--r--media/libstagefright/binding/include/mp4_demuxer/AnnexB.h55
-rw-r--r--media/libstagefright/binding/include/mp4_demuxer/Atom.h27
-rw-r--r--media/libstagefright/binding/include/mp4_demuxer/AtomType.h31
-rw-r--r--media/libstagefright/binding/include/mp4_demuxer/BitReader.h45
-rw-r--r--media/libstagefright/binding/include/mp4_demuxer/Box.h84
-rw-r--r--media/libstagefright/binding/include/mp4_demuxer/BufferStream.h46
-rw-r--r--media/libstagefright/binding/include/mp4_demuxer/ByteReader.h349
-rw-r--r--media/libstagefright/binding/include/mp4_demuxer/ByteWriter.h76
-rw-r--r--media/libstagefright/binding/include/mp4_demuxer/DecoderData.h93
-rw-r--r--media/libstagefright/binding/include/mp4_demuxer/H264.h372
-rw-r--r--media/libstagefright/binding/include/mp4_demuxer/Index.h128
-rw-r--r--media/libstagefright/binding/include/mp4_demuxer/Interval.h147
-rw-r--r--media/libstagefright/binding/include/mp4_demuxer/MP4Metadata.h54
-rw-r--r--media/libstagefright/binding/include/mp4_demuxer/MoofParser.h260
-rw-r--r--media/libstagefright/binding/include/mp4_demuxer/ResourceStream.h49
-rw-r--r--media/libstagefright/binding/include/mp4_demuxer/SinfParser.h51
-rw-r--r--media/libstagefright/binding/include/mp4_demuxer/Stream.h32
-rw-r--r--media/libstagefright/binding/include/mp4parse.h113
-rw-r--r--media/libstagefright/binding/mp4parse-cargo.patch45
-rw-r--r--media/libstagefright/binding/mp4parse/Cargo.toml29
-rw-r--r--media/libstagefright/binding/mp4parse/src/boxes.rs62
-rw-r--r--media/libstagefright/binding/mp4parse/src/lib.rs1704
-rw-r--r--media/libstagefright/binding/mp4parse/src/tests.rs860
-rw-r--r--media/libstagefright/binding/mp4parse/tests/afl.rs53
-rw-r--r--media/libstagefright/binding/mp4parse/tests/minimal.mp4bin0 -> 2591 bytes
-rw-r--r--media/libstagefright/binding/mp4parse/tests/public.rs97
-rw-r--r--media/libstagefright/binding/mp4parse_capi/Cargo.toml26
-rw-r--r--media/libstagefright/binding/mp4parse_capi/build.rs12
-rw-r--r--media/libstagefright/binding/mp4parse_capi/src/lib.rs870
-rwxr-xr-xmedia/libstagefright/binding/update-rust.sh56
-rwxr-xr-xmedia/libstagefright/checkout.sh52
-rw-r--r--media/libstagefright/files.py29
-rw-r--r--media/libstagefright/frameworks/av/include/media/stagefright/DataSource.h100
-rw-r--r--media/libstagefright/frameworks/av/include/media/stagefright/MediaBuffer.h128
-rw-r--r--media/libstagefright/frameworks/av/include/media/stagefright/MediaDefs.h64
-rw-r--r--media/libstagefright/frameworks/av/include/media/stagefright/MediaErrors.h69
-rw-r--r--media/libstagefright/frameworks/av/include/media/stagefright/MediaExtractor.h82
-rw-r--r--media/libstagefright/frameworks/av/include/media/stagefright/MediaSource.h61
-rw-r--r--media/libstagefright/frameworks/av/include/media/stagefright/MetaData.h275
-rw-r--r--media/libstagefright/frameworks/av/include/media/stagefright/Utils.h55
-rw-r--r--media/libstagefright/frameworks/av/include/media/stagefright/foundation/AAtomizer.h51
-rw-r--r--media/libstagefright/frameworks/av/include/media/stagefright/foundation/ABase.h25
-rw-r--r--media/libstagefright/frameworks/av/include/media/stagefright/foundation/ABitReader.h54
-rw-r--r--media/libstagefright/frameworks/av/include/media/stagefright/foundation/ABuffer.h71
-rw-r--r--media/libstagefright/frameworks/av/include/media/stagefright/foundation/ADebug.h86
-rw-r--r--media/libstagefright/frameworks/av/include/media/stagefright/foundation/AHandler.h56
-rw-r--r--media/libstagefright/frameworks/av/include/media/stagefright/foundation/AString.h95
-rw-r--r--media/libstagefright/frameworks/av/include/media/stagefright/foundation/hexdump.h33
-rw-r--r--media/libstagefright/frameworks/av/media/libstagefright/DataSource.cpp219
-rw-r--r--media/libstagefright/frameworks/av/media/libstagefright/ESDS.cpp244
-rw-r--r--media/libstagefright/frameworks/av/media/libstagefright/MPEG4Extractor.cpp2972
-rw-r--r--media/libstagefright/frameworks/av/media/libstagefright/MediaBuffer.cpp215
-rw-r--r--media/libstagefright/frameworks/av/media/libstagefright/MediaDefs.cpp60
-rw-r--r--media/libstagefright/frameworks/av/media/libstagefright/MediaSource.cpp25
-rw-r--r--media/libstagefright/frameworks/av/media/libstagefright/MetaData.cpp367
-rw-r--r--media/libstagefright/frameworks/av/media/libstagefright/SampleIterator.cpp331
-rw-r--r--media/libstagefright/frameworks/av/media/libstagefright/SampleTable.cpp1170
-rw-r--r--media/libstagefright/frameworks/av/media/libstagefright/Utils.cpp619
-rw-r--r--media/libstagefright/frameworks/av/media/libstagefright/foundation/AAtomizer.cpp67
-rw-r--r--media/libstagefright/frameworks/av/media/libstagefright/foundation/ABitReader.cpp103
-rw-r--r--media/libstagefright/frameworks/av/media/libstagefright/foundation/ABuffer.cpp76
-rw-r--r--media/libstagefright/frameworks/av/media/libstagefright/foundation/AString.cpp347
-rw-r--r--media/libstagefright/frameworks/av/media/libstagefright/foundation/hexdump.cpp96
-rw-r--r--media/libstagefright/frameworks/av/media/libstagefright/include/AMRExtractor.h62
-rw-r--r--media/libstagefright/frameworks/av/media/libstagefright/include/ESDS.h72
-rw-r--r--media/libstagefright/frameworks/av/media/libstagefright/include/MPEG4Extractor.h136
-rw-r--r--media/libstagefright/frameworks/av/media/libstagefright/include/SampleIterator.h83
-rw-r--r--media/libstagefright/frameworks/av/media/libstagefright/include/SampleTable.h188
-rw-r--r--media/libstagefright/gtest/TestInterval.cpp89
-rw-r--r--media/libstagefright/gtest/TestMP4Rust.cpp142
-rw-r--r--media/libstagefright/gtest/TestParser.cpp479
-rw-r--r--media/libstagefright/gtest/moz.build49
-rw-r--r--media/libstagefright/gtest/test_case_1156505.mp4bin0 -> 296 bytes
-rw-r--r--media/libstagefright/gtest/test_case_1181213.mp4bin0 -> 2834 bytes
-rw-r--r--media/libstagefright/gtest/test_case_1181215.mp4bin0 -> 3086 bytes
-rw-r--r--media/libstagefright/gtest/test_case_1181220.mp4bin0 -> 2834 bytes
-rw-r--r--media/libstagefright/gtest/test_case_1181223.mp4bin0 -> 2834 bytes
-rw-r--r--media/libstagefright/gtest/test_case_1181719.mp4bin0 -> 3095 bytes
-rw-r--r--media/libstagefright/gtest/test_case_1185230.mp4bin0 -> 3250 bytes
-rw-r--r--media/libstagefright/gtest/test_case_1187067.mp4bin0 -> 2835 bytes
-rw-r--r--media/libstagefright/gtest/test_case_1200326.mp4bin0 -> 1694 bytes
-rw-r--r--media/libstagefright/gtest/test_case_1204580.mp4bin0 -> 5833 bytes
-rwxr-xr-xmedia/libstagefright/gtest/test_case_1216748.mp4bin0 -> 296 bytes
-rw-r--r--media/libstagefright/gtest/test_case_1296473.mp4bin0 -> 5995 bytes
-rw-r--r--media/libstagefright/gtest/test_case_1296532.mp4bin0 -> 152132 bytes
-rw-r--r--media/libstagefright/gtest/test_case_1301065-harder.mp4bin0 -> 632 bytes
-rw-r--r--media/libstagefright/gtest/test_case_1301065-i64max.mp4bin0 -> 632 bytes
-rw-r--r--media/libstagefright/gtest/test_case_1301065-i64min.mp4bin0 -> 632 bytes
-rw-r--r--media/libstagefright/gtest/test_case_1301065-max-ez.mp4bin0 -> 632 bytes
-rw-r--r--media/libstagefright/gtest/test_case_1301065-max-ok.mp4bin0 -> 632 bytes
-rw-r--r--media/libstagefright/gtest/test_case_1301065-overfl.mp4bin0 -> 632 bytes
-rw-r--r--media/libstagefright/gtest/test_case_1301065-u32max.mp4bin0 -> 632 bytes
-rw-r--r--media/libstagefright/gtest/test_case_1301065-u64max.mp4bin0 -> 632 bytes
-rw-r--r--media/libstagefright/gtest/test_case_1301065.mp4bin0 -> 632 bytes
-rw-r--r--media/libstagefright/gtest/test_case_1351094.mp4bin0 -> 80388 bytes
-rw-r--r--media/libstagefright/moz.build172
-rw-r--r--media/libstagefright/patches/frameworks/av.patch1192
-rw-r--r--media/libstagefright/patches/system/core.patch1387
-rw-r--r--media/libstagefright/ports/bsd/include/byteswap.h15
-rw-r--r--media/libstagefright/ports/darwin/include/byteswap.h10
-rw-r--r--media/libstagefright/ports/win32/include/arpa/inet.h9
-rw-r--r--media/libstagefright/ports/win32/include/byteswap.h10
-rw-r--r--media/libstagefright/ports/win32/include/netinet/in.h39
-rw-r--r--media/libstagefright/ports/win32/include/pthread.h1
-rw-r--r--media/libstagefright/ports/win32/include/sys/cdefs.h1
-rw-r--r--media/libstagefright/ports/win32/include/sys/time.h9
-rw-r--r--media/libstagefright/ports/win32/include/unistd.h1
-rw-r--r--media/libstagefright/stubs/empty/ALooper.h0
-rw-r--r--media/libstagefright/stubs/empty/ALooperRoster.h0
-rw-r--r--media/libstagefright/stubs/empty/binder/Parcel.h0
-rw-r--r--media/libstagefright/stubs/empty/hardware/audio.h0
-rw-r--r--media/libstagefright/stubs/empty/media/AudioParameter.h0
-rw-r--r--media/libstagefright/stubs/empty/media/AudioSystem.h0
-rw-r--r--media/libstagefright/stubs/empty/media/MediaPlayerInterface.h0
-rw-r--r--media/libstagefright/stubs/empty/sys/system_properties.h0
-rw-r--r--media/libstagefright/stubs/empty/system/audio.h0
-rw-r--r--media/libstagefright/stubs/include/cutils/atomic.h53
-rw-r--r--media/libstagefright/stubs/include/media/stagefright/foundation/AMessage.h18
-rw-r--r--media/libstagefright/stubs/include/sys/atomics.h10
-rw-r--r--media/libstagefright/stubs/include/ui/GraphicBuffer.h16
-rw-r--r--media/libstagefright/stubs/include/utils/threads.h5
-rw-r--r--media/libstagefright/system/core/debuggerd/backtrace.h31
-rw-r--r--media/libstagefright/system/core/include/android/log.h128
-rw-r--r--media/libstagefright/system/core/include/corkscrew/backtrace.h115
-rw-r--r--media/libstagefright/system/core/include/corkscrew/map_info.h74
-rw-r--r--media/libstagefright/system/core/include/corkscrew/ptrace.h134
-rw-r--r--media/libstagefright/system/core/include/corkscrew/symbol_table.h59
-rw-r--r--media/libstagefright/system/core/include/cutils/jstring.h43
-rw-r--r--media/libstagefright/system/core/include/cutils/log.h1
-rw-r--r--media/libstagefright/system/core/include/cutils/properties.h89
-rw-r--r--media/libstagefright/system/core/include/cutils/sched_policy.h61
-rw-r--r--media/libstagefright/system/core/include/log/event_tag_map.h50
-rw-r--r--media/libstagefright/system/core/include/log/log.h568
-rw-r--r--media/libstagefright/system/core/include/log/logd.h49
-rw-r--r--media/libstagefright/system/core/include/log/logger.h81
-rw-r--r--media/libstagefright/system/core/include/log/logprint.h155
-rw-r--r--media/libstagefright/system/core/include/log/uio.h48
-rw-r--r--media/libstagefright/system/core/include/system/graphics.h304
-rw-r--r--media/libstagefright/system/core/include/sysutils/List.h334
-rw-r--r--media/libstagefright/system/core/include/utils/Atomic.h22
-rw-r--r--media/libstagefright/system/core/include/utils/CallStack.h76
-rw-r--r--media/libstagefright/system/core/include/utils/Condition.h159
-rw-r--r--media/libstagefright/system/core/include/utils/Debug.h48
-rw-r--r--media/libstagefright/system/core/include/utils/Errors.h88
-rw-r--r--media/libstagefright/system/core/include/utils/KeyedVector.h224
-rw-r--r--media/libstagefright/system/core/include/utils/List.h334
-rw-r--r--media/libstagefright/system/core/include/utils/Log.h71
-rw-r--r--media/libstagefright/system/core/include/utils/Mutex.h148
-rw-r--r--media/libstagefright/system/core/include/utils/RWLock.h126
-rw-r--r--media/libstagefright/system/core/include/utils/RefBase.h554
-rw-r--r--media/libstagefright/system/core/include/utils/SharedBuffer.h137
-rw-r--r--media/libstagefright/system/core/include/utils/SortedVector.h275
-rw-r--r--media/libstagefright/system/core/include/utils/String16.h250
-rw-r--r--media/libstagefright/system/core/include/utils/String8.h403
-rw-r--r--media/libstagefright/system/core/include/utils/StrongPointer.h211
-rw-r--r--media/libstagefright/system/core/include/utils/Timers.h106
-rw-r--r--media/libstagefright/system/core/include/utils/TypeHelpers.h302
-rw-r--r--media/libstagefright/system/core/include/utils/Unicode.h172
-rw-r--r--media/libstagefright/system/core/include/utils/Vector.h404
-rw-r--r--media/libstagefright/system/core/include/utils/VectorImpl.h183
-rw-r--r--media/libstagefright/system/core/libcutils/strdup16to8.c168
-rw-r--r--media/libstagefright/system/core/liblog/fake_log_device.c737
-rw-r--r--media/libstagefright/system/core/liblog/logd_write.c312
-rw-r--r--media/libstagefright/system/core/liblog/logprint.c1057
-rw-r--r--media/libstagefright/system/core/libpixelflinger/codeflinger/tinyutils/Errors.h48
-rw-r--r--media/libstagefright/system/core/libpixelflinger/codeflinger/tinyutils/KeyedVector.h203
-rw-r--r--media/libstagefright/system/core/libpixelflinger/codeflinger/tinyutils/SortedVector.h284
-rw-r--r--media/libstagefright/system/core/libpixelflinger/codeflinger/tinyutils/Vector.h353
-rw-r--r--media/libstagefright/system/core/libpixelflinger/codeflinger/tinyutils/VectorImpl.h195
-rw-r--r--media/libstagefright/system/core/libutils/RefBase.cpp657
-rw-r--r--media/libstagefright/system/core/libutils/SharedBuffer.cpp113
-rw-r--r--media/libstagefright/system/core/libutils/Static.cpp49
-rw-r--r--media/libstagefright/system/core/libutils/String16.cpp422
-rw-r--r--media/libstagefright/system/core/libutils/String8.cpp646
-rw-r--r--media/libstagefright/system/core/libutils/Unicode.cpp606
-rw-r--r--media/libstagefright/system/core/libutils/VectorImpl.cpp643
-rwxr-xr-xmedia/libstagefright/update-patches.sh13
192 files changed, 35673 insertions, 0 deletions
diff --git a/media/libstagefright/additional_headers b/media/libstagefright/additional_headers
new file mode 100644
index 000000000..1357302f6
--- /dev/null
+++ b/media/libstagefright/additional_headers
@@ -0,0 +1,55 @@
+frameworks/av/include/media/stagefright/foundation/AAtomizer.h
+frameworks/av/include/media/stagefright/foundation/ABase.h
+frameworks/av/include/media/stagefright/foundation/ABitReader.h
+frameworks/av/include/media/stagefright/foundation/ABuffer.h
+frameworks/av/include/media/stagefright/foundation/ADebug.h
+frameworks/av/include/media/stagefright/foundation/AHandler.h
+frameworks/av/include/media/stagefright/foundation/AString.h
+frameworks/av/include/media/stagefright/foundation/hexdump.h
+frameworks/av/include/media/stagefright/MediaDefs.h
+frameworks/av/include/media/stagefright/MediaErrors.h
+frameworks/av/include/media/stagefright/MediaExtractor.h
+frameworks/av/include/media/stagefright/MediaSource.h
+frameworks/av/include/media/stagefright/MetaData.h
+frameworks/av/include/media/stagefright/MetaData.h
+frameworks/av/media/libstagefright/include/ESDS.h
+frameworks/av/media/libstagefright/include/MPEG4Extractor.h
+frameworks/av/media/libstagefright/include/SampleTable.h
+frameworks/av/media/libstagefright/include/SampleTable.h
+system/core/debuggerd/backtrace.h
+system/core/include/android/log.h
+system/core/include/corkscrew/backtrace.h
+system/core/include/corkscrew/map_info.h
+system/core/include/corkscrew/ptrace.h
+system/core/include/corkscrew/symbol_table.h
+system/core/include/cutils/jstring.h
+system/core/include/cutils/log.h
+system/core/include/cutils/sched_policy.h
+system/core/include/log/event_tag_map.h
+system/core/include/log/logd.h
+system/core/include/log/logger.h
+system/core/include/log/logprint.h
+system/core/include/log/uio.h
+system/core/include/system/graphics.h
+system/core/include/sysutils/List.h
+system/core/include/utils/Atomic.h
+system/core/include/utils/CallStack.h
+system/core/include/utils/Condition.h
+system/core/include/utils/Debug.h
+system/core/include/utils/Errors.h
+system/core/include/utils/KeyedVector.h
+system/core/include/utils/List.h
+system/core/include/utils/Log.h
+system/core/include/utils/Mutex.h
+system/core/include/utils/RWLock.h
+system/core/include/utils/SortedVector.h
+system/core/include/utils/String16.h
+system/core/include/utils/String8.h
+system/core/include/utils/Timers.h
+system/core/include/utils/Vector.h
+system/core/include/utils/VectorImpl.h
+system/core/libpixelflinger/codeflinger/tinyutils/Errors.h
+system/core/libpixelflinger/codeflinger/tinyutils/KeyedVector.h
+system/core/libpixelflinger/codeflinger/tinyutils/SortedVector.h
+system/core/libpixelflinger/codeflinger/tinyutils/Vector.h
+system/core/libpixelflinger/codeflinger/tinyutils/VectorImpl.h
diff --git a/media/libstagefright/binding/Adts.cpp b/media/libstagefright/binding/Adts.cpp
new file mode 100644
index 000000000..4146ff008
--- /dev/null
+++ b/media/libstagefright/binding/Adts.cpp
@@ -0,0 +1,75 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mp4_demuxer/Adts.h"
+#include "MediaData.h"
+#include "mozilla/Array.h"
+#include "mozilla/ArrayUtils.h"
+#include "nsAutoPtr.h"
+
+using namespace mozilla;
+
+namespace mp4_demuxer
+{
+
+int8_t
+Adts::GetFrequencyIndex(uint32_t aSamplesPerSecond)
+{
+ static const uint32_t freq_lookup[] = { 96000, 88200, 64000, 48000, 44100,
+ 32000, 24000, 22050, 16000, 12000,
+ 11025, 8000, 7350, 0};
+
+ int8_t i = 0;
+ while (freq_lookup[i] && aSamplesPerSecond < freq_lookup[i]) {
+ i++;
+ }
+
+ if (!freq_lookup[i]) {
+ return -1;
+ }
+
+ return i;
+}
+
+bool
+Adts::ConvertSample(uint16_t aChannelCount, int8_t aFrequencyIndex,
+ int8_t aProfile, MediaRawData* aSample)
+{
+ static const int kADTSHeaderSize = 7;
+
+ size_t newSize = aSample->Size() + kADTSHeaderSize;
+
+ // ADTS header uses 13 bits for packet size.
+ if (newSize >= (1 << 13) || aChannelCount > 15 ||
+ aFrequencyIndex < 0 || aProfile < 1 || aProfile > 4) {
+ return false;
+ }
+
+ Array<uint8_t, kADTSHeaderSize> header;
+ header[0] = 0xff;
+ header[1] = 0xf1;
+ header[2] =
+ ((aProfile - 1) << 6) + (aFrequencyIndex << 2) + (aChannelCount >> 2);
+ header[3] = ((aChannelCount & 0x3) << 6) + (newSize >> 11);
+ header[4] = (newSize & 0x7ff) >> 3;
+ header[5] = ((newSize & 7) << 5) + 0x1f;
+ header[6] = 0xfc;
+
+ nsAutoPtr<MediaRawDataWriter> writer(aSample->CreateWriter());
+ if (!writer->Prepend(&header[0], ArrayLength(header))) {
+ return false;
+ }
+
+ if (aSample->mCrypto.mValid) {
+ if (aSample->mCrypto.mPlainSizes.Length() == 0) {
+ writer->mCrypto.mPlainSizes.AppendElement(kADTSHeaderSize);
+ writer->mCrypto.mEncryptedSizes.AppendElement(aSample->Size() - kADTSHeaderSize);
+ } else {
+ writer->mCrypto.mPlainSizes[0] += kADTSHeaderSize;
+ }
+ }
+
+ return true;
+}
+}
diff --git a/media/libstagefright/binding/AnnexB.cpp b/media/libstagefright/binding/AnnexB.cpp
new file mode 100644
index 000000000..2ca355757
--- /dev/null
+++ b/media/libstagefright/binding/AnnexB.cpp
@@ -0,0 +1,410 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/ArrayUtils.h"
+#include "mozilla/EndianUtils.h"
+#include "mp4_demuxer/AnnexB.h"
+#include "mp4_demuxer/ByteReader.h"
+#include "mp4_demuxer/ByteWriter.h"
+#include "MediaData.h"
+#include "nsAutoPtr.h"
+
+using namespace mozilla;
+
+namespace mp4_demuxer
+{
+
+static const uint8_t kAnnexBDelimiter[] = { 0, 0, 0, 1 };
+
+bool
+AnnexB::ConvertSampleToAnnexB(mozilla::MediaRawData* aSample, bool aAddSPS)
+{
+ MOZ_ASSERT(aSample);
+
+ if (!IsAVCC(aSample)) {
+ return true;
+ }
+ MOZ_ASSERT(aSample->Data());
+
+ if (!ConvertSampleTo4BytesAVCC(aSample)) {
+ return false;
+ }
+
+ if (aSample->Size() < 4) {
+ // Nothing to do, it's corrupted anyway.
+ return true;
+ }
+
+ ByteReader reader(aSample->Data(), aSample->Size());
+
+ mozilla::Vector<uint8_t> tmp;
+ ByteWriter writer(tmp);
+
+ while (reader.Remaining() >= 4) {
+ uint32_t nalLen = reader.ReadU32();
+ const uint8_t* p = reader.Read(nalLen);
+
+ if (!writer.Write(kAnnexBDelimiter, ArrayLength(kAnnexBDelimiter))) {
+ return false;
+ }
+ if (!p) {
+ break;
+ }
+ if (!writer.Write(p, nalLen)) {
+ return false;
+ }
+ }
+
+ nsAutoPtr<MediaRawDataWriter> samplewriter(aSample->CreateWriter());
+
+ if (!samplewriter->Replace(tmp.begin(), tmp.length())) {
+ return false;
+ }
+
+ // Prepend the Annex B NAL with SPS and PPS tables to keyframes.
+ if (aAddSPS && aSample->mKeyframe) {
+ RefPtr<MediaByteBuffer> annexB =
+ ConvertExtraDataToAnnexB(aSample->mExtraData);
+ if (!samplewriter->Prepend(annexB->Elements(), annexB->Length())) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+already_AddRefed<mozilla::MediaByteBuffer>
+AnnexB::ConvertExtraDataToAnnexB(const mozilla::MediaByteBuffer* aExtraData)
+{
+ // AVCC 6 byte header looks like:
+ // +------+------+------+------+------+------+------+------+
+ // [0] | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 |
+ // +------+------+------+------+------+------+------+------+
+ // [1] | profile |
+ // +------+------+------+------+------+------+------+------+
+ // [2] | compatiblity |
+ // +------+------+------+------+------+------+------+------+
+ // [3] | level |
+ // +------+------+------+------+------+------+------+------+
+ // [4] | unused | nalLenSiz-1 |
+ // +------+------+------+------+------+------+------+------+
+ // [5] | unused | numSps |
+ // +------+------+------+------+------+------+------+------+
+
+ RefPtr<mozilla::MediaByteBuffer> annexB = new mozilla::MediaByteBuffer;
+
+ ByteReader reader(*aExtraData);
+ const uint8_t* ptr = reader.Read(5);
+ if (ptr && ptr[0] == 1) {
+ // Append SPS then PPS
+ ConvertSPSOrPPS(reader, reader.ReadU8() & 31, annexB);
+ ConvertSPSOrPPS(reader, reader.ReadU8(), annexB);
+
+ // MP4Box adds extra bytes that we ignore. I don't know what they do.
+ }
+
+ return annexB.forget();
+}
+
+void
+AnnexB::ConvertSPSOrPPS(ByteReader& aReader, uint8_t aCount,
+ mozilla::MediaByteBuffer* aAnnexB)
+{
+ for (int i = 0; i < aCount; i++) {
+ uint16_t length = aReader.ReadU16();
+
+ const uint8_t* ptr = aReader.Read(length);
+ if (!ptr) {
+ MOZ_ASSERT(false);
+ return;
+ }
+ aAnnexB->AppendElements(kAnnexBDelimiter, ArrayLength(kAnnexBDelimiter));
+ aAnnexB->AppendElements(ptr, length);
+ }
+}
+
+static bool
+FindStartCodeInternal(ByteReader& aBr) {
+ size_t offset = aBr.Offset();
+
+ for (uint32_t i = 0; i < aBr.Align() && aBr.Remaining() >= 3; i++) {
+ if (aBr.PeekU24() == 0x000001) {
+ return true;
+ }
+ aBr.Read(1);
+ }
+
+ while (aBr.Remaining() >= 6) {
+ uint32_t x32 = aBr.PeekU32();
+ if ((x32 - 0x01010101) & (~x32) & 0x80808080) {
+ if ((x32 >> 8) == 0x000001) {
+ return true;
+ }
+ if (x32 == 0x000001) {
+ aBr.Read(1);
+ return true;
+ }
+ if ((x32 & 0xff) == 0) {
+ const uint8_t* p = aBr.Peek(1);
+ if ((x32 & 0xff00) == 0 && p[4] == 1) {
+ aBr.Read(2);
+ return true;
+ }
+ if (p[4] == 0 && p[5] == 1) {
+ aBr.Read(3);
+ return true;
+ }
+ }
+ }
+ aBr.Read(4);
+ }
+
+ while (aBr.Remaining() >= 3) {
+ if (aBr.PeekU24() == 0x000001) {
+ return true;
+ }
+ aBr.Read(1);
+ }
+
+ // No start code were found; Go back to the beginning.
+ aBr.Seek(offset);
+ return false;
+}
+
+static bool
+FindStartCode(ByteReader& aBr, size_t& aStartSize)
+{
+ if (!FindStartCodeInternal(aBr)) {
+ aStartSize = 0;
+ return false;
+ }
+
+ aStartSize = 3;
+ if (aBr.Offset()) {
+ // Check if it's 4-bytes start code
+ aBr.Rewind(1);
+ if (aBr.ReadU8() == 0) {
+ aStartSize = 4;
+ }
+ }
+ aBr.Read(3);
+ return true;
+}
+
+static bool
+ParseNALUnits(ByteWriter& aBw, ByteReader& aBr)
+{
+ size_t startSize;
+
+ bool rv = FindStartCode(aBr, startSize);
+ if (rv) {
+ size_t startOffset = aBr.Offset();
+ while (FindStartCode(aBr, startSize)) {
+ size_t offset = aBr.Offset();
+ size_t sizeNAL = offset - startOffset - startSize;
+ aBr.Seek(startOffset);
+ if (!aBw.WriteU32(sizeNAL)
+ || !aBw.Write(aBr.Read(sizeNAL), sizeNAL)) {
+ return false;
+ }
+ aBr.Read(startSize);
+ startOffset = offset;
+ }
+ }
+ size_t sizeNAL = aBr.Remaining();
+ if (sizeNAL) {
+ if (!aBw.WriteU32(sizeNAL)
+ || !aBw.Write(aBr.Read(sizeNAL), sizeNAL)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+bool
+AnnexB::ConvertSampleToAVCC(mozilla::MediaRawData* aSample)
+{
+ if (IsAVCC(aSample)) {
+ return ConvertSampleTo4BytesAVCC(aSample);
+ }
+ if (!IsAnnexB(aSample)) {
+ // Not AnnexB, nothing to convert.
+ return true;
+ }
+
+ mozilla::Vector<uint8_t> nalu;
+ ByteWriter writer(nalu);
+ ByteReader reader(aSample->Data(), aSample->Size());
+
+ if (!ParseNALUnits(writer, reader)) {
+ return false;
+ }
+ nsAutoPtr<MediaRawDataWriter> samplewriter(aSample->CreateWriter());
+ return samplewriter->Replace(nalu.begin(), nalu.length());
+}
+
+already_AddRefed<mozilla::MediaByteBuffer>
+AnnexB::ExtractExtraData(const mozilla::MediaRawData* aSample)
+{
+ RefPtr<mozilla::MediaByteBuffer> extradata = new mozilla::MediaByteBuffer;
+ if (HasSPS(aSample->mExtraData)) {
+ // We already have an explicit extradata, re-use it.
+ extradata = aSample->mExtraData;
+ return extradata.forget();
+ }
+
+ if (IsAnnexB(aSample)) {
+ // We can't extract data from AnnexB.
+ return extradata.forget();
+ }
+
+ // SPS content
+ mozilla::Vector<uint8_t> sps;
+ ByteWriter spsw(sps);
+ int numSps = 0;
+ // PPS content
+ mozilla::Vector<uint8_t> pps;
+ ByteWriter ppsw(pps);
+ int numPps = 0;
+
+ int nalLenSize;
+ if (IsAVCC(aSample)) {
+ nalLenSize = ((*aSample->mExtraData)[4] & 3) + 1;
+ } else {
+ // We do not have an extradata, assume it's AnnexB converted to AVCC via
+ // ConvertSampleToAVCC.
+ nalLenSize = 4;
+ }
+ ByteReader reader(aSample->Data(), aSample->Size());
+
+ // Find SPS and PPS NALUs in AVCC data
+ while (reader.Remaining() > nalLenSize) {
+ uint32_t nalLen;
+ switch (nalLenSize) {
+ case 1: nalLen = reader.ReadU8(); break;
+ case 2: nalLen = reader.ReadU16(); break;
+ case 3: nalLen = reader.ReadU24(); break;
+ case 4: nalLen = reader.ReadU32(); break;
+ }
+ uint8_t nalType = reader.PeekU8() & 0x1f;
+ const uint8_t* p = reader.Read(nalLen);
+ if (!p) {
+ return extradata.forget();
+ }
+
+ if (nalType == 0x7) { /* SPS */
+ numSps++;
+ if (!spsw.WriteU16(nalLen)
+ || !spsw.Write(p, nalLen)) {
+ return extradata.forget();
+ }
+ } else if (nalType == 0x8) { /* PPS */
+ numPps++;
+ if (!ppsw.WriteU16(nalLen)
+ || !ppsw.Write(p, nalLen)) {
+ return extradata.forget();
+ }
+ }
+ }
+
+ if (numSps && sps.length() > 5) {
+ extradata->AppendElement(1); // version
+ extradata->AppendElement(sps[3]); // profile
+ extradata->AppendElement(sps[4]); // profile compat
+ extradata->AppendElement(sps[5]); // level
+ extradata->AppendElement(0xfc | 3); // nal size - 1
+ extradata->AppendElement(0xe0 | numSps);
+ extradata->AppendElements(sps.begin(), sps.length());
+ extradata->AppendElement(numPps);
+ if (numPps) {
+ extradata->AppendElements(pps.begin(), pps.length());
+ }
+ }
+
+ return extradata.forget();
+}
+
+bool
+AnnexB::HasSPS(const mozilla::MediaRawData* aSample)
+{
+ return HasSPS(aSample->mExtraData);
+}
+
+bool
+AnnexB::HasSPS(const mozilla::MediaByteBuffer* aExtraData)
+{
+ if (!aExtraData) {
+ return false;
+ }
+
+ ByteReader reader(aExtraData);
+ const uint8_t* ptr = reader.Read(5);
+ if (!ptr || !reader.CanRead8()) {
+ return false;
+ }
+ uint8_t numSps = reader.ReadU8() & 0x1f;
+
+ return numSps > 0;
+}
+
+bool
+AnnexB::ConvertSampleTo4BytesAVCC(mozilla::MediaRawData* aSample)
+{
+ MOZ_ASSERT(IsAVCC(aSample));
+
+ int nalLenSize = ((*aSample->mExtraData)[4] & 3) + 1;
+
+ if (nalLenSize == 4) {
+ return true;
+ }
+ mozilla::Vector<uint8_t> dest;
+ ByteWriter writer(dest);
+ ByteReader reader(aSample->Data(), aSample->Size());
+ while (reader.Remaining() > nalLenSize) {
+ uint32_t nalLen;
+ switch (nalLenSize) {
+ case 1: nalLen = reader.ReadU8(); break;
+ case 2: nalLen = reader.ReadU16(); break;
+ case 3: nalLen = reader.ReadU24(); break;
+ case 4: nalLen = reader.ReadU32(); break;
+ }
+ const uint8_t* p = reader.Read(nalLen);
+ if (!p) {
+ return true;
+ }
+ if (!writer.WriteU32(nalLen)
+ || !writer.Write(p, nalLen)) {
+ return false;
+ }
+ }
+ nsAutoPtr<MediaRawDataWriter> samplewriter(aSample->CreateWriter());
+ return samplewriter->Replace(dest.begin(), dest.length());
+}
+
+bool
+AnnexB::IsAVCC(const mozilla::MediaRawData* aSample)
+{
+ return aSample->Size() >= 3 && aSample->mExtraData &&
+ aSample->mExtraData->Length() >= 7 && (*aSample->mExtraData)[0] == 1;
+}
+
+bool
+AnnexB::IsAnnexB(const mozilla::MediaRawData* aSample)
+{
+ if (aSample->Size() < 4) {
+ return false;
+ }
+ uint32_t header = mozilla::BigEndian::readUint32(aSample->Data());
+ return header == 0x00000001 || (header >> 8) == 0x000001;
+}
+
+bool
+AnnexB::CompareExtraData(const mozilla::MediaByteBuffer* aExtraData1,
+ const mozilla::MediaByteBuffer* aExtraData2)
+{
+ // Very crude comparison.
+ return aExtraData1 == aExtraData2 || *aExtraData1 == *aExtraData2;
+}
+
+} // namespace mp4_demuxer
diff --git a/media/libstagefright/binding/BitReader.cpp b/media/libstagefright/binding/BitReader.cpp
new file mode 100644
index 000000000..4bf80caba
--- /dev/null
+++ b/media/libstagefright/binding/BitReader.cpp
@@ -0,0 +1,104 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mp4_demuxer/BitReader.h"
+#include <media/stagefright/foundation/ABitReader.h>
+
+using namespace mozilla;
+using namespace stagefright;
+
+namespace mp4_demuxer
+{
+
+BitReader::BitReader(const mozilla::MediaByteBuffer* aBuffer)
+ : mBitReader(new ABitReader(aBuffer->Elements(), aBuffer->Length()))
+ , mSize(aBuffer->Length()) {}
+
+BitReader::BitReader(const uint8_t* aBuffer, size_t aLength)
+ : mBitReader(new ABitReader(aBuffer, aLength))
+ , mSize(aLength) {}
+
+BitReader::~BitReader() {}
+
+uint32_t
+BitReader::ReadBits(size_t aNum)
+{
+ MOZ_ASSERT(aNum <= 32);
+ if (mBitReader->numBitsLeft() < aNum) {
+ return 0;
+ }
+ return mBitReader->getBits(aNum);
+}
+
+// Read unsigned integer Exp-Golomb-coded.
+uint32_t
+BitReader::ReadUE()
+{
+ uint32_t i = 0;
+
+ while (ReadBit() == 0 && i < 32) {
+ i++;
+ }
+ if (i == 32) {
+ // This can happen if the data is invalid, or if it's
+ // short, since ReadBit() will return 0 when it runs
+ // off the end of the buffer.
+ NS_WARNING("Invalid H.264 data");
+ return 0;
+ }
+ uint32_t r = ReadBits(i);
+ r += (1 << i) - 1;
+ return r;
+}
+
+// Read signed integer Exp-Golomb-coded.
+int32_t
+BitReader::ReadSE()
+{
+ int32_t r = ReadUE();
+ if (r & 1) {
+ return (r+1) / 2;
+ } else {
+ return -r / 2;
+ }
+}
+
+uint64_t
+BitReader::ReadU64()
+{
+ uint64_t hi = ReadU32();
+ uint32_t lo = ReadU32();
+ return (hi << 32) | lo;
+}
+
+uint64_t
+BitReader::ReadUTF8()
+{
+ int64_t val = ReadBits(8);
+ uint32_t top = (val & 0x80) >> 1;
+
+ if ((val & 0xc0) == 0x80 || val >= 0xFE) {
+ // error.
+ return -1;
+ }
+ while (val & top) {
+ int tmp = ReadBits(8) - 128;
+ if (tmp >> 6) {
+ // error.
+ return -1;
+ }
+ val = (val << 6) + tmp;
+ top <<= 5;
+ }
+ val &= (top << 1) - 1;
+ return val;
+}
+
+size_t
+BitReader::BitCount() const
+{
+ return mSize * 8 - mBitReader->numBitsLeft();
+}
+
+} // namespace mp4_demuxer
diff --git a/media/libstagefright/binding/Box.cpp b/media/libstagefright/binding/Box.cpp
new file mode 100644
index 000000000..4bef10816
--- /dev/null
+++ b/media/libstagefright/binding/Box.cpp
@@ -0,0 +1,176 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mp4_demuxer/Box.h"
+#include "mp4_demuxer/Stream.h"
+#include "mozilla/EndianUtils.h"
+#include "mozilla/Unused.h"
+#include <algorithm>
+
+using namespace mozilla;
+
+namespace mp4_demuxer {
+
+// Limit reads to 32MiB max.
+// static
+const uint64_t Box::kMAX_BOX_READ = 32 * 1024 * 1024;
+
+// Returns the offset from the start of the body of a box of type |aType|
+// to the start of its first child.
+static uint32_t
+BoxOffset(AtomType aType)
+{
+ const uint32_t FULLBOX_OFFSET = 4;
+
+ if (aType == AtomType("mp4a") || aType == AtomType("enca")) {
+ // AudioSampleEntry; ISO 14496-12, section 8.16
+ return 28;
+ } else if (aType == AtomType("mp4v") || aType == AtomType("encv")) {
+ // VideoSampleEntry; ISO 14496-12, section 8.16
+ return 78;
+ } else if (aType == AtomType("stsd")) {
+ // SampleDescriptionBox; ISO 14496-12, section 8.16
+ // This is a FullBox, and contains a |count| member before its child
+ // boxes.
+ return FULLBOX_OFFSET + 4;
+ }
+
+ return 0;
+}
+
+Box::Box(BoxContext* aContext, uint64_t aOffset, const Box* aParent)
+ : mContext(aContext), mParent(aParent)
+{
+ uint8_t header[8];
+
+ if (aOffset > INT64_MAX - sizeof(header)) {
+ return;
+ }
+
+ MediaByteRange headerRange(aOffset, aOffset + sizeof(header));
+ if (mParent && !mParent->mRange.Contains(headerRange)) {
+ return;
+ }
+
+ const MediaByteRange* byteRange;
+ for (int i = 0; ; i++) {
+ if (i == mContext->mByteRanges.Length()) {
+ return;
+ }
+
+ byteRange = static_cast<const MediaByteRange*>(&mContext->mByteRanges[i]);
+ if (byteRange->Contains(headerRange)) {
+ break;
+ }
+ }
+
+ size_t bytes;
+ if (!mContext->mSource->CachedReadAt(aOffset, header, sizeof(header),
+ &bytes) ||
+ bytes != sizeof(header)) {
+ return;
+ }
+
+ uint64_t size = BigEndian::readUint32(header);
+ if (size == 1) {
+ uint8_t bigLength[8];
+ if (aOffset > INT64_MAX - sizeof(header) - sizeof(bigLength)) {
+ return;
+ }
+ MediaByteRange bigLengthRange(headerRange.mEnd,
+ headerRange.mEnd + sizeof(bigLength));
+ if ((mParent && !mParent->mRange.Contains(bigLengthRange)) ||
+ !byteRange->Contains(bigLengthRange) ||
+ !mContext->mSource->CachedReadAt(aOffset + sizeof(header), bigLength,
+ sizeof(bigLength), &bytes) ||
+ bytes != sizeof(bigLength)) {
+ return;
+ }
+ size = BigEndian::readUint64(bigLength);
+ mBodyOffset = bigLengthRange.mEnd;
+ } else if (size == 0) {
+ // box extends to end of file.
+ size = mContext->mByteRanges.LastInterval().mEnd - aOffset;
+ mBodyOffset = headerRange.mEnd;
+ } else {
+ mBodyOffset = headerRange.mEnd;
+ }
+
+ if (size > INT64_MAX) {
+ return;
+ }
+ int64_t end = static_cast<int64_t>(aOffset) + static_cast<int64_t>(size);
+ if (end < static_cast<int64_t>(aOffset)) {
+ // Overflowed.
+ return;
+ }
+
+ mType = BigEndian::readUint32(&header[4]);
+ mChildOffset = mBodyOffset + BoxOffset(mType);
+
+ MediaByteRange boxRange(aOffset, end);
+ if (mChildOffset > boxRange.mEnd ||
+ (mParent && !mParent->mRange.Contains(boxRange)) ||
+ !byteRange->Contains(boxRange)) {
+ return;
+ }
+
+ mRange = boxRange;
+}
+
+Box::Box()
+ : mContext(nullptr)
+{}
+
+Box
+Box::Next() const
+{
+ MOZ_ASSERT(IsAvailable());
+ return Box(mContext, mRange.mEnd, mParent);
+}
+
+Box
+Box::FirstChild() const
+{
+ MOZ_ASSERT(IsAvailable());
+ if (mChildOffset == mRange.mEnd) {
+ return Box();
+ }
+ return Box(mContext, mChildOffset, this);
+}
+
+nsTArray<uint8_t>
+Box::Read()
+{
+ nsTArray<uint8_t> out;
+ Unused << Read(&out, mRange);
+ return out;
+}
+
+bool
+Box::Read(nsTArray<uint8_t>* aDest, const MediaByteRange& aRange)
+{
+ int64_t length;
+ if (!mContext->mSource->Length(&length)) {
+ // The HTTP server didn't give us a length to work with.
+ // Limit the read to kMAX_BOX_READ max.
+ length = std::min(aRange.mEnd - mChildOffset, kMAX_BOX_READ);
+ } else {
+ length = aRange.mEnd - mChildOffset;
+ }
+ aDest->SetLength(length);
+ size_t bytes;
+ if (!mContext->mSource->CachedReadAt(mChildOffset, aDest->Elements(),
+ aDest->Length(), &bytes) ||
+ bytes != aDest->Length()) {
+ // Byte ranges are being reported incorrectly
+ NS_WARNING("Read failed in mp4_demuxer::Box::Read()");
+ aDest->Clear();
+ return false;
+ }
+ return true;
+}
+}
diff --git a/media/libstagefright/binding/BufferStream.cpp b/media/libstagefright/binding/BufferStream.cpp
new file mode 100644
index 000000000..6f6a58398
--- /dev/null
+++ b/media/libstagefright/binding/BufferStream.cpp
@@ -0,0 +1,77 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mp4_demuxer/BufferStream.h"
+#include "MediaData.h"
+#include "MediaResource.h"
+#include <algorithm>
+
+using namespace mozilla;
+
+namespace mp4_demuxer {
+
+BufferStream::BufferStream()
+ : mStartOffset(0)
+ , mData(new mozilla::MediaByteBuffer)
+{
+}
+
+BufferStream::BufferStream(mozilla::MediaByteBuffer* aBuffer)
+ : mStartOffset(0)
+ , mData(aBuffer)
+{
+}
+
+BufferStream::~BufferStream()
+{
+}
+
+/*virtual*/ bool
+BufferStream::ReadAt(int64_t aOffset, void* aData, size_t aLength,
+ size_t* aBytesRead)
+{
+ if (aOffset < mStartOffset || aOffset > mStartOffset + mData->Length()) {
+ return false;
+ }
+ *aBytesRead =
+ std::min(aLength, size_t(mStartOffset + mData->Length() - aOffset));
+ memcpy(aData, mData->Elements() + aOffset - mStartOffset, *aBytesRead);
+ return true;
+}
+
+/*virtual*/ bool
+BufferStream::CachedReadAt(int64_t aOffset, void* aData, size_t aLength,
+ size_t* aBytesRead)
+{
+ return ReadAt(aOffset, aData, aLength, aBytesRead);
+}
+
+/*virtual*/ bool
+BufferStream::Length(int64_t* aLength)
+{
+ *aLength = mStartOffset + mData->Length();
+ return true;
+}
+
+/* virtual */ void
+BufferStream::DiscardBefore(int64_t aOffset)
+{
+ if (aOffset > mStartOffset) {
+ mData->RemoveElementsAt(0, aOffset - mStartOffset);
+ mStartOffset = aOffset;
+ }
+}
+
+bool
+BufferStream::AppendBytes(const uint8_t* aData, size_t aLength)
+{
+ return mData->AppendElements(aData, aLength, fallible);
+}
+
+MediaByteRange
+BufferStream::GetByteRange()
+{
+ return MediaByteRange(mStartOffset, mStartOffset + mData->Length());
+}
+}
diff --git a/media/libstagefright/binding/DecoderData.cpp b/media/libstagefright/binding/DecoderData.cpp
new file mode 100644
index 000000000..aaf2fb32c
--- /dev/null
+++ b/media/libstagefright/binding/DecoderData.cpp
@@ -0,0 +1,217 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mp4_demuxer/Adts.h"
+#include "mp4_demuxer/AnnexB.h"
+#include "mp4_demuxer/ByteReader.h"
+#include "mp4_demuxer/DecoderData.h"
+#include <media/stagefright/foundation/ABitReader.h>
+#include "media/stagefright/MetaData.h"
+#include "media/stagefright/MediaDefs.h"
+#include "media/stagefright/Utils.h"
+#include "mozilla/ArrayUtils.h"
+#include "include/ESDS.h"
+
+#ifdef MOZ_RUST_MP4PARSE
+#include "mp4parse.h"
+#endif
+
+using namespace stagefright;
+
+namespace mp4_demuxer
+{
+
+static int32_t
+FindInt32(const MetaData* mMetaData, uint32_t mKey)
+{
+ int32_t value;
+ if (!mMetaData->findInt32(mKey, &value))
+ return 0;
+ return value;
+}
+
+static int64_t
+FindInt64(const MetaData* mMetaData, uint32_t mKey)
+{
+ int64_t value;
+ if (!mMetaData->findInt64(mKey, &value))
+ return 0;
+ return value;
+}
+
+template <typename T, size_t N>
+static bool
+FindData(const MetaData* aMetaData, uint32_t aKey, mozilla::Vector<T, N>* aDest)
+{
+ const void* data;
+ size_t size;
+ uint32_t type;
+
+ aDest->clear();
+ // There's no point in checking that the type matches anything because it
+ // isn't set consistently in the MPEG4Extractor.
+ if (!aMetaData->findData(aKey, &type, &data, &size) || size % sizeof(T)) {
+ return false;
+ }
+
+ aDest->append(reinterpret_cast<const T*>(data), size / sizeof(T));
+ return true;
+}
+
+template <typename T>
+static bool
+FindData(const MetaData* aMetaData, uint32_t aKey, nsTArray<T>* aDest)
+{
+ const void* data;
+ size_t size;
+ uint32_t type;
+
+ aDest->Clear();
+ // There's no point in checking that the type matches anything because it
+ // isn't set consistently in the MPEG4Extractor.
+ if (!aMetaData->findData(aKey, &type, &data, &size) || size % sizeof(T)) {
+ return false;
+ }
+
+ aDest->AppendElements(reinterpret_cast<const T*>(data), size / sizeof(T));
+ return true;
+}
+
+static bool
+FindData(const MetaData* aMetaData, uint32_t aKey, mozilla::MediaByteBuffer* aDest)
+{
+ return FindData(aMetaData, aKey, static_cast<nsTArray<uint8_t>*>(aDest));
+}
+
+bool
+CryptoFile::DoUpdate(const uint8_t* aData, size_t aLength)
+{
+ ByteReader reader(aData, aLength);
+ while (reader.Remaining()) {
+ PsshInfo psshInfo;
+ if (!reader.ReadArray(psshInfo.uuid, 16)) {
+ return false;
+ }
+
+ if (!reader.CanReadType<uint32_t>()) {
+ return false;
+ }
+ auto length = reader.ReadType<uint32_t>();
+
+ if (!reader.ReadArray(psshInfo.data, length)) {
+ return false;
+ }
+ pssh.AppendElement(psshInfo);
+ }
+ return true;
+}
+
+static void
+UpdateTrackInfo(mozilla::TrackInfo& aConfig,
+ const MetaData* aMetaData,
+ const char* aMimeType)
+{
+ mozilla::CryptoTrack& crypto = aConfig.mCrypto;
+ aConfig.mMimeType = aMimeType;
+ aConfig.mDuration = FindInt64(aMetaData, kKeyDuration);
+ aConfig.mMediaTime = FindInt64(aMetaData, kKeyMediaTime);
+ aConfig.mTrackId = FindInt32(aMetaData, kKeyTrackID);
+ aConfig.mCrypto.mValid = aMetaData->findInt32(kKeyCryptoMode, &crypto.mMode) &&
+ aMetaData->findInt32(kKeyCryptoDefaultIVSize, &crypto.mIVSize) &&
+ FindData(aMetaData, kKeyCryptoKey, &crypto.mKeyId);
+}
+
+void
+MP4AudioInfo::Update(const MetaData* aMetaData,
+ const char* aMimeType)
+{
+ UpdateTrackInfo(*this, aMetaData, aMimeType);
+ mChannels = FindInt32(aMetaData, kKeyChannelCount);
+ mBitDepth = FindInt32(aMetaData, kKeySampleSize);
+ mRate = FindInt32(aMetaData, kKeySampleRate);
+ mProfile = FindInt32(aMetaData, kKeyAACProfile);
+
+ if (FindData(aMetaData, kKeyESDS, mExtraData)) {
+ ESDS esds(mExtraData->Elements(), mExtraData->Length());
+
+ const void* data;
+ size_t size;
+ if (esds.getCodecSpecificInfo(&data, &size) == OK) {
+ const uint8_t* cdata = reinterpret_cast<const uint8_t*>(data);
+ mCodecSpecificConfig->AppendElements(cdata, size);
+ if (size > 1) {
+ ABitReader br(cdata, size);
+ mExtendedProfile = br.getBits(5);
+
+ if (mExtendedProfile == 31) { // AAC-ELD => additional 6 bits
+ mExtendedProfile = 32 + br.getBits(6);
+ }
+ }
+ }
+ }
+}
+
+bool
+MP4AudioInfo::IsValid() const
+{
+ return mChannels > 0 && mRate > 0 &&
+ // Accept any mime type here, but if it's aac, validate the profile.
+ (!mMimeType.Equals(MEDIA_MIMETYPE_AUDIO_AAC) ||
+ mProfile > 0 || mExtendedProfile > 0);
+}
+
+void
+MP4VideoInfo::Update(const MetaData* aMetaData, const char* aMimeType)
+{
+ UpdateTrackInfo(*this, aMetaData, aMimeType);
+ mDisplay.width = FindInt32(aMetaData, kKeyDisplayWidth);
+ mDisplay.height = FindInt32(aMetaData, kKeyDisplayHeight);
+ mImage.width = FindInt32(aMetaData, kKeyWidth);
+ mImage.height = FindInt32(aMetaData, kKeyHeight);
+ mRotation = VideoInfo::ToSupportedRotation(FindInt32(aMetaData, kKeyRotation));
+
+ FindData(aMetaData, kKeyAVCC, mExtraData);
+ if (!mExtraData->Length()) {
+ if (FindData(aMetaData, kKeyESDS, mExtraData)) {
+ ESDS esds(mExtraData->Elements(), mExtraData->Length());
+
+ const void* data;
+ size_t size;
+ if (esds.getCodecSpecificInfo(&data, &size) == OK) {
+ const uint8_t* cdata = reinterpret_cast<const uint8_t*>(data);
+ mCodecSpecificConfig->AppendElements(cdata, size);
+ }
+ }
+ }
+
+}
+
+#ifdef MOZ_RUST_MP4PARSE
+void
+MP4VideoInfo::Update(const mp4parse_track_info* track,
+ const mp4parse_track_video_info* video)
+{
+ if (track->codec == MP4PARSE_CODEC_AVC) {
+ mMimeType = MEDIA_MIMETYPE_VIDEO_AVC;
+ } else if (track->codec == MP4PARSE_CODEC_VP9) {
+ mMimeType = NS_LITERAL_CSTRING("video/vp9");
+ }
+ mTrackId = track->track_id;
+ mDuration = track->duration;
+ mMediaTime = track->media_time;
+ mDisplay.width = video->display_width;
+ mDisplay.height = video->display_height;
+ mImage.width = video->image_width;
+ mImage.height = video->image_height;
+}
+#endif
+
+bool
+MP4VideoInfo::IsValid() const
+{
+ return (mDisplay.width > 0 && mDisplay.height > 0) ||
+ (mImage.width > 0 && mImage.height > 0);
+}
+
+}
diff --git a/media/libstagefright/binding/H264.cpp b/media/libstagefright/binding/H264.cpp
new file mode 100644
index 000000000..a34ae2d3a
--- /dev/null
+++ b/media/libstagefright/binding/H264.cpp
@@ -0,0 +1,528 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/ArrayUtils.h"
+#include "mozilla/PodOperations.h"
+#include "mp4_demuxer/AnnexB.h"
+#include "mp4_demuxer/BitReader.h"
+#include "mp4_demuxer/ByteReader.h"
+#include "mp4_demuxer/ByteWriter.h"
+#include "mp4_demuxer/H264.h"
+#include <media/stagefright/foundation/ABitReader.h>
+#include <limits>
+
+using namespace mozilla;
+
+namespace mp4_demuxer
+{
+
+SPSData::SPSData()
+{
+ PodZero(this);
+ // Default values when they aren't defined as per ITU-T H.264 (2014/02).
+ chroma_format_idc = 1;
+ video_format = 5;
+ colour_primaries = 2;
+ transfer_characteristics = 2;
+ sample_ratio = 1.0;
+}
+
+/* static */ already_AddRefed<mozilla::MediaByteBuffer>
+H264::DecodeNALUnit(const mozilla::MediaByteBuffer* aNAL)
+{
+ MOZ_ASSERT(aNAL);
+
+ if (aNAL->Length() < 4) {
+ return nullptr;
+ }
+
+ RefPtr<mozilla::MediaByteBuffer> rbsp = new mozilla::MediaByteBuffer;
+ ByteReader reader(aNAL);
+ uint8_t nal_unit_type = reader.ReadU8() & 0x1f;
+ uint32_t nalUnitHeaderBytes = 1;
+ if (nal_unit_type == 14 || nal_unit_type == 20 || nal_unit_type == 21) {
+ bool svc_extension_flag = false;
+ bool avc_3d_extension_flag = false;
+ if (nal_unit_type != 21) {
+ svc_extension_flag = reader.PeekU8() & 0x80;
+ } else {
+ avc_3d_extension_flag = reader.PeekU8() & 0x80;
+ }
+ if (svc_extension_flag) {
+ nalUnitHeaderBytes += 3;
+ } else if (avc_3d_extension_flag) {
+ nalUnitHeaderBytes += 2;
+ } else {
+ nalUnitHeaderBytes += 3;
+ }
+ }
+ if (!reader.Read(nalUnitHeaderBytes - 1)) {
+ return nullptr;
+ }
+ uint32_t lastbytes = 0xffff;
+ while (reader.Remaining()) {
+ uint8_t byte = reader.ReadU8();
+ if ((lastbytes & 0xffff) == 0 && byte == 0x03) {
+ // reset last two bytes, to detect the 0x000003 sequence again.
+ lastbytes = 0xffff;
+ } else {
+ rbsp->AppendElement(byte);
+ }
+ lastbytes = (lastbytes << 8) | byte;
+ }
+ return rbsp.forget();
+}
+
+static int32_t
+ConditionDimension(float aValue)
+{
+ // This will exclude NaNs and too-big values.
+ if (aValue > 1.0 && aValue <= INT32_MAX)
+ return int32_t(aValue);
+ return 0;
+}
+
+/* static */ bool
+H264::DecodeSPS(const mozilla::MediaByteBuffer* aSPS, SPSData& aDest)
+{
+ if (!aSPS) {
+ return false;
+ }
+ BitReader br(aSPS);
+
+ int32_t lastScale;
+ int32_t nextScale;
+ int32_t deltaScale;
+
+ aDest.profile_idc = br.ReadBits(8);
+ aDest.constraint_set0_flag = br.ReadBit();
+ aDest.constraint_set1_flag = br.ReadBit();
+ aDest.constraint_set2_flag = br.ReadBit();
+ aDest.constraint_set3_flag = br.ReadBit();
+ aDest.constraint_set4_flag = br.ReadBit();
+ aDest.constraint_set5_flag = br.ReadBit();
+ br.ReadBits(2); // reserved_zero_2bits
+ aDest.level_idc = br.ReadBits(8);
+ aDest.seq_parameter_set_id = br.ReadUE();
+ if (aDest.profile_idc == 100 || aDest.profile_idc == 110 ||
+ aDest.profile_idc == 122 || aDest.profile_idc == 244 ||
+ aDest.profile_idc == 44 || aDest.profile_idc == 83 ||
+ aDest.profile_idc == 86 || aDest.profile_idc == 118 ||
+ aDest.profile_idc == 128 || aDest.profile_idc == 138 ||
+ aDest.profile_idc == 139 || aDest.profile_idc == 134) {
+ if ((aDest.chroma_format_idc = br.ReadUE()) == 3) {
+ aDest.separate_colour_plane_flag = br.ReadBit();
+ }
+ br.ReadUE(); // bit_depth_luma_minus8
+ br.ReadUE(); // bit_depth_chroma_minus8
+ br.ReadBit(); // qpprime_y_zero_transform_bypass_flag
+ if (br.ReadBit()) { // seq_scaling_matrix_present_flag
+ for (int idx = 0; idx < ((aDest.chroma_format_idc != 3) ? 8 : 12); ++idx) {
+ if (br.ReadBit()) { // Scaling list present
+ lastScale = nextScale = 8;
+ int sl_n = (idx < 6) ? 16 : 64;
+ for (int sl_i = 0; sl_i < sl_n; sl_i++) {
+ if (nextScale) {
+ deltaScale = br.ReadSE();
+ nextScale = (lastScale + deltaScale + 256) % 256;
+ }
+ lastScale = (nextScale == 0) ? lastScale : nextScale;
+ }
+ }
+ }
+ }
+ } else if (aDest.profile_idc == 183) {
+ aDest.chroma_format_idc = 0;
+ } else {
+ // default value if chroma_format_idc isn't set.
+ aDest.chroma_format_idc = 1;
+ }
+ aDest.log2_max_frame_num = br.ReadUE() + 4;
+ aDest.pic_order_cnt_type = br.ReadUE();
+ if (aDest.pic_order_cnt_type == 0) {
+ aDest.log2_max_pic_order_cnt_lsb = br.ReadUE() + 4;
+ } else if (aDest.pic_order_cnt_type == 1) {
+ aDest.delta_pic_order_always_zero_flag = br.ReadBit();
+ aDest.offset_for_non_ref_pic = br.ReadSE();
+ aDest.offset_for_top_to_bottom_field = br.ReadSE();
+ uint32_t num_ref_frames_in_pic_order_cnt_cycle = br.ReadUE();
+ for (uint32_t i = 0; i < num_ref_frames_in_pic_order_cnt_cycle; i++) {
+ br.ReadSE(); // offset_for_ref_frame[i]
+ }
+ }
+ aDest.max_num_ref_frames = br.ReadUE();
+ aDest.gaps_in_frame_num_allowed_flag = br.ReadBit();
+ aDest.pic_width_in_mbs = br.ReadUE() + 1;
+ aDest.pic_height_in_map_units = br.ReadUE() + 1;
+ aDest.frame_mbs_only_flag = br.ReadBit();
+ if (!aDest.frame_mbs_only_flag) {
+ aDest.pic_height_in_map_units *= 2;
+ aDest.mb_adaptive_frame_field_flag = br.ReadBit();
+ }
+ br.ReadBit(); // direct_8x8_inference_flag
+ aDest.frame_cropping_flag = br.ReadBit();
+ if (aDest.frame_cropping_flag) {
+ aDest.frame_crop_left_offset = br.ReadUE();
+ aDest.frame_crop_right_offset = br.ReadUE();
+ aDest.frame_crop_top_offset = br.ReadUE();
+ aDest.frame_crop_bottom_offset = br.ReadUE();
+ }
+
+ aDest.sample_ratio = 1.0f;
+ aDest.vui_parameters_present_flag = br.ReadBit();
+ if (aDest.vui_parameters_present_flag) {
+ vui_parameters(br, aDest);
+ }
+
+ // Calculate common values.
+
+ uint8_t ChromaArrayType =
+ aDest.separate_colour_plane_flag ? 0 : aDest.chroma_format_idc;
+ // Calculate width.
+ uint32_t CropUnitX = 1;
+ uint32_t SubWidthC = aDest.chroma_format_idc == 3 ? 1 : 2;
+ if (ChromaArrayType != 0) {
+ CropUnitX = SubWidthC;
+ }
+
+ // Calculate Height
+ uint32_t CropUnitY = 2 - aDest.frame_mbs_only_flag;
+ uint32_t SubHeightC = aDest.chroma_format_idc <= 1 ? 2 : 1;
+ if (ChromaArrayType != 0) {
+ CropUnitY *= SubHeightC;
+ }
+
+ uint32_t width = aDest.pic_width_in_mbs * 16;
+ uint32_t height = aDest.pic_height_in_map_units * 16;
+ if (aDest.frame_crop_left_offset <= std::numeric_limits<int32_t>::max() / 4 / CropUnitX &&
+ aDest.frame_crop_right_offset <= std::numeric_limits<int32_t>::max() / 4 / CropUnitX &&
+ aDest.frame_crop_top_offset <= std::numeric_limits<int32_t>::max() / 4 / CropUnitY &&
+ aDest.frame_crop_bottom_offset <= std::numeric_limits<int32_t>::max() / 4 / CropUnitY &&
+ (aDest.frame_crop_left_offset + aDest.frame_crop_right_offset) * CropUnitX < width &&
+ (aDest.frame_crop_top_offset + aDest.frame_crop_bottom_offset) * CropUnitY < height) {
+ aDest.crop_left = aDest.frame_crop_left_offset * CropUnitX;
+ aDest.crop_right = aDest.frame_crop_right_offset * CropUnitX;
+ aDest.crop_top = aDest.frame_crop_top_offset * CropUnitY;
+ aDest.crop_bottom = aDest.frame_crop_bottom_offset * CropUnitY;
+ } else {
+ // Nonsensical value, ignore them.
+ aDest.crop_left = aDest.crop_right = aDest.crop_top = aDest.crop_bottom = 0;
+ }
+
+ aDest.pic_width = width - aDest.crop_left - aDest.crop_right;
+ aDest.pic_height = height - aDest.crop_top - aDest.crop_bottom;
+
+ aDest.interlaced = !aDest.frame_mbs_only_flag;
+
+ // Determine display size.
+ if (aDest.sample_ratio > 1.0) {
+ // Increase the intrinsic width
+ aDest.display_width =
+ ConditionDimension(aDest.pic_width * aDest.sample_ratio);
+ aDest.display_height = aDest.pic_height;
+ } else {
+ // Increase the intrinsic height
+ aDest.display_width = aDest.pic_width;
+ aDest.display_height =
+ ConditionDimension(aDest.pic_height / aDest.sample_ratio);
+ }
+
+ return true;
+}
+
+/* static */ void
+H264::vui_parameters(BitReader& aBr, SPSData& aDest)
+{
+ aDest.aspect_ratio_info_present_flag = aBr.ReadBit();
+ if (aDest.aspect_ratio_info_present_flag) {
+ aDest.aspect_ratio_idc = aBr.ReadBits(8);
+ aDest.sar_width = aDest.sar_height = 0;
+
+ // From E.2.1 VUI parameters semantics (ITU-T H.264 02/2014)
+ switch (aDest.aspect_ratio_idc) {
+ case 0:
+ // Unspecified
+ break;
+ case 1:
+ /*
+ 1:1
+ 7680x4320 16:9 frame without horizontal overscan
+ 3840x2160 16:9 frame without horizontal overscan
+ 1280x720 16:9 frame without horizontal overscan
+ 1920x1080 16:9 frame without horizontal overscan (cropped from 1920x1088)
+ 640x480 4:3 frame without horizontal overscan
+ */
+ aDest.sample_ratio = 1.0f;
+ break;
+ case 2:
+ /*
+ 12:11
+ 720x576 4:3 frame with horizontal overscan
+ 352x288 4:3 frame without horizontal overscan
+ */
+ aDest.sample_ratio = 12.0 / 11.0;
+ break;
+ case 3:
+ /*
+ 10:11
+ 720x480 4:3 frame with horizontal overscan
+ 352x240 4:3 frame without horizontal overscan
+ */
+ aDest.sample_ratio = 10.0 / 11.0;
+ break;
+ case 4:
+ /*
+ 16:11
+ 720x576 16:9 frame with horizontal overscan
+ 528x576 4:3 frame without horizontal overscan
+ */
+ aDest.sample_ratio = 16.0 / 11.0;
+ break;
+ case 5:
+ /*
+ 40:33
+ 720x480 16:9 frame with horizontal overscan
+ 528x480 4:3 frame without horizontal overscan
+ */
+ aDest.sample_ratio = 40.0 / 33.0;
+ break;
+ case 6:
+ /*
+ 24:11
+ 352x576 4:3 frame without horizontal overscan
+ 480x576 16:9 frame with horizontal overscan
+ */
+ aDest.sample_ratio = 24.0 / 11.0;
+ break;
+ case 7:
+ /*
+ 20:11
+ 352x480 4:3 frame without horizontal overscan
+ 480x480 16:9 frame with horizontal overscan
+ */
+ aDest.sample_ratio = 20.0 / 11.0;
+ break;
+ case 8:
+ /*
+ 32:11
+ 352x576 16:9 frame without horizontal overscan
+ */
+ aDest.sample_ratio = 32.0 / 11.0;
+ break;
+ case 9:
+ /*
+ 80:33
+ 352x480 16:9 frame without horizontal overscan
+ */
+ aDest.sample_ratio = 80.0 / 33.0;
+ break;
+ case 10:
+ /*
+ 18:11
+ 480x576 4:3 frame with horizontal overscan
+ */
+ aDest.sample_ratio = 18.0 / 11.0;
+ break;
+ case 11:
+ /*
+ 15:11
+ 480x480 4:3 frame with horizontal overscan
+ */
+ aDest.sample_ratio = 15.0 / 11.0;
+ break;
+ case 12:
+ /*
+ 64:33
+ 528x576 16:9 frame with horizontal overscan
+ */
+ aDest.sample_ratio = 64.0 / 33.0;
+ break;
+ case 13:
+ /*
+ 160:99
+ 528x480 16:9 frame without horizontal overscan
+ */
+ aDest.sample_ratio = 160.0 / 99.0;
+ break;
+ case 14:
+ /*
+ 4:3
+ 1440x1080 16:9 frame without horizontal overscan
+ */
+ aDest.sample_ratio = 4.0 / 3.0;
+ break;
+ case 15:
+ /*
+ 3:2
+ 1280x1080 16:9 frame without horizontal overscan
+ */
+ aDest.sample_ratio = 3.2 / 2.0;
+ break;
+ case 16:
+ /*
+ 2:1
+ 960x1080 16:9 frame without horizontal overscan
+ */
+ aDest.sample_ratio = 2.0 / 1.0;
+ break;
+ case 255:
+ /* Extended_SAR */
+ aDest.sar_width = aBr.ReadBits(16);
+ aDest.sar_height = aBr.ReadBits(16);
+ if (aDest.sar_width && aDest.sar_height) {
+ aDest.sample_ratio = float(aDest.sar_width) / float(aDest.sar_height);
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (aBr.ReadBit()) { //overscan_info_present_flag
+ aDest.overscan_appropriate_flag = aBr.ReadBit();
+ }
+
+ if (aBr.ReadBit()) { // video_signal_type_present_flag
+ aDest.video_format = aBr.ReadBits(3);
+ aDest.video_full_range_flag = aBr.ReadBit();
+ aDest.colour_description_present_flag = aBr.ReadBit();
+ if (aDest.colour_description_present_flag) {
+ aDest.colour_primaries = aBr.ReadBits(8);
+ aDest.transfer_characteristics = aBr.ReadBits(8);
+ aDest.matrix_coefficients = aBr.ReadBits(8);
+ }
+ }
+
+ aDest.chroma_loc_info_present_flag = aBr.ReadBit();
+ if (aDest.chroma_loc_info_present_flag) {
+ aDest.chroma_sample_loc_type_top_field = aBr.ReadUE();
+ aDest.chroma_sample_loc_type_bottom_field = aBr.ReadUE();
+ }
+
+ aDest.timing_info_present_flag = aBr.ReadBit();
+ if (aDest.timing_info_present_flag ) {
+ aDest.num_units_in_tick = aBr.ReadBits(32);
+ aDest.time_scale = aBr.ReadBits(32);
+ aDest.fixed_frame_rate_flag = aBr.ReadBit();
+ }
+}
+
+/* static */ bool
+H264::DecodeSPSFromExtraData(const mozilla::MediaByteBuffer* aExtraData, SPSData& aDest)
+{
+ if (!AnnexB::HasSPS(aExtraData)) {
+ return false;
+ }
+ ByteReader reader(aExtraData);
+
+ if (!reader.Read(5)) {
+ return false;
+ }
+
+ if (!(reader.ReadU8() & 0x1f)) {
+ // No SPS.
+ return false;
+ }
+ uint16_t length = reader.ReadU16();
+
+ if ((reader.PeekU8() & 0x1f) != 7) {
+ // Not a SPS NAL type.
+ return false;
+ }
+
+ const uint8_t* ptr = reader.Read(length);
+ if (!ptr) {
+ return false;
+ }
+
+ RefPtr<mozilla::MediaByteBuffer> rawNAL = new mozilla::MediaByteBuffer;
+ rawNAL->AppendElements(ptr, length);
+
+ RefPtr<mozilla::MediaByteBuffer> sps = DecodeNALUnit(rawNAL);
+
+ if (!sps) {
+ return false;
+ }
+
+ return DecodeSPS(sps, aDest);
+}
+
+/* static */ bool
+H264::EnsureSPSIsSane(SPSData& aSPS)
+{
+ bool valid = true;
+ static const float default_aspect = 4.0f / 3.0f;
+ if (aSPS.sample_ratio <= 0.0f || aSPS.sample_ratio > 6.0f) {
+ if (aSPS.pic_width && aSPS.pic_height) {
+ aSPS.sample_ratio =
+ (float) aSPS.pic_width / (float) aSPS.pic_height;
+ } else {
+ aSPS.sample_ratio = default_aspect;
+ }
+ aSPS.display_width = aSPS.pic_width;
+ aSPS.display_height = aSPS.pic_height;
+ valid = false;
+ }
+ if (aSPS.max_num_ref_frames > 16) {
+ aSPS.max_num_ref_frames = 16;
+ valid = false;
+ }
+ return valid;
+}
+
+/* static */ uint32_t
+H264::ComputeMaxRefFrames(const mozilla::MediaByteBuffer* aExtraData)
+{
+ uint32_t maxRefFrames = 4;
+ // Retrieve video dimensions from H264 SPS NAL.
+ SPSData spsdata;
+ if (DecodeSPSFromExtraData(aExtraData, spsdata)) {
+ // max_num_ref_frames determines the size of the sliding window
+ // we need to queue that many frames in order to guarantee proper
+ // pts frames ordering. Use a minimum of 4 to ensure proper playback of
+ // non compliant videos.
+ maxRefFrames =
+ std::min(std::max(maxRefFrames, spsdata.max_num_ref_frames + 1), 16u);
+ }
+ return maxRefFrames;
+}
+
+/* static */ H264::FrameType
+H264::GetFrameType(const mozilla::MediaRawData* aSample)
+{
+ if (!AnnexB::IsAVCC(aSample)) {
+ // We must have a valid AVCC frame with extradata.
+ return FrameType::INVALID;
+ }
+ MOZ_ASSERT(aSample->Data());
+
+ int nalLenSize = ((*aSample->mExtraData)[4] & 3) + 1;
+
+ ByteReader reader(aSample->Data(), aSample->Size());
+
+ while (reader.Remaining() >= nalLenSize) {
+ uint32_t nalLen;
+ switch (nalLenSize) {
+ case 1: nalLen = reader.ReadU8(); break;
+ case 2: nalLen = reader.ReadU16(); break;
+ case 3: nalLen = reader.ReadU24(); break;
+ case 4: nalLen = reader.ReadU32(); break;
+ }
+ if (!nalLen) {
+ continue;
+ }
+ const uint8_t* p = reader.Read(nalLen);
+ if (!p) {
+ return FrameType::INVALID;
+ }
+ if ((p[0] & 0x1f) == 5) {
+ // IDR NAL.
+ return FrameType::I_FRAME;
+ }
+ }
+
+ return FrameType::OTHER;
+}
+
+} // namespace mp4_demuxer
diff --git a/media/libstagefright/binding/Index.cpp b/media/libstagefright/binding/Index.cpp
new file mode 100644
index 000000000..9d6ab5028
--- /dev/null
+++ b/media/libstagefright/binding/Index.cpp
@@ -0,0 +1,533 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mp4_demuxer/ByteReader.h"
+#include "mp4_demuxer/Index.h"
+#include "mp4_demuxer/Interval.h"
+#include "mp4_demuxer/SinfParser.h"
+#include "nsAutoPtr.h"
+#include "mozilla/RefPtr.h"
+
+#include <algorithm>
+#include <limits>
+
+using namespace stagefright;
+using namespace mozilla;
+using namespace mozilla::media;
+
+namespace mp4_demuxer
+{
+
+class MOZ_STACK_CLASS RangeFinder
+{
+public:
+ // Given that we're processing this in order we don't use a binary search
+ // to find the apropriate time range. Instead we search linearly from the
+ // last used point.
+ explicit RangeFinder(const MediaByteRangeSet& ranges)
+ : mRanges(ranges), mIndex(0)
+ {
+ // Ranges must be normalised for this to work
+ }
+
+ bool Contains(MediaByteRange aByteRange);
+
+private:
+ const MediaByteRangeSet& mRanges;
+ size_t mIndex;
+};
+
+bool
+RangeFinder::Contains(MediaByteRange aByteRange)
+{
+ if (!mRanges.Length()) {
+ return false;
+ }
+
+ if (mRanges[mIndex].ContainsStrict(aByteRange)) {
+ return true;
+ }
+
+ if (aByteRange.mStart < mRanges[mIndex].mStart) {
+ // Search backwards
+ do {
+ if (!mIndex) {
+ return false;
+ }
+ --mIndex;
+ if (mRanges[mIndex].ContainsStrict(aByteRange)) {
+ return true;
+ }
+ } while (aByteRange.mStart < mRanges[mIndex].mStart);
+
+ return false;
+ }
+
+ while (aByteRange.mEnd > mRanges[mIndex].mEnd) {
+ if (mIndex == mRanges.Length() - 1) {
+ return false;
+ }
+ ++mIndex;
+ if (mRanges[mIndex].ContainsStrict(aByteRange)) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+SampleIterator::SampleIterator(Index* aIndex)
+ : mIndex(aIndex)
+ , mCurrentMoof(0)
+ , mCurrentSample(0)
+{
+ mIndex->RegisterIterator(this);
+}
+
+SampleIterator::~SampleIterator()
+{
+ mIndex->UnregisterIterator(this);
+}
+
+already_AddRefed<MediaRawData> SampleIterator::GetNext()
+{
+ Sample* s(Get());
+ if (!s) {
+ return nullptr;
+ }
+
+ int64_t length = std::numeric_limits<int64_t>::max();
+ mIndex->mSource->Length(&length);
+ if (s->mByteRange.mEnd > length) {
+ // We don't have this complete sample.
+ return nullptr;
+ }
+
+ RefPtr<MediaRawData> sample = new MediaRawData();
+ sample->mTimecode= s->mDecodeTime;
+ sample->mTime = s->mCompositionRange.start;
+ sample->mDuration = s->mCompositionRange.Length();
+ sample->mOffset = s->mByteRange.mStart;
+ sample->mKeyframe = s->mSync;
+
+ nsAutoPtr<MediaRawDataWriter> writer(sample->CreateWriter());
+ // Do the blocking read
+ if (!writer->SetSize(s->mByteRange.Length())) {
+ return nullptr;
+ }
+
+ size_t bytesRead;
+ if (!mIndex->mSource->ReadAt(sample->mOffset, writer->Data(), sample->Size(),
+ &bytesRead) || bytesRead != sample->Size()) {
+ return nullptr;
+ }
+
+ if (!s->mCencRange.IsEmpty()) {
+ MoofParser* parser = mIndex->mMoofParser.get();
+
+ if (!parser || !parser->mSinf.IsValid()) {
+ return nullptr;
+ }
+
+ uint8_t ivSize = parser->mSinf.mDefaultIVSize;
+
+ // The size comes from an 8 bit field
+ AutoTArray<uint8_t, 256> cenc;
+ cenc.SetLength(s->mCencRange.Length());
+ if (!mIndex->mSource->ReadAt(s->mCencRange.mStart, cenc.Elements(), cenc.Length(),
+ &bytesRead) || bytesRead != cenc.Length()) {
+ return nullptr;
+ }
+ ByteReader reader(cenc);
+ writer->mCrypto.mValid = true;
+ writer->mCrypto.mIVSize = ivSize;
+
+ if (!reader.ReadArray(writer->mCrypto.mIV, ivSize)) {
+ return nullptr;
+ }
+
+ if (reader.CanRead16()) {
+ uint16_t count = reader.ReadU16();
+
+ if (reader.Remaining() < count * 6) {
+ return nullptr;
+ }
+
+ for (size_t i = 0; i < count; i++) {
+ writer->mCrypto.mPlainSizes.AppendElement(reader.ReadU16());
+ writer->mCrypto.mEncryptedSizes.AppendElement(reader.ReadU32());
+ }
+ } else {
+ // No subsample information means the entire sample is encrypted.
+ writer->mCrypto.mPlainSizes.AppendElement(0);
+ writer->mCrypto.mEncryptedSizes.AppendElement(sample->Size());
+ }
+ }
+
+ Next();
+
+ return sample.forget();
+}
+
+Sample* SampleIterator::Get()
+{
+ if (!mIndex->mMoofParser) {
+ MOZ_ASSERT(!mCurrentMoof);
+ return mCurrentSample < mIndex->mIndex.Length()
+ ? &mIndex->mIndex[mCurrentSample]
+ : nullptr;
+ }
+
+ nsTArray<Moof>& moofs = mIndex->mMoofParser->Moofs();
+ while (true) {
+ if (mCurrentMoof == moofs.Length()) {
+ if (!mIndex->mMoofParser->BlockingReadNextMoof()) {
+ return nullptr;
+ }
+ MOZ_ASSERT(mCurrentMoof < moofs.Length());
+ }
+ if (mCurrentSample < moofs[mCurrentMoof].mIndex.Length()) {
+ break;
+ }
+ mCurrentSample = 0;
+ ++mCurrentMoof;
+ }
+ return &moofs[mCurrentMoof].mIndex[mCurrentSample];
+}
+
+void SampleIterator::Next()
+{
+ ++mCurrentSample;
+}
+
+void SampleIterator::Seek(Microseconds aTime)
+{
+ size_t syncMoof = 0;
+ size_t syncSample = 0;
+ mCurrentMoof = 0;
+ mCurrentSample = 0;
+ Sample* sample;
+ while (!!(sample = Get())) {
+ if (sample->mCompositionRange.start > aTime) {
+ break;
+ }
+ if (sample->mSync) {
+ syncMoof = mCurrentMoof;
+ syncSample = mCurrentSample;
+ }
+ if (sample->mCompositionRange.start == aTime) {
+ break;
+ }
+ Next();
+ }
+ mCurrentMoof = syncMoof;
+ mCurrentSample = syncSample;
+}
+
+Microseconds
+SampleIterator::GetNextKeyframeTime()
+{
+ SampleIterator itr(*this);
+ Sample* sample;
+ while (!!(sample = itr.Get())) {
+ if (sample->mSync) {
+ return sample->mCompositionRange.start;
+ }
+ itr.Next();
+ }
+ return -1;
+}
+
+Index::Index(const nsTArray<Indice>& aIndex,
+ Stream* aSource,
+ uint32_t aTrackId,
+ bool aIsAudio)
+ : mSource(aSource)
+ , mIsAudio(aIsAudio)
+{
+ if (aIndex.IsEmpty()) {
+ mMoofParser = new MoofParser(aSource, aTrackId, aIsAudio);
+ } else {
+ if (!mIndex.SetCapacity(aIndex.Length(), fallible)) {
+ // OOM.
+ return;
+ }
+ media::IntervalSet<int64_t> intervalTime;
+ MediaByteRange intervalRange;
+ bool haveSync = false;
+ bool progressive = true;
+ int64_t lastOffset = 0;
+ for (size_t i = 0; i < aIndex.Length(); i++) {
+ const Indice& indice = aIndex[i];
+ if (indice.sync || mIsAudio) {
+ haveSync = true;
+ }
+ if (!haveSync) {
+ continue;
+ }
+
+ Sample sample;
+ sample.mByteRange = MediaByteRange(indice.start_offset,
+ indice.end_offset);
+ sample.mCompositionRange = Interval<Microseconds>(indice.start_composition,
+ indice.end_composition);
+ sample.mDecodeTime = indice.start_decode;
+ sample.mSync = indice.sync || mIsAudio;
+ // FIXME: Make this infallible after bug 968520 is done.
+ MOZ_ALWAYS_TRUE(mIndex.AppendElement(sample, fallible));
+ if (indice.start_offset < lastOffset) {
+ NS_WARNING("Chunks in MP4 out of order, expect slow down");
+ progressive = false;
+ }
+ lastOffset = indice.end_offset;
+
+ // Pack audio samples in group of 128.
+ if (sample.mSync && progressive && (!mIsAudio || !(i % 128))) {
+ if (mDataOffset.Length()) {
+ auto& last = mDataOffset.LastElement();
+ last.mEndOffset = intervalRange.mEnd;
+ NS_ASSERTION(intervalTime.Length() == 1, "Discontinuous samples between keyframes");
+ last.mTime.start = intervalTime.GetStart();
+ last.mTime.end = intervalTime.GetEnd();
+ }
+ if (!mDataOffset.AppendElement(MP4DataOffset(mIndex.Length() - 1,
+ indice.start_offset),
+ fallible)) {
+ // OOM.
+ return;
+ }
+ intervalTime = media::IntervalSet<int64_t>();
+ intervalRange = MediaByteRange();
+ }
+ intervalTime += media::Interval<int64_t>(sample.mCompositionRange.start,
+ sample.mCompositionRange.end);
+ intervalRange = intervalRange.Span(sample.mByteRange);
+ }
+
+ if (mDataOffset.Length() && progressive) {
+ auto& last = mDataOffset.LastElement();
+ last.mEndOffset = aIndex.LastElement().end_offset;
+ last.mTime = Interval<int64_t>(intervalTime.GetStart(), intervalTime.GetEnd());
+ } else {
+ mDataOffset.Clear();
+ }
+ }
+}
+
+Index::~Index() {}
+
+void
+Index::UpdateMoofIndex(const MediaByteRangeSet& aByteRanges)
+{
+ UpdateMoofIndex(aByteRanges, false);
+}
+
+void
+Index::UpdateMoofIndex(const MediaByteRangeSet& aByteRanges, bool aCanEvict)
+{
+ if (!mMoofParser) {
+ return;
+ }
+ size_t moofs = mMoofParser->Moofs().Length();
+ bool canEvict = aCanEvict && moofs > 1;
+ if (canEvict) {
+ // Check that we can trim the mMoofParser. We can only do so if all
+ // iterators have demuxed all possible samples.
+ for (const SampleIterator* iterator : mIterators) {
+ if ((iterator->mCurrentSample == 0 && iterator->mCurrentMoof == moofs) ||
+ iterator->mCurrentMoof == moofs - 1) {
+ continue;
+ }
+ canEvict = false;
+ break;
+ }
+ }
+ mMoofParser->RebuildFragmentedIndex(aByteRanges, &canEvict);
+ if (canEvict) {
+ // The moofparser got trimmed. Adjust all registered iterators.
+ for (SampleIterator* iterator : mIterators) {
+ iterator->mCurrentMoof -= moofs - 1;
+ }
+ }
+}
+
+Microseconds
+Index::GetEndCompositionIfBuffered(const MediaByteRangeSet& aByteRanges)
+{
+ FallibleTArray<Sample>* index;
+ if (mMoofParser) {
+ if (!mMoofParser->ReachedEnd() || mMoofParser->Moofs().IsEmpty()) {
+ return 0;
+ }
+ index = &mMoofParser->Moofs().LastElement().mIndex;
+ } else {
+ index = &mIndex;
+ }
+
+ Microseconds lastComposition = 0;
+ RangeFinder rangeFinder(aByteRanges);
+ for (size_t i = index->Length(); i--;) {
+ const Sample& sample = (*index)[i];
+ if (!rangeFinder.Contains(sample.mByteRange)) {
+ return 0;
+ }
+ lastComposition = std::max(lastComposition, sample.mCompositionRange.end);
+ if (sample.mSync) {
+ return lastComposition;
+ }
+ }
+ return 0;
+}
+
+TimeIntervals
+Index::ConvertByteRangesToTimeRanges(const MediaByteRangeSet& aByteRanges)
+{
+ if (aByteRanges == mLastCachedRanges) {
+ return mLastBufferedRanges;
+ }
+ mLastCachedRanges = aByteRanges;
+
+ if (mDataOffset.Length()) {
+ TimeIntervals timeRanges;
+ for (const auto& range : aByteRanges) {
+ uint32_t start = mDataOffset.IndexOfFirstElementGt(range.mStart - 1);
+ if (!mIsAudio && start == mDataOffset.Length()) {
+ continue;
+ }
+ uint32_t end = mDataOffset.IndexOfFirstElementGt(range.mEnd, MP4DataOffset::EndOffsetComparator());
+ if (!mIsAudio && end < start) {
+ continue;
+ }
+ if (mIsAudio && start &&
+ range.Intersects(MediaByteRange(mDataOffset[start-1].mStartOffset,
+ mDataOffset[start-1].mEndOffset))) {
+ // Check if previous audio data block contains some available samples.
+ for (size_t i = mDataOffset[start-1].mIndex; i < mIndex.Length(); i++) {
+ if (range.ContainsStrict(mIndex[i].mByteRange)) {
+ timeRanges +=
+ TimeInterval(TimeUnit::FromMicroseconds(mIndex[i].mCompositionRange.start),
+ TimeUnit::FromMicroseconds(mIndex[i].mCompositionRange.end));
+ }
+ }
+ }
+ if (end > start) {
+ timeRanges +=
+ TimeInterval(TimeUnit::FromMicroseconds(mDataOffset[start].mTime.start),
+ TimeUnit::FromMicroseconds(mDataOffset[end-1].mTime.end));
+ }
+ if (end < mDataOffset.Length()) {
+ // Find samples in partial block contained in the byte range.
+ for (size_t i = mDataOffset[end].mIndex;
+ i < mIndex.Length() && range.ContainsStrict(mIndex[i].mByteRange);
+ i++) {
+ timeRanges +=
+ TimeInterval(TimeUnit::FromMicroseconds(mIndex[i].mCompositionRange.start),
+ TimeUnit::FromMicroseconds(mIndex[i].mCompositionRange.end));
+ }
+ }
+ }
+ mLastBufferedRanges = timeRanges;
+ return timeRanges;
+ }
+
+ RangeFinder rangeFinder(aByteRanges);
+ nsTArray<Interval<Microseconds>> timeRanges;
+ nsTArray<FallibleTArray<Sample>*> indexes;
+ if (mMoofParser) {
+ // We take the index out of the moof parser and move it into a local
+ // variable so we don't get concurrency issues. It gets freed when we
+ // exit this function.
+ for (int i = 0; i < mMoofParser->Moofs().Length(); i++) {
+ Moof& moof = mMoofParser->Moofs()[i];
+
+ // We need the entire moof in order to play anything
+ if (rangeFinder.Contains(moof.mRange)) {
+ if (rangeFinder.Contains(moof.mMdatRange)) {
+ Interval<Microseconds>::SemiNormalAppend(timeRanges, moof.mTimeRange);
+ } else {
+ indexes.AppendElement(&moof.mIndex);
+ }
+ }
+ }
+ } else {
+ indexes.AppendElement(&mIndex);
+ }
+
+ bool hasSync = false;
+ for (size_t i = 0; i < indexes.Length(); i++) {
+ FallibleTArray<Sample>* index = indexes[i];
+ for (size_t j = 0; j < index->Length(); j++) {
+ const Sample& sample = (*index)[j];
+ if (!rangeFinder.Contains(sample.mByteRange)) {
+ // We process the index in decode order so we clear hasSync when we hit
+ // a range that isn't buffered.
+ hasSync = false;
+ continue;
+ }
+
+ hasSync |= sample.mSync;
+ if (!hasSync) {
+ continue;
+ }
+
+ Interval<Microseconds>::SemiNormalAppend(timeRanges,
+ sample.mCompositionRange);
+ }
+ }
+
+ // This fixes up when the compositon order differs from the byte range order
+ nsTArray<Interval<Microseconds>> timeRangesNormalized;
+ Interval<Microseconds>::Normalize(timeRanges, &timeRangesNormalized);
+ // convert timeRanges.
+ media::TimeIntervals ranges;
+ for (size_t i = 0; i < timeRangesNormalized.Length(); i++) {
+ ranges +=
+ media::TimeInterval(media::TimeUnit::FromMicroseconds(timeRangesNormalized[i].start),
+ media::TimeUnit::FromMicroseconds(timeRangesNormalized[i].end));
+ }
+ mLastBufferedRanges = ranges;
+ return ranges;
+}
+
+uint64_t
+Index::GetEvictionOffset(Microseconds aTime)
+{
+ uint64_t offset = std::numeric_limits<uint64_t>::max();
+ if (mMoofParser) {
+ // We need to keep the whole moof if we're keeping any of it because the
+ // parser doesn't keep parsed moofs.
+ for (int i = 0; i < mMoofParser->Moofs().Length(); i++) {
+ Moof& moof = mMoofParser->Moofs()[i];
+
+ if (moof.mTimeRange.Length() && moof.mTimeRange.end > aTime) {
+ offset = std::min(offset, uint64_t(std::min(moof.mRange.mStart,
+ moof.mMdatRange.mStart)));
+ }
+ }
+ } else {
+ // We've already parsed and stored the moov so we don't need to keep it.
+ // All we need to keep is the sample data itself.
+ for (size_t i = 0; i < mIndex.Length(); i++) {
+ const Sample& sample = mIndex[i];
+ if (aTime >= sample.mCompositionRange.end) {
+ offset = std::min(offset, uint64_t(sample.mByteRange.mEnd));
+ }
+ }
+ }
+ return offset;
+}
+
+void
+Index::RegisterIterator(SampleIterator* aIterator)
+{
+ mIterators.AppendElement(aIterator);
+}
+
+void
+Index::UnregisterIterator(SampleIterator* aIterator)
+{
+ mIterators.RemoveElement(aIterator);
+}
+
+}
diff --git a/media/libstagefright/binding/MP4Metadata.cpp b/media/libstagefright/binding/MP4Metadata.cpp
new file mode 100644
index 000000000..eb5350704
--- /dev/null
+++ b/media/libstagefright/binding/MP4Metadata.cpp
@@ -0,0 +1,880 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "include/MPEG4Extractor.h"
+#include "media/stagefright/DataSource.h"
+#include "media/stagefright/MediaDefs.h"
+#include "media/stagefright/MediaSource.h"
+#include "media/stagefright/MetaData.h"
+#include "mozilla/Assertions.h"
+#include "mozilla/CheckedInt.h"
+#include "mozilla/EndianUtils.h"
+#include "mozilla/Logging.h"
+#include "mozilla/RefPtr.h"
+#include "mozilla/Telemetry.h"
+#include "mozilla/UniquePtr.h"
+#include "VideoUtils.h"
+#include "mp4_demuxer/MoofParser.h"
+#include "mp4_demuxer/MP4Metadata.h"
+#include "mp4_demuxer/Stream.h"
+#include "MediaPrefs.h"
+
+#include <limits>
+#include <stdint.h>
+#include <vector>
+
+#ifdef MOZ_RUST_MP4PARSE
+// OpusDecoder header is really needed only by MP4 in rust
+#include "OpusDecoder.h"
+#include "mp4parse.h"
+
+struct FreeMP4Parser { void operator()(mp4parse_parser* aPtr) { mp4parse_free(aPtr); } };
+#endif
+
+using namespace stagefright;
+
+namespace mp4_demuxer
+{
+
+class DataSourceAdapter : public DataSource
+{
+public:
+ explicit DataSourceAdapter(Stream* aSource) : mSource(aSource) {}
+
+ ~DataSourceAdapter() {}
+
+ virtual status_t initCheck() const { return NO_ERROR; }
+
+ virtual ssize_t readAt(off64_t offset, void* data, size_t size)
+ {
+ MOZ_ASSERT(((ssize_t)size) >= 0);
+ size_t bytesRead;
+ if (!mSource->ReadAt(offset, data, size, &bytesRead))
+ return ERROR_IO;
+
+ if (bytesRead == 0)
+ return ERROR_END_OF_STREAM;
+
+ MOZ_ASSERT(((ssize_t)bytesRead) > 0);
+ return bytesRead;
+ }
+
+ virtual status_t getSize(off64_t* size)
+ {
+ if (!mSource->Length(size))
+ return ERROR_UNSUPPORTED;
+ return NO_ERROR;
+ }
+
+ virtual uint32_t flags() { return kWantsPrefetching | kIsHTTPBasedSource; }
+
+ virtual status_t reconnectAtOffset(off64_t offset) { return NO_ERROR; }
+
+private:
+ RefPtr<Stream> mSource;
+};
+
+class MP4MetadataStagefright
+{
+public:
+ explicit MP4MetadataStagefright(Stream* aSource);
+ ~MP4MetadataStagefright();
+
+ static bool HasCompleteMetadata(Stream* aSource);
+ static already_AddRefed<mozilla::MediaByteBuffer> Metadata(Stream* aSource);
+ uint32_t GetNumberTracks(mozilla::TrackInfo::TrackType aType) const;
+ mozilla::UniquePtr<mozilla::TrackInfo> GetTrackInfo(mozilla::TrackInfo::TrackType aType,
+ size_t aTrackNumber) const;
+ bool CanSeek() const;
+
+ const CryptoFile& Crypto() const;
+
+ bool ReadTrackIndex(FallibleTArray<Index::Indice>& aDest, mozilla::TrackID aTrackID);
+
+private:
+ int32_t GetTrackNumber(mozilla::TrackID aTrackID);
+ void UpdateCrypto(const stagefright::MetaData* aMetaData);
+ mozilla::UniquePtr<mozilla::TrackInfo> CheckTrack(const char* aMimeType,
+ stagefright::MetaData* aMetaData,
+ int32_t aIndex) const;
+ CryptoFile mCrypto;
+ RefPtr<Stream> mSource;
+ sp<MediaExtractor> mMetadataExtractor;
+ bool mCanSeek;
+};
+
+#ifdef MOZ_RUST_MP4PARSE
+
+// Wrap an mp4_demuxer::Stream to remember the read offset.
+
+class RustStreamAdaptor {
+public:
+ explicit RustStreamAdaptor(Stream* aSource)
+ : mSource(aSource)
+ , mOffset(0)
+ {
+ }
+
+ ~RustStreamAdaptor() {}
+
+ bool Read(uint8_t* buffer, uintptr_t size, size_t* bytes_read);
+
+private:
+ Stream* mSource;
+ CheckedInt<size_t> mOffset;
+};
+
+class MP4MetadataRust
+{
+public:
+ explicit MP4MetadataRust(Stream* aSource);
+ ~MP4MetadataRust();
+
+ static bool HasCompleteMetadata(Stream* aSource);
+ static already_AddRefed<mozilla::MediaByteBuffer> Metadata(Stream* aSource);
+ uint32_t GetNumberTracks(mozilla::TrackInfo::TrackType aType) const;
+ mozilla::UniquePtr<mozilla::TrackInfo> GetTrackInfo(mozilla::TrackInfo::TrackType aType,
+ size_t aTrackNumber) const;
+ bool CanSeek() const;
+
+ const CryptoFile& Crypto() const;
+
+ bool ReadTrackIndex(FallibleTArray<Index::Indice>& aDest, mozilla::TrackID aTrackID);
+
+private:
+ Maybe<uint32_t> TrackTypeToGlobalTrackIndex(mozilla::TrackInfo::TrackType aType, size_t aTrackNumber) const;
+
+ CryptoFile mCrypto;
+ RefPtr<Stream> mSource;
+ RustStreamAdaptor mRustSource;
+ mozilla::UniquePtr<mp4parse_parser, FreeMP4Parser> mRustParser;
+};
+#endif
+
+MP4Metadata::MP4Metadata(Stream* aSource)
+ : mStagefright(MakeUnique<MP4MetadataStagefright>(aSource))
+#ifdef MOZ_RUST_MP4PARSE
+ , mRust(MakeUnique<MP4MetadataRust>(aSource))
+ , mPreferRust(false)
+ , mReportedAudioTrackTelemetry(false)
+ , mReportedVideoTrackTelemetry(false)
+#ifndef RELEASE_OR_BETA
+ , mRustTestMode(MediaPrefs::RustTestMode())
+#endif
+#endif
+{
+}
+
+MP4Metadata::~MP4Metadata()
+{
+}
+
+/*static*/ bool
+MP4Metadata::HasCompleteMetadata(Stream* aSource)
+{
+ return MP4MetadataStagefright::HasCompleteMetadata(aSource);
+}
+
+/*static*/ already_AddRefed<mozilla::MediaByteBuffer>
+MP4Metadata::Metadata(Stream* aSource)
+{
+ return MP4MetadataStagefright::Metadata(aSource);
+}
+
+static const char *
+TrackTypeToString(mozilla::TrackInfo::TrackType aType)
+{
+ switch (aType) {
+ case mozilla::TrackInfo::kAudioTrack:
+ return "audio";
+ case mozilla::TrackInfo::kVideoTrack:
+ return "video";
+ default:
+ return "unknown";
+ }
+}
+
+uint32_t
+MP4Metadata::GetNumberTracks(mozilla::TrackInfo::TrackType aType) const
+{
+ static LazyLogModule sLog("MP4Metadata");
+
+ uint32_t numTracks = mStagefright->GetNumberTracks(aType);
+
+#ifdef MOZ_RUST_MP4PARSE
+ if (!mRust) {
+ return numTracks;
+ }
+
+ uint32_t numTracksRust = mRust->GetNumberTracks(aType);
+ MOZ_LOG(sLog, LogLevel::Info, ("%s tracks found: stagefright=%u rust=%u",
+ TrackTypeToString(aType), numTracks, numTracksRust));
+
+ bool numTracksMatch = numTracks == numTracksRust;
+
+ if (aType == mozilla::TrackInfo::kAudioTrack && !mReportedAudioTrackTelemetry) {
+ Telemetry::Accumulate(Telemetry::MEDIA_RUST_MP4PARSE_TRACK_MATCH_AUDIO,
+ numTracksMatch);
+ mReportedAudioTrackTelemetry = true;
+ } else if (aType == mozilla::TrackInfo::kVideoTrack && !mReportedVideoTrackTelemetry) {
+ Telemetry::Accumulate(Telemetry::MEDIA_RUST_MP4PARSE_TRACK_MATCH_VIDEO,
+ numTracksMatch);
+ mReportedVideoTrackTelemetry = true;
+ }
+
+ if (mPreferRust || ShouldPreferRust()) {
+ MOZ_LOG(sLog, LogLevel::Info, ("Preferring rust demuxer"));
+ mPreferRust = true;
+ return numTracksRust;
+ }
+#endif // MOZ_RUST_MP4PARSE
+
+ return numTracks;
+}
+
+#ifdef MOZ_RUST_MP4PARSE
+bool MP4Metadata::ShouldPreferRust() const {
+ if (!mRust) {
+ return false;
+ }
+ // See if there's an Opus track.
+ uint32_t numTracks = mRust->GetNumberTracks(TrackInfo::kAudioTrack);
+ for (auto i = 0; i < numTracks; i++) {
+ auto info = mRust->GetTrackInfo(TrackInfo::kAudioTrack, i);
+ if (!info) {
+ return false;
+ }
+ if (info->mMimeType.EqualsASCII("audio/opus") ||
+ info->mMimeType.EqualsASCII("audio/flac")) {
+ return true;
+ }
+ }
+
+ numTracks = mRust->GetNumberTracks(TrackInfo::kVideoTrack);
+ for (auto i = 0; i < numTracks; i++) {
+ auto info = mRust->GetTrackInfo(TrackInfo::kVideoTrack, i);
+ if (!info) {
+ return false;
+ }
+ if (info->mMimeType.EqualsASCII("video/vp9")) {
+ return true;
+ }
+ }
+ // Otherwise, fall back.
+ return false;
+}
+#endif // MOZ_RUST_MP4PARSE
+
+mozilla::UniquePtr<mozilla::TrackInfo>
+MP4Metadata::GetTrackInfo(mozilla::TrackInfo::TrackType aType,
+ size_t aTrackNumber) const
+{
+ mozilla::UniquePtr<mozilla::TrackInfo> info =
+ mStagefright->GetTrackInfo(aType, aTrackNumber);
+
+#ifdef MOZ_RUST_MP4PARSE
+ if (!mRust) {
+ return info;
+ }
+
+ mozilla::UniquePtr<mozilla::TrackInfo> infoRust =
+ mRust->GetTrackInfo(aType, aTrackNumber);
+
+#ifndef RELEASE_OR_BETA
+ if (mRustTestMode && info) {
+ MOZ_DIAGNOSTIC_ASSERT(infoRust);
+ MOZ_DIAGNOSTIC_ASSERT(infoRust->mId == info->mId);
+ MOZ_DIAGNOSTIC_ASSERT(infoRust->mKind == info->mKind);
+ MOZ_DIAGNOSTIC_ASSERT(infoRust->mLabel == info->mLabel);
+ MOZ_DIAGNOSTIC_ASSERT(infoRust->mLanguage == info->mLanguage);
+ MOZ_DIAGNOSTIC_ASSERT(infoRust->mEnabled == info->mEnabled);
+ MOZ_DIAGNOSTIC_ASSERT(infoRust->mTrackId == info->mTrackId);
+ MOZ_DIAGNOSTIC_ASSERT(infoRust->mMimeType == info->mMimeType);
+ MOZ_DIAGNOSTIC_ASSERT(infoRust->mDuration == info->mDuration);
+ MOZ_DIAGNOSTIC_ASSERT(infoRust->mMediaTime == info->mMediaTime);
+ switch (aType) {
+ case mozilla::TrackInfo::kAudioTrack: {
+ AudioInfo *audioRust = infoRust->GetAsAudioInfo(), *audio = info->GetAsAudioInfo();
+ MOZ_DIAGNOSTIC_ASSERT(audioRust->mRate == audio->mRate);
+ MOZ_DIAGNOSTIC_ASSERT(audioRust->mChannels == audio->mChannels);
+ MOZ_DIAGNOSTIC_ASSERT(audioRust->mBitDepth == audio->mBitDepth);
+ // TODO: These fields aren't implemented in the Rust demuxer yet.
+ //MOZ_DIAGNOSTIC_ASSERT(audioRust->mProfile != audio->mProfile);
+ //MOZ_DIAGNOSTIC_ASSERT(audioRust->mExtendedProfile != audio->mExtendedProfile);
+ break;
+ }
+ case mozilla::TrackInfo::kVideoTrack: {
+ VideoInfo *videoRust = infoRust->GetAsVideoInfo(), *video = info->GetAsVideoInfo();
+ MOZ_DIAGNOSTIC_ASSERT(videoRust->mDisplay == video->mDisplay);
+ MOZ_DIAGNOSTIC_ASSERT(videoRust->mImage == video->mImage);
+ break;
+ }
+ default:
+ break;
+ }
+ }
+#endif
+
+ if (!mPreferRust) {
+ return info;
+ }
+ MOZ_ASSERT(infoRust);
+ return infoRust;
+#endif
+
+ return info;
+}
+
+bool
+MP4Metadata::CanSeek() const
+{
+ return mStagefright->CanSeek();
+}
+
+const CryptoFile&
+MP4Metadata::Crypto() const
+{
+ return mStagefright->Crypto();
+}
+
+bool
+MP4Metadata::ReadTrackIndex(FallibleTArray<Index::Indice>& aDest, mozilla::TrackID aTrackID)
+{
+#ifdef MOZ_RUST_MP4PARSE
+ if (mRust && mPreferRust && mRust->ReadTrackIndex(aDest, aTrackID)) {
+ return true;
+ }
+ aDest.Clear();
+#endif
+ return mStagefright->ReadTrackIndex(aDest, aTrackID);
+}
+
+static inline bool
+ConvertIndex(FallibleTArray<Index::Indice>& aDest,
+ const nsTArray<stagefright::MediaSource::Indice>& aIndex,
+ int64_t aMediaTime)
+{
+ if (!aDest.SetCapacity(aIndex.Length(), mozilla::fallible)) {
+ return false;
+ }
+ for (size_t i = 0; i < aIndex.Length(); i++) {
+ Index::Indice indice;
+ const stagefright::MediaSource::Indice& s_indice = aIndex[i];
+ indice.start_offset = s_indice.start_offset;
+ indice.end_offset = s_indice.end_offset;
+ indice.start_composition = s_indice.start_composition - aMediaTime;
+ indice.end_composition = s_indice.end_composition - aMediaTime;
+ indice.start_decode = s_indice.start_decode;
+ indice.sync = s_indice.sync;
+ // FIXME: Make this infallible after bug 968520 is done.
+ MOZ_ALWAYS_TRUE(aDest.AppendElement(indice, mozilla::fallible));
+ }
+ return true;
+}
+
+MP4MetadataStagefright::MP4MetadataStagefright(Stream* aSource)
+ : mSource(aSource)
+ , mMetadataExtractor(new MPEG4Extractor(new DataSourceAdapter(mSource)))
+ , mCanSeek(mMetadataExtractor->flags() & MediaExtractor::CAN_SEEK)
+{
+ sp<MetaData> metaData = mMetadataExtractor->getMetaData();
+
+ if (metaData.get()) {
+ UpdateCrypto(metaData.get());
+ }
+}
+
+MP4MetadataStagefright::~MP4MetadataStagefright()
+{
+}
+
+uint32_t
+MP4MetadataStagefright::GetNumberTracks(mozilla::TrackInfo::TrackType aType) const
+{
+ size_t tracks = mMetadataExtractor->countTracks();
+ uint32_t total = 0;
+ for (size_t i = 0; i < tracks; i++) {
+ sp<MetaData> metaData = mMetadataExtractor->getTrackMetaData(i);
+
+ const char* mimeType;
+ if (metaData == nullptr || !metaData->findCString(kKeyMIMEType, &mimeType)) {
+ continue;
+ }
+ switch (aType) {
+ case mozilla::TrackInfo::kAudioTrack:
+ if (!strncmp(mimeType, "audio/", 6) &&
+ CheckTrack(mimeType, metaData.get(), i)) {
+ total++;
+ }
+ break;
+ case mozilla::TrackInfo::kVideoTrack:
+ if (!strncmp(mimeType, "video/", 6) &&
+ CheckTrack(mimeType, metaData.get(), i)) {
+ total++;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ return total;
+}
+
+mozilla::UniquePtr<mozilla::TrackInfo>
+MP4MetadataStagefright::GetTrackInfo(mozilla::TrackInfo::TrackType aType,
+ size_t aTrackNumber) const
+{
+ size_t tracks = mMetadataExtractor->countTracks();
+ if (!tracks) {
+ return nullptr;
+ }
+ int32_t index = -1;
+ const char* mimeType;
+ sp<MetaData> metaData;
+
+ size_t i = 0;
+ while (i < tracks) {
+ metaData = mMetadataExtractor->getTrackMetaData(i);
+
+ if (metaData == nullptr || !metaData->findCString(kKeyMIMEType, &mimeType)) {
+ continue;
+ }
+ switch (aType) {
+ case mozilla::TrackInfo::kAudioTrack:
+ if (!strncmp(mimeType, "audio/", 6) &&
+ CheckTrack(mimeType, metaData.get(), i)) {
+ index++;
+ }
+ break;
+ case mozilla::TrackInfo::kVideoTrack:
+ if (!strncmp(mimeType, "video/", 6) &&
+ CheckTrack(mimeType, metaData.get(), i)) {
+ index++;
+ }
+ break;
+ default:
+ break;
+ }
+ if (index == aTrackNumber) {
+ break;
+ }
+ i++;
+ }
+ if (index < 0) {
+ return nullptr;
+ }
+
+ UniquePtr<mozilla::TrackInfo> e = CheckTrack(mimeType, metaData.get(), index);
+
+ if (e) {
+ metaData = mMetadataExtractor->getMetaData();
+ int64_t movieDuration;
+ if (!e->mDuration &&
+ metaData->findInt64(kKeyMovieDuration, &movieDuration)) {
+ // No duration in track, use movie extend header box one.
+ e->mDuration = movieDuration;
+ }
+ }
+
+ return e;
+}
+
+mozilla::UniquePtr<mozilla::TrackInfo>
+MP4MetadataStagefright::CheckTrack(const char* aMimeType,
+ stagefright::MetaData* aMetaData,
+ int32_t aIndex) const
+{
+ sp<MediaSource> track = mMetadataExtractor->getTrack(aIndex);
+ if (!track.get()) {
+ return nullptr;
+ }
+
+ UniquePtr<mozilla::TrackInfo> e;
+
+ if (!strncmp(aMimeType, "audio/", 6)) {
+ auto info = mozilla::MakeUnique<MP4AudioInfo>();
+ info->Update(aMetaData, aMimeType);
+ e = Move(info);
+ } else if (!strncmp(aMimeType, "video/", 6)) {
+ auto info = mozilla::MakeUnique<MP4VideoInfo>();
+ info->Update(aMetaData, aMimeType);
+ e = Move(info);
+ }
+
+ if (e && e->IsValid()) {
+ return e;
+ }
+
+ return nullptr;
+}
+
+bool
+MP4MetadataStagefright::CanSeek() const
+{
+ return mCanSeek;
+}
+
+const CryptoFile&
+MP4MetadataStagefright::Crypto() const
+{
+ return mCrypto;
+}
+
+void
+MP4MetadataStagefright::UpdateCrypto(const MetaData* aMetaData)
+{
+ const void* data;
+ size_t size;
+ uint32_t type;
+
+ // There's no point in checking that the type matches anything because it
+ // isn't set consistently in the MPEG4Extractor.
+ if (!aMetaData->findData(kKeyPssh, &type, &data, &size)) {
+ return;
+ }
+ mCrypto.Update(reinterpret_cast<const uint8_t*>(data), size);
+}
+
+bool
+MP4MetadataStagefright::ReadTrackIndex(FallibleTArray<Index::Indice>& aDest, mozilla::TrackID aTrackID)
+{
+ size_t numTracks = mMetadataExtractor->countTracks();
+ int32_t trackNumber = GetTrackNumber(aTrackID);
+ if (trackNumber < 0) {
+ return false;
+ }
+ sp<MediaSource> track = mMetadataExtractor->getTrack(trackNumber);
+ if (!track.get()) {
+ return false;
+ }
+ sp<MetaData> metadata = mMetadataExtractor->getTrackMetaData(trackNumber);
+ int64_t mediaTime;
+ if (!metadata->findInt64(kKeyMediaTime, &mediaTime)) {
+ mediaTime = 0;
+ }
+ bool rv = ConvertIndex(aDest, track->exportIndex(), mediaTime);
+
+ return rv;
+}
+
+int32_t
+MP4MetadataStagefright::GetTrackNumber(mozilla::TrackID aTrackID)
+{
+ size_t numTracks = mMetadataExtractor->countTracks();
+ for (size_t i = 0; i < numTracks; i++) {
+ sp<MetaData> metaData = mMetadataExtractor->getTrackMetaData(i);
+ if (!metaData.get()) {
+ continue;
+ }
+ int32_t value;
+ if (metaData->findInt32(kKeyTrackID, &value) && value == aTrackID) {
+ return i;
+ }
+ }
+ return -1;
+}
+
+/*static*/ bool
+MP4MetadataStagefright::HasCompleteMetadata(Stream* aSource)
+{
+ auto parser = mozilla::MakeUnique<MoofParser>(aSource, 0, false);
+ return parser->HasMetadata();
+}
+
+/*static*/ already_AddRefed<mozilla::MediaByteBuffer>
+MP4MetadataStagefright::Metadata(Stream* aSource)
+{
+ auto parser = mozilla::MakeUnique<MoofParser>(aSource, 0, false);
+ return parser->Metadata();
+}
+
+#ifdef MOZ_RUST_MP4PARSE
+bool
+RustStreamAdaptor::Read(uint8_t* buffer, uintptr_t size, size_t* bytes_read)
+{
+ if (!mOffset.isValid()) {
+ static LazyLogModule sLog("MP4Metadata");
+ MOZ_LOG(sLog, LogLevel::Error, ("Overflow in source stream offset"));
+ return false;
+ }
+ bool rv = mSource->ReadAt(mOffset.value(), buffer, size, bytes_read);
+ if (rv) {
+ mOffset += *bytes_read;
+ }
+ return rv;
+}
+
+// Wrapper to allow rust to call our read adaptor.
+static intptr_t
+read_source(uint8_t* buffer, uintptr_t size, void* userdata)
+{
+ MOZ_ASSERT(buffer);
+ MOZ_ASSERT(userdata);
+
+ auto source = reinterpret_cast<RustStreamAdaptor*>(userdata);
+ size_t bytes_read = 0;
+ bool rv = source->Read(buffer, size, &bytes_read);
+ if (!rv) {
+ static LazyLogModule sLog("MP4Metadata");
+ MOZ_LOG(sLog, LogLevel::Warning, ("Error reading source data"));
+ return -1;
+ }
+ return bytes_read;
+}
+
+MP4MetadataRust::MP4MetadataRust(Stream* aSource)
+ : mSource(aSource)
+ , mRustSource(aSource)
+{
+ mp4parse_io io = { read_source, &mRustSource };
+ mRustParser.reset(mp4parse_new(&io));
+ MOZ_ASSERT(mRustParser);
+
+ static LazyLogModule sLog("MP4Metadata");
+ mp4parse_error rv = mp4parse_read(mRustParser.get());
+ MOZ_LOG(sLog, LogLevel::Debug, ("rust parser returned %d\n", rv));
+ Telemetry::Accumulate(Telemetry::MEDIA_RUST_MP4PARSE_SUCCESS,
+ rv == MP4PARSE_OK);
+ if (rv != MP4PARSE_OK) {
+ MOZ_ASSERT(rv > 0);
+ Telemetry::Accumulate(Telemetry::MEDIA_RUST_MP4PARSE_ERROR_CODE, rv);
+ }
+}
+
+MP4MetadataRust::~MP4MetadataRust()
+{
+}
+
+bool
+TrackTypeEqual(TrackInfo::TrackType aLHS, mp4parse_track_type aRHS)
+{
+ switch (aLHS) {
+ case TrackInfo::kAudioTrack:
+ return aRHS == MP4PARSE_TRACK_TYPE_AUDIO;
+ case TrackInfo::kVideoTrack:
+ return aRHS == MP4PARSE_TRACK_TYPE_VIDEO;
+ default:
+ return false;
+ }
+}
+
+uint32_t
+MP4MetadataRust::GetNumberTracks(mozilla::TrackInfo::TrackType aType) const
+{
+ static LazyLogModule sLog("MP4Metadata");
+
+ uint32_t tracks;
+ auto rv = mp4parse_get_track_count(mRustParser.get(), &tracks);
+ if (rv != MP4PARSE_OK) {
+ MOZ_LOG(sLog, LogLevel::Warning,
+ ("rust parser error %d counting tracks", rv));
+ return 0;
+ }
+ MOZ_LOG(sLog, LogLevel::Info, ("rust parser found %u tracks", tracks));
+
+ uint32_t total = 0;
+ for (uint32_t i = 0; i < tracks; ++i) {
+ mp4parse_track_info track_info;
+ rv = mp4parse_get_track_info(mRustParser.get(), i, &track_info);
+ if (rv != MP4PARSE_OK) {
+ continue;
+ }
+ if (TrackTypeEqual(aType, track_info.track_type)) {
+ total += 1;
+ }
+ }
+
+ return total;
+}
+
+Maybe<uint32_t>
+MP4MetadataRust::TrackTypeToGlobalTrackIndex(mozilla::TrackInfo::TrackType aType, size_t aTrackNumber) const
+{
+ uint32_t tracks;
+ auto rv = mp4parse_get_track_count(mRustParser.get(), &tracks);
+ if (rv != MP4PARSE_OK) {
+ return Nothing();
+ }
+
+ /* The MP4Metadata API uses a per-TrackType index of tracks, but mp4parse
+ (and libstagefright) use a global track index. Convert the index by
+ counting the tracks of the requested type and returning the global
+ track index when a match is found. */
+ uint32_t perType = 0;
+ for (uint32_t i = 0; i < tracks; ++i) {
+ mp4parse_track_info track_info;
+ rv = mp4parse_get_track_info(mRustParser.get(), i, &track_info);
+ if (rv != MP4PARSE_OK) {
+ continue;
+ }
+ if (TrackTypeEqual(aType, track_info.track_type)) {
+ if (perType == aTrackNumber) {
+ return Some(i);
+ }
+ perType += 1;
+ }
+ }
+
+ return Nothing();
+}
+
+mozilla::UniquePtr<mozilla::TrackInfo>
+MP4MetadataRust::GetTrackInfo(mozilla::TrackInfo::TrackType aType,
+ size_t aTrackNumber) const
+{
+ static LazyLogModule sLog("MP4Metadata");
+
+ Maybe<uint32_t> trackIndex = TrackTypeToGlobalTrackIndex(aType, aTrackNumber);
+ if (trackIndex.isNothing()) {
+ return nullptr;
+ }
+
+ mp4parse_track_info info;
+ auto rv = mp4parse_get_track_info(mRustParser.get(), trackIndex.value(), &info);
+ if (rv != MP4PARSE_OK) {
+ MOZ_LOG(sLog, LogLevel::Warning, ("mp4parse_get_track_info returned %d", rv));
+ return nullptr;
+ }
+#ifdef DEBUG
+ const char* codec_string = "unrecognized";
+ switch (info.codec) {
+ case MP4PARSE_CODEC_UNKNOWN: codec_string = "unknown"; break;
+ case MP4PARSE_CODEC_AAC: codec_string = "aac"; break;
+ case MP4PARSE_CODEC_OPUS: codec_string = "opus"; break;
+ case MP4PARSE_CODEC_FLAC: codec_string = "flac"; break;
+ case MP4PARSE_CODEC_AVC: codec_string = "h.264"; break;
+ case MP4PARSE_CODEC_VP9: codec_string = "vp9"; break;
+ case MP4PARSE_CODEC_MP3: codec_string = "mp3"; break;
+ }
+ MOZ_LOG(sLog, LogLevel::Debug, ("track codec %s (%u)\n",
+ codec_string, info.codec));
+#endif
+
+ // This specialization interface is crazy.
+ UniquePtr<mozilla::TrackInfo> e;
+ switch (aType) {
+ case TrackInfo::TrackType::kAudioTrack: {
+ mp4parse_track_audio_info audio;
+ auto rv = mp4parse_get_track_audio_info(mRustParser.get(), trackIndex.value(), &audio);
+ if (rv != MP4PARSE_OK) {
+ MOZ_LOG(sLog, LogLevel::Warning, ("mp4parse_get_track_audio_info returned error %d", rv));
+ return nullptr;
+ }
+ auto track = mozilla::MakeUnique<mozilla::AudioInfo>();
+ if (info.codec == MP4PARSE_CODEC_OPUS) {
+ track->mMimeType = NS_LITERAL_CSTRING("audio/opus");
+ // The Opus decoder expects the container's codec delay or
+ // pre-skip value, in microseconds, as a 64-bit int at the
+ // start of the codec-specific config blob.
+ MOZ_ASSERT(audio.codec_specific_config.data);
+ MOZ_ASSERT(audio.codec_specific_config.length >= 12);
+ uint16_t preskip =
+ LittleEndian::readUint16(audio.codec_specific_config.data + 10);
+ MOZ_LOG(sLog, LogLevel::Debug,
+ ("Copying opus pre-skip value of %d as CodecDelay.",(int)preskip));
+ OpusDataDecoder::AppendCodecDelay(track->mCodecSpecificConfig,
+ mozilla::FramesToUsecs(preskip, 48000).value());
+ } else if (info.codec == MP4PARSE_CODEC_AAC) {
+ track->mMimeType = MEDIA_MIMETYPE_AUDIO_AAC;
+ } else if (info.codec == MP4PARSE_CODEC_FLAC) {
+ track->mMimeType = MEDIA_MIMETYPE_AUDIO_FLAC;
+ } else if (info.codec == MP4PARSE_CODEC_MP3) {
+ track->mMimeType = MEDIA_MIMETYPE_AUDIO_MPEG;
+ }
+ track->mCodecSpecificConfig->AppendElements(
+ audio.codec_specific_config.data,
+ audio.codec_specific_config.length);
+ track->mRate = audio.sample_rate;
+ track->mChannels = audio.channels;
+ track->mBitDepth = audio.bit_depth;
+ track->mDuration = info.duration;
+ track->mMediaTime = info.media_time;
+ track->mTrackId = info.track_id;
+ e = Move(track);
+ }
+ break;
+ case TrackInfo::TrackType::kVideoTrack: {
+ mp4parse_track_video_info video;
+ auto rv = mp4parse_get_track_video_info(mRustParser.get(), trackIndex.value(), &video);
+ if (rv != MP4PARSE_OK) {
+ MOZ_LOG(sLog, LogLevel::Warning, ("mp4parse_get_track_video_info returned error %d", rv));
+ return nullptr;
+ }
+ auto track = mozilla::MakeUnique<MP4VideoInfo>();
+ track->Update(&info, &video);
+ e = Move(track);
+ }
+ break;
+ default:
+ MOZ_LOG(sLog, LogLevel::Warning, ("unhandled track type %d", aType));
+ return nullptr;
+ break;
+ }
+
+ // No duration in track, use fragment_duration.
+ if (e && !e->mDuration) {
+ mp4parse_fragment_info info;
+ auto rv = mp4parse_get_fragment_info(mRustParser.get(), &info);
+ if (rv == MP4PARSE_OK) {
+ e->mDuration = info.fragment_duration;
+ }
+ }
+
+ if (e && e->IsValid()) {
+ return e;
+ }
+ MOZ_LOG(sLog, LogLevel::Debug, ("TrackInfo didn't validate"));
+
+ return nullptr;
+}
+
+bool
+MP4MetadataRust::CanSeek() const
+{
+ MOZ_ASSERT(false, "Not yet implemented");
+ return false;
+}
+
+const CryptoFile&
+MP4MetadataRust::Crypto() const
+{
+ MOZ_ASSERT(false, "Not yet implemented");
+ return mCrypto;
+}
+
+bool
+MP4MetadataRust::ReadTrackIndex(FallibleTArray<Index::Indice>& aDest, mozilla::TrackID aTrackID)
+{
+ uint8_t fragmented = false;
+ auto rv = mp4parse_is_fragmented(mRustParser.get(), aTrackID, &fragmented);
+ if (rv != MP4PARSE_OK) {
+ return false;
+ }
+
+ if (fragmented) {
+ return true;
+ }
+
+ // For non-fragmented mp4.
+ NS_WARNING("Not yet implemented");
+
+ return false;
+}
+
+/*static*/ bool
+MP4MetadataRust::HasCompleteMetadata(Stream* aSource)
+{
+ MOZ_ASSERT(false, "Not yet implemented");
+ return false;
+}
+
+/*static*/ already_AddRefed<mozilla::MediaByteBuffer>
+MP4MetadataRust::Metadata(Stream* aSource)
+{
+ MOZ_ASSERT(false, "Not yet implemented");
+ return nullptr;
+}
+#endif
+
+} // namespace mp4_demuxer
diff --git a/media/libstagefright/binding/MoofParser.cpp b/media/libstagefright/binding/MoofParser.cpp
new file mode 100644
index 000000000..ced054282
--- /dev/null
+++ b/media/libstagefright/binding/MoofParser.cpp
@@ -0,0 +1,925 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mp4_demuxer/MoofParser.h"
+#include "mp4_demuxer/Box.h"
+#include "mp4_demuxer/SinfParser.h"
+#include <limits>
+#include "Intervals.h"
+
+#include "mozilla/CheckedInt.h"
+#include "mozilla/Logging.h"
+
+#if defined(MOZ_FMP4)
+extern mozilla::LogModule* GetDemuxerLog();
+
+#define STRINGIFY(x) #x
+#define TOSTRING(x) STRINGIFY(x)
+#define LOG(name, arg, ...) MOZ_LOG(GetDemuxerLog(), mozilla::LogLevel::Debug, (TOSTRING(name) "(%p)::%s: " arg, this, __func__, ##__VA_ARGS__))
+#else
+#define LOG(...)
+#endif
+
+namespace mp4_demuxer
+{
+
+using namespace stagefright;
+using namespace mozilla;
+
+bool
+MoofParser::RebuildFragmentedIndex(const MediaByteRangeSet& aByteRanges)
+{
+ BoxContext context(mSource, aByteRanges);
+ return RebuildFragmentedIndex(context);
+}
+
+bool
+MoofParser::RebuildFragmentedIndex(
+ const MediaByteRangeSet& aByteRanges, bool* aCanEvict)
+{
+ MOZ_ASSERT(aCanEvict);
+ if (*aCanEvict && mMoofs.Length() > 1) {
+ MOZ_ASSERT(mMoofs.Length() == mMediaRanges.Length());
+ mMoofs.RemoveElementsAt(0, mMoofs.Length() - 1);
+ mMediaRanges.RemoveElementsAt(0, mMediaRanges.Length() - 1);
+ *aCanEvict = true;
+ } else {
+ *aCanEvict = false;
+ }
+ return RebuildFragmentedIndex(aByteRanges);
+}
+
+bool
+MoofParser::RebuildFragmentedIndex(BoxContext& aContext)
+{
+ bool foundValidMoof = false;
+ bool foundMdat = false;
+
+ for (Box box(&aContext, mOffset); box.IsAvailable(); box = box.Next()) {
+ if (box.IsType("moov") && mInitRange.IsEmpty()) {
+ mInitRange = MediaByteRange(0, box.Range().mEnd);
+ ParseMoov(box);
+ } else if (box.IsType("moof")) {
+ Moof moof(box, mTrex, mMvhd, mMdhd, mEdts, mSinf, &mLastDecodeTime, mIsAudio);
+
+ if (!moof.IsValid() && !box.Next().IsAvailable()) {
+ // Moof isn't valid abort search for now.
+ break;
+ }
+
+ if (!mMoofs.IsEmpty()) {
+ // Stitch time ranges together in the case of a (hopefully small) time
+ // range gap between moofs.
+ mMoofs.LastElement().FixRounding(moof);
+ }
+
+ mMoofs.AppendElement(moof);
+ mMediaRanges.AppendElement(moof.mRange);
+ foundValidMoof = true;
+ } else if (box.IsType("mdat") && !Moofs().IsEmpty()) {
+ // Check if we have all our data from last moof.
+ Moof& moof = Moofs().LastElement();
+ media::Interval<int64_t> datarange(moof.mMdatRange.mStart, moof.mMdatRange.mEnd, 0);
+ media::Interval<int64_t> mdat(box.Range().mStart, box.Range().mEnd, 0);
+ if (datarange.Intersects(mdat)) {
+ mMediaRanges.LastElement() =
+ mMediaRanges.LastElement().Span(box.Range());
+ }
+ }
+ mOffset = box.NextOffset();
+ }
+ return foundValidMoof;
+}
+
+MediaByteRange
+MoofParser::FirstCompleteMediaHeader()
+{
+ if (Moofs().IsEmpty()) {
+ return MediaByteRange();
+ }
+ return Moofs()[0].mRange;
+}
+
+MediaByteRange
+MoofParser::FirstCompleteMediaSegment()
+{
+ for (uint32_t i = 0 ; i < mMediaRanges.Length(); i++) {
+ if (mMediaRanges[i].Contains(Moofs()[i].mMdatRange)) {
+ return mMediaRanges[i];
+ }
+ }
+ return MediaByteRange();
+}
+
+class BlockingStream : public Stream {
+public:
+ explicit BlockingStream(Stream* aStream) : mStream(aStream)
+ {
+ }
+
+ bool ReadAt(int64_t offset, void* data, size_t size, size_t* bytes_read)
+ override
+ {
+ return mStream->ReadAt(offset, data, size, bytes_read);
+ }
+
+ bool CachedReadAt(int64_t offset, void* data, size_t size, size_t* bytes_read)
+ override
+ {
+ return mStream->ReadAt(offset, data, size, bytes_read);
+ }
+
+ virtual bool Length(int64_t* size) override
+ {
+ return mStream->Length(size);
+ }
+
+private:
+ RefPtr<Stream> mStream;
+};
+
+bool
+MoofParser::BlockingReadNextMoof()
+{
+ int64_t length = std::numeric_limits<int64_t>::max();
+ mSource->Length(&length);
+ MediaByteRangeSet byteRanges;
+ byteRanges += MediaByteRange(0, length);
+ RefPtr<mp4_demuxer::BlockingStream> stream = new BlockingStream(mSource);
+
+ BoxContext context(stream, byteRanges);
+ for (Box box(&context, mOffset); box.IsAvailable(); box = box.Next()) {
+ if (box.IsType("moof")) {
+ byteRanges.Clear();
+ byteRanges += MediaByteRange(mOffset, box.Range().mEnd);
+ return RebuildFragmentedIndex(context);
+ }
+ }
+ return false;
+}
+
+void
+MoofParser::ScanForMetadata(mozilla::MediaByteRange& aFtyp,
+ mozilla::MediaByteRange& aMoov)
+{
+ int64_t length = std::numeric_limits<int64_t>::max();
+ mSource->Length(&length);
+ MediaByteRangeSet byteRanges;
+ byteRanges += MediaByteRange(0, length);
+ RefPtr<mp4_demuxer::BlockingStream> stream = new BlockingStream(mSource);
+
+ BoxContext context(stream, byteRanges);
+ for (Box box(&context, mOffset); box.IsAvailable(); box = box.Next()) {
+ if (box.IsType("ftyp")) {
+ aFtyp = box.Range();
+ continue;
+ }
+ if (box.IsType("moov")) {
+ aMoov = box.Range();
+ break;
+ }
+ }
+ mInitRange = aFtyp.Span(aMoov);
+}
+
+bool
+MoofParser::HasMetadata()
+{
+ MediaByteRange ftyp;
+ MediaByteRange moov;
+ ScanForMetadata(ftyp, moov);
+ return !!ftyp.Length() && !!moov.Length();
+}
+
+already_AddRefed<mozilla::MediaByteBuffer>
+MoofParser::Metadata()
+{
+ MediaByteRange ftyp;
+ MediaByteRange moov;
+ ScanForMetadata(ftyp, moov);
+ CheckedInt<MediaByteBuffer::size_type> ftypLength = ftyp.Length();
+ CheckedInt<MediaByteBuffer::size_type> moovLength = moov.Length();
+ if (!ftypLength.isValid() || !moovLength.isValid()
+ || !ftypLength.value() || !moovLength.value()) {
+ // No ftyp or moov, or they cannot be used as array size.
+ return nullptr;
+ }
+ CheckedInt<MediaByteBuffer::size_type> totalLength = ftypLength + moovLength;
+ if (!totalLength.isValid()) {
+ // Addition overflow, or sum cannot be used as array size.
+ return nullptr;
+ }
+ RefPtr<MediaByteBuffer> metadata = new MediaByteBuffer();
+ if (!metadata->SetLength(totalLength.value(), fallible)) {
+ // OOM
+ return nullptr;
+ }
+
+ RefPtr<mp4_demuxer::BlockingStream> stream = new BlockingStream(mSource);
+ size_t read;
+ bool rv =
+ stream->ReadAt(ftyp.mStart, metadata->Elements(), ftypLength.value(), &read);
+ if (!rv || read != ftypLength.value()) {
+ return nullptr;
+ }
+ rv =
+ stream->ReadAt(moov.mStart, metadata->Elements() + ftypLength.value(), moovLength.value(), &read);
+ if (!rv || read != moovLength.value()) {
+ return nullptr;
+ }
+ return metadata.forget();
+}
+
+Interval<Microseconds>
+MoofParser::GetCompositionRange(const MediaByteRangeSet& aByteRanges)
+{
+ Interval<Microseconds> compositionRange;
+ BoxContext context(mSource, aByteRanges);
+ for (size_t i = 0; i < mMoofs.Length(); i++) {
+ Moof& moof = mMoofs[i];
+ Box box(&context, moof.mRange.mStart);
+ if (box.IsAvailable()) {
+ compositionRange = compositionRange.Extents(moof.mTimeRange);
+ }
+ }
+ return compositionRange;
+}
+
+bool
+MoofParser::ReachedEnd()
+{
+ int64_t length;
+ return mSource->Length(&length) && mOffset == length;
+}
+
+void
+MoofParser::ParseMoov(Box& aBox)
+{
+ for (Box box = aBox.FirstChild(); box.IsAvailable(); box = box.Next()) {
+ if (box.IsType("mvhd")) {
+ mMvhd = Mvhd(box);
+ } else if (box.IsType("trak")) {
+ ParseTrak(box);
+ } else if (box.IsType("mvex")) {
+ ParseMvex(box);
+ }
+ }
+}
+
+void
+MoofParser::ParseTrak(Box& aBox)
+{
+ Tkhd tkhd;
+ for (Box box = aBox.FirstChild(); box.IsAvailable(); box = box.Next()) {
+ if (box.IsType("tkhd")) {
+ tkhd = Tkhd(box);
+ } else if (box.IsType("mdia")) {
+ if (!mTrex.mTrackId || tkhd.mTrackId == mTrex.mTrackId) {
+ ParseMdia(box, tkhd);
+ }
+ } else if (box.IsType("edts") &&
+ (!mTrex.mTrackId || tkhd.mTrackId == mTrex.mTrackId)) {
+ mEdts = Edts(box);
+ }
+ }
+}
+
+void
+MoofParser::ParseMdia(Box& aBox, Tkhd& aTkhd)
+{
+ for (Box box = aBox.FirstChild(); box.IsAvailable(); box = box.Next()) {
+ if (box.IsType("mdhd")) {
+ mMdhd = Mdhd(box);
+ } else if (box.IsType("minf")) {
+ ParseMinf(box);
+ }
+ }
+}
+
+void
+MoofParser::ParseMvex(Box& aBox)
+{
+ for (Box box = aBox.FirstChild(); box.IsAvailable(); box = box.Next()) {
+ if (box.IsType("trex")) {
+ Trex trex = Trex(box);
+ if (!mTrex.mTrackId || trex.mTrackId == mTrex.mTrackId) {
+ auto trackId = mTrex.mTrackId;
+ mTrex = trex;
+ // Keep the original trackId, as should it be 0 we want to continue
+ // parsing all tracks.
+ mTrex.mTrackId = trackId;
+ }
+ }
+ }
+}
+
+void
+MoofParser::ParseMinf(Box& aBox)
+{
+ for (Box box = aBox.FirstChild(); box.IsAvailable(); box = box.Next()) {
+ if (box.IsType("stbl")) {
+ ParseStbl(box);
+ }
+ }
+}
+
+void
+MoofParser::ParseStbl(Box& aBox)
+{
+ for (Box box = aBox.FirstChild(); box.IsAvailable(); box = box.Next()) {
+ if (box.IsType("stsd")) {
+ ParseStsd(box);
+ }
+ }
+}
+
+void
+MoofParser::ParseStsd(Box& aBox)
+{
+ for (Box box = aBox.FirstChild(); box.IsAvailable(); box = box.Next()) {
+ if (box.IsType("encv") || box.IsType("enca")) {
+ ParseEncrypted(box);
+ }
+ }
+}
+
+void
+MoofParser::ParseEncrypted(Box& aBox)
+{
+ for (Box box = aBox.FirstChild(); box.IsAvailable(); box = box.Next()) {
+ // Some MP4 files have been found to have multiple sinf boxes in the same
+ // enc* box. This does not match spec anyway, so just choose the first
+ // one that parses properly.
+ if (box.IsType("sinf")) {
+ mSinf = Sinf(box);
+
+ if (mSinf.IsValid()) {
+ break;
+ }
+ }
+ }
+}
+
+class CtsComparator
+{
+public:
+ bool Equals(Sample* const aA, Sample* const aB) const
+ {
+ return aA->mCompositionRange.start == aB->mCompositionRange.start;
+ }
+ bool
+ LessThan(Sample* const aA, Sample* const aB) const
+ {
+ return aA->mCompositionRange.start < aB->mCompositionRange.start;
+ }
+};
+
+Moof::Moof(Box& aBox, Trex& aTrex, Mvhd& aMvhd, Mdhd& aMdhd, Edts& aEdts, Sinf& aSinf, uint64_t* aDecodeTime, bool aIsAudio)
+ : mRange(aBox.Range())
+ , mMaxRoundingError(35000)
+{
+ for (Box box = aBox.FirstChild(); box.IsAvailable(); box = box.Next()) {
+ if (box.IsType("traf")) {
+ ParseTraf(box, aTrex, aMvhd, aMdhd, aEdts, aSinf, aDecodeTime, aIsAudio);
+ }
+ }
+ if (IsValid()) {
+ if (mIndex.Length()) {
+ // Ensure the samples are contiguous with no gaps.
+ nsTArray<Sample*> ctsOrder;
+ for (auto& sample : mIndex) {
+ ctsOrder.AppendElement(&sample);
+ }
+ ctsOrder.Sort(CtsComparator());
+
+ for (size_t i = 1; i < ctsOrder.Length(); i++) {
+ ctsOrder[i-1]->mCompositionRange.end = ctsOrder[i]->mCompositionRange.start;
+ }
+
+ // In MP4, the duration of a sample is defined as the delta between two decode
+ // timestamps. The operation above has updated the duration of each sample
+ // as a Sample's duration is mCompositionRange.end - mCompositionRange.start
+ // MSE's TrackBuffersManager expects dts that increased by the sample's
+ // duration, so we rewrite the dts accordingly.
+ int64_t presentationDuration =
+ ctsOrder.LastElement()->mCompositionRange.end
+ - ctsOrder[0]->mCompositionRange.start;
+ int64_t endDecodeTime =
+ aMdhd.ToMicroseconds((int64_t)*aDecodeTime - aEdts.mMediaStart)
+ + aMvhd.ToMicroseconds(aEdts.mEmptyOffset);
+ int64_t decodeDuration = endDecodeTime - mIndex[0].mDecodeTime;
+ double adjust = (double)decodeDuration / presentationDuration;
+ int64_t dtsOffset = mIndex[0].mDecodeTime;
+ int64_t compositionDuration = 0;
+ // Adjust the dts, ensuring that the new adjusted dts will never be greater
+ // than decodeTime (the next moof's decode start time).
+ for (auto& sample : mIndex) {
+ sample.mDecodeTime = dtsOffset + int64_t(compositionDuration * adjust);
+ compositionDuration += sample.mCompositionRange.Length();
+ }
+ mTimeRange = Interval<Microseconds>(ctsOrder[0]->mCompositionRange.start,
+ ctsOrder.LastElement()->mCompositionRange.end);
+ }
+ ProcessCenc();
+ }
+}
+
+bool
+Moof::GetAuxInfo(AtomType aType, nsTArray<MediaByteRange>* aByteRanges)
+{
+ aByteRanges->Clear();
+
+ Saiz* saiz = nullptr;
+ for (int i = 0; ; i++) {
+ if (i == mSaizs.Length()) {
+ return false;
+ }
+ if (mSaizs[i].mAuxInfoType == aType) {
+ saiz = &mSaizs[i];
+ break;
+ }
+ }
+ Saio* saio = nullptr;
+ for (int i = 0; ; i++) {
+ if (i == mSaios.Length()) {
+ return false;
+ }
+ if (mSaios[i].mAuxInfoType == aType) {
+ saio = &mSaios[i];
+ break;
+ }
+ }
+
+ if (saio->mOffsets.Length() == 1) {
+ aByteRanges->SetCapacity(saiz->mSampleInfoSize.Length());
+ uint64_t offset = mRange.mStart + saio->mOffsets[0];
+ for (size_t i = 0; i < saiz->mSampleInfoSize.Length(); i++) {
+ aByteRanges->AppendElement(
+ MediaByteRange(offset, offset + saiz->mSampleInfoSize[i]));
+ offset += saiz->mSampleInfoSize[i];
+ }
+ return true;
+ }
+
+ if (saio->mOffsets.Length() == saiz->mSampleInfoSize.Length()) {
+ aByteRanges->SetCapacity(saiz->mSampleInfoSize.Length());
+ for (size_t i = 0; i < saio->mOffsets.Length(); i++) {
+ uint64_t offset = mRange.mStart + saio->mOffsets[i];
+ aByteRanges->AppendElement(
+ MediaByteRange(offset, offset + saiz->mSampleInfoSize[i]));
+ }
+ return true;
+ }
+
+ return false;
+}
+
+bool
+Moof::ProcessCenc()
+{
+ nsTArray<MediaByteRange> cencRanges;
+ if (!GetAuxInfo(AtomType("cenc"), &cencRanges) ||
+ cencRanges.Length() != mIndex.Length()) {
+ return false;
+ }
+ for (int i = 0; i < cencRanges.Length(); i++) {
+ mIndex[i].mCencRange = cencRanges[i];
+ }
+ return true;
+}
+
+void
+Moof::ParseTraf(Box& aBox, Trex& aTrex, Mvhd& aMvhd, Mdhd& aMdhd, Edts& aEdts, Sinf& aSinf, uint64_t* aDecodeTime, bool aIsAudio)
+{
+ MOZ_ASSERT(aDecodeTime);
+ Tfhd tfhd(aTrex);
+ Tfdt tfdt;
+ for (Box box = aBox.FirstChild(); box.IsAvailable(); box = box.Next()) {
+ if (box.IsType("tfhd")) {
+ tfhd = Tfhd(box, aTrex);
+ } else if (!aTrex.mTrackId || tfhd.mTrackId == aTrex.mTrackId) {
+ if (box.IsType("tfdt")) {
+ tfdt = Tfdt(box);
+ } else if (box.IsType("saiz")) {
+ mSaizs.AppendElement(Saiz(box, aSinf.mDefaultEncryptionType));
+ } else if (box.IsType("saio")) {
+ mSaios.AppendElement(Saio(box, aSinf.mDefaultEncryptionType));
+ }
+ }
+ }
+ if (aTrex.mTrackId && tfhd.mTrackId != aTrex.mTrackId) {
+ return;
+ }
+ // Now search for TRUN boxes.
+ uint64_t decodeTime =
+ tfdt.IsValid() ? tfdt.mBaseMediaDecodeTime : *aDecodeTime;
+ for (Box box = aBox.FirstChild(); box.IsAvailable(); box = box.Next()) {
+ if (box.IsType("trun")) {
+ if (ParseTrun(box, tfhd, aMvhd, aMdhd, aEdts, &decodeTime, aIsAudio)) {
+ mValid = true;
+ } else {
+ mValid = false;
+ break;
+ }
+ }
+ }
+ *aDecodeTime = decodeTime;
+}
+
+void
+Moof::FixRounding(const Moof& aMoof) {
+ Microseconds gap = aMoof.mTimeRange.start - mTimeRange.end;
+ if (gap > 0 && gap <= mMaxRoundingError) {
+ mTimeRange.end = aMoof.mTimeRange.start;
+ }
+}
+
+bool
+Moof::ParseTrun(Box& aBox, Tfhd& aTfhd, Mvhd& aMvhd, Mdhd& aMdhd, Edts& aEdts, uint64_t* aDecodeTime, bool aIsAudio)
+{
+ if (!aTfhd.IsValid() || !aMvhd.IsValid() || !aMdhd.IsValid() ||
+ !aEdts.IsValid()) {
+ LOG(Moof, "Invalid dependencies: aTfhd(%d) aMvhd(%d) aMdhd(%d) aEdts(%d)",
+ aTfhd.IsValid(), aMvhd.IsValid(), aMdhd.IsValid(), !aEdts.IsValid());
+ return false;
+ }
+
+ BoxReader reader(aBox);
+ if (!reader->CanReadType<uint32_t>()) {
+ LOG(Moof, "Incomplete Box (missing flags)");
+ return false;
+ }
+ uint32_t flags = reader->ReadU32();
+ uint8_t version = flags >> 24;
+
+ if (!reader->CanReadType<uint32_t>()) {
+ LOG(Moof, "Incomplete Box (missing sampleCount)");
+ return false;
+ }
+ uint32_t sampleCount = reader->ReadU32();
+ if (sampleCount == 0) {
+ return true;
+ }
+
+ size_t need =
+ ((flags & 1) ? sizeof(uint32_t) : 0) +
+ ((flags & 4) ? sizeof(uint32_t) : 0);
+ uint16_t flag[] = { 0x100, 0x200, 0x400, 0x800, 0 };
+ for (size_t i = 0; flag[i]; i++) {
+ if (flags & flag[i]) {
+ need += sizeof(uint32_t) * sampleCount;
+ }
+ }
+ if (reader->Remaining() < need) {
+ LOG(Moof, "Incomplete Box (have:%lld need:%lld)",
+ reader->Remaining(), need);
+ return false;
+ }
+
+ uint64_t offset = aTfhd.mBaseDataOffset + (flags & 1 ? reader->ReadU32() : 0);
+ uint32_t firstSampleFlags =
+ flags & 4 ? reader->ReadU32() : aTfhd.mDefaultSampleFlags;
+ uint64_t decodeTime = *aDecodeTime;
+ nsTArray<Interval<Microseconds>> timeRanges;
+
+ if (!mIndex.SetCapacity(sampleCount, fallible)) {
+ LOG(Moof, "Out of Memory");
+ return false;
+ }
+
+ for (size_t i = 0; i < sampleCount; i++) {
+ uint32_t sampleDuration =
+ flags & 0x100 ? reader->ReadU32() : aTfhd.mDefaultSampleDuration;
+ uint32_t sampleSize =
+ flags & 0x200 ? reader->ReadU32() : aTfhd.mDefaultSampleSize;
+ uint32_t sampleFlags =
+ flags & 0x400 ? reader->ReadU32()
+ : i ? aTfhd.mDefaultSampleFlags : firstSampleFlags;
+ int32_t ctsOffset = 0;
+ if (flags & 0x800) {
+ ctsOffset = reader->Read32();
+ }
+
+ Sample sample;
+ sample.mByteRange = MediaByteRange(offset, offset + sampleSize);
+ offset += sampleSize;
+
+ sample.mDecodeTime =
+ aMdhd.ToMicroseconds((int64_t)decodeTime - aEdts.mMediaStart) + aMvhd.ToMicroseconds(aEdts.mEmptyOffset);
+ sample.mCompositionRange = Interval<Microseconds>(
+ aMdhd.ToMicroseconds((int64_t)decodeTime + ctsOffset - aEdts.mMediaStart) + aMvhd.ToMicroseconds(aEdts.mEmptyOffset),
+ aMdhd.ToMicroseconds((int64_t)decodeTime + ctsOffset + sampleDuration - aEdts.mMediaStart) + aMvhd.ToMicroseconds(aEdts.mEmptyOffset));
+ decodeTime += sampleDuration;
+
+ // Sometimes audio streams don't properly mark their samples as keyframes,
+ // because every audio sample is a keyframe.
+ sample.mSync = !(sampleFlags & 0x1010000) || aIsAudio;
+
+ // FIXME: Make this infallible after bug 968520 is done.
+ MOZ_ALWAYS_TRUE(mIndex.AppendElement(sample, fallible));
+
+ mMdatRange = mMdatRange.Span(sample.mByteRange);
+ }
+ mMaxRoundingError += aMdhd.ToMicroseconds(sampleCount);
+
+ *aDecodeTime = decodeTime;
+
+ return true;
+}
+
+Tkhd::Tkhd(Box& aBox)
+{
+ BoxReader reader(aBox);
+ if (!reader->CanReadType<uint32_t>()) {
+ LOG(Tkhd, "Incomplete Box (missing flags)");
+ return;
+ }
+ uint32_t flags = reader->ReadU32();
+ uint8_t version = flags >> 24;
+ size_t need =
+ 3*(version ? sizeof(int64_t) : sizeof(int32_t)) + 2*sizeof(int32_t);
+ if (reader->Remaining() < need) {
+ LOG(Tkhd, "Incomplete Box (have:%lld need:%lld)",
+ (uint64_t)reader->Remaining(), (uint64_t)need);
+ return;
+ }
+ if (version == 0) {
+ mCreationTime = reader->ReadU32();
+ mModificationTime = reader->ReadU32();
+ mTrackId = reader->ReadU32();
+ uint32_t reserved = reader->ReadU32();
+ NS_ASSERTION(!reserved, "reserved should be 0");
+ mDuration = reader->ReadU32();
+ } else if (version == 1) {
+ mCreationTime = reader->ReadU64();
+ mModificationTime = reader->ReadU64();
+ mTrackId = reader->ReadU32();
+ uint32_t reserved = reader->ReadU32();
+ NS_ASSERTION(!reserved, "reserved should be 0");
+ mDuration = reader->ReadU64();
+ }
+ // We don't care about whatever else may be in the box.
+ mValid = true;
+}
+
+Mvhd::Mvhd(Box& aBox)
+{
+ BoxReader reader(aBox);
+ if (!reader->CanReadType<uint32_t>()) {
+ LOG(Mdhd, "Incomplete Box (missing flags)");
+ return;
+ }
+ uint32_t flags = reader->ReadU32();
+ uint8_t version = flags >> 24;
+ size_t need =
+ 3*(version ? sizeof(int64_t) : sizeof(int32_t)) + sizeof(uint32_t);
+ if (reader->Remaining() < need) {
+ LOG(Mvhd, "Incomplete Box (have:%lld need:%lld)",
+ (uint64_t)reader->Remaining(), (uint64_t)need);
+ return;
+ }
+
+ if (version == 0) {
+ mCreationTime = reader->ReadU32();
+ mModificationTime = reader->ReadU32();
+ mTimescale = reader->ReadU32();
+ mDuration = reader->ReadU32();
+ } else if (version == 1) {
+ mCreationTime = reader->ReadU64();
+ mModificationTime = reader->ReadU64();
+ mTimescale = reader->ReadU32();
+ mDuration = reader->ReadU64();
+ } else {
+ return;
+ }
+ // We don't care about whatever else may be in the box.
+ if (mTimescale) {
+ mValid = true;
+ }
+}
+
+Mdhd::Mdhd(Box& aBox)
+ : Mvhd(aBox)
+{
+}
+
+Trex::Trex(Box& aBox)
+{
+ BoxReader reader(aBox);
+ if (reader->Remaining() < 6*sizeof(uint32_t)) {
+ LOG(Trex, "Incomplete Box (have:%lld need:%lld)",
+ (uint64_t)reader->Remaining(), (uint64_t)6*sizeof(uint32_t));
+ return;
+ }
+ mFlags = reader->ReadU32();
+ mTrackId = reader->ReadU32();
+ mDefaultSampleDescriptionIndex = reader->ReadU32();
+ mDefaultSampleDuration = reader->ReadU32();
+ mDefaultSampleSize = reader->ReadU32();
+ mDefaultSampleFlags = reader->ReadU32();
+ mValid = true;
+}
+
+Tfhd::Tfhd(Box& aBox, Trex& aTrex)
+ : Trex(aTrex)
+{
+ MOZ_ASSERT(aBox.IsType("tfhd"));
+ MOZ_ASSERT(aBox.Parent()->IsType("traf"));
+ MOZ_ASSERT(aBox.Parent()->Parent()->IsType("moof"));
+
+ BoxReader reader(aBox);
+ if (!reader->CanReadType<uint32_t>()) {
+ LOG(Tfhd, "Incomplete Box (missing flags)");
+ return;
+ }
+ mFlags = reader->ReadU32();
+ size_t need = sizeof(uint32_t) /* trackid */;
+ uint8_t flag[] = { 1, 2, 8, 0x10, 0x20, 0 };
+ uint8_t flagSize[] = { sizeof(uint64_t), sizeof(uint32_t), sizeof(uint32_t), sizeof(uint32_t), sizeof(uint32_t) };
+ for (size_t i = 0; flag[i]; i++) {
+ if (mFlags & flag[i]) {
+ need += flagSize[i];
+ }
+ }
+ if (reader->Remaining() < need) {
+ LOG(Tfhd, "Incomplete Box (have:%lld need:%lld)",
+ (uint64_t)reader->Remaining(), (uint64_t)need);
+ return;
+ }
+ mTrackId = reader->ReadU32();
+ mBaseDataOffset =
+ mFlags & 1 ? reader->ReadU64() : aBox.Parent()->Parent()->Offset();
+ if (mFlags & 2) {
+ mDefaultSampleDescriptionIndex = reader->ReadU32();
+ }
+ if (mFlags & 8) {
+ mDefaultSampleDuration = reader->ReadU32();
+ }
+ if (mFlags & 0x10) {
+ mDefaultSampleSize = reader->ReadU32();
+ }
+ if (mFlags & 0x20) {
+ mDefaultSampleFlags = reader->ReadU32();
+ }
+ mValid = true;
+}
+
+Tfdt::Tfdt(Box& aBox)
+{
+ BoxReader reader(aBox);
+ if (!reader->CanReadType<uint32_t>()) {
+ LOG(Tfdt, "Incomplete Box (missing flags)");
+ return;
+ }
+ uint32_t flags = reader->ReadU32();
+ uint8_t version = flags >> 24;
+ size_t need = version ? sizeof(uint64_t) : sizeof(uint32_t) ;
+ if (reader->Remaining() < need) {
+ LOG(Tfdt, "Incomplete Box (have:%lld need:%lld)",
+ (uint64_t)reader->Remaining(), (uint64_t)need);
+ return;
+ }
+ if (version == 0) {
+ mBaseMediaDecodeTime = reader->ReadU32();
+ } else if (version == 1) {
+ mBaseMediaDecodeTime = reader->ReadU64();
+ }
+ mValid = true;
+}
+
+Edts::Edts(Box& aBox)
+ : mMediaStart(0)
+ , mEmptyOffset(0)
+{
+ Box child = aBox.FirstChild();
+ if (!child.IsType("elst")) {
+ return;
+ }
+
+ BoxReader reader(child);
+ if (!reader->CanReadType<uint32_t>()) {
+ LOG(Edts, "Incomplete Box (missing flags)");
+ return;
+ }
+ uint32_t flags = reader->ReadU32();
+ uint8_t version = flags >> 24;
+ size_t need =
+ sizeof(uint32_t) + 2*(version ? sizeof(int64_t) : sizeof(uint32_t));
+ if (reader->Remaining() < need) {
+ LOG(Edts, "Incomplete Box (have:%lld need:%lld)",
+ (uint64_t)reader->Remaining(), (uint64_t)need);
+ return;
+ }
+ bool emptyEntry = false;
+ uint32_t entryCount = reader->ReadU32();
+ for (uint32_t i = 0; i < entryCount; i++) {
+ uint64_t segment_duration;
+ int64_t media_time;
+ if (version == 1) {
+ segment_duration = reader->ReadU64();
+ media_time = reader->Read64();
+ } else {
+ segment_duration = reader->ReadU32();
+ media_time = reader->Read32();
+ }
+ if (media_time == -1 && i) {
+ LOG(Edts, "Multiple empty edit, not handled");
+ } else if (media_time == -1) {
+ mEmptyOffset = segment_duration;
+ emptyEntry = true;
+ } else if (i > 1 || (i > 0 && !emptyEntry)) {
+ LOG(Edts, "More than one edit entry, not handled. A/V sync will be wrong");
+ break;
+ } else {
+ mMediaStart = media_time;
+ }
+ reader->ReadU32(); // media_rate_integer and media_rate_fraction
+ }
+}
+
+Saiz::Saiz(Box& aBox, AtomType aDefaultType)
+ : mAuxInfoType(aDefaultType)
+ , mAuxInfoTypeParameter(0)
+{
+ BoxReader reader(aBox);
+ if (!reader->CanReadType<uint32_t>()) {
+ LOG(Saiz, "Incomplete Box (missing flags)");
+ return;
+ }
+ uint32_t flags = reader->ReadU32();
+ uint8_t version = flags >> 24;
+ size_t need =
+ ((flags & 1) ? 2*sizeof(uint32_t) : 0) + sizeof(uint8_t) + sizeof(uint32_t);
+ if (reader->Remaining() < need) {
+ LOG(Saiz, "Incomplete Box (have:%lld need:%lld)",
+ (uint64_t)reader->Remaining(), (uint64_t)need);
+ return;
+ }
+ if (flags & 1) {
+ mAuxInfoType = reader->ReadU32();
+ mAuxInfoTypeParameter = reader->ReadU32();
+ }
+ uint8_t defaultSampleInfoSize = reader->ReadU8();
+ uint32_t count = reader->ReadU32();
+ if (defaultSampleInfoSize) {
+ if (!mSampleInfoSize.SetLength(count, fallible)) {
+ LOG(Saiz, "OOM");
+ return;
+ }
+ memset(mSampleInfoSize.Elements(), defaultSampleInfoSize, mSampleInfoSize.Length());
+ } else {
+ if (!reader->ReadArray(mSampleInfoSize, count)) {
+ LOG(Saiz, "Incomplete Box (OOM or missing count:%u)", count);
+ return;
+ }
+ }
+ mValid = true;
+}
+
+Saio::Saio(Box& aBox, AtomType aDefaultType)
+ : mAuxInfoType(aDefaultType)
+ , mAuxInfoTypeParameter(0)
+{
+ BoxReader reader(aBox);
+ if (!reader->CanReadType<uint32_t>()) {
+ LOG(Saio, "Incomplete Box (missing flags)");
+ return;
+ }
+ uint32_t flags = reader->ReadU32();
+ uint8_t version = flags >> 24;
+ size_t need = ((flags & 1) ? (2*sizeof(uint32_t)) : 0) + sizeof(uint32_t);
+ if (reader->Remaining() < need) {
+ LOG(Saio, "Incomplete Box (have:%lld need:%lld)",
+ (uint64_t)reader->Remaining(), (uint64_t)need);
+ return;
+ }
+ if (flags & 1) {
+ mAuxInfoType = reader->ReadU32();
+ mAuxInfoTypeParameter = reader->ReadU32();
+ }
+ size_t count = reader->ReadU32();
+ need = (version ? sizeof(uint64_t) : sizeof(uint32_t)) * count;
+ if (reader->Remaining() < need) {
+ LOG(Saio, "Incomplete Box (have:%lld need:%lld)",
+ (uint64_t)reader->Remaining(), (uint64_t)need);
+ return;
+ }
+ if (!mOffsets.SetCapacity(count, fallible)) {
+ LOG(Saiz, "OOM");
+ return;
+ }
+ if (version == 0) {
+ for (size_t i = 0; i < count; i++) {
+ MOZ_ALWAYS_TRUE(mOffsets.AppendElement(reader->ReadU32(), fallible));
+ }
+ } else {
+ for (size_t i = 0; i < count; i++) {
+ MOZ_ALWAYS_TRUE(mOffsets.AppendElement(reader->ReadU64(), fallible));
+ }
+ }
+ mValid = true;
+}
+
+#undef LOG
+}
diff --git a/media/libstagefright/binding/ResourceStream.cpp b/media/libstagefright/binding/ResourceStream.cpp
new file mode 100644
index 000000000..2fd042cf4
--- /dev/null
+++ b/media/libstagefright/binding/ResourceStream.cpp
@@ -0,0 +1,67 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mp4_demuxer/ResourceStream.h"
+
+namespace mp4_demuxer {
+
+ResourceStream::ResourceStream(mozilla::MediaResource* aResource)
+ : mResource(aResource)
+ , mPinCount(0)
+{
+ MOZ_ASSERT(aResource);
+}
+
+ResourceStream::~ResourceStream()
+{
+ MOZ_ASSERT(mPinCount == 0);
+}
+
+bool
+ResourceStream::ReadAt(int64_t aOffset, void* aBuffer, size_t aCount,
+ size_t* aBytesRead)
+{
+ uint32_t sum = 0;
+ uint32_t bytesRead = 0;
+ do {
+ uint64_t offset = aOffset + sum;
+ char* buffer = reinterpret_cast<char*>(aBuffer) + sum;
+ uint32_t toRead = aCount - sum;
+ nsresult rv = mResource->ReadAt(offset, buffer, toRead, &bytesRead);
+ if (NS_FAILED(rv)) {
+ return false;
+ }
+ sum += bytesRead;
+ } while (sum < aCount && bytesRead > 0);
+
+ *aBytesRead = sum;
+ return true;
+}
+
+bool
+ResourceStream::CachedReadAt(int64_t aOffset, void* aBuffer, size_t aCount,
+ size_t* aBytesRead)
+{
+ nsresult rv = mResource->ReadFromCache(reinterpret_cast<char*>(aBuffer),
+ aOffset, aCount);
+ if (NS_FAILED(rv)) {
+ *aBytesRead = 0;
+ return false;
+ }
+ *aBytesRead = aCount;
+ return true;
+}
+
+bool
+ResourceStream::Length(int64_t* aSize)
+{
+ if (mResource->GetLength() < 0)
+ return false;
+ *aSize = mResource->GetLength();
+ return true;
+}
+
+} // namespace mp4_demuxer
diff --git a/media/libstagefright/binding/SinfParser.cpp b/media/libstagefright/binding/SinfParser.cpp
new file mode 100644
index 000000000..5cf3aa553
--- /dev/null
+++ b/media/libstagefright/binding/SinfParser.cpp
@@ -0,0 +1,72 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/Unused.h"
+#include "mp4_demuxer/SinfParser.h"
+#include "mp4_demuxer/AtomType.h"
+#include "mp4_demuxer/Box.h"
+
+namespace mp4_demuxer {
+
+Sinf::Sinf(Box& aBox)
+ : mDefaultIVSize(0)
+ , mDefaultEncryptionType()
+{
+ SinfParser parser(aBox);
+ if (parser.GetSinf().IsValid()) {
+ *this = parser.GetSinf();
+ }
+}
+
+SinfParser::SinfParser(Box& aBox)
+{
+ for (Box box = aBox.FirstChild(); box.IsAvailable(); box = box.Next()) {
+ if (box.IsType("schm")) {
+ ParseSchm(box);
+ } else if (box.IsType("schi")) {
+ ParseSchi(box);
+ }
+ }
+}
+
+void
+SinfParser::ParseSchm(Box& aBox)
+{
+ BoxReader reader(aBox);
+
+ if (reader->Remaining() < 8) {
+ return;
+ }
+
+ mozilla::Unused << reader->ReadU32(); // flags -- ignore
+ mSinf.mDefaultEncryptionType = reader->ReadU32();
+}
+
+void
+SinfParser::ParseSchi(Box& aBox)
+{
+ for (Box box = aBox.FirstChild(); box.IsAvailable(); box = box.Next()) {
+ if (box.IsType("tenc")) {
+ ParseTenc(box);
+ }
+ }
+}
+
+void
+SinfParser::ParseTenc(Box& aBox)
+{
+ BoxReader reader(aBox);
+
+ if (reader->Remaining() < 24) {
+ return;
+ }
+
+ mozilla::Unused << reader->ReadU32(); // flags -- ignore
+
+ uint32_t isEncrypted = reader->ReadU24();
+ mSinf.mDefaultIVSize = reader->ReadU8();
+ memcpy(mSinf.mDefaultKeyID, reader->Read(16), 16);
+}
+
+}
diff --git a/media/libstagefright/binding/include/demuxer/TrackDemuxer.h b/media/libstagefright/binding/include/demuxer/TrackDemuxer.h
new file mode 100644
index 000000000..c3f72648b
--- /dev/null
+++ b/media/libstagefright/binding/include/demuxer/TrackDemuxer.h
@@ -0,0 +1,34 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef TRACK_DEMUXER_H_
+#define TRACK_DEMUXER_H_
+
+template <class T> struct already_AddRefed;
+
+namespace mozilla {
+
+class MediaRawData;
+class MediaByteRange;
+
+class TrackDemuxer {
+public:
+ typedef int64_t Microseconds;
+
+ TrackDemuxer() {}
+ virtual ~TrackDemuxer() {}
+
+ virtual void Seek(Microseconds aTime) = 0;
+
+ // DemuxSample returns nullptr on end of stream or error.
+ virtual already_AddRefed<MediaRawData> DemuxSample() = 0;
+
+ // Returns timestamp of next keyframe, or -1 if demuxer can't
+ // report this.
+ virtual Microseconds GetNextKeyframeTime() = 0;
+};
+
+}
+
+#endif
diff --git a/media/libstagefright/binding/include/mp4_demuxer/Adts.h b/media/libstagefright/binding/include/mp4_demuxer/Adts.h
new file mode 100644
index 000000000..8d03beb4b
--- /dev/null
+++ b/media/libstagefright/binding/include/mp4_demuxer/Adts.h
@@ -0,0 +1,26 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef ADTS_H_
+#define ADTS_H_
+
+#include <stdint.h>
+
+namespace mozilla {
+class MediaRawData;
+}
+
+namespace mp4_demuxer
+{
+
+class Adts
+{
+public:
+ static int8_t GetFrequencyIndex(uint32_t aSamplesPerSecond);
+ static bool ConvertSample(uint16_t aChannelCount, int8_t aFrequencyIndex,
+ int8_t aProfile, mozilla::MediaRawData* aSample);
+};
+}
+
+#endif
diff --git a/media/libstagefright/binding/include/mp4_demuxer/AnnexB.h b/media/libstagefright/binding/include/mp4_demuxer/AnnexB.h
new file mode 100644
index 000000000..879e40c84
--- /dev/null
+++ b/media/libstagefright/binding/include/mp4_demuxer/AnnexB.h
@@ -0,0 +1,55 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MP4_DEMUXER_ANNEX_B_H_
+#define MP4_DEMUXER_ANNEX_B_H_
+
+template <class T> struct already_AddRefed;
+
+namespace mozilla {
+class MediaRawData;
+class MediaByteBuffer;
+}
+namespace mp4_demuxer
+{
+class ByteReader;
+
+class AnnexB
+{
+public:
+ // All conversions assume size of NAL length field is 4 bytes.
+ // Convert a sample from AVCC format to Annex B.
+ static bool ConvertSampleToAnnexB(mozilla::MediaRawData* aSample, bool aAddSPS = true);
+ // Convert a sample from Annex B to AVCC.
+ // an AVCC extradata must not be set.
+ static bool ConvertSampleToAVCC(mozilla::MediaRawData* aSample);
+ static bool ConvertSampleTo4BytesAVCC(mozilla::MediaRawData* aSample);
+
+ // Parse an AVCC extradata and construct the Annex B sample header.
+ static already_AddRefed<mozilla::MediaByteBuffer> ConvertExtraDataToAnnexB(
+ const mozilla::MediaByteBuffer* aExtraData);
+ // Extract SPS and PPS NALs from aSample, aSample must be in AVCC format.
+ // If aSample already contains an extradata with an SPS, it will be returned
+ // otherwise the SPS/PPS NALs are searched in-band.
+ static already_AddRefed<mozilla::MediaByteBuffer> ExtractExtraData(
+ const mozilla::MediaRawData* aSample);
+ static bool HasSPS(const mozilla::MediaRawData* aSample);
+ static bool HasSPS(const mozilla::MediaByteBuffer* aExtraData);
+ // Returns true if format is AVCC and sample has valid extradata.
+ static bool IsAVCC(const mozilla::MediaRawData* aSample);
+ // Returns true if format is AnnexB.
+ static bool IsAnnexB(const mozilla::MediaRawData* aSample);
+ // Return true if both extradata are equal.
+ static bool CompareExtraData(const mozilla::MediaByteBuffer* aExtraData1,
+ const mozilla::MediaByteBuffer* aExtraData2);
+
+private:
+ // AVCC box parser helper.
+ static void ConvertSPSOrPPS(ByteReader& aReader, uint8_t aCount,
+ mozilla::MediaByteBuffer* aAnnexB);
+};
+
+} // namespace mp4_demuxer
+
+#endif // MP4_DEMUXER_ANNEX_B_H_
diff --git a/media/libstagefright/binding/include/mp4_demuxer/Atom.h b/media/libstagefright/binding/include/mp4_demuxer/Atom.h
new file mode 100644
index 000000000..48f2878d4
--- /dev/null
+++ b/media/libstagefright/binding/include/mp4_demuxer/Atom.h
@@ -0,0 +1,27 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef ATOM_H_
+#define ATOM_H_
+
+namespace mp4_demuxer {
+
+class Atom
+{
+public:
+ Atom()
+ : mValid(false)
+ {
+ }
+ virtual bool IsValid()
+ {
+ return mValid;
+ }
+protected:
+ bool mValid;
+};
+
+}
+
+#endif // ATOM_H_
diff --git a/media/libstagefright/binding/include/mp4_demuxer/AtomType.h b/media/libstagefright/binding/include/mp4_demuxer/AtomType.h
new file mode 100644
index 000000000..95baedfe7
--- /dev/null
+++ b/media/libstagefright/binding/include/mp4_demuxer/AtomType.h
@@ -0,0 +1,31 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef ATOM_TYPE_H_
+#define ATOM_TYPE_H_
+
+#include <stdint.h>
+#include "mozilla/EndianUtils.h"
+
+using namespace mozilla;
+
+namespace mp4_demuxer {
+
+class AtomType
+{
+public:
+ AtomType() : mType(0) { }
+ MOZ_IMPLICIT AtomType(uint32_t aType) : mType(aType) { }
+ MOZ_IMPLICIT AtomType(const char* aType) : mType(BigEndian::readUint32(aType)) { }
+ bool operator==(const AtomType& aType) const { return mType == aType.mType; }
+ bool operator!() const { return !mType; }
+
+private:
+ uint32_t mType;
+};
+}
+
+#endif
diff --git a/media/libstagefright/binding/include/mp4_demuxer/BitReader.h b/media/libstagefright/binding/include/mp4_demuxer/BitReader.h
new file mode 100644
index 000000000..27a10338f
--- /dev/null
+++ b/media/libstagefright/binding/include/mp4_demuxer/BitReader.h
@@ -0,0 +1,45 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef BIT_READER_H_
+#define BIT_READER_H_
+
+#include "nsAutoPtr.h"
+#include "MediaData.h"
+
+namespace stagefright { class ABitReader; }
+
+namespace mp4_demuxer
+{
+
+class BitReader
+{
+public:
+ explicit BitReader(const mozilla::MediaByteBuffer* aBuffer);
+ BitReader(const uint8_t* aBuffer, size_t aLength);
+ ~BitReader();
+ uint32_t ReadBits(size_t aNum);
+ uint32_t ReadBit() { return ReadBits(1); }
+ uint32_t ReadU32() { return ReadBits(32); }
+ uint64_t ReadU64();
+
+ // Read the UTF-8 sequence and convert it to its 64-bit UCS-4 encoded form.
+ // Return 0xfffffffffffffff if sequence was invalid.
+ uint64_t ReadUTF8();
+ // Read unsigned integer Exp-Golomb-coded.
+ uint32_t ReadUE();
+ // Read signed integer Exp-Golomb-coded.
+ int32_t ReadSE();
+
+ // Return the number of bits parsed so far;
+ size_t BitCount() const;
+
+private:
+ nsAutoPtr<stagefright::ABitReader> mBitReader;
+ const size_t mSize;
+};
+
+} // namespace mp4_demuxer
+
+#endif // BIT_READER_H_ \ No newline at end of file
diff --git a/media/libstagefright/binding/include/mp4_demuxer/Box.h b/media/libstagefright/binding/include/mp4_demuxer/Box.h
new file mode 100644
index 000000000..f53404a1d
--- /dev/null
+++ b/media/libstagefright/binding/include/mp4_demuxer/Box.h
@@ -0,0 +1,84 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef BOX_H_
+#define BOX_H_
+
+#include <stdint.h>
+#include "nsTArray.h"
+#include "MediaResource.h"
+#include "mozilla/EndianUtils.h"
+#include "mp4_demuxer/AtomType.h"
+#include "mp4_demuxer/ByteReader.h"
+
+using namespace mozilla;
+
+namespace mp4_demuxer {
+
+class Stream;
+
+class BoxContext
+{
+public:
+ BoxContext(Stream* aSource, const MediaByteRangeSet& aByteRanges)
+ : mSource(aSource), mByteRanges(aByteRanges)
+ {
+ }
+
+ RefPtr<Stream> mSource;
+ const MediaByteRangeSet& mByteRanges;
+};
+
+class Box
+{
+public:
+ Box(BoxContext* aContext, uint64_t aOffset, const Box* aParent = nullptr);
+ Box();
+
+ bool IsAvailable() const { return !mRange.IsEmpty(); }
+ uint64_t Offset() const { return mRange.mStart; }
+ uint64_t Length() const { return mRange.mEnd - mRange.mStart; }
+ uint64_t NextOffset() const { return mRange.mEnd; }
+ const MediaByteRange& Range() const { return mRange; }
+ const Box* Parent() const { return mParent; }
+ bool IsType(const char* aType) const { return mType == AtomType(aType); }
+
+ Box Next() const;
+ Box FirstChild() const;
+ nsTArray<uint8_t> Read();
+ bool Read(nsTArray<uint8_t>* aDest, const MediaByteRange& aRange);
+
+ static const uint64_t kMAX_BOX_READ;
+
+private:
+ bool Contains(MediaByteRange aRange) const;
+ BoxContext* mContext;
+ mozilla::MediaByteRange mRange;
+ uint64_t mBodyOffset;
+ uint64_t mChildOffset;
+ AtomType mType;
+ const Box* mParent;
+};
+
+// BoxReader takes a copy of a box contents and serves through an AutoByteReader.
+MOZ_RAII
+class BoxReader
+{
+public:
+ explicit BoxReader(Box& aBox)
+ : mBuffer(aBox.Read())
+ , mReader(mBuffer.Elements(), mBuffer.Length())
+ {
+ }
+ ByteReader* operator->() { return &mReader; }
+
+private:
+ nsTArray<uint8_t> mBuffer;
+ ByteReader mReader;
+};
+}
+
+#endif
diff --git a/media/libstagefright/binding/include/mp4_demuxer/BufferStream.h b/media/libstagefright/binding/include/mp4_demuxer/BufferStream.h
new file mode 100644
index 000000000..bb703db5d
--- /dev/null
+++ b/media/libstagefright/binding/include/mp4_demuxer/BufferStream.h
@@ -0,0 +1,46 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef BUFFER_STREAM_H_
+#define BUFFER_STREAM_H_
+
+#include "mp4_demuxer/Stream.h"
+#include "nsTArray.h"
+#include "MediaResource.h"
+
+namespace mozilla {
+class MediaByteBuffer;
+}
+
+namespace mp4_demuxer {
+
+class BufferStream : public Stream
+{
+public:
+ /* BufferStream does not take ownership of aData nor does it make a copy.
+ * Therefore BufferStream shouldn't get used after aData is destroyed.
+ */
+ BufferStream();
+ explicit BufferStream(mozilla::MediaByteBuffer* aBuffer);
+
+ virtual bool ReadAt(int64_t aOffset, void* aData, size_t aLength,
+ size_t* aBytesRead) override;
+ virtual bool CachedReadAt(int64_t aOffset, void* aData, size_t aLength,
+ size_t* aBytesRead) override;
+ virtual bool Length(int64_t* aLength) override;
+
+ virtual void DiscardBefore(int64_t aOffset) override;
+
+ bool AppendBytes(const uint8_t* aData, size_t aLength);
+
+ mozilla::MediaByteRange GetByteRange();
+
+private:
+ ~BufferStream();
+ int64_t mStartOffset;
+ RefPtr<mozilla::MediaByteBuffer> mData;
+};
+}
+
+#endif
diff --git a/media/libstagefright/binding/include/mp4_demuxer/ByteReader.h b/media/libstagefright/binding/include/mp4_demuxer/ByteReader.h
new file mode 100644
index 000000000..9c7df04bd
--- /dev/null
+++ b/media/libstagefright/binding/include/mp4_demuxer/ByteReader.h
@@ -0,0 +1,349 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef BYTE_READER_H_
+#define BYTE_READER_H_
+
+#include "mozilla/EndianUtils.h"
+#include "mozilla/Vector.h"
+#include "nsTArray.h"
+#include "MediaData.h"
+
+namespace mp4_demuxer {
+
+class MOZ_RAII ByteReader
+{
+public:
+ ByteReader() : mPtr(nullptr), mRemaining(0) {}
+ explicit ByteReader(const mozilla::Vector<uint8_t>& aData)
+ : mPtr(aData.begin()), mRemaining(aData.length()), mLength(aData.length())
+ {
+ }
+ ByteReader(const uint8_t* aData, size_t aSize)
+ : mPtr(aData), mRemaining(aSize), mLength(aSize)
+ {
+ }
+ template<size_t S>
+ explicit ByteReader(const AutoTArray<uint8_t, S>& aData)
+ : mPtr(aData.Elements()), mRemaining(aData.Length()), mLength(aData.Length())
+ {
+ }
+ explicit ByteReader(const nsTArray<uint8_t>& aData)
+ : mPtr(aData.Elements()), mRemaining(aData.Length()), mLength(aData.Length())
+ {
+ }
+ explicit ByteReader(const mozilla::MediaByteBuffer* aData)
+ : mPtr(aData->Elements()), mRemaining(aData->Length()), mLength(aData->Length())
+ {
+ }
+
+ void SetData(const nsTArray<uint8_t>& aData)
+ {
+ MOZ_ASSERT(!mPtr && !mRemaining);
+ mPtr = aData.Elements();
+ mRemaining = aData.Length();
+ mLength = mRemaining;
+ }
+
+ ~ByteReader()
+ {
+ }
+
+ size_t Offset()
+ {
+ return mLength - mRemaining;
+ }
+
+ size_t Remaining() const { return mRemaining; }
+
+ bool CanRead8() { return mRemaining >= 1; }
+
+ uint8_t ReadU8()
+ {
+ auto ptr = Read(1);
+ if (!ptr) {
+ MOZ_ASSERT(false);
+ return 0;
+ }
+ return *ptr;
+ }
+
+ bool CanRead16() { return mRemaining >= 2; }
+
+ uint16_t ReadU16()
+ {
+ auto ptr = Read(2);
+ if (!ptr) {
+ MOZ_ASSERT(false);
+ return 0;
+ }
+ return mozilla::BigEndian::readUint16(ptr);
+ }
+
+ int16_t ReadLE16()
+ {
+ auto ptr = Read(2);
+ if (!ptr) {
+ MOZ_ASSERT(false);
+ return 0;
+ }
+ return mozilla::LittleEndian::readInt16(ptr);
+ }
+
+ uint32_t ReadU24()
+ {
+ auto ptr = Read(3);
+ if (!ptr) {
+ MOZ_ASSERT(false);
+ return 0;
+ }
+ return ptr[0] << 16 | ptr[1] << 8 | ptr[2];
+ }
+
+ uint32_t Read24()
+ {
+ return (uint32_t)ReadU24();
+ }
+
+ int32_t ReadLE24()
+ {
+ auto ptr = Read(3);
+ if (!ptr) {
+ MOZ_ASSERT(false);
+ return 0;
+ }
+ int32_t result = int32_t(ptr[2] << 16 | ptr[1] << 8 | ptr[0]);
+ if (result & 0x00800000u) {
+ result -= 0x1000000;
+ }
+ return result;
+ }
+
+ bool CanRead32() { return mRemaining >= 4; }
+
+ uint32_t ReadU32()
+ {
+ auto ptr = Read(4);
+ if (!ptr) {
+ MOZ_ASSERT(false);
+ return 0;
+ }
+ return mozilla::BigEndian::readUint32(ptr);
+ }
+
+ int32_t Read32()
+ {
+ auto ptr = Read(4);
+ if (!ptr) {
+ MOZ_ASSERT(false);
+ return 0;
+ }
+ return mozilla::BigEndian::readInt32(ptr);
+ }
+
+ uint64_t ReadU64()
+ {
+ auto ptr = Read(8);
+ if (!ptr) {
+ MOZ_ASSERT(false);
+ return 0;
+ }
+ return mozilla::BigEndian::readUint64(ptr);
+ }
+
+ int64_t Read64()
+ {
+ auto ptr = Read(8);
+ if (!ptr) {
+ MOZ_ASSERT(false);
+ return 0;
+ }
+ return mozilla::BigEndian::readInt64(ptr);
+ }
+
+ const uint8_t* Read(size_t aCount)
+ {
+ if (aCount > mRemaining) {
+ mRemaining = 0;
+ return nullptr;
+ }
+ mRemaining -= aCount;
+
+ const uint8_t* result = mPtr;
+ mPtr += aCount;
+
+ return result;
+ }
+
+ const uint8_t* Rewind(size_t aCount)
+ {
+ MOZ_ASSERT(aCount <= Offset());
+ size_t rewind = Offset();
+ if (aCount < rewind) {
+ rewind = aCount;
+ }
+ mRemaining += rewind;
+ mPtr -= rewind;
+ return mPtr;
+ }
+
+ uint8_t PeekU8()
+ {
+ auto ptr = Peek(1);
+ if (!ptr) {
+ MOZ_ASSERT(false);
+ return 0;
+ }
+ return *ptr;
+ }
+
+ uint16_t PeekU16()
+ {
+ auto ptr = Peek(2);
+ if (!ptr) {
+ MOZ_ASSERT(false);
+ return 0;
+ }
+ return mozilla::BigEndian::readUint16(ptr);
+ }
+
+ uint32_t PeekU24()
+ {
+ auto ptr = Peek(3);
+ if (!ptr) {
+ MOZ_ASSERT(false);
+ return 0;
+ }
+ return ptr[0] << 16 | ptr[1] << 8 | ptr[2];
+ }
+
+ uint32_t Peek24()
+ {
+ return (uint32_t)PeekU24();
+ }
+
+ uint32_t PeekU32()
+ {
+ auto ptr = Peek(4);
+ if (!ptr) {
+ MOZ_ASSERT(false);
+ return 0;
+ }
+ return mozilla::BigEndian::readUint32(ptr);
+ }
+
+ int32_t Peek32()
+ {
+ auto ptr = Peek(4);
+ if (!ptr) {
+ MOZ_ASSERT(false);
+ return 0;
+ }
+ return mozilla::BigEndian::readInt32(ptr);
+ }
+
+ uint64_t PeekU64()
+ {
+ auto ptr = Peek(8);
+ if (!ptr) {
+ MOZ_ASSERT(false);
+ return 0;
+ }
+ return mozilla::BigEndian::readUint64(ptr);
+ }
+
+ int64_t Peek64()
+ {
+ auto ptr = Peek(8);
+ if (!ptr) {
+ MOZ_ASSERT(false);
+ return 0;
+ }
+ return mozilla::BigEndian::readInt64(ptr);
+ }
+
+ const uint8_t* Peek(size_t aCount)
+ {
+ if (aCount > mRemaining) {
+ return nullptr;
+ }
+ return mPtr;
+ }
+
+ const uint8_t* Seek(size_t aOffset)
+ {
+ if (aOffset >= mLength) {
+ MOZ_ASSERT(false);
+ return nullptr;
+ }
+
+ mPtr = mPtr - Offset() + aOffset;
+ mRemaining = mLength - aOffset;
+ return mPtr;
+ }
+
+ const uint8_t* Reset()
+ {
+ mPtr -= Offset();
+ mRemaining = mLength;
+ return mPtr;
+ }
+
+ uint32_t Align()
+ {
+ return 4 - ((intptr_t)mPtr & 3);
+ }
+
+ template <typename T> bool CanReadType() { return mRemaining >= sizeof(T); }
+
+ template <typename T> T ReadType()
+ {
+ auto ptr = Read(sizeof(T));
+ if (!ptr) {
+ MOZ_ASSERT(false);
+ return 0;
+ }
+ return *reinterpret_cast<const T*>(ptr);
+ }
+
+ template <typename T>
+ MOZ_MUST_USE bool ReadArray(nsTArray<T>& aDest, size_t aLength)
+ {
+ auto ptr = Read(aLength * sizeof(T));
+ if (!ptr) {
+ return false;
+ }
+
+ aDest.Clear();
+ aDest.AppendElements(reinterpret_cast<const T*>(ptr), aLength);
+ return true;
+ }
+
+ template <typename T>
+ MOZ_MUST_USE bool ReadArray(FallibleTArray<T>& aDest, size_t aLength)
+ {
+ auto ptr = Read(aLength * sizeof(T));
+ if (!ptr) {
+ return false;
+ }
+
+ aDest.Clear();
+ if (!aDest.SetCapacity(aLength, mozilla::fallible)) {
+ return false;
+ }
+ MOZ_ALWAYS_TRUE(aDest.AppendElements(reinterpret_cast<const T*>(ptr),
+ aLength,
+ mozilla::fallible));
+ return true;
+ }
+
+private:
+ const uint8_t* mPtr;
+ size_t mRemaining;
+ size_t mLength;
+};
+
+} // namespace mp4_demuxer
+
+#endif
diff --git a/media/libstagefright/binding/include/mp4_demuxer/ByteWriter.h b/media/libstagefright/binding/include/mp4_demuxer/ByteWriter.h
new file mode 100644
index 000000000..48ebdd460
--- /dev/null
+++ b/media/libstagefright/binding/include/mp4_demuxer/ByteWriter.h
@@ -0,0 +1,76 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef BYTE_WRITER_H_
+#define BYTE_WRITER_H_
+
+#include "mozilla/EndianUtils.h"
+#include "mozilla/Vector.h"
+#include "nsTArray.h"
+
+namespace mp4_demuxer {
+
+class ByteWriter
+{
+public:
+ explicit ByteWriter(mozilla::Vector<uint8_t>& aData)
+ : mPtr(aData)
+ {
+ }
+ ~ByteWriter()
+ {
+ }
+
+ MOZ_MUST_USE bool WriteU8(uint8_t aByte)
+ {
+ return Write(&aByte, 1);
+ }
+
+ MOZ_MUST_USE bool WriteU16(uint16_t aShort)
+ {
+ uint8_t c[2];
+ mozilla::BigEndian::writeUint16(&c[0], aShort);
+ return Write(&c[0], 2);
+ }
+
+ MOZ_MUST_USE bool WriteU32(uint32_t aLong)
+ {
+ uint8_t c[4];
+ mozilla::BigEndian::writeUint32(&c[0], aLong);
+ return Write(&c[0], 4);
+ }
+
+ MOZ_MUST_USE bool Write32(int32_t aLong)
+ {
+ uint8_t c[4];
+ mozilla::BigEndian::writeInt32(&c[0], aLong);
+ return Write(&c[0], 4);
+ }
+
+ MOZ_MUST_USE bool WriteU64(uint64_t aLongLong)
+ {
+ uint8_t c[8];
+ mozilla::BigEndian::writeUint64(&c[0], aLongLong);
+ return Write(&c[0], 8);
+ }
+
+ MOZ_MUST_USE bool Write64(int64_t aLongLong)
+ {
+ uint8_t c[8];
+ mozilla::BigEndian::writeInt64(&c[0], aLongLong);
+ return Write(&c[0], 8);
+ }
+
+ MOZ_MUST_USE bool Write(const uint8_t* aSrc, size_t aCount)
+ {
+ return mPtr.append(aSrc, aCount);
+ }
+
+private:
+ mozilla::Vector<uint8_t>& mPtr;
+};
+
+} // namespace mp4_demuxer
+
+#endif
diff --git a/media/libstagefright/binding/include/mp4_demuxer/DecoderData.h b/media/libstagefright/binding/include/mp4_demuxer/DecoderData.h
new file mode 100644
index 000000000..87262c26a
--- /dev/null
+++ b/media/libstagefright/binding/include/mp4_demuxer/DecoderData.h
@@ -0,0 +1,93 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef DECODER_DATA_H_
+#define DECODER_DATA_H_
+
+#include "MediaData.h"
+#include "MediaInfo.h"
+#include "mozilla/Types.h"
+#include "mozilla/Vector.h"
+#include "mozilla/RefPtr.h"
+#include "nsString.h"
+#include "nsTArray.h"
+#include "nsString.h"
+
+namespace stagefright
+{
+class MetaData;
+}
+
+#ifdef MOZ_RUST_MP4PARSE
+extern "C" {
+typedef struct mp4parse_track_info mp4parse_track_info;
+typedef struct mp4parse_track_audio_info mp4parse_track_audio_info;
+typedef struct mp4parse_track_video_info mp4parse_track_video_info;
+}
+#endif
+
+namespace mp4_demuxer
+{
+
+class MP4Demuxer;
+
+struct PsshInfo
+{
+ PsshInfo() {}
+ PsshInfo(const PsshInfo& aOther) : uuid(aOther.uuid), data(aOther.data) {}
+ nsTArray<uint8_t> uuid;
+ nsTArray<uint8_t> data;
+};
+
+class CryptoFile
+{
+public:
+ CryptoFile() : valid(false) {}
+ CryptoFile(const CryptoFile& aCryptoFile) : valid(aCryptoFile.valid)
+ {
+ pssh.AppendElements(aCryptoFile.pssh);
+ }
+
+ void Update(const uint8_t* aData, size_t aLength)
+ {
+ valid = DoUpdate(aData, aLength);
+ }
+
+ bool valid;
+ nsTArray<PsshInfo> pssh;
+
+private:
+ bool DoUpdate(const uint8_t* aData, size_t aLength);
+};
+
+class MP4AudioInfo : public mozilla::AudioInfo
+{
+public:
+ MP4AudioInfo() = default;
+
+ void Update(const stagefright::MetaData* aMetaData,
+ const char* aMimeType);
+
+ virtual bool IsValid() const override;
+};
+
+class MP4VideoInfo : public mozilla::VideoInfo
+{
+public:
+ MP4VideoInfo() = default;
+
+ void Update(const stagefright::MetaData* aMetaData,
+ const char* aMimeType);
+
+#ifdef MOZ_RUST_MP4PARSE
+ void Update(const mp4parse_track_info* track,
+ const mp4parse_track_video_info* video);
+#endif
+
+ virtual bool IsValid() const override;
+};
+
+}
+
+#endif
diff --git a/media/libstagefright/binding/include/mp4_demuxer/H264.h b/media/libstagefright/binding/include/mp4_demuxer/H264.h
new file mode 100644
index 000000000..2aa710f61
--- /dev/null
+++ b/media/libstagefright/binding/include/mp4_demuxer/H264.h
@@ -0,0 +1,372 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MP4_DEMUXER_H264_H_
+#define MP4_DEMUXER_H264_H_
+
+#include "mp4_demuxer/DecoderData.h"
+
+namespace mp4_demuxer
+{
+
+class BitReader;
+
+struct SPSData
+{
+ /* Decoded Members */
+ /*
+ pic_width is the decoded width according to:
+ pic_width = ((pic_width_in_mbs_minus1 + 1) * 16)
+ - (frame_crop_left_offset + frame_crop_right_offset) * 2
+ */
+ uint32_t pic_width;
+ /*
+ pic_height is the decoded height according to:
+ pic_height = (2 - frame_mbs_only_flag) * ((pic_height_in_map_units_minus1 + 1) * 16)
+ - (frame_crop_top_offset + frame_crop_bottom_offset) * 2
+ */
+ uint32_t pic_height;
+
+ bool interlaced;
+
+ /*
+ Displayed size.
+ display_width and display_height are adjusted according to the display
+ sample aspect ratio.
+ */
+ uint32_t display_width;
+ uint32_t display_height;
+
+ float sample_ratio;
+
+ uint32_t crop_left;
+ uint32_t crop_right;
+ uint32_t crop_top;
+ uint32_t crop_bottom;
+
+ /*
+ H264 decoding parameters according to ITU-T H.264 (T-REC-H.264-201402-I/en)
+ http://www.itu.int/rec/T-REC-H.264-201402-I/en
+ */
+
+ bool constraint_set0_flag;
+ bool constraint_set1_flag;
+ bool constraint_set2_flag;
+ bool constraint_set3_flag;
+ bool constraint_set4_flag;
+ bool constraint_set5_flag;
+
+ /*
+ profile_idc and level_idc indicate the profile and level to which the coded
+ video sequence conforms when the SVC sequence parameter set is the active
+ SVC sequence parameter set.
+ */
+ uint8_t profile_idc;
+ uint8_t level_idc;
+
+ /*
+ seq_parameter_set_id identifies the sequence parameter set that is referred
+ to by the picture parameter set. The value of seq_parameter_set_id shall be
+ in the range of 0 to 31, inclusive.
+ */
+ uint8_t seq_parameter_set_id;
+
+ /*
+ chroma_format_idc specifies the chroma sampling relative to the luma
+ sampling as specified in clause 6.2. The value of chroma_format_idc shall be
+ in the range of 0 to 3, inclusive. When chroma_format_idc is not present,
+ it shall be inferred to be equal to 1 (4:2:0 chroma format).
+ When profile_idc is equal to 183, chroma_format_idc shall be equal to 0
+ (4:0:0 chroma format).
+ */
+ uint8_t chroma_format_idc;
+
+ /*
+ separate_colour_plane_flag equal to 1 specifies that the three colour
+ components of the 4:4:4 chroma format are coded separately.
+ separate_colour_plane_flag equal to 0 specifies that the colour components
+ are not coded separately. When separate_colour_plane_flag is not present,
+ it shall be inferred to be equal to 0. When separate_colour_plane_flag is
+ equal to 1, the primary coded picture consists of three separate components,
+ each of which consists of coded samples of one colour plane (Y, Cb or Cr)
+ that each use the monochrome coding syntax. In this case, each colour plane
+ is associated with a specific colour_plane_id value.
+ */
+ bool separate_colour_plane_flag;
+
+ /*
+ log2_max_frame_num_minus4 specifies the value of the variable
+ MaxFrameNum that is used in frame_num related derivations as
+ follows:
+
+ MaxFrameNum = 2( log2_max_frame_num_minus4 + 4 ). The value of
+ log2_max_frame_num_minus4 shall be in the range of 0 to 12, inclusive.
+ */
+ uint8_t log2_max_frame_num;
+
+ /*
+ pic_order_cnt_type specifies the method to decode picture order
+ count (as specified in subclause 8.2.1). The value of
+ pic_order_cnt_type shall be in the range of 0 to 2, inclusive.
+ */
+ uint8_t pic_order_cnt_type;
+
+ /*
+ log2_max_pic_order_cnt_lsb_minus4 specifies the value of the
+ variable MaxPicOrderCntLsb that is used in the decoding
+ process for picture order count as specified in subclause
+ 8.2.1 as follows:
+
+ MaxPicOrderCntLsb = 2( log2_max_pic_order_cnt_lsb_minus4 + 4 )
+
+ The value of log2_max_pic_order_cnt_lsb_minus4 shall be in
+ the range of 0 to 12, inclusive.
+ */
+ uint8_t log2_max_pic_order_cnt_lsb;
+
+ /*
+ delta_pic_order_always_zero_flag equal to 1 specifies that
+ delta_pic_order_cnt[ 0 ] and delta_pic_order_cnt[ 1 ] are
+ not present in the slice headers of the sequence and shall
+ be inferred to be equal to 0.
+ */
+ bool delta_pic_order_always_zero_flag;
+
+ /*
+ offset_for_non_ref_pic is used to calculate the picture
+ order count of a non-reference picture as specified in
+ 8.2.1. The value of offset_for_non_ref_pic shall be in the
+ range of -231 to 231 - 1, inclusive.
+ */
+ int8_t offset_for_non_ref_pic;
+
+ /*
+ offset_for_top_to_bottom_field is used to calculate the
+ picture order count of a bottom field as specified in
+ subclause 8.2.1. The value of offset_for_top_to_bottom_field
+ shall be in the range of -231 to 231 - 1, inclusive.
+ */
+ int8_t offset_for_top_to_bottom_field;
+
+ /*
+ max_num_ref_frames specifies the maximum number of short-term and
+ long-term reference frames, complementary reference field pairs,
+ and non-paired reference fields that may be used by the decoding
+ process for inter prediction of any picture in the
+ sequence. max_num_ref_frames also determines the size of the sliding
+ window operation as specified in subclause 8.2.5.3. The value of
+ max_num_ref_frames shall be in the range of 0 to MaxDpbSize (as
+ specified in subclause A.3.1 or A.3.2), inclusive.
+ */
+ uint32_t max_num_ref_frames;
+
+ /*
+ gaps_in_frame_num_value_allowed_flag specifies the allowed
+ values of frame_num as specified in subclause 7.4.3 and the
+ decoding process in case of an inferred gap between values of
+ frame_num as specified in subclause 8.2.5.2.
+ */
+ bool gaps_in_frame_num_allowed_flag;
+
+ /*
+ pic_width_in_mbs_minus1 plus 1 specifies the width of each
+ decoded picture in units of macroblocks. 16 macroblocks in a row
+ */
+ uint32_t pic_width_in_mbs;
+
+ /*
+ pic_height_in_map_units_minus1 plus 1 specifies the height in
+ slice group map units of a decoded frame or field. 16
+ macroblocks in each column.
+ */
+ uint32_t pic_height_in_map_units;
+
+ /*
+ frame_mbs_only_flag equal to 0 specifies that coded pictures of
+ the coded video sequence may either be coded fields or coded
+ frames. frame_mbs_only_flag equal to 1 specifies that every
+ coded picture of the coded video sequence is a coded frame
+ containing only frame macroblocks.
+ */
+ bool frame_mbs_only_flag;
+
+ /*
+ mb_adaptive_frame_field_flag equal to 0 specifies no
+ switching between frame and field macroblocks within a
+ picture. mb_adaptive_frame_field_flag equal to 1 specifies
+ the possible use of switching between frame and field
+ macroblocks within frames. When mb_adaptive_frame_field_flag
+ is not present, it shall be inferred to be equal to 0.
+ */
+ bool mb_adaptive_frame_field_flag;
+
+ /*
+ frame_cropping_flag equal to 1 specifies that the frame cropping
+ offset parameters follow next in the sequence parameter
+ set. frame_cropping_flag equal to 0 specifies that the frame
+ cropping offset parameters are not present.
+ */
+ bool frame_cropping_flag;
+ uint32_t frame_crop_left_offset;;
+ uint32_t frame_crop_right_offset;
+ uint32_t frame_crop_top_offset;
+ uint32_t frame_crop_bottom_offset;
+
+ // VUI Parameters
+
+ /*
+ vui_parameters_present_flag equal to 1 specifies that the
+ vui_parameters( ) syntax structure as specified in Annex E is
+ present. vui_parameters_present_flag equal to 0 specifies that
+ the vui_parameters( ) syntax structure as specified in Annex E
+ is not present.
+ */
+ bool vui_parameters_present_flag;
+
+ /*
+ aspect_ratio_info_present_flag equal to 1 specifies that
+ aspect_ratio_idc is present. aspect_ratio_info_present_flag
+ equal to 0 specifies that aspect_ratio_idc is not present.
+ */
+ bool aspect_ratio_info_present_flag;
+
+ /*
+ aspect_ratio_idc specifies the value of the sample aspect
+ ratio of the luma samples. Table E-1 shows the meaning of
+ the code. When aspect_ratio_idc indicates Extended_SAR, the
+ sample aspect ratio is represented by sar_width and
+ sar_height. When the aspect_ratio_idc syntax element is not
+ present, aspect_ratio_idc value shall be inferred to be
+ equal to 0.
+ */
+ uint8_t aspect_ratio_idc;
+ uint32_t sar_width;
+ uint32_t sar_height;
+
+ /*
+ video_signal_type_present_flag equal to 1 specifies that video_format,
+ video_full_range_flag and colour_description_present_flag are present.
+ video_signal_type_present_flag equal to 0, specify that video_format,
+ video_full_range_flag and colour_description_present_flag are not present.
+ */
+ bool video_signal_type_present_flag;
+
+ /*
+ overscan_info_present_flag equal to1 specifies that the
+ overscan_appropriate_flag is present. When overscan_info_present_flag is
+ equal to 0 or is not present, the preferred display method for the video
+ signal is unspecified (Unspecified).
+ */
+ bool overscan_info_present_flag;
+ /*
+ overscan_appropriate_flag equal to 1 indicates that the cropped decoded
+ pictures output are suitable for display using overscan.
+ overscan_appropriate_flag equal to 0 indicates that the cropped decoded
+ pictures output contain visually important information in the entire region
+ out to the edges of the cropping rectangle of the picture
+ */
+ bool overscan_appropriate_flag;
+
+ /*
+ video_format indicates the representation of the pictures as specified in
+ Table E-2, before being coded in accordance with this
+ Recommendation | International Standard. When the video_format syntax element
+ is not present, video_format value shall be inferred to be equal to 5.
+ (Unspecified video format)
+ */
+ uint8_t video_format;
+
+ /*
+ video_full_range_flag indicates the black level and range of the luma and
+ chroma signals as derived from E′Y, E′PB, and E′PR or E′R, E′G, and E′B
+ real-valued component signals.
+ When the video_full_range_flag syntax element is not present, the value of
+ video_full_range_flag shall be inferred to be equal to 0.
+ */
+ bool video_full_range_flag;
+
+ /*
+ colour_description_present_flag equal to1 specifies that colour_primaries,
+ transfer_characteristics and matrix_coefficients are present.
+ colour_description_present_flag equal to 0 specifies that colour_primaries,
+ transfer_characteristics and matrix_coefficients are not present.
+ */
+ bool colour_description_present_flag;
+
+ /*
+ colour_primaries indicates the chromaticity coordinates of the source
+ primaries as specified in Table E-3 in terms of the CIE 1931 definition of
+ x and y as specified by ISO 11664-1.
+ When the colour_primaries syntax element is not present, the value of
+ colour_primaries shall be inferred to be equal to 2 (the chromaticity is
+ unspecified or is determined by the application).
+ */
+ uint8_t colour_primaries;
+
+ /*
+ transfer_characteristics indicates the opto-electronic transfer
+ characteristic of the source picture as specified in Table E-4 as a function
+ of a linear optical intensity input Lc with a nominal real-valued range of 0
+ to 1.
+ When the transfer_characteristics syntax element is not present, the value
+ of transfer_characteristics shall be inferred to be equal to 2
+ (the transfer characteristics are unspecified or are determined by the
+ application).
+ */
+ uint8_t transfer_characteristics;
+
+ uint8_t matrix_coefficients;
+ bool chroma_loc_info_present_flag;
+ uint32_t chroma_sample_loc_type_top_field;
+ uint32_t chroma_sample_loc_type_bottom_field;
+ bool timing_info_present_flag;
+ uint32_t num_units_in_tick;
+ uint32_t time_scale;
+ bool fixed_frame_rate_flag;
+
+ SPSData();
+};
+
+class H264
+{
+public:
+ static bool DecodeSPSFromExtraData(const mozilla::MediaByteBuffer* aExtraData, SPSData& aDest);
+
+ /* Extract RAW BYTE SEQUENCE PAYLOAD from NAL content.
+ Returns nullptr if invalid content.
+ This is compliant to ITU H.264 7.3.1 Syntax in tabular form NAL unit syntax
+ */
+ static already_AddRefed<mozilla::MediaByteBuffer> DecodeNALUnit(const mozilla::MediaByteBuffer* aNAL);
+
+ /* Decode SPS NAL RBSP and fill SPSData structure */
+ static bool DecodeSPS(const mozilla::MediaByteBuffer* aSPS, SPSData& aDest);
+
+ // Ensure that SPS data makes sense, Return true if SPS data was, and false
+ // otherwise. If false, then content will be adjusted accordingly.
+ static bool EnsureSPSIsSane(SPSData& aSPS);
+
+ // If the given aExtraData is valid, return the aExtraData.max_num_ref_frames
+ // clamped to be in the range of [4, 16]; otherwise return 4.
+ static uint32_t ComputeMaxRefFrames(const mozilla::MediaByteBuffer* aExtraData);
+
+ enum class FrameType
+ {
+ I_FRAME,
+ OTHER,
+ INVALID,
+ };
+
+ // Returns the frame type. Returns I_FRAME if the sample is an IDR
+ // (Instantaneous Decoding Refresh) Picture.
+ static FrameType GetFrameType(const mozilla::MediaRawData* aSample);
+
+private:
+ static void vui_parameters(BitReader& aBr, SPSData& aDest);
+ // Read HRD parameters, all data is ignored.
+ static void hrd_parameters(BitReader& aBr);
+};
+
+} // namespace mp4_demuxer
+
+#endif // MP4_DEMUXER_H264_H_
diff --git a/media/libstagefright/binding/include/mp4_demuxer/Index.h b/media/libstagefright/binding/include/mp4_demuxer/Index.h
new file mode 100644
index 000000000..d566c9459
--- /dev/null
+++ b/media/libstagefright/binding/include/mp4_demuxer/Index.h
@@ -0,0 +1,128 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef INDEX_H_
+#define INDEX_H_
+
+#include "MediaData.h"
+#include "MediaResource.h"
+#include "TimeUnits.h"
+#include "mp4_demuxer/MoofParser.h"
+#include "mp4_demuxer/Interval.h"
+#include "mp4_demuxer/Stream.h"
+#include "nsISupportsImpl.h"
+
+template<class T> class nsAutoPtr;
+
+namespace mp4_demuxer
+{
+
+class Index;
+
+typedef int64_t Microseconds;
+
+class SampleIterator
+{
+public:
+ explicit SampleIterator(Index* aIndex);
+ ~SampleIterator();
+ already_AddRefed<mozilla::MediaRawData> GetNext();
+ void Seek(Microseconds aTime);
+ Microseconds GetNextKeyframeTime();
+private:
+ Sample* Get();
+ void Next();
+ RefPtr<Index> mIndex;
+ friend class Index;
+ size_t mCurrentMoof;
+ size_t mCurrentSample;
+};
+
+class Index
+{
+public:
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(Index)
+
+ struct Indice
+ {
+ uint64_t start_offset;
+ uint64_t end_offset;
+ uint64_t start_composition;
+ uint64_t end_composition;
+ uint64_t start_decode;
+ bool sync;
+ };
+
+ struct MP4DataOffset
+ {
+ MP4DataOffset(uint32_t aIndex, int64_t aStartOffset)
+ : mIndex(aIndex)
+ , mStartOffset(aStartOffset)
+ , mEndOffset(0)
+ {}
+
+ bool operator==(int64_t aStartOffset) const {
+ return mStartOffset == aStartOffset;
+ }
+
+ bool operator!=(int64_t aStartOffset) const {
+ return mStartOffset != aStartOffset;
+ }
+
+ bool operator<(int64_t aStartOffset) const {
+ return mStartOffset < aStartOffset;
+ }
+
+ struct EndOffsetComparator {
+ bool Equals(const MP4DataOffset& a, const int64_t& b) const {
+ return a.mEndOffset == b;
+ }
+
+ bool LessThan(const MP4DataOffset& a, const int64_t& b) const {
+ return a.mEndOffset < b;
+ }
+ };
+
+ uint32_t mIndex;
+ int64_t mStartOffset;
+ int64_t mEndOffset;
+ Interval<Microseconds> mTime;
+ };
+
+ Index(const nsTArray<Indice>& aIndex,
+ Stream* aSource,
+ uint32_t aTrackId,
+ bool aIsAudio);
+
+ void UpdateMoofIndex(const mozilla::MediaByteRangeSet& aByteRanges,
+ bool aCanEvict);
+ void UpdateMoofIndex(const mozilla::MediaByteRangeSet& aByteRanges);
+ Microseconds GetEndCompositionIfBuffered(
+ const mozilla::MediaByteRangeSet& aByteRanges);
+ mozilla::media::TimeIntervals ConvertByteRangesToTimeRanges(
+ const mozilla::MediaByteRangeSet& aByteRanges);
+ uint64_t GetEvictionOffset(Microseconds aTime);
+ bool IsFragmented() { return mMoofParser; }
+
+ friend class SampleIterator;
+
+private:
+ ~Index();
+ void RegisterIterator(SampleIterator* aIterator);
+ void UnregisterIterator(SampleIterator* aIterator);
+
+ Stream* mSource;
+ FallibleTArray<Sample> mIndex;
+ FallibleTArray<MP4DataOffset> mDataOffset;
+ nsAutoPtr<MoofParser> mMoofParser;
+ nsTArray<SampleIterator*> mIterators;
+
+ // ConvertByteRangesToTimeRanges cache
+ mozilla::MediaByteRangeSet mLastCachedRanges;
+ mozilla::media::TimeIntervals mLastBufferedRanges;
+ bool mIsAudio;
+};
+}
+
+#endif
diff --git a/media/libstagefright/binding/include/mp4_demuxer/Interval.h b/media/libstagefright/binding/include/mp4_demuxer/Interval.h
new file mode 100644
index 000000000..2c3d0f8af
--- /dev/null
+++ b/media/libstagefright/binding/include/mp4_demuxer/Interval.h
@@ -0,0 +1,147 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef INTERVAL_H_
+#define INTERVAL_H_
+
+#include "nsTArray.h"
+#include <algorithm>
+
+namespace mp4_demuxer
+{
+
+template <typename T>
+struct Interval
+{
+ Interval() : start(0), end(0) {}
+ Interval(T aStart, T aEnd) : start(aStart), end(aEnd)
+ {
+ MOZ_ASSERT(aStart <= aEnd);
+ }
+ T Length() { return end - start; }
+ Interval Intersection(const Interval& aOther) const
+ {
+ T s = start > aOther.start ? start : aOther.start;
+ T e = end < aOther.end ? end : aOther.end;
+ if (s > e) {
+ return Interval();
+ }
+ return Interval(s, e);
+ }
+ bool Contains(const Interval& aOther) const
+ {
+ return aOther.start >= start && aOther.end <= end;
+ }
+ bool operator==(const Interval& aOther) const
+ {
+ return start == aOther.start && end == aOther.end;
+ }
+ bool operator!=(const Interval& aOther) const { return !(*this == aOther); }
+ bool IsNull() const
+ {
+ return end == start;
+ }
+ Interval Extents(const Interval& aOther) const
+ {
+ if (IsNull()) {
+ return aOther;
+ }
+ return Interval(std::min(start, aOther.start),
+ std::max(end, aOther.end));
+ }
+
+ T start;
+ T end;
+
+ static void SemiNormalAppend(nsTArray<Interval<T>>& aIntervals,
+ Interval<T> aInterval)
+ {
+ if (!aIntervals.IsEmpty() &&
+ aIntervals.LastElement().end == aInterval.start) {
+ aIntervals.LastElement().end = aInterval.end;
+ } else {
+ aIntervals.AppendElement(aInterval);
+ }
+ }
+
+ static void Normalize(const nsTArray<Interval<T>>& aIntervals,
+ nsTArray<Interval<T>>* aNormalized)
+ {
+ if (!aNormalized || !aIntervals.Length()) {
+ MOZ_ASSERT(aNormalized);
+ return;
+ }
+ MOZ_ASSERT(aNormalized->IsEmpty());
+
+ nsTArray<Interval<T>> sorted;
+ sorted = aIntervals;
+ sorted.Sort(Compare());
+
+ Interval<T> current = sorted[0];
+ for (size_t i = 1; i < sorted.Length(); i++) {
+ MOZ_ASSERT(sorted[i].start <= sorted[i].end);
+ if (current.Contains(sorted[i])) {
+ continue;
+ }
+ if (current.end >= sorted[i].start) {
+ current.end = sorted[i].end;
+ } else {
+ aNormalized->AppendElement(current);
+ current = sorted[i];
+ }
+ }
+ aNormalized->AppendElement(current);
+ }
+
+ static void Intersection(const nsTArray<Interval<T>>& a0,
+ const nsTArray<Interval<T>>& a1,
+ nsTArray<Interval<T>>* aIntersection)
+ {
+ MOZ_ASSERT(IsNormalized(a0));
+ MOZ_ASSERT(IsNormalized(a1));
+ size_t i0 = 0;
+ size_t i1 = 0;
+ while (i0 < a0.Length() && i1 < a1.Length()) {
+ Interval i = a0[i0].Intersection(a1[i1]);
+ if (i.Length()) {
+ aIntersection->AppendElement(i);
+ }
+ if (a0[i0].end < a1[i1].end) {
+ i0++;
+ // Assert that the array is sorted
+ MOZ_ASSERT(i0 == a0.Length() || a0[i0 - 1].start < a0[i0].start);
+ } else {
+ i1++;
+ // Assert that the array is sorted
+ MOZ_ASSERT(i1 == a1.Length() || a1[i1 - 1].start < a1[i1].start);
+ }
+ }
+ }
+
+ static bool IsNormalized(const nsTArray<Interval<T>>& aIntervals)
+ {
+ for (size_t i = 1; i < aIntervals.Length(); i++) {
+ if (aIntervals[i - 1].end >= aIntervals[i].start) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ struct Compare
+ {
+ bool Equals(const Interval<T>& a0, const Interval<T>& a1) const
+ {
+ return a0.start == a1.start && a0.end == a1.end;
+ }
+
+ bool LessThan(const Interval<T>& a0, const Interval<T>& a1) const
+ {
+ return a0.start < a1.start;
+ }
+ };
+};
+}
+
+#endif
diff --git a/media/libstagefright/binding/include/mp4_demuxer/MP4Metadata.h b/media/libstagefright/binding/include/mp4_demuxer/MP4Metadata.h
new file mode 100644
index 000000000..83eca69d3
--- /dev/null
+++ b/media/libstagefright/binding/include/mp4_demuxer/MP4Metadata.h
@@ -0,0 +1,54 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MP4METADATA_H_
+#define MP4METADATA_H_
+
+#include "mozilla/UniquePtr.h"
+#include "mp4_demuxer/DecoderData.h"
+#include "mp4_demuxer/Index.h"
+#include "MediaData.h"
+#include "MediaInfo.h"
+#include "Stream.h"
+
+namespace mp4_demuxer
+{
+
+class MP4MetadataStagefright;
+class MP4MetadataRust;
+
+class MP4Metadata
+{
+public:
+ explicit MP4Metadata(Stream* aSource);
+ ~MP4Metadata();
+
+ static bool HasCompleteMetadata(Stream* aSource);
+ static already_AddRefed<mozilla::MediaByteBuffer> Metadata(Stream* aSource);
+ uint32_t GetNumberTracks(mozilla::TrackInfo::TrackType aType) const;
+ mozilla::UniquePtr<mozilla::TrackInfo> GetTrackInfo(mozilla::TrackInfo::TrackType aType,
+ size_t aTrackNumber) const;
+ bool CanSeek() const;
+
+ const CryptoFile& Crypto() const;
+
+ bool ReadTrackIndex(FallibleTArray<Index::Indice>& aDest, mozilla::TrackID aTrackID);
+
+private:
+ UniquePtr<MP4MetadataStagefright> mStagefright;
+#ifdef MOZ_RUST_MP4PARSE
+ UniquePtr<MP4MetadataRust> mRust;
+ mutable bool mPreferRust;
+ mutable bool mReportedAudioTrackTelemetry;
+ mutable bool mReportedVideoTrackTelemetry;
+#ifndef RELEASE_OR_BETA
+ mutable bool mRustTestMode;
+#endif
+ bool ShouldPreferRust() const;
+#endif
+};
+
+} // namespace mp4_demuxer
+
+#endif // MP4METADATA_H_
diff --git a/media/libstagefright/binding/include/mp4_demuxer/MoofParser.h b/media/libstagefright/binding/include/mp4_demuxer/MoofParser.h
new file mode 100644
index 000000000..814f806fc
--- /dev/null
+++ b/media/libstagefright/binding/include/mp4_demuxer/MoofParser.h
@@ -0,0 +1,260 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOOF_PARSER_H_
+#define MOOF_PARSER_H_
+
+#include "mp4_demuxer/Atom.h"
+#include "mp4_demuxer/AtomType.h"
+#include "mp4_demuxer/SinfParser.h"
+#include "mp4_demuxer/Stream.h"
+#include "mp4_demuxer/Interval.h"
+#include "MediaResource.h"
+
+namespace mp4_demuxer {
+typedef int64_t Microseconds;
+
+class Box;
+class BoxContext;
+class Moof;
+
+class Mvhd : public Atom
+{
+public:
+ Mvhd()
+ : mCreationTime(0)
+ , mModificationTime(0)
+ , mTimescale(0)
+ , mDuration(0)
+ {
+ }
+ explicit Mvhd(Box& aBox);
+
+ Microseconds ToMicroseconds(int64_t aTimescaleUnits)
+ {
+ int64_t major = aTimescaleUnits / mTimescale;
+ int64_t remainder = aTimescaleUnits % mTimescale;
+ return major * 1000000ll + remainder * 1000000ll / mTimescale;
+ }
+
+ uint64_t mCreationTime;
+ uint64_t mModificationTime;
+ uint32_t mTimescale;
+ uint64_t mDuration;
+};
+
+class Tkhd : public Mvhd
+{
+public:
+ Tkhd()
+ : mTrackId(0)
+ {
+ }
+ explicit Tkhd(Box& aBox);
+
+ uint32_t mTrackId;
+};
+
+class Mdhd : public Mvhd
+{
+public:
+ Mdhd() = default;
+ explicit Mdhd(Box& aBox);
+};
+
+class Trex : public Atom
+{
+public:
+ explicit Trex(uint32_t aTrackId)
+ : mFlags(0)
+ , mTrackId(aTrackId)
+ , mDefaultSampleDescriptionIndex(0)
+ , mDefaultSampleDuration(0)
+ , mDefaultSampleSize(0)
+ , mDefaultSampleFlags(0)
+ {
+ }
+
+ explicit Trex(Box& aBox);
+
+ uint32_t mFlags;
+ uint32_t mTrackId;
+ uint32_t mDefaultSampleDescriptionIndex;
+ uint32_t mDefaultSampleDuration;
+ uint32_t mDefaultSampleSize;
+ uint32_t mDefaultSampleFlags;
+};
+
+class Tfhd : public Trex
+{
+public:
+ explicit Tfhd(Trex& aTrex)
+ : Trex(aTrex)
+ , mBaseDataOffset(0)
+ {
+ mValid = aTrex.IsValid();
+ }
+ Tfhd(Box& aBox, Trex& aTrex);
+
+ uint64_t mBaseDataOffset;
+};
+
+class Tfdt : public Atom
+{
+public:
+ Tfdt()
+ : mBaseMediaDecodeTime(0)
+ {
+ }
+ explicit Tfdt(Box& aBox);
+
+ uint64_t mBaseMediaDecodeTime;
+};
+
+class Edts : public Atom
+{
+public:
+ Edts()
+ : mMediaStart(0)
+ , mEmptyOffset(0)
+ {
+ }
+ explicit Edts(Box& aBox);
+ virtual bool IsValid()
+ {
+ // edts is optional
+ return true;
+ }
+
+ int64_t mMediaStart;
+ int64_t mEmptyOffset;
+};
+
+struct Sample
+{
+ mozilla::MediaByteRange mByteRange;
+ mozilla::MediaByteRange mCencRange;
+ Microseconds mDecodeTime;
+ Interval<Microseconds> mCompositionRange;
+ bool mSync;
+};
+
+class Saiz final : public Atom
+{
+public:
+ Saiz(Box& aBox, AtomType aDefaultType);
+
+ AtomType mAuxInfoType;
+ uint32_t mAuxInfoTypeParameter;
+ FallibleTArray<uint8_t> mSampleInfoSize;
+};
+
+class Saio final : public Atom
+{
+public:
+ Saio(Box& aBox, AtomType aDefaultType);
+
+ AtomType mAuxInfoType;
+ uint32_t mAuxInfoTypeParameter;
+ FallibleTArray<uint64_t> mOffsets;
+};
+
+class AuxInfo {
+public:
+ AuxInfo(int64_t aMoofOffset, Saiz& aSaiz, Saio& aSaio);
+
+private:
+ int64_t mMoofOffset;
+ Saiz& mSaiz;
+ Saio& mSaio;
+};
+
+class Moof final : public Atom
+{
+public:
+ Moof(Box& aBox, Trex& aTrex, Mvhd& aMvhd, Mdhd& aMdhd, Edts& aEdts, Sinf& aSinf, uint64_t* aDecoderTime, bool aIsAudio);
+ bool GetAuxInfo(AtomType aType, nsTArray<MediaByteRange>* aByteRanges);
+ void FixRounding(const Moof& aMoof);
+
+ mozilla::MediaByteRange mRange;
+ mozilla::MediaByteRange mMdatRange;
+ Interval<Microseconds> mTimeRange;
+ FallibleTArray<Sample> mIndex;
+
+ nsTArray<Saiz> mSaizs;
+ nsTArray<Saio> mSaios;
+
+private:
+ // aDecodeTime is updated to the end of the parsed TRAF on return.
+ void ParseTraf(Box& aBox, Trex& aTrex, Mvhd& aMvhd, Mdhd& aMdhd, Edts& aEdts, Sinf& aSinf, uint64_t* aDecodeTime, bool aIsAudio);
+ // aDecodeTime is updated to the end of the parsed TRUN on return.
+ bool ParseTrun(Box& aBox, Tfhd& aTfhd, Mvhd& aMvhd, Mdhd& aMdhd, Edts& aEdts, uint64_t* aDecodeTime, bool aIsAudio);
+ void ParseSaiz(Box& aBox);
+ void ParseSaio(Box& aBox);
+ bool ProcessCenc();
+ uint64_t mMaxRoundingError;
+};
+
+class MoofParser
+{
+public:
+ MoofParser(Stream* aSource, uint32_t aTrackId, bool aIsAudio)
+ : mSource(aSource)
+ , mOffset(0)
+ , mTrex(aTrackId)
+ , mIsAudio(aIsAudio)
+ , mLastDecodeTime(0)
+ {
+ // Setting the mTrex.mTrackId to 0 is a nasty work around for calculating
+ // the composition range for MSE. We need an array of tracks.
+ }
+ bool RebuildFragmentedIndex(
+ const mozilla::MediaByteRangeSet& aByteRanges);
+ // If *aCanEvict is set to true. then will remove all moofs already parsed
+ // from index then rebuild the index. *aCanEvict is set to true upon return if
+ // some moofs were removed.
+ bool RebuildFragmentedIndex(
+ const mozilla::MediaByteRangeSet& aByteRanges, bool* aCanEvict);
+ bool RebuildFragmentedIndex(BoxContext& aContext);
+ Interval<Microseconds> GetCompositionRange(
+ const mozilla::MediaByteRangeSet& aByteRanges);
+ bool ReachedEnd();
+ void ParseMoov(Box& aBox);
+ void ParseTrak(Box& aBox);
+ void ParseMdia(Box& aBox, Tkhd& aTkhd);
+ void ParseMvex(Box& aBox);
+
+ void ParseMinf(Box& aBox);
+ void ParseStbl(Box& aBox);
+ void ParseStsd(Box& aBox);
+ void ParseEncrypted(Box& aBox);
+ void ParseSinf(Box& aBox);
+
+ bool BlockingReadNextMoof();
+ bool HasMetadata();
+ already_AddRefed<mozilla::MediaByteBuffer> Metadata();
+ MediaByteRange FirstCompleteMediaSegment();
+ MediaByteRange FirstCompleteMediaHeader();
+
+ mozilla::MediaByteRange mInitRange;
+ RefPtr<Stream> mSource;
+ uint64_t mOffset;
+ Mvhd mMvhd;
+ Mdhd mMdhd;
+ Trex mTrex;
+ Tfdt mTfdt;
+ Edts mEdts;
+ Sinf mSinf;
+ nsTArray<Moof>& Moofs() { return mMoofs; }
+private:
+ void ScanForMetadata(mozilla::MediaByteRange& aFtyp,
+ mozilla::MediaByteRange& aMoov);
+ nsTArray<Moof> mMoofs;
+ nsTArray<MediaByteRange> mMediaRanges;
+ bool mIsAudio;
+ uint64_t mLastDecodeTime;
+};
+}
+
+#endif
diff --git a/media/libstagefright/binding/include/mp4_demuxer/ResourceStream.h b/media/libstagefright/binding/include/mp4_demuxer/ResourceStream.h
new file mode 100644
index 000000000..219465f17
--- /dev/null
+++ b/media/libstagefright/binding/include/mp4_demuxer/ResourceStream.h
@@ -0,0 +1,49 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef RESOURCESTREAM_H_
+#define RESOURCESTREAM_H_
+
+#include "MediaResource.h"
+#include "mp4_demuxer/Stream.h"
+#include "mozilla/RefPtr.h"
+
+namespace mp4_demuxer
+{
+
+class ResourceStream : public Stream
+{
+public:
+ explicit ResourceStream(mozilla::MediaResource* aResource);
+
+ virtual bool ReadAt(int64_t offset, void* aBuffer, size_t aCount,
+ size_t* aBytesRead) override;
+ virtual bool CachedReadAt(int64_t aOffset, void* aBuffer, size_t aCount,
+ size_t* aBytesRead) override;
+ virtual bool Length(int64_t* size) override;
+
+ void Pin()
+ {
+ mResource->Pin();
+ ++mPinCount;
+ }
+
+ void Unpin()
+ {
+ mResource->Unpin();
+ MOZ_ASSERT(mPinCount);
+ --mPinCount;
+ }
+
+protected:
+ virtual ~ResourceStream();
+
+private:
+ RefPtr<mozilla::MediaResource> mResource;
+ uint32_t mPinCount;
+};
+
+}
+
+#endif // RESOURCESTREAM_H_
diff --git a/media/libstagefright/binding/include/mp4_demuxer/SinfParser.h b/media/libstagefright/binding/include/mp4_demuxer/SinfParser.h
new file mode 100644
index 000000000..782a87907
--- /dev/null
+++ b/media/libstagefright/binding/include/mp4_demuxer/SinfParser.h
@@ -0,0 +1,51 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+
+#ifndef SINF_PARSER_H_
+#define SINF_PARSER_H_
+
+#include "mp4_demuxer/Atom.h"
+#include "mp4_demuxer/AtomType.h"
+
+namespace mp4_demuxer {
+
+class Box;
+
+class Sinf : public Atom
+{
+public:
+ Sinf()
+ : mDefaultIVSize(0)
+ , mDefaultEncryptionType()
+ {}
+ explicit Sinf(Box& aBox);
+
+ virtual bool IsValid() override
+ {
+ return !!mDefaultIVSize && !!mDefaultEncryptionType;
+ }
+
+ uint8_t mDefaultIVSize;
+ AtomType mDefaultEncryptionType;
+ uint8_t mDefaultKeyID[16];
+};
+
+class SinfParser
+{
+public:
+ explicit SinfParser(Box& aBox);
+
+ Sinf& GetSinf() { return mSinf; }
+private:
+ void ParseSchm(Box& aBox);
+ void ParseSchi(Box& aBox);
+ void ParseTenc(Box& aBox);
+
+ Sinf mSinf;
+};
+
+}
+
+#endif // SINF_PARSER_H_
diff --git a/media/libstagefright/binding/include/mp4_demuxer/Stream.h b/media/libstagefright/binding/include/mp4_demuxer/Stream.h
new file mode 100644
index 000000000..379478ec7
--- /dev/null
+++ b/media/libstagefright/binding/include/mp4_demuxer/Stream.h
@@ -0,0 +1,32 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef STREAM_H_
+#define STREAM_H_
+
+#include "nsISupportsImpl.h"
+
+namespace mp4_demuxer
+{
+
+class Stream
+{
+public:
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(Stream);
+
+ virtual bool ReadAt(int64_t offset, void* data, size_t size,
+ size_t* bytes_read) = 0;
+ virtual bool CachedReadAt(int64_t offset, void* data, size_t size,
+ size_t* bytes_read) = 0;
+ virtual bool Length(int64_t* size) = 0;
+
+ virtual void DiscardBefore(int64_t offset) {}
+
+protected:
+ virtual ~Stream() {}
+};
+
+}
+
+#endif
diff --git a/media/libstagefright/binding/include/mp4parse.h b/media/libstagefright/binding/include/mp4parse.h
new file mode 100644
index 000000000..6650661cb
--- /dev/null
+++ b/media/libstagefright/binding/include/mp4parse.h
@@ -0,0 +1,113 @@
+
+#ifndef cheddar_generated_mp4parse_h
+#define cheddar_generated_mp4parse_h
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdint.h>
+#include <stdbool.h>
+
+// THIS FILE IS AUTOGENERATED BY mp4parse-rust/build.rs - DO NOT EDIT
+
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at https://mozilla.org/MPL/2.0/.
+
+typedef enum mp4parse_error {
+ MP4PARSE_OK = 0,
+ MP4PARSE_ERROR_BADARG = 1,
+ MP4PARSE_ERROR_INVALID = 2,
+ MP4PARSE_ERROR_UNSUPPORTED = 3,
+ MP4PARSE_ERROR_EOF = 4,
+ MP4PARSE_ERROR_IO = 5,
+} mp4parse_error;
+
+typedef enum mp4parse_track_type {
+ MP4PARSE_TRACK_TYPE_VIDEO = 0,
+ MP4PARSE_TRACK_TYPE_AUDIO = 1,
+} mp4parse_track_type;
+
+typedef enum mp4parse_codec {
+ MP4PARSE_CODEC_UNKNOWN,
+ MP4PARSE_CODEC_AAC,
+ MP4PARSE_CODEC_FLAC,
+ MP4PARSE_CODEC_OPUS,
+ MP4PARSE_CODEC_AVC,
+ MP4PARSE_CODEC_VP9,
+ MP4PARSE_CODEC_MP3,
+} mp4parse_codec;
+
+typedef struct mp4parse_track_info {
+ mp4parse_track_type track_type;
+ mp4parse_codec codec;
+ uint32_t track_id;
+ uint64_t duration;
+ int64_t media_time;
+} mp4parse_track_info;
+
+typedef struct mp4parse_codec_specific_config {
+ uint32_t length;
+ uint8_t const* data;
+} mp4parse_codec_specific_config;
+
+typedef struct mp4parse_track_audio_info {
+ uint16_t channels;
+ uint16_t bit_depth;
+ uint32_t sample_rate;
+ mp4parse_codec_specific_config codec_specific_config;
+} mp4parse_track_audio_info;
+
+typedef struct mp4parse_track_video_info {
+ uint32_t display_width;
+ uint32_t display_height;
+ uint16_t image_width;
+ uint16_t image_height;
+} mp4parse_track_video_info;
+
+typedef struct mp4parse_fragment_info {
+ uint64_t fragment_duration;
+} mp4parse_fragment_info;
+
+typedef struct mp4parse_parser mp4parse_parser;
+
+typedef struct mp4parse_io {
+ intptr_t (*read)(uint8_t* buffer, uintptr_t size, void* userdata);
+ void* userdata;
+} mp4parse_io;
+
+/// Allocate an `mp4parse_parser*` to read from the supplied `mp4parse_io`.
+mp4parse_parser* mp4parse_new(mp4parse_io const* io);
+
+/// Free an `mp4parse_parser*` allocated by `mp4parse_new()`.
+void mp4parse_free(mp4parse_parser* parser);
+
+/// Run the `mp4parse_parser*` allocated by `mp4parse_new()` until EOF or error.
+mp4parse_error mp4parse_read(mp4parse_parser* parser);
+
+/// Return the number of tracks parsed by previous `mp4parse_read()` call.
+mp4parse_error mp4parse_get_track_count(mp4parse_parser const* parser, uint32_t* count);
+
+/// Fill the supplied `mp4parse_track_info` with metadata for `track`.
+mp4parse_error mp4parse_get_track_info(mp4parse_parser* parser, uint32_t track_index, mp4parse_track_info* info);
+
+/// Fill the supplied `mp4parse_track_audio_info` with metadata for `track`.
+mp4parse_error mp4parse_get_track_audio_info(mp4parse_parser* parser, uint32_t track_index, mp4parse_track_audio_info* info);
+
+/// Fill the supplied `mp4parse_track_video_info` with metadata for `track`.
+mp4parse_error mp4parse_get_track_video_info(mp4parse_parser* parser, uint32_t track_index, mp4parse_track_video_info* info);
+
+mp4parse_error mp4parse_get_fragment_info(mp4parse_parser* parser, mp4parse_fragment_info* info);
+
+mp4parse_error mp4parse_is_fragmented(mp4parse_parser* parser, uint32_t track_id, uint8_t* fragmented);
+
+
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif
diff --git a/media/libstagefright/binding/mp4parse-cargo.patch b/media/libstagefright/binding/mp4parse-cargo.patch
new file mode 100644
index 000000000..9b0ca7134
--- /dev/null
+++ b/media/libstagefright/binding/mp4parse-cargo.patch
@@ -0,0 +1,45 @@
+diff --git a/media/libstagefright/binding/mp4parse/Cargo.toml b/media/libstagefright/binding/mp4parse/Cargo.toml
+index ff9422c..814c4c6 100644
+--- a/media/libstagefright/binding/mp4parse/Cargo.toml
++++ b/media/libstagefright/binding/mp4parse/Cargo.toml
+@@ -18,17 +18,11 @@ exclude = [
+ ]
+
+ [dependencies]
+-byteorder = "0.5.0"
+-afl = { version = "0.1.1", optional = true }
+-afl-plugin = { version = "0.1.1", optional = true }
+-abort_on_panic = { version = "1.0.0", optional = true }
++byteorder = "0.5.0"
+
+ [dev-dependencies]
+ test-assembler = "0.1.2"
+
+-[features]
+-fuzz = ["afl", "afl-plugin", "abort_on_panic"]
+-
+ # Somewhat heavy-handed, but we want at least -Z force-overflow-checks=on.
+ [profile.release]
+ debug-assertions = true
+diff --git a/media/libstagefright/binding/mp4parse_capi/Cargo.toml b/media/libstagefright/binding/mp4parse_capi/Cargo.toml
+index aeeebc65..5c0836a 100644
+--- a/media/libstagefright/binding/mp4parse_capi/Cargo.toml
++++ b/media/libstagefright/binding/mp4parse_capi/Cargo.toml
+@@ -18,17 +18,9 @@ exclude = [
+ "*.mp4",
+ ]
+
+-build = "build.rs"
+-
+ [dependencies]
+ "mp4parse" = {version = "0.6.0", path = "../mp4parse"}
+
+-[build-dependencies]
+-rusty-cheddar = "0.3.2"
+-
+-[features]
+-fuzz = ["mp4parse/fuzz"]
+-
+ # Somewhat heavy-handed, but we want at least -Z force-overflow-checks=on.
+ [profile.release]
+ debug-assertions = true
diff --git a/media/libstagefright/binding/mp4parse/Cargo.toml b/media/libstagefright/binding/mp4parse/Cargo.toml
new file mode 100644
index 000000000..affcef72b
--- /dev/null
+++ b/media/libstagefright/binding/mp4parse/Cargo.toml
@@ -0,0 +1,29 @@
+[package]
+name = "mp4parse"
+version = "0.6.0"
+authors = [
+ "Ralph Giles <giles@mozilla.com>",
+ "Matthew Gregan <kinetik@flim.org>",
+ "Alfredo Yang <ayang@mozilla.com>",
+]
+
+description = "Parser for ISO base media file format (mp4)"
+documentation = "https://mp4parse-docs.surge.sh/mp4parse/"
+license = "MPL-2.0"
+
+repository = "https://github.com/mozilla/mp4parse-rust"
+
+# Avoid complaints about trying to package test files.
+exclude = [
+ "*.mp4",
+]
+
+[dependencies]
+byteorder = "0.5.0"
+
+[dev-dependencies]
+test-assembler = "0.1.2"
+
+# Somewhat heavy-handed, but we want at least -Z force-overflow-checks=on.
+[profile.release]
+debug-assertions = true
diff --git a/media/libstagefright/binding/mp4parse/src/boxes.rs b/media/libstagefright/binding/mp4parse/src/boxes.rs
new file mode 100644
index 000000000..689439ade
--- /dev/null
+++ b/media/libstagefright/binding/mp4parse/src/boxes.rs
@@ -0,0 +1,62 @@
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at https://mozilla.org/MPL/2.0/.
+
+macro_rules! box_database {
+ ($($boxenum:ident $boxtype:expr),*,) => {
+ #[derive(Debug, Clone, Copy, PartialEq)]
+ pub enum BoxType {
+ $($boxenum),*,
+ UnknownBox(u32),
+ }
+
+ impl From<u32> for BoxType {
+ fn from(t: u32) -> BoxType {
+ use self::BoxType::*;
+ match t {
+ $($boxtype => $boxenum),*,
+ _ => UnknownBox(t),
+ }
+ }
+ }
+ }
+}
+
+box_database!(
+ FileTypeBox 0x66747970, // "ftyp"
+ MovieBox 0x6d6f6f76, // "moov"
+ MovieHeaderBox 0x6d766864, // "mvhd"
+ TrackBox 0x7472616b, // "trak"
+ TrackHeaderBox 0x746b6864, // "tkhd"
+ EditBox 0x65647473, // "edts"
+ MediaBox 0x6d646961, // "mdia"
+ EditListBox 0x656c7374, // "elst"
+ MediaHeaderBox 0x6d646864, // "mdhd"
+ HandlerBox 0x68646c72, // "hdlr"
+ MediaInformationBox 0x6d696e66, // "minf"
+ SampleTableBox 0x7374626c, // "stbl"
+ SampleDescriptionBox 0x73747364, // "stsd"
+ TimeToSampleBox 0x73747473, // "stts"
+ SampleToChunkBox 0x73747363, // "stsc"
+ SampleSizeBox 0x7374737a, // "stsz"
+ ChunkOffsetBox 0x7374636f, // "stco"
+ ChunkLargeOffsetBox 0x636f3634, // "co64"
+ SyncSampleBox 0x73747373, // "stss"
+ AVCSampleEntry 0x61766331, // "avc1"
+ AVC3SampleEntry 0x61766333, // "avc3" - Need to check official name in spec.
+ AVCConfigurationBox 0x61766343, // "avcC"
+ MP4AudioSampleEntry 0x6d703461, // "mp4a"
+ ESDBox 0x65736473, // "esds"
+ VP8SampleEntry 0x76703038, // "vp08"
+ VP9SampleEntry 0x76703039, // "vp09"
+ VPCodecConfigurationBox 0x76706343, // "vpcC"
+ FLACSampleEntry 0x664c6143, // "fLaC"
+ FLACSpecificBox 0x64664c61, // "dfLa"
+ OpusSampleEntry 0x4f707573, // "Opus"
+ OpusSpecificBox 0x644f7073, // "dOps"
+ ProtectedVisualSampleEntry 0x656e6376, // "encv" - Need to check official name in spec.
+ ProtectedAudioSampleEntry 0x656e6361, // "enca" - Need to check official name in spec.
+ MovieExtendsBox 0x6d766578, // "mvex"
+ MovieExtendsHeaderBox 0x6d656864, // "mehd"
+ QTWaveAtom 0x77617665, // "wave" - quicktime atom
+);
diff --git a/media/libstagefright/binding/mp4parse/src/lib.rs b/media/libstagefright/binding/mp4parse/src/lib.rs
new file mode 100644
index 000000000..37606a5e2
--- /dev/null
+++ b/media/libstagefright/binding/mp4parse/src/lib.rs
@@ -0,0 +1,1704 @@
+//! Module for parsing ISO Base Media Format aka video/mp4 streams.
+
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at https://mozilla.org/MPL/2.0/.
+#![cfg_attr(feature = "fuzz", feature(plugin))]
+#![cfg_attr(feature = "fuzz", plugin(afl_plugin))]
+#[cfg(feature = "fuzz")]
+extern crate afl;
+
+extern crate byteorder;
+use byteorder::ReadBytesExt;
+use std::io::{Read, Take};
+use std::io::Cursor;
+use std::cmp;
+
+mod boxes;
+use boxes::BoxType;
+
+// Unit tests.
+#[cfg(test)]
+mod tests;
+
+// Arbitrary buffer size limit used for raw read_bufs on a box.
+const BUF_SIZE_LIMIT: u64 = 1024 * 1024;
+
+static DEBUG_MODE: std::sync::atomic::AtomicBool = std::sync::atomic::ATOMIC_BOOL_INIT;
+
+pub fn set_debug_mode(mode: bool) {
+ DEBUG_MODE.store(mode, std::sync::atomic::Ordering::SeqCst);
+}
+
+#[inline(always)]
+fn get_debug_mode() -> bool {
+ DEBUG_MODE.load(std::sync::atomic::Ordering::Relaxed)
+}
+
+macro_rules! log {
+ ($($args:tt)*) => (
+ if get_debug_mode() {
+ println!( $( $args )* );
+ }
+ )
+}
+
+/// Describes parser failures.
+///
+/// This enum wraps the standard `io::Error` type, unified with
+/// our own parser error states and those of crates we use.
+#[derive(Debug)]
+pub enum Error {
+ /// Parse error caused by corrupt or malformed data.
+ InvalidData(&'static str),
+ /// Parse error caused by limited parser support rather than invalid data.
+ Unsupported(&'static str),
+ /// Reflect `std::io::ErrorKind::UnexpectedEof` for short data.
+ UnexpectedEOF,
+ /// Propagate underlying errors from `std::io`.
+ Io(std::io::Error),
+ /// read_mp4 terminated without detecting a moov box.
+ NoMoov,
+}
+
+impl From<std::io::Error> for Error {
+ fn from(err: std::io::Error) -> Error {
+ match err.kind() {
+ std::io::ErrorKind::UnexpectedEof => Error::UnexpectedEOF,
+ _ => Error::Io(err),
+ }
+ }
+}
+
+impl From<std::string::FromUtf8Error> for Error {
+ fn from(_: std::string::FromUtf8Error) -> Error {
+ Error::InvalidData("invalid utf8")
+ }
+}
+
+/// Result shorthand using our Error enum.
+pub type Result<T> = std::result::Result<T, Error>;
+
+/// Basic ISO box structure.
+///
+/// mp4 files are a sequence of possibly-nested 'box' structures. Each box
+/// begins with a header describing the length of the box's data and a
+/// four-byte box type which identifies the type of the box. Together these
+/// are enough to interpret the contents of that section of the file.
+#[derive(Debug, Clone, Copy)]
+struct BoxHeader {
+ /// Box type.
+ name: BoxType,
+ /// Size of the box in bytes.
+ size: u64,
+ /// Offset to the start of the contained data (or header size).
+ offset: u64,
+}
+
+/// File type box 'ftyp'.
+#[derive(Debug)]
+struct FileTypeBox {
+ major_brand: u32,
+ minor_version: u32,
+ compatible_brands: Vec<u32>,
+}
+
+/// Movie header box 'mvhd'.
+#[derive(Debug)]
+struct MovieHeaderBox {
+ pub timescale: u32,
+ duration: u64,
+}
+
+/// Track header box 'tkhd'
+#[derive(Debug, Clone)]
+pub struct TrackHeaderBox {
+ track_id: u32,
+ pub disabled: bool,
+ pub duration: u64,
+ pub width: u32,
+ pub height: u32,
+}
+
+/// Edit list box 'elst'
+#[derive(Debug)]
+struct EditListBox {
+ edits: Vec<Edit>,
+}
+
+#[derive(Debug)]
+struct Edit {
+ segment_duration: u64,
+ media_time: i64,
+ media_rate_integer: i16,
+ media_rate_fraction: i16,
+}
+
+/// Media header box 'mdhd'
+#[derive(Debug)]
+struct MediaHeaderBox {
+ timescale: u32,
+ duration: u64,
+}
+
+// Chunk offset box 'stco' or 'co64'
+#[derive(Debug)]
+struct ChunkOffsetBox {
+ offsets: Vec<u64>,
+}
+
+// Sync sample box 'stss'
+#[derive(Debug)]
+struct SyncSampleBox {
+ samples: Vec<u32>,
+}
+
+// Sample to chunk box 'stsc'
+#[derive(Debug)]
+struct SampleToChunkBox {
+ samples: Vec<SampleToChunk>,
+}
+
+#[derive(Debug)]
+struct SampleToChunk {
+ first_chunk: u32,
+ samples_per_chunk: u32,
+ sample_description_index: u32,
+}
+
+// Sample size box 'stsz'
+#[derive(Debug)]
+struct SampleSizeBox {
+ sample_size: u32,
+ sample_sizes: Vec<u32>,
+}
+
+// Time to sample box 'stts'
+#[derive(Debug)]
+struct TimeToSampleBox {
+ samples: Vec<Sample>,
+}
+
+#[derive(Debug)]
+struct Sample {
+ sample_count: u32,
+ sample_delta: u32,
+}
+
+// Handler reference box 'hdlr'
+#[derive(Debug)]
+struct HandlerBox {
+ handler_type: u32,
+}
+
+// Sample description box 'stsd'
+#[derive(Debug)]
+struct SampleDescriptionBox {
+ descriptions: Vec<SampleEntry>,
+}
+
+#[derive(Debug, Clone)]
+pub enum SampleEntry {
+ Audio(AudioSampleEntry),
+ Video(VideoSampleEntry),
+ Unknown,
+}
+
+#[allow(non_camel_case_types)]
+#[derive(Debug, Clone)]
+pub struct ES_Descriptor {
+ pub audio_codec: CodecType,
+ pub audio_sample_rate: Option<u32>,
+ pub audio_channel_count: Option<u16>,
+ pub codec_specific_config: Vec<u8>,
+}
+
+#[allow(non_camel_case_types)]
+#[derive(Debug, Clone)]
+pub enum AudioCodecSpecific {
+ ES_Descriptor(ES_Descriptor),
+ FLACSpecificBox(FLACSpecificBox),
+ OpusSpecificBox(OpusSpecificBox),
+}
+
+#[derive(Debug, Clone)]
+pub struct AudioSampleEntry {
+ data_reference_index: u16,
+ pub channelcount: u16,
+ pub samplesize: u16,
+ pub samplerate: u32,
+ pub codec_specific: AudioCodecSpecific,
+}
+
+#[derive(Debug, Clone)]
+pub enum VideoCodecSpecific {
+ AVCConfig(Vec<u8>),
+ VPxConfig(VPxConfigBox),
+}
+
+#[derive(Debug, Clone)]
+pub struct VideoSampleEntry {
+ data_reference_index: u16,
+ pub width: u16,
+ pub height: u16,
+ pub codec_specific: VideoCodecSpecific,
+}
+
+/// Represent a Video Partition Codec Configuration 'vpcC' box (aka vp9).
+#[derive(Debug, Clone)]
+pub struct VPxConfigBox {
+ profile: u8,
+ level: u8,
+ pub bit_depth: u8,
+ pub color_space: u8, // Really an enum
+ pub chroma_subsampling: u8,
+ transfer_function: u8,
+ video_full_range: bool,
+ pub codec_init: Vec<u8>, // Empty for vp8/vp9.
+}
+
+#[derive(Debug, Clone)]
+pub struct FLACMetadataBlock {
+ pub block_type: u8,
+ pub data: Vec<u8>,
+}
+
+/// Represet a FLACSpecificBox 'dfLa'
+#[derive(Debug, Clone)]
+pub struct FLACSpecificBox {
+ version: u8,
+ pub blocks: Vec<FLACMetadataBlock>,
+}
+
+#[derive(Debug, Clone)]
+struct ChannelMappingTable {
+ stream_count: u8,
+ coupled_count: u8,
+ channel_mapping: Vec<u8>,
+}
+
+/// Represent an OpusSpecificBox 'dOps'
+#[derive(Debug, Clone)]
+pub struct OpusSpecificBox {
+ pub version: u8,
+ output_channel_count: u8,
+ pre_skip: u16,
+ input_sample_rate: u32,
+ output_gain: i16,
+ channel_mapping_family: u8,
+ channel_mapping_table: Option<ChannelMappingTable>,
+}
+
+#[derive(Debug)]
+pub struct MovieExtendsBox {
+ pub fragment_duration: Option<MediaScaledTime>,
+}
+
+/// Internal data structures.
+#[derive(Debug, Default)]
+pub struct MediaContext {
+ pub timescale: Option<MediaTimeScale>,
+ /// Tracks found in the file.
+ pub tracks: Vec<Track>,
+ pub mvex: Option<MovieExtendsBox>,
+}
+
+impl MediaContext {
+ pub fn new() -> MediaContext {
+ Default::default()
+ }
+}
+
+#[derive(Debug, PartialEq)]
+pub enum TrackType {
+ Audio,
+ Video,
+ Unknown,
+}
+
+impl Default for TrackType {
+ fn default() -> Self { TrackType::Unknown }
+}
+
+#[derive(Debug, Clone, Copy, PartialEq)]
+pub enum CodecType {
+ Unknown,
+ MP3,
+ AAC,
+ FLAC,
+ Opus,
+ H264,
+ VP9,
+ VP8,
+ EncryptedVideo,
+ EncryptedAudio,
+}
+
+impl Default for CodecType {
+ fn default() -> Self { CodecType::Unknown }
+}
+
+/// The media's global (mvhd) timescale in units per second.
+#[derive(Debug, Copy, Clone, PartialEq)]
+pub struct MediaTimeScale(pub u64);
+
+/// A time to be scaled by the media's global (mvhd) timescale.
+#[derive(Debug, Copy, Clone, PartialEq)]
+pub struct MediaScaledTime(pub u64);
+
+/// The track's local (mdhd) timescale.
+/// Members are timescale units per second and the track id.
+#[derive(Debug, Copy, Clone, PartialEq)]
+pub struct TrackTimeScale(pub u64, pub usize);
+
+/// A time to be scaled by the track's local (mdhd) timescale.
+/// Members are time in scale units and the track id.
+#[derive(Debug, Copy, Clone, PartialEq)]
+pub struct TrackScaledTime(pub u64, pub usize);
+
+/// A fragmented file contains no sample data in stts, stsc, and stco.
+#[derive(Debug, Default)]
+pub struct EmptySampleTableBoxes {
+ pub empty_stts : bool,
+ pub empty_stsc : bool,
+ pub empty_stco : bool,
+}
+
+/// Check boxes contain data.
+impl EmptySampleTableBoxes {
+ pub fn all_empty(&self) -> bool {
+ self.empty_stts & self.empty_stsc & self.empty_stco
+ }
+}
+
+#[derive(Debug, Default)]
+pub struct Track {
+ id: usize,
+ pub track_type: TrackType,
+ pub empty_duration: Option<MediaScaledTime>,
+ pub media_time: Option<TrackScaledTime>,
+ pub timescale: Option<TrackTimeScale>,
+ pub duration: Option<TrackScaledTime>,
+ pub track_id: Option<u32>,
+ pub codec_type: CodecType,
+ pub empty_sample_boxes: EmptySampleTableBoxes,
+ pub data: Option<SampleEntry>,
+ pub tkhd: Option<TrackHeaderBox>, // TODO(kinetik): find a nicer way to export this.
+}
+
+impl Track {
+ fn new(id: usize) -> Track {
+ Track { id: id, ..Default::default() }
+ }
+}
+
+struct BMFFBox<'a, T: 'a + Read> {
+ head: BoxHeader,
+ content: Take<&'a mut T>,
+}
+
+struct BoxIter<'a, T: 'a + Read> {
+ src: &'a mut T,
+}
+
+impl<'a, T: Read> BoxIter<'a, T> {
+ fn new(src: &mut T) -> BoxIter<T> {
+ BoxIter { src: src }
+ }
+
+ fn next_box(&mut self) -> Result<Option<BMFFBox<T>>> {
+ let r = read_box_header(self.src);
+ match r {
+ Ok(h) => Ok(Some(BMFFBox {
+ head: h,
+ content: self.src.take(h.size - h.offset),
+ })),
+ Err(Error::UnexpectedEOF) => Ok(None),
+ Err(e) => Err(e),
+ }
+ }
+}
+
+impl<'a, T: Read> Read for BMFFBox<'a, T> {
+ fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
+ self.content.read(buf)
+ }
+}
+
+impl<'a, T: Read> BMFFBox<'a, T> {
+ fn bytes_left(&self) -> usize {
+ self.content.limit() as usize
+ }
+
+ fn get_header(&self) -> &BoxHeader {
+ &self.head
+ }
+
+ fn box_iter<'b>(&'b mut self) -> BoxIter<BMFFBox<'a, T>> {
+ BoxIter::new(self)
+ }
+}
+
+/// Read and parse a box header.
+///
+/// Call this first to determine the type of a particular mp4 box
+/// and its length. Used internally for dispatching to specific
+/// parsers for the internal content, or to get the length to
+/// skip unknown or uninteresting boxes.
+fn read_box_header<T: ReadBytesExt>(src: &mut T) -> Result<BoxHeader> {
+ let size32 = try!(be_u32(src));
+ let name = BoxType::from(try!(be_u32(src)));
+ let size = match size32 {
+ // valid only for top-level box and indicates it's the last box in the file. usually mdat.
+ 0 => return Err(Error::Unsupported("unknown sized box")),
+ 1 => {
+ let size64 = try!(be_u64(src));
+ if size64 < 16 {
+ return Err(Error::InvalidData("malformed wide size"));
+ }
+ size64
+ }
+ 2...7 => return Err(Error::InvalidData("malformed size")),
+ _ => size32 as u64,
+ };
+ let offset = match size32 {
+ 1 => 4 + 4 + 8,
+ _ => 4 + 4,
+ };
+ assert!(offset <= size);
+ Ok(BoxHeader {
+ name: name,
+ size: size,
+ offset: offset,
+ })
+}
+
+/// Parse the extra header fields for a full box.
+fn read_fullbox_extra<T: ReadBytesExt>(src: &mut T) -> Result<(u8, u32)> {
+ let version = try!(src.read_u8());
+ let flags_a = try!(src.read_u8());
+ let flags_b = try!(src.read_u8());
+ let flags_c = try!(src.read_u8());
+ Ok((version,
+ (flags_a as u32) << 16 | (flags_b as u32) << 8 | (flags_c as u32)))
+}
+
+/// Skip over the entire contents of a box.
+fn skip_box_content<T: Read>(src: &mut BMFFBox<T>) -> Result<()> {
+ // Skip the contents of unknown chunks.
+ let to_skip = {
+ let header = src.get_header();
+ log!("{:?} (skipped)", header);
+ (header.size - header.offset) as usize
+ };
+ assert!(to_skip == src.bytes_left());
+ skip(src, to_skip)
+}
+
+/// Skip over the remain data of a box.
+fn skip_box_remain<T: Read>(src: &mut BMFFBox<T>) -> Result<()> {
+ let remain = {
+ let header = src.get_header();
+ let len = src.bytes_left();
+ log!("remain {} (skipped) in {:?}", len, header);
+ len
+ };
+ skip(src, remain)
+}
+
+macro_rules! check_parser_state {
+ ( $src:expr ) => {
+ if $src.limit() > 0 {
+ log!("bad parser state: {} content bytes left", $src.limit());
+ return Err(Error::InvalidData("unread box content or bad parser sync"));
+ }
+ }
+}
+
+/// Read the contents of a box, including sub boxes.
+///
+/// Metadata is accumulated in the passed-through `MediaContext` struct,
+/// which can be examined later.
+pub fn read_mp4<T: Read>(f: &mut T, context: &mut MediaContext) -> Result<()> {
+ let mut found_ftyp = false;
+ let mut found_moov = false;
+ // TODO(kinetik): Top-level parsing should handle zero-sized boxes
+ // rather than throwing an error.
+ let mut iter = BoxIter::new(f);
+ while let Some(mut b) = try!(iter.next_box()) {
+ // box ordering: ftyp before any variable length box (inc. moov),
+ // but may not be first box in file if file signatures etc. present
+ // fragmented mp4 order: ftyp, moov, pairs of moof/mdat (1-multiple), mfra
+
+ // "special": uuid, wide (= 8 bytes)
+ // isom: moov, mdat, free, skip, udta, ftyp, moof, mfra
+ // iso2: pdin, meta
+ // iso3: meco
+ // iso5: styp, sidx, ssix, prft
+ // unknown, maybe: id32
+
+ // qt: pnot
+
+ // possibly allow anything where all printable and/or all lowercase printable
+ // "four printable characters from the ISO 8859-1 character set"
+ match b.head.name {
+ BoxType::FileTypeBox => {
+ let ftyp = try!(read_ftyp(&mut b));
+ found_ftyp = true;
+ log!("{:?}", ftyp);
+ }
+ BoxType::MovieBox => {
+ try!(read_moov(&mut b, context));
+ found_moov = true;
+ }
+ _ => try!(skip_box_content(&mut b)),
+ };
+ check_parser_state!(b.content);
+ if found_moov {
+ log!("found moov {}, could stop pure 'moov' parser now", if found_ftyp {
+ "and ftyp"
+ } else {
+ "but no ftyp"
+ });
+ }
+ }
+
+ // XXX(kinetik): This isn't perfect, as a "moov" with no contents is
+ // treated as okay but we haven't found anything useful. Needs more
+ // thought for clearer behaviour here.
+ if found_moov {
+ Ok(())
+ } else {
+ Err(Error::NoMoov)
+ }
+}
+
+fn parse_mvhd<T: Read>(f: &mut BMFFBox<T>) -> Result<(MovieHeaderBox, Option<MediaTimeScale>)> {
+ let mvhd = try!(read_mvhd(f));
+ if mvhd.timescale == 0 {
+ return Err(Error::InvalidData("zero timescale in mdhd"));
+ }
+ let timescale = Some(MediaTimeScale(mvhd.timescale as u64));
+ Ok((mvhd, timescale))
+}
+
+fn read_moov<T: Read>(f: &mut BMFFBox<T>, context: &mut MediaContext) -> Result<()> {
+ let mut iter = f.box_iter();
+ while let Some(mut b) = try!(iter.next_box()) {
+ match b.head.name {
+ BoxType::MovieHeaderBox => {
+ let (mvhd, timescale) = try!(parse_mvhd(&mut b));
+ context.timescale = timescale;
+ log!("{:?}", mvhd);
+ }
+ BoxType::TrackBox => {
+ let mut track = Track::new(context.tracks.len());
+ try!(read_trak(&mut b, &mut track));
+ context.tracks.push(track);
+ }
+ BoxType::MovieExtendsBox => {
+ let mvex = try!(read_mvex(&mut b));
+ log!("{:?}", mvex);
+ context.mvex = Some(mvex);
+ }
+ _ => try!(skip_box_content(&mut b)),
+ };
+ check_parser_state!(b.content);
+ }
+ Ok(())
+}
+
+fn read_mvex<T: Read>(src: &mut BMFFBox<T>) -> Result<MovieExtendsBox> {
+ let mut iter = src.box_iter();
+ let mut fragment_duration = None;
+ while let Some(mut b) = try!(iter.next_box()) {
+ match b.head.name {
+ BoxType::MovieExtendsHeaderBox => {
+ let duration = try!(read_mehd(&mut b));
+ fragment_duration = Some(duration);
+ },
+ _ => try!(skip_box_content(&mut b)),
+ }
+ }
+ Ok(MovieExtendsBox {
+ fragment_duration: fragment_duration,
+ })
+}
+
+fn read_mehd<T: Read>(src: &mut BMFFBox<T>) -> Result<MediaScaledTime> {
+ let (version, _) = try!(read_fullbox_extra(src));
+ let fragment_duration = match version {
+ 1 => try!(be_u64(src)),
+ 0 => try!(be_u32(src)) as u64,
+ _ => return Err(Error::InvalidData("unhandled mehd version")),
+ };
+ Ok(MediaScaledTime(fragment_duration))
+}
+
+fn read_trak<T: Read>(f: &mut BMFFBox<T>, track: &mut Track) -> Result<()> {
+ let mut iter = f.box_iter();
+ while let Some(mut b) = try!(iter.next_box()) {
+ match b.head.name {
+ BoxType::TrackHeaderBox => {
+ let tkhd = try!(read_tkhd(&mut b));
+ track.track_id = Some(tkhd.track_id);
+ track.tkhd = Some(tkhd.clone());
+ log!("{:?}", tkhd);
+ }
+ BoxType::EditBox => try!(read_edts(&mut b, track)),
+ BoxType::MediaBox => try!(read_mdia(&mut b, track)),
+ _ => try!(skip_box_content(&mut b)),
+ };
+ check_parser_state!(b.content);
+ }
+ Ok(())
+}
+
+fn read_edts<T: Read>(f: &mut BMFFBox<T>, track: &mut Track) -> Result<()> {
+ let mut iter = f.box_iter();
+ while let Some(mut b) = try!(iter.next_box()) {
+ match b.head.name {
+ BoxType::EditListBox => {
+ let elst = try!(read_elst(&mut b));
+ let mut empty_duration = 0;
+ let mut idx = 0;
+ if elst.edits.len() > 2 {
+ return Err(Error::Unsupported("more than two edits"));
+ }
+ if elst.edits[idx].media_time == -1 {
+ empty_duration = elst.edits[idx].segment_duration;
+ if elst.edits.len() < 2 {
+ return Err(Error::InvalidData("expected additional edit"));
+ }
+ idx += 1;
+ }
+ track.empty_duration = Some(MediaScaledTime(empty_duration));
+ if elst.edits[idx].media_time < 0 {
+ return Err(Error::InvalidData("unexpected negative media time in edit"));
+ }
+ track.media_time = Some(TrackScaledTime(elst.edits[idx].media_time as u64,
+ track.id));
+ log!("{:?}", elst);
+ }
+ _ => try!(skip_box_content(&mut b)),
+ };
+ check_parser_state!(b.content);
+ }
+ Ok(())
+}
+
+fn parse_mdhd<T: Read>(f: &mut BMFFBox<T>, track: &mut Track) -> Result<(MediaHeaderBox, Option<TrackScaledTime>, Option<TrackTimeScale>)> {
+ let mdhd = try!(read_mdhd(f));
+ let duration = match mdhd.duration {
+ std::u64::MAX => None,
+ duration => Some(TrackScaledTime(duration, track.id)),
+ };
+ if mdhd.timescale == 0 {
+ return Err(Error::InvalidData("zero timescale in mdhd"));
+ }
+ let timescale = Some(TrackTimeScale(mdhd.timescale as u64, track.id));
+ Ok((mdhd, duration, timescale))
+}
+
+fn read_mdia<T: Read>(f: &mut BMFFBox<T>, track: &mut Track) -> Result<()> {
+ let mut iter = f.box_iter();
+ while let Some(mut b) = try!(iter.next_box()) {
+ match b.head.name {
+ BoxType::MediaHeaderBox => {
+ let (mdhd, duration, timescale) = try!(parse_mdhd(&mut b, track));
+ track.duration = duration;
+ track.timescale = timescale;
+ log!("{:?}", mdhd);
+ }
+ BoxType::HandlerBox => {
+ let hdlr = try!(read_hdlr(&mut b));
+ match hdlr.handler_type {
+ 0x76696465 /* 'vide' */ => track.track_type = TrackType::Video,
+ 0x736f756e /* 'soun' */ => track.track_type = TrackType::Audio,
+ _ => (),
+ }
+ log!("{:?}", hdlr);
+ }
+ BoxType::MediaInformationBox => try!(read_minf(&mut b, track)),
+ _ => try!(skip_box_content(&mut b)),
+ };
+ check_parser_state!(b.content);
+ }
+ Ok(())
+}
+
+fn read_minf<T: Read>(f: &mut BMFFBox<T>, track: &mut Track) -> Result<()> {
+ let mut iter = f.box_iter();
+ while let Some(mut b) = try!(iter.next_box()) {
+ match b.head.name {
+ BoxType::SampleTableBox => try!(read_stbl(&mut b, track)),
+ _ => try!(skip_box_content(&mut b)),
+ };
+ check_parser_state!(b.content);
+ }
+ Ok(())
+}
+
+fn read_stbl<T: Read>(f: &mut BMFFBox<T>, track: &mut Track) -> Result<()> {
+ let mut iter = f.box_iter();
+ while let Some(mut b) = try!(iter.next_box()) {
+ match b.head.name {
+ BoxType::SampleDescriptionBox => {
+ let stsd = try!(read_stsd(&mut b, track));
+ log!("{:?}", stsd);
+ }
+ BoxType::TimeToSampleBox => {
+ let stts = try!(read_stts(&mut b));
+ track.empty_sample_boxes.empty_stts = stts.samples.is_empty();
+ log!("{:?}", stts);
+ }
+ BoxType::SampleToChunkBox => {
+ let stsc = try!(read_stsc(&mut b));
+ track.empty_sample_boxes.empty_stsc = stsc.samples.is_empty();
+ log!("{:?}", stsc);
+ }
+ BoxType::SampleSizeBox => {
+ let stsz = try!(read_stsz(&mut b));
+ log!("{:?}", stsz);
+ }
+ BoxType::ChunkOffsetBox => {
+ let stco = try!(read_stco(&mut b));
+ track.empty_sample_boxes.empty_stco = stco.offsets.is_empty();
+ log!("{:?}", stco);
+ }
+ BoxType::ChunkLargeOffsetBox => {
+ let co64 = try!(read_co64(&mut b));
+ log!("{:?}", co64);
+ }
+ BoxType::SyncSampleBox => {
+ let stss = try!(read_stss(&mut b));
+ log!("{:?}", stss);
+ }
+ _ => try!(skip_box_content(&mut b)),
+ };
+ check_parser_state!(b.content);
+ }
+ Ok(())
+}
+
+/// Parse an ftyp box.
+fn read_ftyp<T: Read>(src: &mut BMFFBox<T>) -> Result<FileTypeBox> {
+ let major = try!(be_u32(src));
+ let minor = try!(be_u32(src));
+ let bytes_left = src.bytes_left();
+ if bytes_left % 4 != 0 {
+ return Err(Error::InvalidData("invalid ftyp size"));
+ }
+ // Is a brand_count of zero valid?
+ let brand_count = bytes_left / 4;
+ let mut brands = Vec::new();
+ for _ in 0..brand_count {
+ brands.push(try!(be_u32(src)));
+ }
+ Ok(FileTypeBox {
+ major_brand: major,
+ minor_version: minor,
+ compatible_brands: brands,
+ })
+}
+
+/// Parse an mvhd box.
+fn read_mvhd<T: Read>(src: &mut BMFFBox<T>) -> Result<MovieHeaderBox> {
+ let (version, _) = try!(read_fullbox_extra(src));
+ match version {
+ // 64 bit creation and modification times.
+ 1 => {
+ try!(skip(src, 16));
+ }
+ // 32 bit creation and modification times.
+ 0 => {
+ try!(skip(src, 8));
+ }
+ _ => return Err(Error::InvalidData("unhandled mvhd version")),
+ }
+ let timescale = try!(be_u32(src));
+ let duration = match version {
+ 1 => try!(be_u64(src)),
+ 0 => {
+ let d = try!(be_u32(src));
+ if d == std::u32::MAX {
+ std::u64::MAX
+ } else {
+ d as u64
+ }
+ }
+ _ => return Err(Error::InvalidData("unhandled mvhd version")),
+ };
+ // Skip remaining fields.
+ try!(skip(src, 80));
+ Ok(MovieHeaderBox {
+ timescale: timescale,
+ duration: duration,
+ })
+}
+
+/// Parse a tkhd box.
+fn read_tkhd<T: Read>(src: &mut BMFFBox<T>) -> Result<TrackHeaderBox> {
+ let (version, flags) = try!(read_fullbox_extra(src));
+ let disabled = flags & 0x1u32 == 0 || flags & 0x2u32 == 0;
+ match version {
+ // 64 bit creation and modification times.
+ 1 => {
+ try!(skip(src, 16));
+ }
+ // 32 bit creation and modification times.
+ 0 => {
+ try!(skip(src, 8));
+ }
+ _ => return Err(Error::InvalidData("unhandled tkhd version")),
+ }
+ let track_id = try!(be_u32(src));
+ try!(skip(src, 4));
+ let duration = match version {
+ 1 => try!(be_u64(src)),
+ 0 => try!(be_u32(src)) as u64,
+ _ => return Err(Error::InvalidData("unhandled tkhd version")),
+ };
+ // Skip uninteresting fields.
+ try!(skip(src, 52));
+ let width = try!(be_u32(src));
+ let height = try!(be_u32(src));
+ Ok(TrackHeaderBox {
+ track_id: track_id,
+ disabled: disabled,
+ duration: duration,
+ width: width,
+ height: height,
+ })
+}
+
+/// Parse a elst box.
+fn read_elst<T: Read>(src: &mut BMFFBox<T>) -> Result<EditListBox> {
+ let (version, _) = try!(read_fullbox_extra(src));
+ let edit_count = try!(be_u32(src));
+ if edit_count == 0 {
+ return Err(Error::InvalidData("invalid edit count"));
+ }
+ let mut edits = Vec::new();
+ for _ in 0..edit_count {
+ let (segment_duration, media_time) = match version {
+ 1 => {
+ // 64 bit segment duration and media times.
+ (try!(be_u64(src)), try!(be_i64(src)))
+ }
+ 0 => {
+ // 32 bit segment duration and media times.
+ (try!(be_u32(src)) as u64, try!(be_i32(src)) as i64)
+ }
+ _ => return Err(Error::InvalidData("unhandled elst version")),
+ };
+ let media_rate_integer = try!(be_i16(src));
+ let media_rate_fraction = try!(be_i16(src));
+ edits.push(Edit {
+ segment_duration: segment_duration,
+ media_time: media_time,
+ media_rate_integer: media_rate_integer,
+ media_rate_fraction: media_rate_fraction,
+ })
+ }
+
+ Ok(EditListBox {
+ edits: edits,
+ })
+}
+
+/// Parse a mdhd box.
+fn read_mdhd<T: Read>(src: &mut BMFFBox<T>) -> Result<MediaHeaderBox> {
+ let (version, _) = try!(read_fullbox_extra(src));
+ let (timescale, duration) = match version {
+ 1 => {
+ // Skip 64-bit creation and modification times.
+ try!(skip(src, 16));
+
+ // 64 bit duration.
+ (try!(be_u32(src)), try!(be_u64(src)))
+ }
+ 0 => {
+ // Skip 32-bit creation and modification times.
+ try!(skip(src, 8));
+
+ // 32 bit duration.
+ let timescale = try!(be_u32(src));
+ let duration = {
+ // Since we convert the 32-bit duration to 64-bit by
+ // upcasting, we need to preserve the special all-1s
+ // ("unknown") case by hand.
+ let d = try!(be_u32(src));
+ if d == std::u32::MAX {
+ std::u64::MAX
+ } else {
+ d as u64
+ }
+ };
+ (timescale, duration)
+ }
+ _ => return Err(Error::InvalidData("unhandled mdhd version")),
+ };
+
+ // Skip uninteresting fields.
+ try!(skip(src, 4));
+
+ Ok(MediaHeaderBox {
+ timescale: timescale,
+ duration: duration,
+ })
+}
+
+/// Parse a stco box.
+fn read_stco<T: Read>(src: &mut BMFFBox<T>) -> Result<ChunkOffsetBox> {
+ let (_, _) = try!(read_fullbox_extra(src));
+ let offset_count = try!(be_u32(src));
+ let mut offsets = Vec::new();
+ for _ in 0..offset_count {
+ offsets.push(try!(be_u32(src)) as u64);
+ }
+
+ // Padding could be added in some contents.
+ try!(skip_box_remain(src));
+
+ Ok(ChunkOffsetBox {
+ offsets: offsets,
+ })
+}
+
+/// Parse a co64 box.
+fn read_co64<T: Read>(src: &mut BMFFBox<T>) -> Result<ChunkOffsetBox> {
+ let (_, _) = try!(read_fullbox_extra(src));
+ let offset_count = try!(be_u32(src));
+ let mut offsets = Vec::new();
+ for _ in 0..offset_count {
+ offsets.push(try!(be_u64(src)));
+ }
+
+ // Padding could be added in some contents.
+ try!(skip_box_remain(src));
+
+ Ok(ChunkOffsetBox {
+ offsets: offsets,
+ })
+}
+
+/// Parse a stss box.
+fn read_stss<T: Read>(src: &mut BMFFBox<T>) -> Result<SyncSampleBox> {
+ let (_, _) = try!(read_fullbox_extra(src));
+ let sample_count = try!(be_u32(src));
+ let mut samples = Vec::new();
+ for _ in 0..sample_count {
+ samples.push(try!(be_u32(src)));
+ }
+
+ // Padding could be added in some contents.
+ try!(skip_box_remain(src));
+
+ Ok(SyncSampleBox {
+ samples: samples,
+ })
+}
+
+/// Parse a stsc box.
+fn read_stsc<T: Read>(src: &mut BMFFBox<T>) -> Result<SampleToChunkBox> {
+ let (_, _) = try!(read_fullbox_extra(src));
+ let sample_count = try!(be_u32(src));
+ let mut samples = Vec::new();
+ for _ in 0..sample_count {
+ let first_chunk = try!(be_u32(src));
+ let samples_per_chunk = try!(be_u32(src));
+ let sample_description_index = try!(be_u32(src));
+ samples.push(SampleToChunk {
+ first_chunk: first_chunk,
+ samples_per_chunk: samples_per_chunk,
+ sample_description_index: sample_description_index,
+ });
+ }
+
+ // Padding could be added in some contents.
+ try!(skip_box_remain(src));
+
+ Ok(SampleToChunkBox {
+ samples: samples,
+ })
+}
+
+/// Parse a stsz box.
+fn read_stsz<T: Read>(src: &mut BMFFBox<T>) -> Result<SampleSizeBox> {
+ let (_, _) = try!(read_fullbox_extra(src));
+ let sample_size = try!(be_u32(src));
+ let sample_count = try!(be_u32(src));
+ let mut sample_sizes = Vec::new();
+ if sample_size == 0 {
+ for _ in 0..sample_count {
+ sample_sizes.push(try!(be_u32(src)));
+ }
+ }
+
+ // Padding could be added in some contents.
+ try!(skip_box_remain(src));
+
+ Ok(SampleSizeBox {
+ sample_size: sample_size,
+ sample_sizes: sample_sizes,
+ })
+}
+
+/// Parse a stts box.
+fn read_stts<T: Read>(src: &mut BMFFBox<T>) -> Result<TimeToSampleBox> {
+ let (_, _) = try!(read_fullbox_extra(src));
+ let sample_count = try!(be_u32(src));
+ let mut samples = Vec::new();
+ for _ in 0..sample_count {
+ let sample_count = try!(be_u32(src));
+ let sample_delta = try!(be_u32(src));
+ samples.push(Sample {
+ sample_count: sample_count,
+ sample_delta: sample_delta,
+ });
+ }
+
+ // Padding could be added in some contents.
+ try!(skip_box_remain(src));
+
+ Ok(TimeToSampleBox {
+ samples: samples,
+ })
+}
+
+/// Parse a VPx Config Box.
+fn read_vpcc<T: Read>(src: &mut BMFFBox<T>) -> Result<VPxConfigBox> {
+ let (version, _) = try!(read_fullbox_extra(src));
+ if version != 0 {
+ return Err(Error::Unsupported("unknown vpcC version"));
+ }
+
+ let profile = try!(src.read_u8());
+ let level = try!(src.read_u8());
+ let (bit_depth, color_space) = {
+ let byte = try!(src.read_u8());
+ ((byte >> 4) & 0x0f, byte & 0x0f)
+ };
+ let (chroma_subsampling, transfer_function, video_full_range) = {
+ let byte = try!(src.read_u8());
+ ((byte >> 4) & 0x0f, (byte >> 1) & 0x07, (byte & 1) == 1)
+ };
+
+ let codec_init_size = try!(be_u16(src));
+ let codec_init = try!(read_buf(src, codec_init_size as usize));
+
+ // TODO(rillian): validate field value ranges.
+ Ok(VPxConfigBox {
+ profile: profile,
+ level: level,
+ bit_depth: bit_depth,
+ color_space: color_space,
+ chroma_subsampling: chroma_subsampling,
+ transfer_function: transfer_function,
+ video_full_range: video_full_range,
+ codec_init: codec_init,
+ })
+}
+
+fn read_flac_metadata<T: Read>(src: &mut BMFFBox<T>) -> Result<FLACMetadataBlock> {
+ let temp = try!(src.read_u8());
+ let block_type = temp & 0x7f;
+ let length = try!(be_u24(src));
+ if length as usize > src.bytes_left() {
+ return Err(Error::InvalidData(
+ "FLACMetadataBlock larger than parent box"));
+ }
+ let data = try!(read_buf(src, length as usize));
+ Ok(FLACMetadataBlock {
+ block_type: block_type,
+ data: data,
+ })
+}
+
+fn read_esds<T: Read>(src: &mut BMFFBox<T>) -> Result<ES_Descriptor> {
+ // Tags for elementary stream description
+ const ESDESCR_TAG: u8 = 0x03;
+ const DECODER_CONFIG_TAG: u8 = 0x04;
+ const DECODER_SPECIFIC_TAG: u8 = 0x05;
+
+ let frequency_table =
+ vec![(0x1, 96000), (0x1, 88200), (0x2, 64000), (0x3, 48000),
+ (0x4, 44100), (0x5, 32000), (0x6, 24000), (0x7, 22050),
+ (0x8, 16000), (0x9, 12000), (0xa, 11025), (0xb, 8000),
+ (0xc, 7350)];
+
+ let (_, _) = try!(read_fullbox_extra(src));
+
+ let esds_size = src.head.size - src.head.offset - 4;
+ if esds_size > BUF_SIZE_LIMIT {
+ return Err(Error::InvalidData("esds box exceeds BUF_SIZE_LIMIT"));
+ }
+ let esds_array = try!(read_buf(src, esds_size as usize));
+
+ // Parsing DecoderConfig descriptor to get the object_profile_indicator
+ // for correct codec type, audio sample rate and channel counts.
+ let (object_profile_indicator, sample_frequency, channels) = {
+ let mut object_profile: u8 = 0;
+ let mut sample_frequency = None;
+ let mut channels = None;
+
+ // clone a esds cursor for parsing.
+ let esds = &mut Cursor::new(&esds_array);
+ let next_tag = try!(esds.read_u8());
+
+ if next_tag != ESDESCR_TAG {
+ return Err(Error::Unsupported("fail to parse ES descriptor"));
+ }
+
+ let esds_extend = try!(esds.read_u8());
+ // extension tag start from 0x80.
+ let esds_end = if esds_extend >= 0x80 {
+ // skip remaining extension.
+ try!(skip(esds, 2));
+ esds.position() + try!(esds.read_u8()) as u64
+ } else {
+ esds.position() + esds_extend as u64
+ };
+ try!(skip(esds, 2));
+
+ let esds_flags = try!(esds.read_u8());
+
+ // Stream dependency flag, first bit from left most.
+ if esds_flags & 0x80 > 0 {
+ // Skip uninteresting fields.
+ try!(skip(esds, 2));
+ }
+
+ // Url flag, second bit from left most.
+ if esds_flags & 0x40 > 0 {
+ // Skip uninteresting fields.
+ let skip_es_len: usize = try!(esds.read_u8()) as usize + 2;
+ try!(skip(esds, skip_es_len));
+ }
+
+ // find DecoderConfig descriptor (tag = DECODER_CONFIG_TAG)
+ if esds_end > esds.position() {
+ let next_tag = try!(esds.read_u8());
+ if next_tag == DECODER_CONFIG_TAG {
+ let dcds_extend = try!(esds.read_u8());
+ // extension tag start from 0x80.
+ if dcds_extend >= 0x80 {
+ // skip remains extension and length.
+ try!(skip(esds, 3));
+ }
+
+ object_profile = try!(esds.read_u8());
+
+ // Skip uninteresting fields.
+ try!(skip(esds, 12));
+ }
+ }
+
+
+ // find DecoderSpecific descriptor (tag = DECODER_SPECIFIC_TAG)
+ if esds_end > esds.position() {
+ let next_tag = try!(esds.read_u8());
+ if next_tag == DECODER_SPECIFIC_TAG {
+ let dsds_extend = try!(esds.read_u8());
+ // extension tag start from 0x80.
+ if dsds_extend >= 0x80 {
+ // skip remains extension and length.
+ try!(skip(esds, 3));
+ }
+
+ let audio_specific_config = try!(be_u16(esds));
+
+ let sample_index = (audio_specific_config & 0x07FF) >> 7;
+
+ let channel_counts = (audio_specific_config & 0x007F) >> 3;
+
+ sample_frequency =
+ frequency_table.iter().find(|item| item.0 == sample_index).map(|x| x.1);
+
+ channels = Some(channel_counts);
+ }
+ }
+
+ (object_profile, sample_frequency, channels)
+ };
+
+ let codec = match object_profile_indicator {
+ 0x40 | 0x41 => CodecType::AAC,
+ 0x6B => CodecType::MP3,
+ _ => CodecType::Unknown,
+ };
+
+ if codec == CodecType::Unknown {
+ return Err(Error::Unsupported("unknown audio codec"));
+ }
+
+ Ok(ES_Descriptor {
+ audio_codec: codec,
+ audio_sample_rate: sample_frequency,
+ audio_channel_count: channels,
+ codec_specific_config: esds_array,
+ })
+}
+
+/// Parse `FLACSpecificBox`.
+fn read_dfla<T: Read>(src: &mut BMFFBox<T>) -> Result<FLACSpecificBox> {
+ let (version, flags) = try!(read_fullbox_extra(src));
+ if version != 0 {
+ return Err(Error::Unsupported("unknown dfLa (FLAC) version"));
+ }
+ if flags != 0 {
+ return Err(Error::InvalidData("no-zero dfLa (FLAC) flags"));
+ }
+ let mut blocks = Vec::new();
+ while src.bytes_left() > 0 {
+ let block = try!(read_flac_metadata(src));
+ blocks.push(block);
+ }
+ // The box must have at least one meta block, and the first block
+ // must be the METADATA_BLOCK_STREAMINFO
+ if blocks.is_empty() {
+ return Err(Error::InvalidData("FLACSpecificBox missing metadata"));
+ } else if blocks[0].block_type != 0 {
+ println!("flac metadata block:\n {:?}", blocks[0]);
+ return Err(Error::InvalidData(
+ "FLACSpecificBox must have STREAMINFO metadata first"));
+ } else if blocks[0].data.len() != 34 {
+ return Err(Error::InvalidData(
+ "FLACSpecificBox STREAMINFO block is the wrong size"));
+ }
+ Ok(FLACSpecificBox {
+ version: version,
+ blocks: blocks,
+ })
+}
+
+/// Parse `OpusSpecificBox`.
+fn read_dops<T: Read>(src: &mut BMFFBox<T>) -> Result<OpusSpecificBox> {
+ let version = try!(src.read_u8());
+ if version != 0 {
+ return Err(Error::Unsupported("unknown dOps (Opus) version"));
+ }
+
+ let output_channel_count = try!(src.read_u8());
+ let pre_skip = try!(be_u16(src));
+ let input_sample_rate = try!(be_u32(src));
+ let output_gain = try!(be_i16(src));
+ let channel_mapping_family = try!(src.read_u8());
+
+ let channel_mapping_table = if channel_mapping_family == 0 {
+ None
+ } else {
+ let stream_count = try!(src.read_u8());
+ let coupled_count = try!(src.read_u8());
+ let channel_mapping = try!(read_buf(src, output_channel_count as usize));
+
+ Some(ChannelMappingTable {
+ stream_count: stream_count,
+ coupled_count: coupled_count,
+ channel_mapping: channel_mapping,
+ })
+ };
+
+ // TODO(kinetik): validate field value ranges.
+ Ok(OpusSpecificBox {
+ version: version,
+ output_channel_count: output_channel_count,
+ pre_skip: pre_skip,
+ input_sample_rate: input_sample_rate,
+ output_gain: output_gain,
+ channel_mapping_family: channel_mapping_family,
+ channel_mapping_table: channel_mapping_table,
+ })
+}
+
+/// Re-serialize the Opus codec-specific config data as an `OpusHead` packet.
+///
+/// Some decoders expect the initialization data in the format used by the
+/// Ogg and WebM encapsulations. To support this we prepend the `OpusHead`
+/// tag and byte-swap the data from big- to little-endian relative to the
+/// dOps box.
+pub fn serialize_opus_header<W: byteorder::WriteBytesExt + std::io::Write>(opus: &OpusSpecificBox, dst: &mut W) -> Result<()> {
+ match dst.write(b"OpusHead") {
+ Err(e) => return Err(Error::from(e)),
+ Ok(bytes) => {
+ if bytes != 8 {
+ return Err(Error::InvalidData("Couldn't write OpusHead tag."));
+ }
+ }
+ }
+ // In mp4 encapsulation, the version field is 0, but in ogg
+ // it is 1. While decoders generally accept zero as well, write
+ // out the version of the header we're supporting rather than
+ // whatever we parsed out of mp4.
+ try!(dst.write_u8(1));
+ try!(dst.write_u8(opus.output_channel_count));
+ try!(dst.write_u16::<byteorder::LittleEndian>(opus.pre_skip));
+ try!(dst.write_u32::<byteorder::LittleEndian>(opus.input_sample_rate));
+ try!(dst.write_i16::<byteorder::LittleEndian>(opus.output_gain));
+ try!(dst.write_u8(opus.channel_mapping_family));
+ match opus.channel_mapping_table {
+ None => {}
+ Some(ref table) => {
+ try!(dst.write_u8(table.stream_count));
+ try!(dst.write_u8(table.coupled_count));
+ match dst.write(&table.channel_mapping) {
+ Err(e) => return Err(Error::from(e)),
+ Ok(bytes) => {
+ if bytes != table.channel_mapping.len() {
+ return Err(Error::InvalidData("Couldn't write channel mapping table data."));
+ }
+ }
+ }
+ }
+ };
+ Ok(())
+}
+
+/// Parse a hdlr box.
+fn read_hdlr<T: Read>(src: &mut BMFFBox<T>) -> Result<HandlerBox> {
+ let (_, _) = try!(read_fullbox_extra(src));
+
+ // Skip uninteresting fields.
+ try!(skip(src, 4));
+
+ let handler_type = try!(be_u32(src));
+
+ // Skip uninteresting fields.
+ try!(skip(src, 12));
+
+ let bytes_left = src.bytes_left();
+ let _name = try!(read_null_terminated_string(src, bytes_left));
+
+ Ok(HandlerBox {
+ handler_type: handler_type,
+ })
+}
+
+/// Parse an video description inside an stsd box.
+fn read_video_sample_entry<T: Read>(src: &mut BMFFBox<T>, track: &mut Track) -> Result<SampleEntry> {
+ let name = src.get_header().name;
+ track.codec_type = match name {
+ BoxType::AVCSampleEntry | BoxType::AVC3SampleEntry => CodecType::H264,
+ BoxType::VP8SampleEntry => CodecType::VP8,
+ BoxType::VP9SampleEntry => CodecType::VP9,
+ BoxType::ProtectedVisualSampleEntry => CodecType::EncryptedVideo,
+ _ => CodecType::Unknown,
+ };
+
+ // Skip uninteresting fields.
+ try!(skip(src, 6));
+
+ let data_reference_index = try!(be_u16(src));
+
+ // Skip uninteresting fields.
+ try!(skip(src, 16));
+
+ let width = try!(be_u16(src));
+ let height = try!(be_u16(src));
+
+ // Skip uninteresting fields.
+ try!(skip(src, 14));
+
+ let _compressorname = try!(read_fixed_length_pascal_string(src, 32));
+
+ // Skip uninteresting fields.
+ try!(skip(src, 4));
+
+ // Skip clap/pasp/etc. for now.
+ let mut codec_specific = None;
+ let mut iter = src.box_iter();
+ while let Some(mut b) = try!(iter.next_box()) {
+ match b.head.name {
+ BoxType::AVCConfigurationBox => {
+ if (name != BoxType::AVCSampleEntry &&
+ name != BoxType::AVC3SampleEntry &&
+ name != BoxType::ProtectedVisualSampleEntry) ||
+ codec_specific.is_some() {
+ return Err(Error::InvalidData("malformed video sample entry"));
+ }
+ let avcc_size = b.head.size - b.head.offset;
+ if avcc_size > BUF_SIZE_LIMIT {
+ return Err(Error::InvalidData("avcC box exceeds BUF_SIZE_LIMIT"));
+ }
+ let avcc = try!(read_buf(&mut b.content, avcc_size as usize));
+ // TODO(kinetik): Parse avcC box? For now we just stash the data.
+ codec_specific = Some(VideoCodecSpecific::AVCConfig(avcc));
+ }
+ BoxType::VPCodecConfigurationBox => { // vpcC
+ if (name != BoxType::VP8SampleEntry &&
+ name != BoxType::VP9SampleEntry) ||
+ codec_specific.is_some() {
+ return Err(Error::InvalidData("malformed video sample entry"));
+ }
+ let vpcc = try!(read_vpcc(&mut b));
+ codec_specific = Some(VideoCodecSpecific::VPxConfig(vpcc));
+ }
+ _ => try!(skip_box_content(&mut b)),
+ }
+ check_parser_state!(b.content);
+ }
+
+ codec_specific
+ .map(|codec_specific| SampleEntry::Video(VideoSampleEntry {
+ data_reference_index: data_reference_index,
+ width: width,
+ height: height,
+ codec_specific: codec_specific,
+ }))
+ .ok_or_else(|| Error::InvalidData("malformed video sample entry"))
+}
+
+fn read_qt_wave_atom<T: Read>(src: &mut BMFFBox<T>) -> Result<ES_Descriptor> {
+ let mut codec_specific = None;
+ let mut iter = src.box_iter();
+ while let Some(mut b) = try!(iter.next_box()) {
+ match b.head.name {
+ BoxType::ESDBox => {
+ let esds = try!(read_esds(&mut b));
+ codec_specific = Some(esds);
+ },
+ _ => try!(skip_box_content(&mut b)),
+ }
+ }
+
+ codec_specific.ok_or_else(|| Error::InvalidData("malformed audio sample entry"))
+}
+
+/// Parse an audio description inside an stsd box.
+fn read_audio_sample_entry<T: Read>(src: &mut BMFFBox<T>, track: &mut Track) -> Result<SampleEntry> {
+ let name = src.get_header().name;
+ track.codec_type = match name {
+ // TODO(kinetik): stagefright inspects ESDS to detect MP3 (audio/mpeg).
+ BoxType::MP4AudioSampleEntry => CodecType::AAC,
+ BoxType::FLACSampleEntry => CodecType::FLAC,
+ BoxType::OpusSampleEntry => CodecType::Opus,
+ BoxType::ProtectedAudioSampleEntry => CodecType::EncryptedAudio,
+ _ => CodecType::Unknown,
+ };
+
+ // Skip uninteresting fields.
+ try!(skip(src, 6));
+
+ let data_reference_index = try!(be_u16(src));
+
+ // XXX(kinetik): This is "reserved" in BMFF, but some old QT MOV variant
+ // uses it, need to work out if we have to support it. Without checking
+ // here and reading extra fields after samplerate (or bailing with an
+ // error), the parser loses sync completely.
+ let version = try!(be_u16(src));
+
+ // Skip uninteresting fields.
+ try!(skip(src, 6));
+
+ let channelcount = try!(be_u16(src));
+ let samplesize = try!(be_u16(src));
+
+ // Skip uninteresting fields.
+ try!(skip(src, 4));
+
+ let samplerate = try!(be_u32(src));
+
+ match version {
+ 0 => (),
+ 1 => {
+ // Quicktime sound sample description version 1.
+ // Skip uninteresting fields.
+ try!(skip(src, 16));
+ },
+ _ => return Err(Error::Unsupported("unsupported non-isom audio sample entry")),
+ }
+
+ // Skip chan/etc. for now.
+ let mut codec_specific = None;
+ let mut iter = src.box_iter();
+ while let Some(mut b) = try!(iter.next_box()) {
+ match b.head.name {
+ BoxType::ESDBox => {
+ if (name != BoxType::MP4AudioSampleEntry &&
+ name != BoxType::ProtectedAudioSampleEntry) ||
+ codec_specific.is_some() {
+ return Err(Error::InvalidData("malformed audio sample entry"));
+ }
+
+ let esds = try!(read_esds(&mut b));
+ track.codec_type = esds.audio_codec;
+ codec_specific = Some(AudioCodecSpecific::ES_Descriptor(esds));
+ }
+ BoxType::FLACSpecificBox => {
+ if name != BoxType::FLACSampleEntry ||
+ codec_specific.is_some() {
+ return Err(Error::InvalidData("malformed audio sample entry"));
+ }
+ let dfla = try!(read_dfla(&mut b));
+ track.codec_type = CodecType::FLAC;
+ codec_specific = Some(AudioCodecSpecific::FLACSpecificBox(dfla));
+ }
+ BoxType::OpusSpecificBox => {
+ if name != BoxType::OpusSampleEntry ||
+ codec_specific.is_some() {
+ return Err(Error::InvalidData("malformed audio sample entry"));
+ }
+ let dops = try!(read_dops(&mut b));
+ track.codec_type = CodecType::Opus;
+ codec_specific = Some(AudioCodecSpecific::OpusSpecificBox(dops));
+ }
+ BoxType::QTWaveAtom => {
+ let qt_esds = try!(read_qt_wave_atom(&mut b));
+ track.codec_type = qt_esds.audio_codec;
+ codec_specific = Some(AudioCodecSpecific::ES_Descriptor(qt_esds));
+ }
+ _ => try!(skip_box_content(&mut b)),
+ }
+ check_parser_state!(b.content);
+ }
+
+ codec_specific
+ .map(|codec_specific| SampleEntry::Audio(AudioSampleEntry {
+ data_reference_index: data_reference_index,
+ channelcount: channelcount,
+ samplesize: samplesize,
+ samplerate: samplerate,
+ codec_specific: codec_specific,
+ }))
+ .ok_or_else(|| Error::InvalidData("malformed audio sample entry"))
+}
+
+/// Parse a stsd box.
+fn read_stsd<T: Read>(src: &mut BMFFBox<T>, track: &mut Track) -> Result<SampleDescriptionBox> {
+ let (_, _) = try!(read_fullbox_extra(src));
+
+ let description_count = try!(be_u32(src));
+ let mut descriptions = Vec::new();
+
+ {
+ // TODO(kinetik): check if/when more than one desc per track? do we need to support?
+ let mut iter = src.box_iter();
+ while let Some(mut b) = try!(iter.next_box()) {
+ let description = match track.track_type {
+ TrackType::Video => read_video_sample_entry(&mut b, track),
+ TrackType::Audio => read_audio_sample_entry(&mut b, track),
+ TrackType::Unknown => Err(Error::Unsupported("unknown track type")),
+ };
+ let description = match description {
+ Ok(desc) => desc,
+ Err(Error::Unsupported(_)) => {
+ // read_{audio,video}_desc may have returned Unsupported
+ // after partially reading the box content, so we can't
+ // simply use skip_box_content here.
+ let to_skip = b.bytes_left();
+ try!(skip(&mut b, to_skip));
+ SampleEntry::Unknown
+ }
+ Err(e) => return Err(e),
+ };
+ if track.data.is_none() {
+ track.data = Some(description.clone());
+ } else {
+ log!("** don't know how to handle multiple descriptions **");
+ }
+ descriptions.push(description);
+ check_parser_state!(b.content);
+ if descriptions.len() == description_count as usize {
+ break;
+ }
+ }
+ }
+
+ // Padding could be added in some contents.
+ try!(skip_box_remain(src));
+
+ Ok(SampleDescriptionBox {
+ descriptions: descriptions,
+ })
+}
+
+/// Skip a number of bytes that we don't care to parse.
+fn skip<T: Read>(src: &mut T, mut bytes: usize) -> Result<()> {
+ const BUF_SIZE: usize = 64 * 1024;
+ let mut buf = vec![0; BUF_SIZE];
+ while bytes > 0 {
+ let buf_size = cmp::min(bytes, BUF_SIZE);
+ let len = try!(src.take(buf_size as u64).read(&mut buf));
+ if len == 0 {
+ return Err(Error::UnexpectedEOF);
+ }
+ bytes -= len;
+ }
+ Ok(())
+}
+
+/// Read size bytes into a Vector or return error.
+fn read_buf<T: ReadBytesExt>(src: &mut T, size: usize) -> Result<Vec<u8>> {
+ let mut buf = vec![0; size];
+ let r = try!(src.read(&mut buf));
+ if r != size {
+ return Err(Error::InvalidData("failed buffer read"));
+ }
+ Ok(buf)
+}
+
+// TODO(kinetik): Find a copy of ISO/IEC 14496-1 to confirm various string encodings.
+// XXX(kinetik): definition of "null-terminated" string is fuzzy, we have:
+// - zero or more byte strings, with a single null terminating the string.
+// - zero byte strings with no null terminator (i.e. zero space in the box for the string)
+// - length-prefixed strings with no null terminator (e.g. bear_rotate_0.mp4)
+fn read_null_terminated_string<T: ReadBytesExt>(src: &mut T, mut size: usize) -> Result<String> {
+ let mut buf = Vec::new();
+ while size > 0 {
+ let c = try!(src.read_u8());
+ if c == 0 {
+ break;
+ }
+ buf.push(c);
+ size -= 1;
+ }
+ String::from_utf8(buf).map_err(From::from)
+}
+
+#[allow(dead_code)]
+fn read_pascal_string<T: ReadBytesExt>(src: &mut T) -> Result<String> {
+ let len = try!(src.read_u8());
+ let buf = try!(read_buf(src, len as usize));
+ String::from_utf8(buf).map_err(From::from)
+}
+
+// Weird string encoding with a length prefix and a fixed sized buffer which
+// contains padding if the string doesn't fill the buffer.
+fn read_fixed_length_pascal_string<T: Read>(src: &mut T, size: usize) -> Result<String> {
+ assert!(size > 0);
+ let len = cmp::min(try!(src.read_u8()) as usize, size - 1);
+ let buf = try!(read_buf(src, len));
+ try!(skip(src, size - 1 - buf.len()));
+ String::from_utf8(buf).map_err(From::from)
+}
+
+fn be_i16<T: ReadBytesExt>(src: &mut T) -> Result<i16> {
+ src.read_i16::<byteorder::BigEndian>().map_err(From::from)
+}
+
+fn be_i32<T: ReadBytesExt>(src: &mut T) -> Result<i32> {
+ src.read_i32::<byteorder::BigEndian>().map_err(From::from)
+}
+
+fn be_i64<T: ReadBytesExt>(src: &mut T) -> Result<i64> {
+ src.read_i64::<byteorder::BigEndian>().map_err(From::from)
+}
+
+fn be_u16<T: ReadBytesExt>(src: &mut T) -> Result<u16> {
+ src.read_u16::<byteorder::BigEndian>().map_err(From::from)
+}
+
+fn be_u24<T: ReadBytesExt>(src: &mut T) -> Result<u32> {
+ src.read_uint::<byteorder::BigEndian>(3)
+ .map(|v| v as u32)
+ .map_err(From::from)
+}
+
+fn be_u32<T: ReadBytesExt>(src: &mut T) -> Result<u32> {
+ src.read_u32::<byteorder::BigEndian>().map_err(From::from)
+}
+
+fn be_u64<T: ReadBytesExt>(src: &mut T) -> Result<u64> {
+ src.read_u64::<byteorder::BigEndian>().map_err(From::from)
+}
diff --git a/media/libstagefright/binding/mp4parse/src/tests.rs b/media/libstagefright/binding/mp4parse/src/tests.rs
new file mode 100644
index 000000000..7063b3790
--- /dev/null
+++ b/media/libstagefright/binding/mp4parse/src/tests.rs
@@ -0,0 +1,860 @@
+//! Module for parsing ISO Base Media Format aka video/mp4 streams.
+//! Internal unit tests.
+
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at https://mozilla.org/MPL/2.0/.
+
+use std::io::Cursor;
+use super::read_mp4;
+use super::MediaContext;
+use super::Error;
+extern crate test_assembler;
+use self::test_assembler::*;
+
+use boxes::BoxType;
+
+enum BoxSize {
+ Short(u32),
+ Long(u64),
+ UncheckedShort(u32),
+ UncheckedLong(u64),
+ Auto,
+}
+
+fn make_box<F>(size: BoxSize, name: &[u8; 4], func: F) -> Cursor<Vec<u8>>
+ where F: Fn(Section) -> Section
+{
+ let mut section = Section::new();
+ let box_size = Label::new();
+ section = match size {
+ BoxSize::Short(size) | BoxSize::UncheckedShort(size) => section.B32(size),
+ BoxSize::Long(_) | BoxSize::UncheckedLong(_) => section.B32(1),
+ BoxSize::Auto => section.B32(&box_size),
+ };
+ section = section.append_bytes(name);
+ section = match size {
+ // The spec allows the 32-bit size to be 0 to indicate unknown
+ // length streams. It's not clear if this is valid when using a
+ // 64-bit size, so prohibit it for now.
+ BoxSize::Long(size) => {
+ assert!(size > 0);
+ section.B64(size)
+ }
+ BoxSize::UncheckedLong(size) => section.B64(size),
+ _ => section,
+ };
+ section = func(section);
+ match size {
+ BoxSize::Short(size) => {
+ if size > 0 {
+ assert_eq!(size as u64, section.size())
+ }
+ }
+ BoxSize::Long(size) => assert_eq!(size, section.size()),
+ BoxSize::Auto => {
+ assert!(section.size() <= u32::max_value() as u64,
+ "Tried to use a long box with BoxSize::Auto");
+ box_size.set_const(section.size());
+ }
+ // Skip checking BoxSize::Unchecked* cases.
+ _ => (),
+ }
+ Cursor::new(section.get_contents().unwrap())
+}
+
+fn make_fullbox<F>(size: BoxSize, name: &[u8; 4], version: u8, func: F) -> Cursor<Vec<u8>>
+ where F: Fn(Section) -> Section
+{
+ make_box(size, name, |s| {
+ func(s.B8(version)
+ .B8(0)
+ .B8(0)
+ .B8(0))
+ })
+}
+
+#[test]
+fn read_box_header_short() {
+ let mut stream = make_box(BoxSize::Short(8), b"test", |s| s);
+ let header = super::read_box_header(&mut stream).unwrap();
+ assert_eq!(header.name, BoxType::UnknownBox(0x74657374)); // "test"
+ assert_eq!(header.size, 8);
+}
+
+#[test]
+fn read_box_header_long() {
+ let mut stream = make_box(BoxSize::Long(16), b"test", |s| s);
+ let header = super::read_box_header(&mut stream).unwrap();
+ assert_eq!(header.name, BoxType::UnknownBox(0x74657374)); // "test"
+ assert_eq!(header.size, 16);
+}
+
+#[test]
+fn read_box_header_short_unknown_size() {
+ let mut stream = make_box(BoxSize::Short(0), b"test", |s| s);
+ match super::read_box_header(&mut stream) {
+ Err(Error::Unsupported(s)) => assert_eq!(s, "unknown sized box"),
+ _ => panic!("unexpected result reading box with unknown size"),
+ };
+}
+
+#[test]
+fn read_box_header_short_invalid_size() {
+ let mut stream = make_box(BoxSize::UncheckedShort(2), b"test", |s| s);
+ match super::read_box_header(&mut stream) {
+ Err(Error::InvalidData(s)) => assert_eq!(s, "malformed size"),
+ _ => panic!("unexpected result reading box with invalid size"),
+ };
+}
+
+#[test]
+fn read_box_header_long_invalid_size() {
+ let mut stream = make_box(BoxSize::UncheckedLong(2), b"test", |s| s);
+ match super::read_box_header(&mut stream) {
+ Err(Error::InvalidData(s)) => assert_eq!(s, "malformed wide size"),
+ _ => panic!("unexpected result reading box with invalid size"),
+ };
+}
+
+#[test]
+fn read_ftyp() {
+ let mut stream = make_box(BoxSize::Short(24), b"ftyp", |s| {
+ s.append_bytes(b"mp42")
+ .B32(0) // minor version
+ .append_bytes(b"isom")
+ .append_bytes(b"mp42")
+ });
+ let mut iter = super::BoxIter::new(&mut stream);
+ let mut stream = iter.next_box().unwrap().unwrap();
+ assert_eq!(stream.head.name, BoxType::FileTypeBox);
+ assert_eq!(stream.head.size, 24);
+ let parsed = super::read_ftyp(&mut stream).unwrap();
+ assert_eq!(parsed.major_brand, 0x6d703432); // mp42
+ assert_eq!(parsed.minor_version, 0);
+ assert_eq!(parsed.compatible_brands.len(), 2);
+ assert_eq!(parsed.compatible_brands[0], 0x69736f6d); // isom
+ assert_eq!(parsed.compatible_brands[1], 0x6d703432); // mp42
+}
+
+#[test]
+fn read_truncated_ftyp() {
+ // We declare a 24 byte box, but only write 20 bytes.
+ let mut stream = make_box(BoxSize::UncheckedShort(24), b"ftyp", |s| {
+ s.append_bytes(b"mp42")
+ .B32(0) // minor version
+ .append_bytes(b"isom")
+ });
+ let mut context = MediaContext::new();
+ match read_mp4(&mut stream, &mut context) {
+ Err(Error::UnexpectedEOF) => (),
+ Ok(_) => assert!(false, "expected an error result"),
+ _ => assert!(false, "expected a different error result"),
+ }
+}
+
+#[test]
+fn read_ftyp_case() {
+ // Brands in BMFF are represented as a u32, so it would seem clear that
+ // 0x6d703432 ("mp42") is not equal to 0x4d503432 ("MP42"), but some
+ // demuxers treat these as case-insensitive strings, e.g. street.mp4's
+ // major brand is "MP42". I haven't seen case-insensitive
+ // compatible_brands (which we also test here), but it doesn't seem
+ // unlikely given the major_brand behaviour.
+ let mut stream = make_box(BoxSize::Auto, b"ftyp", |s| {
+ s.append_bytes(b"MP42")
+ .B32(0) // minor version
+ .append_bytes(b"ISOM")
+ .append_bytes(b"MP42")
+ });
+ let mut iter = super::BoxIter::new(&mut stream);
+ let mut stream = iter.next_box().unwrap().unwrap();
+ assert_eq!(stream.head.name, BoxType::FileTypeBox);
+ assert_eq!(stream.head.size, 24);
+ let parsed = super::read_ftyp(&mut stream).unwrap();
+ assert_eq!(parsed.major_brand, 0x4d503432);
+ assert_eq!(parsed.minor_version, 0);
+ assert_eq!(parsed.compatible_brands.len(), 2);
+ assert_eq!(parsed.compatible_brands[0], 0x49534f4d); // ISOM
+ assert_eq!(parsed.compatible_brands[1], 0x4d503432); // MP42
+}
+
+#[test]
+fn read_elst_v0() {
+ let mut stream = make_fullbox(BoxSize::Short(28), b"elst", 0, |s| {
+ s.B32(1) // list count
+ // first entry
+ .B32(1234) // duration
+ .B32(5678) // time
+ .B16(12) // rate integer
+ .B16(34) // rate fraction
+ });
+ let mut iter = super::BoxIter::new(&mut stream);
+ let mut stream = iter.next_box().unwrap().unwrap();
+ assert_eq!(stream.head.name, BoxType::EditListBox);
+ assert_eq!(stream.head.size, 28);
+ let parsed = super::read_elst(&mut stream).unwrap();
+ assert_eq!(parsed.edits.len(), 1);
+ assert_eq!(parsed.edits[0].segment_duration, 1234);
+ assert_eq!(parsed.edits[0].media_time, 5678);
+ assert_eq!(parsed.edits[0].media_rate_integer, 12);
+ assert_eq!(parsed.edits[0].media_rate_fraction, 34);
+}
+
+#[test]
+fn read_elst_v1() {
+ let mut stream = make_fullbox(BoxSize::Short(56), b"elst", 1, |s| {
+ s.B32(2) // list count
+ // first entry
+ .B64(1234) // duration
+ .B64(5678) // time
+ .B16(12) // rate integer
+ .B16(34) // rate fraction
+ // second entry
+ .B64(1234) // duration
+ .B64(5678) // time
+ .B16(12) // rate integer
+ .B16(34) // rate fraction
+ });
+ let mut iter = super::BoxIter::new(&mut stream);
+ let mut stream = iter.next_box().unwrap().unwrap();
+ assert_eq!(stream.head.name, BoxType::EditListBox);
+ assert_eq!(stream.head.size, 56);
+ let parsed = super::read_elst(&mut stream).unwrap();
+ assert_eq!(parsed.edits.len(), 2);
+ assert_eq!(parsed.edits[1].segment_duration, 1234);
+ assert_eq!(parsed.edits[1].media_time, 5678);
+ assert_eq!(parsed.edits[1].media_rate_integer, 12);
+ assert_eq!(parsed.edits[1].media_rate_fraction, 34);
+}
+
+#[test]
+fn read_mdhd_v0() {
+ let mut stream = make_fullbox(BoxSize::Short(32), b"mdhd", 0, |s| {
+ s.B32(0)
+ .B32(0)
+ .B32(1234) // timescale
+ .B32(5678) // duration
+ .B32(0)
+ });
+ let mut iter = super::BoxIter::new(&mut stream);
+ let mut stream = iter.next_box().unwrap().unwrap();
+ assert_eq!(stream.head.name, BoxType::MediaHeaderBox);
+ assert_eq!(stream.head.size, 32);
+ let parsed = super::read_mdhd(&mut stream).unwrap();
+ assert_eq!(parsed.timescale, 1234);
+ assert_eq!(parsed.duration, 5678);
+}
+
+#[test]
+fn read_mdhd_v1() {
+ let mut stream = make_fullbox(BoxSize::Short(44), b"mdhd", 1, |s| {
+ s.B64(0)
+ .B64(0)
+ .B32(1234) // timescale
+ .B64(5678) // duration
+ .B32(0)
+ });
+ let mut iter = super::BoxIter::new(&mut stream);
+ let mut stream = iter.next_box().unwrap().unwrap();
+ assert_eq!(stream.head.name, BoxType::MediaHeaderBox);
+ assert_eq!(stream.head.size, 44);
+ let parsed = super::read_mdhd(&mut stream).unwrap();
+ assert_eq!(parsed.timescale, 1234);
+ assert_eq!(parsed.duration, 5678);
+}
+
+#[test]
+fn read_mdhd_unknown_duration() {
+ let mut stream = make_fullbox(BoxSize::Short(32), b"mdhd", 0, |s| {
+ s.B32(0)
+ .B32(0)
+ .B32(1234) // timescale
+ .B32(::std::u32::MAX) // duration
+ .B32(0)
+ });
+ let mut iter = super::BoxIter::new(&mut stream);
+ let mut stream = iter.next_box().unwrap().unwrap();
+ assert_eq!(stream.head.name, BoxType::MediaHeaderBox);
+ assert_eq!(stream.head.size, 32);
+ let parsed = super::read_mdhd(&mut stream).unwrap();
+ assert_eq!(parsed.timescale, 1234);
+ assert_eq!(parsed.duration, ::std::u64::MAX);
+}
+
+#[test]
+fn read_mdhd_invalid_timescale() {
+ let mut stream = make_fullbox(BoxSize::Short(44), b"mdhd", 1, |s| {
+ s.B64(0)
+ .B64(0)
+ .B32(0) // timescale
+ .B64(5678) // duration
+ .B32(0)
+ });
+ let mut iter = super::BoxIter::new(&mut stream);
+ let mut stream = iter.next_box().unwrap().unwrap();
+ assert_eq!(stream.head.name, BoxType::MediaHeaderBox);
+ assert_eq!(stream.head.size, 44);
+ let r = super::parse_mdhd(&mut stream, &mut super::Track::new(0));
+ assert_eq!(r.is_err(), true);
+}
+
+#[test]
+fn read_mvhd_v0() {
+ let mut stream = make_fullbox(BoxSize::Short(108), b"mvhd", 0, |s| {
+ s.B32(0)
+ .B32(0)
+ .B32(1234)
+ .B32(5678)
+ .append_repeated(0, 80)
+ });
+ let mut iter = super::BoxIter::new(&mut stream);
+ let mut stream = iter.next_box().unwrap().unwrap();
+ assert_eq!(stream.head.name, BoxType::MovieHeaderBox);
+ assert_eq!(stream.head.size, 108);
+ let parsed = super::read_mvhd(&mut stream).unwrap();
+ assert_eq!(parsed.timescale, 1234);
+ assert_eq!(parsed.duration, 5678);
+}
+
+#[test]
+fn read_mvhd_v1() {
+ let mut stream = make_fullbox(BoxSize::Short(120), b"mvhd", 1, |s| {
+ s.B64(0)
+ .B64(0)
+ .B32(1234)
+ .B64(5678)
+ .append_repeated(0, 80)
+ });
+ let mut iter = super::BoxIter::new(&mut stream);
+ let mut stream = iter.next_box().unwrap().unwrap();
+ assert_eq!(stream.head.name, BoxType::MovieHeaderBox);
+ assert_eq!(stream.head.size, 120);
+ let parsed = super::read_mvhd(&mut stream).unwrap();
+ assert_eq!(parsed.timescale, 1234);
+ assert_eq!(parsed.duration, 5678);
+}
+
+#[test]
+fn read_mvhd_invalid_timescale() {
+ let mut stream = make_fullbox(BoxSize::Short(120), b"mvhd", 1, |s| {
+ s.B64(0)
+ .B64(0)
+ .B32(0)
+ .B64(5678)
+ .append_repeated(0, 80)
+ });
+ let mut iter = super::BoxIter::new(&mut stream);
+ let mut stream = iter.next_box().unwrap().unwrap();
+ assert_eq!(stream.head.name, BoxType::MovieHeaderBox);
+ assert_eq!(stream.head.size, 120);
+ let r = super::parse_mvhd(&mut stream);
+ assert_eq!(r.is_err(), true);
+}
+
+#[test]
+fn read_mvhd_unknown_duration() {
+ let mut stream = make_fullbox(BoxSize::Short(108), b"mvhd", 0, |s| {
+ s.B32(0)
+ .B32(0)
+ .B32(1234)
+ .B32(::std::u32::MAX)
+ .append_repeated(0, 80)
+ });
+ let mut iter = super::BoxIter::new(&mut stream);
+ let mut stream = iter.next_box().unwrap().unwrap();
+ assert_eq!(stream.head.name, BoxType::MovieHeaderBox);
+ assert_eq!(stream.head.size, 108);
+ let parsed = super::read_mvhd(&mut stream).unwrap();
+ assert_eq!(parsed.timescale, 1234);
+ assert_eq!(parsed.duration, ::std::u64::MAX);
+}
+
+#[test]
+fn read_vpcc() {
+ let data_length = 12u16;
+ let mut stream = make_fullbox(BoxSize::Auto, b"vpcC", 0, |s| {
+ s.B8(2)
+ .B8(0)
+ .B8(0x82)
+ .B8(0)
+ .B16(data_length)
+ .append_repeated(42, data_length as usize)
+ });
+ let mut iter = super::BoxIter::new(&mut stream);
+ let mut stream = iter.next_box().unwrap().unwrap();
+ assert_eq!(stream.head.name, BoxType::VPCodecConfigurationBox);
+ let r = super::read_vpcc(&mut stream);
+ assert!(r.is_ok());
+}
+
+#[test]
+fn read_hdlr() {
+ let mut stream = make_fullbox(BoxSize::Short(45), b"hdlr", 0, |s| {
+ s.B32(0)
+ .append_bytes(b"vide")
+ .B32(0)
+ .B32(0)
+ .B32(0)
+ .append_bytes(b"VideoHandler")
+ .B8(0) // null-terminate string
+ });
+ let mut iter = super::BoxIter::new(&mut stream);
+ let mut stream = iter.next_box().unwrap().unwrap();
+ assert_eq!(stream.head.name, BoxType::HandlerBox);
+ assert_eq!(stream.head.size, 45);
+ let parsed = super::read_hdlr(&mut stream).unwrap();
+ assert_eq!(parsed.handler_type, 0x76696465); // vide
+}
+
+#[test]
+fn read_hdlr_short_name() {
+ let mut stream = make_fullbox(BoxSize::Short(33), b"hdlr", 0, |s| {
+ s.B32(0)
+ .append_bytes(b"vide")
+ .B32(0)
+ .B32(0)
+ .B32(0)
+ .B8(0) // null-terminate string
+ });
+ let mut iter = super::BoxIter::new(&mut stream);
+ let mut stream = iter.next_box().unwrap().unwrap();
+ assert_eq!(stream.head.name, BoxType::HandlerBox);
+ assert_eq!(stream.head.size, 33);
+ let parsed = super::read_hdlr(&mut stream).unwrap();
+ assert_eq!(parsed.handler_type, 0x76696465); // vide
+}
+
+#[test]
+fn read_hdlr_zero_length_name() {
+ let mut stream = make_fullbox(BoxSize::Short(32), b"hdlr", 0, |s| {
+ s.B32(0)
+ .append_bytes(b"vide")
+ .B32(0)
+ .B32(0)
+ .B32(0)
+ });
+ let mut iter = super::BoxIter::new(&mut stream);
+ let mut stream = iter.next_box().unwrap().unwrap();
+ assert_eq!(stream.head.name, BoxType::HandlerBox);
+ assert_eq!(stream.head.size, 32);
+ let parsed = super::read_hdlr(&mut stream).unwrap();
+ assert_eq!(parsed.handler_type, 0x76696465); // vide
+}
+
+fn flac_streaminfo() -> Vec<u8> {
+ vec![
+ 0x10, 0x00, 0x10, 0x00, 0x00, 0x0a, 0x11, 0x00,
+ 0x38, 0x32, 0x0a, 0xc4, 0x42, 0xf0, 0x00, 0xc9,
+ 0xdf, 0xae, 0xb5, 0x66, 0xfc, 0x02, 0x15, 0xa3,
+ 0xb1, 0x54, 0x61, 0x47, 0x0f, 0xfb, 0x05, 0x00,
+ 0x33, 0xad,
+ ]
+}
+
+#[test]
+fn read_flac() {
+ let mut stream = make_box(BoxSize::Auto, b"fLaC", |s| {
+ s.append_repeated(0, 6) // reserved
+ .B16(1) // data reference index
+ .B32(0) // reserved
+ .B32(0) // reserved
+ .B16(2) // channel count
+ .B16(16) // bits per sample
+ .B16(0) // pre_defined
+ .B16(0) // reserved
+ .B32(44100 << 16) // Sample rate
+ .append_bytes(&make_dfla(FlacBlockType::StreamInfo, true,
+ &flac_streaminfo(), FlacBlockLength::Correct)
+ .into_inner())
+ });
+ let mut iter = super::BoxIter::new(&mut stream);
+ let mut stream = iter.next_box().unwrap().unwrap();
+ let mut track = super::Track::new(0);
+ let r = super::read_audio_sample_entry(&mut stream, &mut track);
+ r.unwrap();
+}
+
+#[derive(Clone, Copy)]
+enum FlacBlockType {
+ StreamInfo = 0,
+ _Padding = 1,
+ _Application = 2,
+ _Seektable = 3,
+ _Comment = 4,
+ _Cuesheet = 5,
+ _Picture = 6,
+ _Reserved,
+ _Invalid = 127,
+}
+
+enum FlacBlockLength {
+ Correct,
+ Incorrect(usize),
+}
+
+fn make_dfla(block_type: FlacBlockType, last: bool, data: &Vec<u8>,
+ data_length: FlacBlockLength) -> Cursor<Vec<u8>> {
+ assert!(data.len() < 1<<24);
+ make_fullbox(BoxSize::Auto, b"dfLa", 0, |s| {
+ let flag = match last {
+ true => 1,
+ false => 0,
+ };
+ let size = match data_length {
+ FlacBlockLength::Correct => (data.len() as u32) & 0xffffff,
+ FlacBlockLength::Incorrect(size) => {
+ assert!(size < 1<<24);
+ (size as u32) & 0xffffff
+ }
+ };
+ let block_type = (block_type as u32) & 0x7f;
+ s.B32(flag << 31 | block_type << 24 | size)
+ .append_bytes(data)
+ })
+}
+
+#[test]
+fn read_dfla() {
+ let mut stream = make_dfla(FlacBlockType::StreamInfo, true,
+ &flac_streaminfo(), FlacBlockLength::Correct);
+ let mut iter = super::BoxIter::new(&mut stream);
+ let mut stream = iter.next_box().unwrap().unwrap();
+ assert_eq!(stream.head.name, BoxType::FLACSpecificBox);
+ let dfla = super::read_dfla(&mut stream).unwrap();
+ assert_eq!(dfla.version, 0);
+}
+
+#[test]
+fn long_flac_metadata() {
+ let streaminfo = flac_streaminfo();
+ let mut stream = make_dfla(FlacBlockType::StreamInfo, true,
+ &streaminfo,
+ FlacBlockLength::Incorrect(streaminfo.len() + 4));
+ let mut iter = super::BoxIter::new(&mut stream);
+ let mut stream = iter.next_box().unwrap().unwrap();
+ assert_eq!(stream.head.name, BoxType::FLACSpecificBox);
+ let r = super::read_dfla(&mut stream);
+ assert!(r.is_err());
+}
+
+#[test]
+fn read_opus() {
+ let mut stream = make_box(BoxSize::Auto, b"Opus", |s| {
+ s.append_repeated(0, 6)
+ .B16(1) // data reference index
+ .B32(0)
+ .B32(0)
+ .B16(2) // channel count
+ .B16(16) // bits per sample
+ .B16(0)
+ .B16(0)
+ .B32(48000 << 16) // Sample rate is always 48 kHz for Opus.
+ .append_bytes(&make_dops().into_inner())
+ });
+ let mut iter = super::BoxIter::new(&mut stream);
+ let mut stream = iter.next_box().unwrap().unwrap();
+ let mut track = super::Track::new(0);
+ let r = super::read_audio_sample_entry(&mut stream, &mut track);
+ assert!(r.is_ok());
+}
+
+fn make_dops() -> Cursor<Vec<u8>> {
+ make_box(BoxSize::Auto, b"dOps", |s| {
+ s.B8(0) // version
+ .B8(2) // channel count
+ .B16(348) // pre-skip
+ .B32(44100) // original sample rate
+ .B16(0) // gain
+ .B8(0) // channel mapping
+ })
+}
+
+#[test]
+fn read_dops() {
+ let mut stream = make_dops();
+ let mut iter = super::BoxIter::new(&mut stream);
+ let mut stream = iter.next_box().unwrap().unwrap();
+ assert_eq!(stream.head.name, BoxType::OpusSpecificBox);
+ let r = super::read_dops(&mut stream);
+ assert!(r.is_ok());
+}
+
+#[test]
+fn serialize_opus_header() {
+ let opus = super::OpusSpecificBox {
+ version: 0,
+ output_channel_count: 1,
+ pre_skip: 342,
+ input_sample_rate: 24000,
+ output_gain: 0,
+ channel_mapping_family: 0,
+ channel_mapping_table: None,
+ };
+ let mut v = Vec::<u8>::new();
+ super::serialize_opus_header(&opus, &mut v).unwrap();
+ assert!(v.len() == 19);
+ assert!(v == vec![
+ 0x4f, 0x70, 0x75, 0x73, 0x48,0x65, 0x61, 0x64,
+ 0x01, 0x01, 0x56, 0x01,
+ 0xc0, 0x5d, 0x00, 0x00,
+ 0x00, 0x00, 0x00,
+ ]);
+ let opus = super::OpusSpecificBox {
+ version: 0,
+ output_channel_count: 6,
+ pre_skip: 152,
+ input_sample_rate: 48000,
+ output_gain: 0,
+ channel_mapping_family: 1,
+ channel_mapping_table: Some(super::ChannelMappingTable {
+ stream_count: 4,
+ coupled_count: 2,
+ channel_mapping: vec![0, 4, 1, 2, 3, 5],
+ }),
+ };
+ let mut v = Vec::<u8>::new();
+ super::serialize_opus_header(&opus, &mut v).unwrap();
+ assert!(v.len() == 27);
+ assert!(v == vec![
+ 0x4f, 0x70, 0x75, 0x73, 0x48,0x65, 0x61, 0x64,
+ 0x01, 0x06, 0x98, 0x00,
+ 0x80, 0xbb, 0x00, 0x00,
+ 0x00, 0x00, 0x01, 0x04, 0x02,
+ 0x00, 0x04, 0x01, 0x02, 0x03, 0x05,
+ ]);
+}
+
+#[test]
+fn avcc_limit() {
+ let mut stream = make_box(BoxSize::Auto, b"avc1", |s| {
+ s.append_repeated(0, 6)
+ .B16(1)
+ .append_repeated(0, 16)
+ .B16(320)
+ .B16(240)
+ .append_repeated(0, 14)
+ .append_repeated(0, 32)
+ .append_repeated(0, 4)
+ .B32(0xffffffff)
+ .append_bytes(b"avcC")
+ .append_repeated(0, 100)
+ });
+ let mut iter = super::BoxIter::new(&mut stream);
+ let mut stream = iter.next_box().unwrap().unwrap();
+ let mut track = super::Track::new(0);
+ match super::read_video_sample_entry(&mut stream, &mut track) {
+ Err(Error::InvalidData(s)) => assert_eq!(s, "avcC box exceeds BUF_SIZE_LIMIT"),
+ Ok(_) => assert!(false, "expected an error result"),
+ _ => assert!(false, "expected a different error result"),
+ }
+}
+
+#[test]
+fn esds_limit() {
+ let mut stream = make_box(BoxSize::Auto, b"mp4a", |s| {
+ s.append_repeated(0, 6)
+ .B16(1)
+ .B32(0)
+ .B32(0)
+ .B16(2)
+ .B16(16)
+ .B16(0)
+ .B16(0)
+ .B32(48000 << 16)
+ .B32(0xffffffff)
+ .append_bytes(b"esds")
+ .append_repeated(0, 100)
+ });
+ let mut iter = super::BoxIter::new(&mut stream);
+ let mut stream = iter.next_box().unwrap().unwrap();
+ let mut track = super::Track::new(0);
+ match super::read_audio_sample_entry(&mut stream, &mut track) {
+ Err(Error::InvalidData(s)) => assert_eq!(s, "esds box exceeds BUF_SIZE_LIMIT"),
+ Ok(_) => assert!(false, "expected an error result"),
+ _ => assert!(false, "expected a different error result"),
+ }
+}
+
+#[test]
+fn esds_limit_2() {
+ let mut stream = make_box(BoxSize::Auto, b"mp4a", |s| {
+ s.append_repeated(0, 6)
+ .B16(1)
+ .B32(0)
+ .B32(0)
+ .B16(2)
+ .B16(16)
+ .B16(0)
+ .B16(0)
+ .B32(48000 << 16)
+ .B32(8)
+ .append_bytes(b"esds")
+ .append_repeated(0, 4)
+ });
+ let mut iter = super::BoxIter::new(&mut stream);
+ let mut stream = iter.next_box().unwrap().unwrap();
+ let mut track = super::Track::new(0);
+ match super::read_audio_sample_entry(&mut stream, &mut track) {
+ Err(Error::UnexpectedEOF) => (),
+ Ok(_) => assert!(false, "expected an error result"),
+ _ => assert!(false, "expected a different error result"),
+ }
+}
+
+#[test]
+fn read_elst_zero_entries() {
+ let mut stream = make_fullbox(BoxSize::Auto, b"elst", 0, |s| {
+ s.B32(0)
+ .B16(12)
+ .B16(34)
+ });
+ let mut iter = super::BoxIter::new(&mut stream);
+ let mut stream = iter.next_box().unwrap().unwrap();
+ match super::read_elst(&mut stream) {
+ Err(Error::InvalidData(s)) => assert_eq!(s, "invalid edit count"),
+ Ok(_) => assert!(false, "expected an error result"),
+ _ => assert!(false, "expected a different error result"),
+ }
+}
+
+fn make_elst() -> Cursor<Vec<u8>> {
+ make_fullbox(BoxSize::Auto, b"elst", 1, |s| {
+ s.B32(1)
+ // first entry
+ .B64(1234) // duration
+ .B64(0xffffffffffffffff) // time
+ .B16(12) // rate integer
+ .B16(34) // rate fraction
+ })
+}
+
+#[test]
+fn read_edts_bogus() {
+ // First edit list entry has a media_time of -1, so we expect a second
+ // edit list entry to be present to provide a valid media_time.
+ let mut stream = make_box(BoxSize::Auto, b"edts", |s| {
+ s.append_bytes(&make_elst().into_inner())
+ });
+ let mut iter = super::BoxIter::new(&mut stream);
+ let mut stream = iter.next_box().unwrap().unwrap();
+ let mut track = super::Track::new(0);
+ match super::read_edts(&mut stream, &mut track) {
+ Err(Error::InvalidData(s)) => assert_eq!(s, "expected additional edit"),
+ Ok(_) => assert!(false, "expected an error result"),
+ _ => assert!(false, "expected a different error result"),
+ }
+}
+
+#[test]
+fn invalid_pascal_string() {
+ // String claims to be 32 bytes long (we provide 33 bytes to account for
+ // the 1 byte length prefix).
+ let pstr = "\x20xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
+ let mut stream = Cursor::new(pstr);
+ // Reader wants to limit the total read length to 32 bytes, so any
+ // returned string must be no longer than 31 bytes.
+ let s = super::read_fixed_length_pascal_string(&mut stream, 32).unwrap();
+ assert_eq!(s.len(), 31);
+}
+
+#[test]
+fn skip_padding_in_boxes() {
+ // Padding data could be added in the end of these boxes. Parser needs to skip
+ // them instead of returning error.
+ let box_names = vec![b"stts", b"stsc", b"stsz", b"stco", b"co64", b"stss"];
+
+ for name in box_names {
+ let mut stream = make_fullbox(BoxSize::Auto, name, 1, |s| {
+ s.append_repeated(0, 100) // add padding data
+ });
+ let mut iter = super::BoxIter::new(&mut stream);
+ let mut stream = iter.next_box().unwrap().unwrap();
+ match name {
+ b"stts" => {
+ super::read_stts(&mut stream).expect("fail to skip padding: stts");
+ },
+ b"stsc" => {
+ super::read_stsc(&mut stream).expect("fail to skip padding: stsc");
+ },
+ b"stsz" => {
+ super::read_stsz(&mut stream).expect("fail to skip padding: stsz");
+ },
+ b"stco" => {
+ super::read_stco(&mut stream).expect("fail to skip padding: stco");
+ },
+ b"co64" => {
+ super::read_co64(&mut stream).expect("fail to skip padding: co64");
+ },
+ b"stss" => {
+ super::read_stss(&mut stream).expect("fail to skip padding: stss");
+ },
+ _ => (),
+ }
+ }
+}
+
+#[test]
+fn skip_padding_in_stsd() {
+ // Padding data could be added in the end of stsd boxes. Parser needs to skip
+ // them instead of returning error.
+ let avc = make_box(BoxSize::Auto, b"avc1", |s| {
+ s.append_repeated(0, 6)
+ .B16(1)
+ .append_repeated(0, 16)
+ .B16(320)
+ .B16(240)
+ .append_repeated(0, 14)
+ .append_repeated(0, 32)
+ .append_repeated(0, 4)
+ .B32(0xffffffff)
+ .append_bytes(b"avcC")
+ .append_repeated(0, 100)
+ }).into_inner();
+ let mut stream = make_fullbox(BoxSize::Auto, b"stsd", 0, |s| {
+ s.B32(1)
+ .append_bytes(avc.as_slice())
+ .append_repeated(0, 100) // add padding data
+ });
+
+ let mut iter = super::BoxIter::new(&mut stream);
+ let mut stream = iter.next_box().unwrap().unwrap();
+ super::read_stsd(&mut stream, &mut super::Track::new(0))
+ .expect("fail to skip padding: stsd");
+}
+
+#[test]
+fn read_qt_wave_atom() {
+ let esds = make_fullbox(BoxSize::Auto, b"esds", 0, |s| {
+ s.B8(0x03) // elementary stream descriptor tag
+ .B8(0x0b) // esds length
+ .append_repeated(0, 2)
+ .B8(0x00) // flags
+ .B8(0x04) // decoder config descriptor tag
+ .B8(0x0d) // dcds length
+ .B8(0x6b) // mp3
+ .append_repeated(0, 12)
+ }).into_inner();
+ let wave = make_box(BoxSize::Auto, b"wave", |s| {
+ s.append_bytes(esds.as_slice())
+ }).into_inner();
+ let mut stream = make_box(BoxSize::Auto, b"mp4a", |s| {
+ s.append_repeated(0, 6)
+ .B16(1) // data_reference_count
+ .B16(1) // verion: qt -> 1
+ .append_repeated(0, 6)
+ .B16(2)
+ .B16(16)
+ .append_repeated(0, 4)
+ .B32(48000 << 16)
+ .append_repeated(0, 16)
+ .append_bytes(wave.as_slice())
+ });
+
+ let mut iter = super::BoxIter::new(&mut stream);
+ let mut stream = iter.next_box().unwrap().unwrap();
+ let mut track = super::Track::new(0);
+ super::read_audio_sample_entry(&mut stream, &mut track)
+ .expect("fail to read qt wave atom");
+ assert_eq!(track.codec_type, super::CodecType::MP3);
+}
diff --git a/media/libstagefright/binding/mp4parse/tests/afl.rs b/media/libstagefright/binding/mp4parse/tests/afl.rs
new file mode 100644
index 000000000..d4ffec0df
--- /dev/null
+++ b/media/libstagefright/binding/mp4parse/tests/afl.rs
@@ -0,0 +1,53 @@
+/// Regression tests from American Fuzzy Lop test cases.
+
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at https://mozilla.org/MPL/2.0/.
+
+/// These all caused panics at some point during development.
+
+extern crate mp4parse;
+
+use std::io::Cursor;
+
+/// https://github.com/mozilla/mp4parse-rust/issues/2
+///
+/// Test a box with 4-byte size, smaller than the smallest header.
+#[test]
+fn fuzz_2() {
+ let mut c = Cursor::new(b"\x00\x00\x00\x04\xa6\x00\x04\xa6".to_vec());
+ let mut context = mp4parse::MediaContext::new();
+ let _ = mp4parse::read_mp4(&mut c, &mut context);
+}
+
+/// https://github.com/mozilla/mp4parse-rust/issues/4
+///
+/// Test a large (64 bit) box header with zero declared size.
+#[test]
+fn fuzz_4() {
+ let mut c = Cursor::new(b"\x00\x00\x00\x01\x30\x30\x30\x30\x00\x00\x00\x00\x00\x00\x00\x00".to_vec());
+ let mut context = mp4parse::MediaContext::new();
+ let _ = mp4parse::read_mp4(&mut c, &mut context);
+}
+
+/// https://github.com/mozilla/mp4parse-rust/issues/5
+///
+/// Declares 202116104 compatible brands but does not supply them,
+/// verifying read is properly bounded at the end of the stream.
+#[test]
+fn fuzz_5() {
+ let mut c = Cursor::new(b"\x30\x30\x30\x30\x66\x74\x79\x70\x30\x30\x30\x30\x30\x30\x30\x30".to_vec());
+ let mut context = mp4parse::MediaContext::new();
+ let _ = mp4parse::read_mp4(&mut c, &mut context);
+}
+
+/// https://github.com/mozilla/mp4parse-rust/issues/6
+///
+/// Declares an ftyp box with a single invalid (short - 3 byte) compatible
+/// brand and excludes the extra 3 bytes from the stream.
+#[test]
+fn fuzz_6() {
+ let mut c = Cursor::new(b"\x00\x00\x00\x13\x66\x74\x79\x70\x30\x30\x30\x30\x30\x30\x30\x30".to_vec());
+ let mut context = mp4parse::MediaContext::new();
+ let _ = mp4parse::read_mp4(&mut c, &mut context);
+}
diff --git a/media/libstagefright/binding/mp4parse/tests/minimal.mp4 b/media/libstagefright/binding/mp4parse/tests/minimal.mp4
new file mode 100644
index 000000000..9fe1e6722
--- /dev/null
+++ b/media/libstagefright/binding/mp4parse/tests/minimal.mp4
Binary files differ
diff --git a/media/libstagefright/binding/mp4parse/tests/public.rs b/media/libstagefright/binding/mp4parse/tests/public.rs
new file mode 100644
index 000000000..1d36f5f5a
--- /dev/null
+++ b/media/libstagefright/binding/mp4parse/tests/public.rs
@@ -0,0 +1,97 @@
+/// Check if needed fields are still public.
+
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at https://mozilla.org/MPL/2.0/.
+
+extern crate mp4parse as mp4;
+
+use std::io::{Cursor, Read};
+use std::fs::File;
+
+// Taken from https://github.com/GuillaumeGomez/audio-video-metadata/blob/9dff40f565af71d5502e03a2e78ae63df95cfd40/src/metadata.rs#L53
+#[test]
+fn public_api() {
+ let mut fd = File::open("tests/minimal.mp4").expect("Unknown file");
+ let mut buf = Vec::new();
+ fd.read_to_end(&mut buf).expect("File error");
+
+ let mut c = Cursor::new(&buf);
+ let mut context = mp4::MediaContext::new();
+ mp4::read_mp4(&mut c, &mut context).expect("read_mp4 failed");
+ assert_eq!(context.timescale, Some(mp4::MediaTimeScale(1000)));
+ for track in context.tracks {
+ match track.data {
+ Some(mp4::SampleEntry::Video(v)) => {
+ // track part
+ assert_eq!(track.duration, Some(mp4::TrackScaledTime(512, 0)));
+ assert_eq!(track.empty_duration, Some(mp4::MediaScaledTime(0)));
+ assert_eq!(track.media_time, Some(mp4::TrackScaledTime(0, 0)));
+ assert_eq!(track.timescale, Some(mp4::TrackTimeScale(12800, 0)));
+ assert_eq!(v.width, 320);
+ assert_eq!(v.height, 240);
+
+ // track.tkhd part
+ let tkhd = track.tkhd.unwrap();
+ assert_eq!(tkhd.disabled, false);
+ assert_eq!(tkhd.duration, 40);
+ assert_eq!(tkhd.width, 20971520);
+ assert_eq!(tkhd.height, 15728640);
+
+ // track.data part
+ assert_eq!(match v.codec_specific {
+ mp4::VideoCodecSpecific::AVCConfig(v) => {
+ assert!(v.len() > 0);
+ "AVC"
+ }
+ mp4::VideoCodecSpecific::VPxConfig(vpx) => {
+ // We don't enter in here, we just check if fields are public.
+ assert!(vpx.bit_depth > 0);
+ assert!(vpx.color_space > 0);
+ assert!(vpx.chroma_subsampling > 0);
+ assert!(vpx.codec_init.len() > 0);
+ "VPx"
+ }
+ }, "AVC");
+ }
+ Some(mp4::SampleEntry::Audio(a)) => {
+ // track part
+ assert_eq!(track.duration, Some(mp4::TrackScaledTime(2944, 1)));
+ assert_eq!(track.empty_duration, Some(mp4::MediaScaledTime(0)));
+ assert_eq!(track.media_time, Some(mp4::TrackScaledTime(1024, 1)));
+ assert_eq!(track.timescale, Some(mp4::TrackTimeScale(48000, 1)));
+
+ // track.tkhd part
+ let tkhd = track.tkhd.unwrap();
+ assert_eq!(tkhd.disabled, false);
+ assert_eq!(tkhd.duration, 62);
+ assert_eq!(tkhd.width, 0);
+ assert_eq!(tkhd.height, 0);
+
+ // track.data part
+ assert_eq!(match a.codec_specific {
+ mp4::AudioCodecSpecific::ES_Descriptor(esds) => {
+ assert_eq!(esds.audio_codec, mp4::CodecType::AAC);
+ assert_eq!(esds.audio_sample_rate.unwrap(), 48000);
+ "ES"
+ }
+ mp4::AudioCodecSpecific::FLACSpecificBox(flac) => {
+ // STREAMINFO block must be present and first.
+ assert!(flac.blocks.len() > 0);
+ assert!(flac.blocks[0].block_type == 0);
+ assert!(flac.blocks[0].data.len() == 34);
+ "FLAC"
+ }
+ mp4::AudioCodecSpecific::OpusSpecificBox(opus) => {
+ // We don't enter in here, we just check if fields are public.
+ assert!(opus.version > 0);
+ "Opus"
+ }
+ }, "ES");
+ assert!(a.samplesize > 0);
+ assert!(a.samplerate > 0);
+ }
+ Some(mp4::SampleEntry::Unknown) | None => {}
+ }
+ }
+}
diff --git a/media/libstagefright/binding/mp4parse_capi/Cargo.toml b/media/libstagefright/binding/mp4parse_capi/Cargo.toml
new file mode 100644
index 000000000..5c0836abe
--- /dev/null
+++ b/media/libstagefright/binding/mp4parse_capi/Cargo.toml
@@ -0,0 +1,26 @@
+[package]
+name = "mp4parse_capi"
+version = "0.6.0"
+authors = [
+ "Ralph Giles <giles@mozilla.com>",
+ "Matthew Gregan <kinetik@flim.org>",
+ "Alfredo Yang <ayang@mozilla.com>",
+]
+
+description = "Parser for ISO base media file format (mp4)"
+documentation = "https://mp4parse-docs.surge.sh/mp4parse/"
+license = "MPL-2.0"
+
+repository = "https://github.com/mozilla/mp4parse-rust"
+
+# Avoid complaints about trying to package test files.
+exclude = [
+ "*.mp4",
+]
+
+[dependencies]
+"mp4parse" = {version = "0.6.0", path = "../mp4parse"}
+
+# Somewhat heavy-handed, but we want at least -Z force-overflow-checks=on.
+[profile.release]
+debug-assertions = true
diff --git a/media/libstagefright/binding/mp4parse_capi/build.rs b/media/libstagefright/binding/mp4parse_capi/build.rs
new file mode 100644
index 000000000..29f2a85ce
--- /dev/null
+++ b/media/libstagefright/binding/mp4parse_capi/build.rs
@@ -0,0 +1,12 @@
+extern crate cheddar;
+
+fn main() {
+ println!("cargo:rerun-if-changed=src/lib.rs");
+ // Generate mp4parse.h.
+ cheddar::Cheddar::new().expect("could not read manifest")
+ .insert_code("// THIS FILE IS AUTOGENERATED BY mp4parse-rust/build.rs - DO NOT EDIT\n\n")
+ .insert_code("// This Source Code Form is subject to the terms of the Mozilla Public\n")
+ .insert_code("// License, v. 2.0. If a copy of the MPL was not distributed with this\n")
+ .insert_code("// file, You can obtain one at https://mozilla.org/MPL/2.0/.")
+ .run_build("include/mp4parse.h");
+}
diff --git a/media/libstagefright/binding/mp4parse_capi/src/lib.rs b/media/libstagefright/binding/mp4parse_capi/src/lib.rs
new file mode 100644
index 000000000..f52d8b169
--- /dev/null
+++ b/media/libstagefright/binding/mp4parse_capi/src/lib.rs
@@ -0,0 +1,870 @@
+//! C API for mp4parse module.
+//!
+//! Parses ISO Base Media Format aka video/mp4 streams.
+//!
+//! # Examples
+//!
+//! ```rust
+//! extern crate mp4parse_capi;
+//! use std::io::Read;
+//!
+//! extern fn buf_read(buf: *mut u8, size: usize, userdata: *mut std::os::raw::c_void) -> isize {
+//! let mut input: &mut std::fs::File = unsafe { &mut *(userdata as *mut _) };
+//! let mut buf = unsafe { std::slice::from_raw_parts_mut(buf, size) };
+//! match input.read(&mut buf) {
+//! Ok(n) => n as isize,
+//! Err(_) => -1,
+//! }
+//! }
+//!
+//! let mut file = std::fs::File::open("../mp4parse/tests/minimal.mp4").unwrap();
+//! let io = mp4parse_capi::mp4parse_io {
+//! read: buf_read,
+//! userdata: &mut file as *mut _ as *mut std::os::raw::c_void
+//! };
+//! unsafe {
+//! let parser = mp4parse_capi::mp4parse_new(&io);
+//! let rv = mp4parse_capi::mp4parse_read(parser);
+//! assert_eq!(rv, mp4parse_capi::mp4parse_error::MP4PARSE_OK);
+//! mp4parse_capi::mp4parse_free(parser);
+//! }
+//! ```
+
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at https://mozilla.org/MPL/2.0/.
+
+extern crate mp4parse;
+
+use std::io::Read;
+use std::collections::HashMap;
+
+// Symbols we need from our rust api.
+use mp4parse::MediaContext;
+use mp4parse::TrackType;
+use mp4parse::read_mp4;
+use mp4parse::Error;
+use mp4parse::SampleEntry;
+use mp4parse::AudioCodecSpecific;
+use mp4parse::VideoCodecSpecific;
+use mp4parse::MediaTimeScale;
+use mp4parse::MediaScaledTime;
+use mp4parse::TrackTimeScale;
+use mp4parse::TrackScaledTime;
+use mp4parse::serialize_opus_header;
+use mp4parse::CodecType;
+
+// rusty-cheddar's C enum generation doesn't namespace enum members by
+// prefixing them, so we're forced to do it in our member names until
+// https://github.com/Sean1708/rusty-cheddar/pull/35 is fixed. Importing
+// the members into the module namespace avoids doubling up on the
+// namespacing on the Rust side.
+use mp4parse_error::*;
+use mp4parse_track_type::*;
+
+#[repr(C)]
+#[derive(PartialEq, Debug)]
+pub enum mp4parse_error {
+ MP4PARSE_OK = 0,
+ MP4PARSE_ERROR_BADARG = 1,
+ MP4PARSE_ERROR_INVALID = 2,
+ MP4PARSE_ERROR_UNSUPPORTED = 3,
+ MP4PARSE_ERROR_EOF = 4,
+ MP4PARSE_ERROR_IO = 5,
+}
+
+#[repr(C)]
+#[derive(PartialEq, Debug)]
+pub enum mp4parse_track_type {
+ MP4PARSE_TRACK_TYPE_VIDEO = 0,
+ MP4PARSE_TRACK_TYPE_AUDIO = 1,
+}
+
+#[repr(C)]
+#[derive(PartialEq, Debug)]
+pub enum mp4parse_codec {
+ MP4PARSE_CODEC_UNKNOWN,
+ MP4PARSE_CODEC_AAC,
+ MP4PARSE_CODEC_FLAC,
+ MP4PARSE_CODEC_OPUS,
+ MP4PARSE_CODEC_AVC,
+ MP4PARSE_CODEC_VP9,
+ MP4PARSE_CODEC_MP3,
+}
+
+#[repr(C)]
+pub struct mp4parse_track_info {
+ pub track_type: mp4parse_track_type,
+ pub codec: mp4parse_codec,
+ pub track_id: u32,
+ pub duration: u64,
+ pub media_time: i64, // wants to be u64? understand how elst adjustment works
+ // TODO(kinetik): include crypto guff
+}
+
+#[repr(C)]
+pub struct mp4parse_codec_specific_config {
+ pub length: u32,
+ pub data: *const u8,
+}
+
+impl Default for mp4parse_codec_specific_config {
+ fn default() -> Self {
+ mp4parse_codec_specific_config {
+ length: 0,
+ data: std::ptr::null_mut(),
+ }
+ }
+}
+
+#[derive(Default)]
+#[repr(C)]
+pub struct mp4parse_track_audio_info {
+ pub channels: u16,
+ pub bit_depth: u16,
+ pub sample_rate: u32,
+ // TODO(kinetik):
+ // int32_t profile;
+ // int32_t extended_profile; // check types
+ codec_specific_config: mp4parse_codec_specific_config,
+}
+
+#[repr(C)]
+pub struct mp4parse_track_video_info {
+ pub display_width: u32,
+ pub display_height: u32,
+ pub image_width: u16,
+ pub image_height: u16,
+ // TODO(kinetik):
+ // extra_data
+ // codec_specific_config
+}
+
+#[repr(C)]
+pub struct mp4parse_fragment_info {
+ pub fragment_duration: u64,
+ // TODO:
+ // info in trex box.
+}
+
+// Even though mp4parse_parser is opaque to C, rusty-cheddar won't let us
+// use more than one member, so we introduce *another* wrapper.
+struct Wrap {
+ context: MediaContext,
+ io: mp4parse_io,
+ poisoned: bool,
+ opus_header: HashMap<u32, Vec<u8>>,
+}
+
+#[repr(C)]
+#[allow(non_camel_case_types)]
+pub struct mp4parse_parser(Wrap);
+
+impl mp4parse_parser {
+ fn context(&self) -> &MediaContext {
+ &self.0.context
+ }
+
+ fn context_mut(&mut self) -> &mut MediaContext {
+ &mut self.0.context
+ }
+
+ fn io_mut(&mut self) -> &mut mp4parse_io {
+ &mut self.0.io
+ }
+
+ fn poisoned(&self) -> bool {
+ self.0.poisoned
+ }
+
+ fn set_poisoned(&mut self, poisoned: bool) {
+ self.0.poisoned = poisoned;
+ }
+
+ fn opus_header_mut(&mut self) -> &mut HashMap<u32, Vec<u8>> {
+ &mut self.0.opus_header
+ }
+}
+
+#[repr(C)]
+#[derive(Clone)]
+pub struct mp4parse_io {
+ pub read: extern fn(buffer: *mut u8, size: usize, userdata: *mut std::os::raw::c_void) -> isize,
+ pub userdata: *mut std::os::raw::c_void,
+}
+
+impl Read for mp4parse_io {
+ fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
+ if buf.len() > isize::max_value() as usize {
+ return Err(std::io::Error::new(std::io::ErrorKind::Other, "buf length overflow in mp4parse_io Read impl"));
+ }
+ let rv = (self.read)(buf.as_mut_ptr(), buf.len(), self.userdata);
+ if rv >= 0 {
+ Ok(rv as usize)
+ } else {
+ Err(std::io::Error::new(std::io::ErrorKind::Other, "I/O error in mp4parse_io Read impl"))
+ }
+ }
+}
+
+// C API wrapper functions.
+
+/// Allocate an `mp4parse_parser*` to read from the supplied `mp4parse_io`.
+#[no_mangle]
+pub unsafe extern fn mp4parse_new(io: *const mp4parse_io) -> *mut mp4parse_parser {
+ if io.is_null() || (*io).userdata.is_null() {
+ return std::ptr::null_mut();
+ }
+ // is_null() isn't available on a fn type because it can't be null (in
+ // Rust) by definition. But since this value is coming from the C API,
+ // it *could* be null. Ideally, we'd wrap it in an Option to represent
+ // reality, but this causes rusty-cheddar to emit the wrong type
+ // (https://github.com/Sean1708/rusty-cheddar/issues/30).
+ if ((*io).read as *mut std::os::raw::c_void).is_null() {
+ return std::ptr::null_mut();
+ }
+ let parser = Box::new(mp4parse_parser(Wrap {
+ context: MediaContext::new(),
+ io: (*io).clone(),
+ poisoned: false,
+ opus_header: HashMap::new(),
+ }));
+ Box::into_raw(parser)
+}
+
+/// Free an `mp4parse_parser*` allocated by `mp4parse_new()`.
+#[no_mangle]
+pub unsafe extern fn mp4parse_free(parser: *mut mp4parse_parser) {
+ assert!(!parser.is_null());
+ let _ = Box::from_raw(parser);
+}
+
+/// Run the `mp4parse_parser*` allocated by `mp4parse_new()` until EOF or error.
+#[no_mangle]
+pub unsafe extern fn mp4parse_read(parser: *mut mp4parse_parser) -> mp4parse_error {
+ // Validate arguments from C.
+ if parser.is_null() || (*parser).poisoned() {
+ return MP4PARSE_ERROR_BADARG;
+ }
+
+ let mut context = (*parser).context_mut();
+ let mut io = (*parser).io_mut();
+
+ let r = read_mp4(io, context);
+ match r {
+ Ok(_) => MP4PARSE_OK,
+ Err(Error::NoMoov) | Err(Error::InvalidData(_)) => {
+ // Block further calls. We've probable lost sync.
+ (*parser).set_poisoned(true);
+ MP4PARSE_ERROR_INVALID
+ }
+ Err(Error::Unsupported(_)) => MP4PARSE_ERROR_UNSUPPORTED,
+ Err(Error::UnexpectedEOF) => MP4PARSE_ERROR_EOF,
+ Err(Error::Io(_)) => {
+ // Block further calls after a read failure.
+ // Getting std::io::ErrorKind::UnexpectedEof is normal
+ // but our From trait implementation should have converted
+ // those to our Error::UnexpectedEOF variant.
+ (*parser).set_poisoned(true);
+ MP4PARSE_ERROR_IO
+ }
+ }
+}
+
+/// Return the number of tracks parsed by previous `mp4parse_read()` call.
+#[no_mangle]
+pub unsafe extern fn mp4parse_get_track_count(parser: *const mp4parse_parser, count: *mut u32) -> mp4parse_error {
+ // Validate arguments from C.
+ if parser.is_null() || count.is_null() || (*parser).poisoned() {
+ return MP4PARSE_ERROR_BADARG;
+ }
+ let context = (*parser).context();
+
+ // Make sure the track count fits in a u32.
+ if context.tracks.len() > u32::max_value() as usize {
+ return MP4PARSE_ERROR_INVALID;
+ }
+ *count = context.tracks.len() as u32;
+ MP4PARSE_OK
+}
+
+/// Calculate numerator * scale / denominator, if possible.
+///
+/// Applying the associativity of integer arithmetic, we divide first
+/// and add the remainder after multiplying each term separately
+/// to preserve precision while leaving more headroom. That is,
+/// (n * s) / d is split into floor(n / d) * s + (n % d) * s / d.
+///
+/// Return None on overflow or if the denominator is zero.
+fn rational_scale(numerator: u64, denominator: u64, scale: u64) -> Option<u64> {
+ if denominator == 0 {
+ return None;
+ }
+ let integer = numerator / denominator;
+ let remainder = numerator % denominator;
+ match integer.checked_mul(scale) {
+ Some(integer) => remainder.checked_mul(scale)
+ .and_then(|remainder| (remainder/denominator).checked_add(integer)),
+ None => None,
+ }
+}
+
+fn media_time_to_us(time: MediaScaledTime, scale: MediaTimeScale) -> Option<u64> {
+ let microseconds_per_second = 1000000;
+ rational_scale(time.0, scale.0, microseconds_per_second)
+}
+
+fn track_time_to_us(time: TrackScaledTime, scale: TrackTimeScale) -> Option<u64> {
+ assert!(time.1 == scale.1);
+ let microseconds_per_second = 1000000;
+ rational_scale(time.0, scale.0, microseconds_per_second)
+}
+
+/// Fill the supplied `mp4parse_track_info` with metadata for `track`.
+#[no_mangle]
+pub unsafe extern fn mp4parse_get_track_info(parser: *mut mp4parse_parser, track_index: u32, info: *mut mp4parse_track_info) -> mp4parse_error {
+ if parser.is_null() || info.is_null() || (*parser).poisoned() {
+ return MP4PARSE_ERROR_BADARG;
+ }
+
+ let context = (*parser).context_mut();
+ let track_index: usize = track_index as usize;
+ let info: &mut mp4parse_track_info = &mut *info;
+
+ if track_index >= context.tracks.len() {
+ return MP4PARSE_ERROR_BADARG;
+ }
+
+ info.track_type = match context.tracks[track_index].track_type {
+ TrackType::Video => MP4PARSE_TRACK_TYPE_VIDEO,
+ TrackType::Audio => MP4PARSE_TRACK_TYPE_AUDIO,
+ TrackType::Unknown => return MP4PARSE_ERROR_UNSUPPORTED,
+ };
+
+ info.codec = match context.tracks[track_index].data {
+ Some(SampleEntry::Audio(ref audio)) => match audio.codec_specific {
+ AudioCodecSpecific::OpusSpecificBox(_) =>
+ mp4parse_codec::MP4PARSE_CODEC_OPUS,
+ AudioCodecSpecific::FLACSpecificBox(_) =>
+ mp4parse_codec::MP4PARSE_CODEC_FLAC,
+ AudioCodecSpecific::ES_Descriptor(ref esds) if esds.audio_codec == CodecType::AAC =>
+ mp4parse_codec::MP4PARSE_CODEC_AAC,
+ AudioCodecSpecific::ES_Descriptor(ref esds) if esds.audio_codec == CodecType::MP3 =>
+ mp4parse_codec::MP4PARSE_CODEC_MP3,
+ AudioCodecSpecific::ES_Descriptor(_) =>
+ mp4parse_codec::MP4PARSE_CODEC_UNKNOWN,
+ },
+ Some(SampleEntry::Video(ref video)) => match video.codec_specific {
+ VideoCodecSpecific::VPxConfig(_) =>
+ mp4parse_codec::MP4PARSE_CODEC_VP9,
+ VideoCodecSpecific::AVCConfig(_) =>
+ mp4parse_codec::MP4PARSE_CODEC_AVC,
+ },
+ _ => mp4parse_codec::MP4PARSE_CODEC_UNKNOWN,
+ };
+
+ let track = &context.tracks[track_index];
+
+ if let (Some(track_timescale),
+ Some(context_timescale)) = (track.timescale,
+ context.timescale) {
+ let media_time =
+ match track.media_time.map_or(Some(0), |media_time| {
+ track_time_to_us(media_time, track_timescale) }) {
+ Some(time) => time as i64,
+ None => return MP4PARSE_ERROR_INVALID,
+ };
+ let empty_duration =
+ match track.empty_duration.map_or(Some(0), |empty_duration| {
+ media_time_to_us(empty_duration, context_timescale) }) {
+ Some(time) => time as i64,
+ None => return MP4PARSE_ERROR_INVALID,
+ };
+ info.media_time = media_time - empty_duration;
+
+ if let Some(track_duration) = track.duration {
+ match track_time_to_us(track_duration, track_timescale) {
+ Some(duration) => info.duration = duration,
+ None => return MP4PARSE_ERROR_INVALID,
+ }
+ } else {
+ // Duration unknown; stagefright returns 0 for this.
+ info.duration = 0
+ }
+ } else {
+ return MP4PARSE_ERROR_INVALID
+ }
+
+ info.track_id = match track.track_id {
+ Some(track_id) => track_id,
+ None => return MP4PARSE_ERROR_INVALID,
+ };
+
+ MP4PARSE_OK
+}
+
+/// Fill the supplied `mp4parse_track_audio_info` with metadata for `track`.
+#[no_mangle]
+pub unsafe extern fn mp4parse_get_track_audio_info(parser: *mut mp4parse_parser, track_index: u32, info: *mut mp4parse_track_audio_info) -> mp4parse_error {
+ if parser.is_null() || info.is_null() || (*parser).poisoned() {
+ return MP4PARSE_ERROR_BADARG;
+ }
+
+ let context = (*parser).context_mut();
+
+ if track_index as usize >= context.tracks.len() {
+ return MP4PARSE_ERROR_BADARG;
+ }
+
+ let track = &context.tracks[track_index as usize];
+
+ match track.track_type {
+ TrackType::Audio => {}
+ _ => return MP4PARSE_ERROR_INVALID,
+ };
+
+ let audio = match track.data {
+ Some(ref data) => data,
+ None => return MP4PARSE_ERROR_INVALID,
+ };
+
+ let audio = match *audio {
+ SampleEntry::Audio(ref x) => x,
+ _ => return MP4PARSE_ERROR_INVALID,
+ };
+
+ (*info).channels = audio.channelcount;
+ (*info).bit_depth = audio.samplesize;
+ (*info).sample_rate = audio.samplerate >> 16; // 16.16 fixed point
+
+ match audio.codec_specific {
+ AudioCodecSpecific::ES_Descriptor(ref v) => {
+ if v.codec_specific_config.len() > std::u32::MAX as usize {
+ return MP4PARSE_ERROR_INVALID;
+ }
+ (*info).codec_specific_config.length = v.codec_specific_config.len() as u32;
+ (*info).codec_specific_config.data = v.codec_specific_config.as_ptr();
+ if let Some(rate) = v.audio_sample_rate {
+ (*info).sample_rate = rate;
+ }
+ if let Some(channels) = v.audio_channel_count {
+ (*info).channels = channels;
+ }
+ }
+ AudioCodecSpecific::FLACSpecificBox(ref flac) => {
+ // Return the STREAMINFO metadata block in the codec_specific.
+ let streaminfo = &flac.blocks[0];
+ if streaminfo.block_type != 0 || streaminfo.data.len() != 34 {
+ return MP4PARSE_ERROR_INVALID;
+ }
+ (*info).codec_specific_config.length = streaminfo.data.len() as u32;
+ (*info).codec_specific_config.data = streaminfo.data.as_ptr();
+ }
+ AudioCodecSpecific::OpusSpecificBox(ref opus) => {
+ let mut v = Vec::new();
+ match serialize_opus_header(opus, &mut v) {
+ Err(_) => {
+ return MP4PARSE_ERROR_INVALID;
+ }
+ Ok(_) => {
+ let header = (*parser).opus_header_mut();
+ header.insert(track_index, v);
+ match header.get(&track_index) {
+ None => {}
+ Some(v) => {
+ if v.len() > std::u32::MAX as usize {
+ return MP4PARSE_ERROR_INVALID;
+ }
+ (*info).codec_specific_config.length = v.len() as u32;
+ (*info).codec_specific_config.data = v.as_ptr();
+ }
+ }
+ }
+ }
+ }
+ }
+
+ MP4PARSE_OK
+}
+
+/// Fill the supplied `mp4parse_track_video_info` with metadata for `track`.
+#[no_mangle]
+pub unsafe extern fn mp4parse_get_track_video_info(parser: *mut mp4parse_parser, track_index: u32, info: *mut mp4parse_track_video_info) -> mp4parse_error {
+ if parser.is_null() || info.is_null() || (*parser).poisoned() {
+ return MP4PARSE_ERROR_BADARG;
+ }
+
+ let context = (*parser).context_mut();
+
+ if track_index as usize >= context.tracks.len() {
+ return MP4PARSE_ERROR_BADARG;
+ }
+
+ let track = &context.tracks[track_index as usize];
+
+ match track.track_type {
+ TrackType::Video => {}
+ _ => return MP4PARSE_ERROR_INVALID,
+ };
+
+ let video = match track.data {
+ Some(ref data) => data,
+ None => return MP4PARSE_ERROR_INVALID,
+ };
+
+ let video = match *video {
+ SampleEntry::Video(ref x) => x,
+ _ => return MP4PARSE_ERROR_INVALID,
+ };
+
+ if let Some(ref tkhd) = track.tkhd {
+ (*info).display_width = tkhd.width >> 16; // 16.16 fixed point
+ (*info).display_height = tkhd.height >> 16; // 16.16 fixed point
+ } else {
+ return MP4PARSE_ERROR_INVALID;
+ }
+ (*info).image_width = video.width;
+ (*info).image_height = video.height;
+
+ MP4PARSE_OK
+}
+
+#[no_mangle]
+pub unsafe extern fn mp4parse_get_fragment_info(parser: *mut mp4parse_parser, info: *mut mp4parse_fragment_info) -> mp4parse_error {
+ if parser.is_null() || info.is_null() || (*parser).poisoned() {
+ return MP4PARSE_ERROR_BADARG;
+ }
+
+ let context = (*parser).context();
+ let info: &mut mp4parse_fragment_info = &mut *info;
+
+ info.fragment_duration = 0;
+
+ let duration = match context.mvex {
+ Some(ref mvex) => mvex.fragment_duration,
+ None => return MP4PARSE_ERROR_INVALID,
+ };
+
+ if let (Some(time), Some(scale)) = (duration, context.timescale) {
+ info.fragment_duration = match media_time_to_us(time, scale) {
+ Some(time_us) => time_us as u64,
+ None => return MP4PARSE_ERROR_INVALID,
+ }
+ }
+
+ MP4PARSE_OK
+}
+
+// A fragmented file needs mvex table and contains no data in stts, stsc, and stco boxes.
+#[no_mangle]
+pub unsafe extern fn mp4parse_is_fragmented(parser: *mut mp4parse_parser, track_id: u32, fragmented: *mut u8) -> mp4parse_error {
+ if parser.is_null() || (*parser).poisoned() {
+ return MP4PARSE_ERROR_BADARG;
+ }
+
+ let context = (*parser).context_mut();
+ let tracks = &context.tracks;
+ (*fragmented) = false as u8;
+
+ if context.mvex.is_none() {
+ return MP4PARSE_OK;
+ }
+
+ // check sample tables.
+ let mut iter = tracks.iter();
+ match iter.find(|track| track.track_id == Some(track_id)) {
+ Some(track) if track.empty_sample_boxes.all_empty() => (*fragmented) = true as u8,
+ Some(_) => {},
+ None => return MP4PARSE_ERROR_BADARG,
+ }
+
+ MP4PARSE_OK
+}
+
+#[cfg(test)]
+extern fn panic_read(_: *mut u8, _: usize, _: *mut std::os::raw::c_void) -> isize {
+ panic!("panic_read shouldn't be called in these tests");
+}
+
+#[cfg(test)]
+extern fn error_read(_: *mut u8, _: usize, _: *mut std::os::raw::c_void) -> isize {
+ -1
+}
+
+#[cfg(test)]
+extern fn valid_read(buf: *mut u8, size: usize, userdata: *mut std::os::raw::c_void) -> isize {
+ let mut input: &mut std::fs::File = unsafe { &mut *(userdata as *mut _) };
+
+ let mut buf = unsafe { std::slice::from_raw_parts_mut(buf, size) };
+ match input.read(&mut buf) {
+ Ok(n) => n as isize,
+ Err(_) => -1,
+ }
+}
+
+#[test]
+fn new_parser() {
+ let mut dummy_value: u32 = 42;
+ let io = mp4parse_io {
+ read: panic_read,
+ userdata: &mut dummy_value as *mut _ as *mut std::os::raw::c_void,
+ };
+ unsafe {
+ let parser = mp4parse_new(&io);
+ assert!(!parser.is_null());
+ mp4parse_free(parser);
+ }
+}
+
+#[test]
+#[should_panic(expected = "assertion failed")]
+fn free_null_parser() {
+ unsafe {
+ mp4parse_free(std::ptr::null_mut());
+ }
+}
+
+#[test]
+fn get_track_count_null_parser() {
+ unsafe {
+ let mut count: u32 = 0;
+ let rv = mp4parse_get_track_count(std::ptr::null(), std::ptr::null_mut());
+ assert!(rv == MP4PARSE_ERROR_BADARG);
+ let rv = mp4parse_get_track_count(std::ptr::null(), &mut count);
+ assert!(rv == MP4PARSE_ERROR_BADARG);
+ }
+}
+
+#[test]
+fn arg_validation() {
+ unsafe {
+ // Passing a null mp4parse_io is an error.
+ let parser = mp4parse_new(std::ptr::null());
+ assert!(parser.is_null());
+
+ let null_mut: *mut std::os::raw::c_void = std::ptr::null_mut();
+
+ // Passing an mp4parse_io with null members is an error.
+ let io = mp4parse_io { read: std::mem::transmute(null_mut),
+ userdata: null_mut };
+ let parser = mp4parse_new(&io);
+ assert!(parser.is_null());
+
+ let io = mp4parse_io { read: panic_read,
+ userdata: null_mut };
+ let parser = mp4parse_new(&io);
+ assert!(parser.is_null());
+
+ let mut dummy_value = 42;
+ let io = mp4parse_io {
+ read: std::mem::transmute(null_mut),
+ userdata: &mut dummy_value as *mut _ as *mut std::os::raw::c_void,
+ };
+ let parser = mp4parse_new(&io);
+ assert!(parser.is_null());
+
+ // Passing a null mp4parse_parser is an error.
+ assert_eq!(MP4PARSE_ERROR_BADARG, mp4parse_read(std::ptr::null_mut()));
+
+ let mut dummy_info = mp4parse_track_info {
+ track_type: MP4PARSE_TRACK_TYPE_VIDEO,
+ codec: mp4parse_codec::MP4PARSE_CODEC_UNKNOWN,
+ track_id: 0,
+ duration: 0,
+ media_time: 0,
+ };
+ assert_eq!(MP4PARSE_ERROR_BADARG, mp4parse_get_track_info(std::ptr::null_mut(), 0, &mut dummy_info));
+
+ let mut dummy_video = mp4parse_track_video_info {
+ display_width: 0,
+ display_height: 0,
+ image_width: 0,
+ image_height: 0,
+ };
+ assert_eq!(MP4PARSE_ERROR_BADARG, mp4parse_get_track_video_info(std::ptr::null_mut(), 0, &mut dummy_video));
+
+ let mut dummy_audio = Default::default();
+ assert_eq!(MP4PARSE_ERROR_BADARG, mp4parse_get_track_audio_info(std::ptr::null_mut(), 0, &mut dummy_audio));
+ }
+}
+
+#[test]
+fn arg_validation_with_parser() {
+ unsafe {
+ let mut dummy_value = 42;
+ let io = mp4parse_io {
+ read: error_read,
+ userdata: &mut dummy_value as *mut _ as *mut std::os::raw::c_void,
+ };
+ let parser = mp4parse_new(&io);
+ assert!(!parser.is_null());
+
+ // Our mp4parse_io read should simply fail with an error.
+ assert_eq!(MP4PARSE_ERROR_IO, mp4parse_read(parser));
+
+ // The parser is now poisoned and unusable.
+ assert_eq!(MP4PARSE_ERROR_BADARG, mp4parse_read(parser));
+
+ // Null info pointers are an error.
+ assert_eq!(MP4PARSE_ERROR_BADARG, mp4parse_get_track_info(parser, 0, std::ptr::null_mut()));
+ assert_eq!(MP4PARSE_ERROR_BADARG, mp4parse_get_track_video_info(parser, 0, std::ptr::null_mut()));
+ assert_eq!(MP4PARSE_ERROR_BADARG, mp4parse_get_track_audio_info(parser, 0, std::ptr::null_mut()));
+
+ let mut dummy_info = mp4parse_track_info {
+ track_type: MP4PARSE_TRACK_TYPE_VIDEO,
+ codec: mp4parse_codec::MP4PARSE_CODEC_UNKNOWN,
+ track_id: 0,
+ duration: 0,
+ media_time: 0,
+ };
+ assert_eq!(MP4PARSE_ERROR_BADARG, mp4parse_get_track_info(parser, 0, &mut dummy_info));
+
+ let mut dummy_video = mp4parse_track_video_info {
+ display_width: 0,
+ display_height: 0,
+ image_width: 0,
+ image_height: 0,
+ };
+ assert_eq!(MP4PARSE_ERROR_BADARG, mp4parse_get_track_video_info(parser, 0, &mut dummy_video));
+
+ let mut dummy_audio = Default::default();
+ assert_eq!(MP4PARSE_ERROR_BADARG, mp4parse_get_track_audio_info(parser, 0, &mut dummy_audio));
+
+ mp4parse_free(parser);
+ }
+}
+
+#[test]
+fn get_track_count_poisoned_parser() {
+ unsafe {
+ let mut dummy_value = 42;
+ let io = mp4parse_io {
+ read: error_read,
+ userdata: &mut dummy_value as *mut _ as *mut std::os::raw::c_void,
+ };
+ let parser = mp4parse_new(&io);
+ assert!(!parser.is_null());
+
+ // Our mp4parse_io read should simply fail with an error.
+ assert_eq!(MP4PARSE_ERROR_IO, mp4parse_read(parser));
+
+ let mut count: u32 = 0;
+ let rv = mp4parse_get_track_count(parser, &mut count);
+ assert!(rv == MP4PARSE_ERROR_BADARG);
+ }
+}
+
+#[test]
+fn arg_validation_with_data() {
+ unsafe {
+ let mut file = std::fs::File::open("../mp4parse/tests/minimal.mp4").unwrap();
+ let io = mp4parse_io { read: valid_read,
+ userdata: &mut file as *mut _ as *mut std::os::raw::c_void };
+ let parser = mp4parse_new(&io);
+ assert!(!parser.is_null());
+
+ assert_eq!(MP4PARSE_OK, mp4parse_read(parser));
+
+ let mut count: u32 = 0;
+ assert_eq!(MP4PARSE_OK, mp4parse_get_track_count(parser, &mut count));
+ assert_eq!(2, count);
+
+ let mut info = mp4parse_track_info {
+ track_type: MP4PARSE_TRACK_TYPE_VIDEO,
+ codec: mp4parse_codec::MP4PARSE_CODEC_UNKNOWN,
+ track_id: 0,
+ duration: 0,
+ media_time: 0,
+ };
+ assert_eq!(MP4PARSE_OK, mp4parse_get_track_info(parser, 0, &mut info));
+ assert_eq!(info.track_type, MP4PARSE_TRACK_TYPE_VIDEO);
+ assert_eq!(info.codec, mp4parse_codec::MP4PARSE_CODEC_AVC);
+ assert_eq!(info.track_id, 1);
+ assert_eq!(info.duration, 40000);
+ assert_eq!(info.media_time, 0);
+
+ assert_eq!(MP4PARSE_OK, mp4parse_get_track_info(parser, 1, &mut info));
+ assert_eq!(info.track_type, MP4PARSE_TRACK_TYPE_AUDIO);
+ assert_eq!(info.codec, mp4parse_codec::MP4PARSE_CODEC_AAC);
+ assert_eq!(info.track_id, 2);
+ assert_eq!(info.duration, 61333);
+ assert_eq!(info.media_time, 21333);
+
+ let mut video = mp4parse_track_video_info {
+ display_width: 0,
+ display_height: 0,
+ image_width: 0,
+ image_height: 0,
+ };
+ assert_eq!(MP4PARSE_OK, mp4parse_get_track_video_info(parser, 0, &mut video));
+ assert_eq!(video.display_width, 320);
+ assert_eq!(video.display_height, 240);
+ assert_eq!(video.image_width, 320);
+ assert_eq!(video.image_height, 240);
+
+ let mut audio = Default::default();
+ assert_eq!(MP4PARSE_OK, mp4parse_get_track_audio_info(parser, 1, &mut audio));
+ assert_eq!(audio.channels, 1);
+ assert_eq!(audio.bit_depth, 16);
+ assert_eq!(audio.sample_rate, 48000);
+
+ // Test with an invalid track number.
+ let mut info = mp4parse_track_info {
+ track_type: MP4PARSE_TRACK_TYPE_VIDEO,
+ codec: mp4parse_codec::MP4PARSE_CODEC_UNKNOWN,
+ track_id: 0,
+ duration: 0,
+ media_time: 0,
+ };
+ assert_eq!(MP4PARSE_ERROR_BADARG, mp4parse_get_track_info(parser, 3, &mut info));
+ assert_eq!(info.track_type, MP4PARSE_TRACK_TYPE_VIDEO);
+ assert_eq!(info.codec, mp4parse_codec::MP4PARSE_CODEC_UNKNOWN);
+ assert_eq!(info.track_id, 0);
+ assert_eq!(info.duration, 0);
+ assert_eq!(info.media_time, 0);
+
+ let mut video = mp4parse_track_video_info { display_width: 0,
+ display_height: 0,
+ image_width: 0,
+ image_height: 0 };
+ assert_eq!(MP4PARSE_ERROR_BADARG, mp4parse_get_track_video_info(parser, 3, &mut video));
+ assert_eq!(video.display_width, 0);
+ assert_eq!(video.display_height, 0);
+ assert_eq!(video.image_width, 0);
+ assert_eq!(video.image_height, 0);
+
+ let mut audio = Default::default();
+ assert_eq!(MP4PARSE_ERROR_BADARG, mp4parse_get_track_audio_info(parser, 3, &mut audio));
+ assert_eq!(audio.channels, 0);
+ assert_eq!(audio.bit_depth, 0);
+ assert_eq!(audio.sample_rate, 0);
+
+ mp4parse_free(parser);
+ }
+}
+
+#[test]
+fn rational_scale_overflow() {
+ assert_eq!(rational_scale(17, 3, 1000), Some(5666));
+ let large = 0x4000_0000_0000_0000;
+ assert_eq!(rational_scale(large, 2, 2), Some(large));
+ assert_eq!(rational_scale(large, 4, 4), Some(large));
+ assert_eq!(rational_scale(large, 2, 8), None);
+ assert_eq!(rational_scale(large, 8, 4), Some(large/2));
+ assert_eq!(rational_scale(large + 1, 4, 4), Some(large+1));
+ assert_eq!(rational_scale(large, 40, 1000), None);
+}
+
+#[test]
+fn media_time_overflow() {
+ let scale = MediaTimeScale(90000);
+ let duration = MediaScaledTime(9007199254710000);
+ assert_eq!(media_time_to_us(duration, scale), Some(100079991719000000));
+}
+
+#[test]
+fn track_time_overflow() {
+ let scale = TrackTimeScale(44100, 0);
+ let duration = TrackScaledTime(4413527634807900, 0);
+ assert_eq!(track_time_to_us(duration, scale), Some(100079991719000000));
+}
diff --git a/media/libstagefright/binding/update-rust.sh b/media/libstagefright/binding/update-rust.sh
new file mode 100755
index 000000000..a8a462f6d
--- /dev/null
+++ b/media/libstagefright/binding/update-rust.sh
@@ -0,0 +1,56 @@
+#!/bin/sh -e
+# Script to update mp4parse-rust sources to latest upstream
+
+# Default version.
+VER=v0.6.0
+
+# Accept version or commit from the command line.
+if test -n "$1"; then
+ VER=$1
+fi
+
+echo "Fetching sources..."
+rm -rf _upstream
+git clone https://github.com/mozilla/mp4parse-rust _upstream/mp4parse
+pushd _upstream/mp4parse
+git checkout ${VER}
+echo "Verifying sources..."
+pushd mp4parse
+cargo test
+popd
+echo "Constructing C api header..."
+pushd mp4parse_capi
+cargo build
+echo "Verifying sources..."
+cargo test
+popd
+popd
+rm -rf mp4parse
+mkdir -p mp4parse/src
+cp _upstream/mp4parse/mp4parse/Cargo.toml mp4parse/
+cp _upstream/mp4parse/mp4parse/src/*.rs mp4parse/src/
+mkdir -p mp4parse/tests
+cp _upstream/mp4parse/mp4parse/tests/*.rs mp4parse/tests/
+cp _upstream/mp4parse/mp4parse/tests/*.mp4 mp4parse/tests/
+rm -rf mp4parse_capi
+mkdir -p mp4parse_capi/src
+cp _upstream/mp4parse/mp4parse_capi/Cargo.toml mp4parse_capi/
+cp _upstream/mp4parse/mp4parse_capi/build.rs mp4parse_capi/
+cp _upstream/mp4parse/mp4parse_capi/include/mp4parse.h include/
+cp _upstream/mp4parse/mp4parse_capi/src/*.rs mp4parse_capi/src/
+
+echo "Applying patches..."
+patch -p4 < mp4parse-cargo.patch
+
+echo "Cleaning up..."
+rm -rf _upstream
+
+echo "Updating gecko Cargo.lock..."
+pushd ../../../toolkit/library/rust/
+cargo update --package mp4parse_capi
+popd
+pushd ../../../toolkit/library/gtest/rust/
+cargo update --package mp4parse_capi
+popd
+
+echo "Updated to ${VER}."
diff --git a/media/libstagefright/checkout.sh b/media/libstagefright/checkout.sh
new file mode 100755
index 000000000..42dfb87fd
--- /dev/null
+++ b/media/libstagefright/checkout.sh
@@ -0,0 +1,52 @@
+#!/bin/bash -e
+set -o pipefail
+abort () {
+ errcode=$?
+ echo "Error: That didn't work..."
+ exit $errcode
+}
+trap abort ERR
+
+cd `dirname "$0"`
+
+SITE=https://android.googlesource.com/platform
+for TAGFILE in `find patches -name \*.tag`
+do
+ REPO=${TAGFILE:8:-4}
+ DEST=android/${REPO}
+ if [[ ! -e ${DEST} ]]
+ then
+ mkdir -p `dirname ${DEST}`
+ echo Cloning from ${SITE}/${REPO}
+ git clone ${SITE}/${REPO} ${DEST}
+ fi
+
+ rm -fR ${REPO}
+ TAG=`cat $TAGFILE`
+ (cd $DEST && git reset --hard 2>&1 && git checkout ${TAG} 2>&1) > /dev/null
+done
+
+FILES=`python files.py`
+HEADERS=`cat additional_headers`
+for FILE in $FILES $HEADERS frameworks/av/media/libstagefright/include/AMRExtractor.h
+do
+ echo Copying ${FILE}
+ mkdir -p `dirname ${FILE}`
+ cp android/${FILE} ${FILE}
+done
+
+for PATCH in `find patches -name \*.patch`
+do
+ REPO=${PATCH:8:-6}
+ echo Patching repo ${REPO}
+ for FILE in `grep -- '--- a/' ${PATCH} | colrm 1 6`
+ do
+ if [[ ! -e ${FILE} ]]
+ then
+ echo Copying ${REPO}/${FILE}
+ mkdir -p `dirname ${REPO}/${FILE}`
+ cp android/${REPO}/${FILE} ${REPO}/${FILE}
+ fi
+ done
+ (cd ${REPO} && patch -p1 || true) < $PATCH
+done
diff --git a/media/libstagefright/files.py b/media/libstagefright/files.py
new file mode 100644
index 000000000..4d6775fa4
--- /dev/null
+++ b/media/libstagefright/files.py
@@ -0,0 +1,29 @@
+#!/usr/bin/python
+
+import os
+import sys
+
+DEFINES={}
+CONFIG={}
+CXXFLAGS=[]
+CONFIG['_MSC_VER'] = 0
+CONFIG['OS_TARGET'] = 0
+
+class Exports(object):
+ def __init__(self):
+ self.mp4_demuxer=[]
+
+EXPORTS=Exports()
+
+SOURCES=[]
+UNIFIED_SOURCES=[]
+LOCAL_INCLUDES=[]
+
+try:
+ execfile('moz.build')
+except:
+ sys.exit(1)
+
+for f in SOURCES+UNIFIED_SOURCES:
+ if not f.startswith('binding/'):
+ print f
diff --git a/media/libstagefright/frameworks/av/include/media/stagefright/DataSource.h b/media/libstagefright/frameworks/av/include/media/stagefright/DataSource.h
new file mode 100644
index 000000000..f6b30b0e8
--- /dev/null
+++ b/media/libstagefright/frameworks/av/include/media/stagefright/DataSource.h
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef DATA_SOURCE_H_
+
+#define DATA_SOURCE_H_
+
+#include <sys/types.h>
+
+#include <media/stagefright/MediaErrors.h>
+#include <utils/RefBase.h>
+
+namespace stagefright {
+
+class String8;
+
+class DataSource : public RefBase {
+public:
+ enum Flags {
+ kWantsPrefetching = 1,
+ kStreamedFromLocalHost = 2,
+ kIsCachingDataSource = 4,
+ kIsHTTPBasedSource = 8,
+ };
+
+ DataSource() {}
+
+ virtual status_t initCheck() const = 0;
+
+ virtual ssize_t readAt(off64_t offset, void *data, size_t size) = 0;
+
+ // Convenience methods:
+ bool getUInt16(off64_t offset, uint16_t *x);
+ bool getUInt24(off64_t offset, uint32_t *x); // 3 byte int, returned as a 32-bit int
+ bool getUInt32(off64_t offset, uint32_t *x);
+ bool getUInt64(off64_t offset, uint64_t *x);
+
+ // May return ERROR_UNSUPPORTED.
+ virtual status_t getSize(off64_t *size);
+
+ virtual uint32_t flags() {
+ return 0;
+ }
+
+ virtual status_t reconnectAtOffset(off64_t offset) {
+ return ERROR_UNSUPPORTED;
+ }
+
+#if 0
+ ////////////////////////////////////////////////////////////////////////////
+
+ bool sniff(String8 *mimeType, float *confidence, sp<AMessage> *meta);
+
+ // The sniffer can optionally fill in "meta" with an AMessage containing
+ // a dictionary of values that helps the corresponding extractor initialize
+ // its state without duplicating effort already exerted by the sniffer.
+ typedef bool (*SnifferFunc)(
+ const sp<DataSource> &source, String8 *mimeType,
+ float *confidence, sp<AMessage> *meta);
+
+ static void RegisterSniffer(SnifferFunc func);
+ static void RegisterDefaultSniffers();
+
+ // for DRM
+ virtual sp<DecryptHandle> DrmInitialization(const char *mime = NULL) {
+ return NULL;
+ }
+ virtual void getDrmInfo(sp<DecryptHandle> &handle, DrmManagerClient **client) {};
+
+ virtual String8 getUri() {
+ return String8();
+ }
+#endif
+
+ virtual String8 getMIMEType() const;
+
+protected:
+ virtual ~DataSource() {}
+
+private:
+ DataSource(const DataSource &);
+ DataSource &operator=(const DataSource &);
+};
+
+} // namespace stagefright
+
+#endif // DATA_SOURCE_H_
diff --git a/media/libstagefright/frameworks/av/include/media/stagefright/MediaBuffer.h b/media/libstagefright/frameworks/av/include/media/stagefright/MediaBuffer.h
new file mode 100644
index 000000000..ffbfcfc79
--- /dev/null
+++ b/media/libstagefright/frameworks/av/include/media/stagefright/MediaBuffer.h
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef MEDIA_BUFFER_H_
+
+#define MEDIA_BUFFER_H_
+
+#include <pthread.h>
+
+#include <utils/Errors.h>
+#include <utils/RefBase.h>
+#include "nsTArray.h"
+
+namespace stagefright {
+
+struct ABuffer;
+class GraphicBuffer;
+class MediaBuffer;
+class MediaBufferObserver;
+class MetaData;
+
+class MediaBufferObserver {
+public:
+ MediaBufferObserver() {}
+ virtual ~MediaBufferObserver() {}
+
+ virtual void signalBufferReturned(MediaBuffer *buffer) = 0;
+
+private:
+ MediaBufferObserver(const MediaBufferObserver &);
+ MediaBufferObserver &operator=(const MediaBufferObserver &);
+};
+
+class MediaBuffer {
+public:
+ // The underlying data remains the responsibility of the caller!
+ MediaBuffer(void *data, size_t size);
+
+ MediaBuffer(size_t size);
+
+ MediaBuffer(const sp<GraphicBuffer>& graphicBuffer);
+
+ MediaBuffer(const sp<ABuffer> &buffer);
+
+ // Decrements the reference count and deletes it if the reference
+ // count drops to 0.
+ void release();
+
+ // Increments the reference count.
+ void add_ref();
+
+ void *data() const;
+ size_t size() const;
+
+ size_t range_offset() const;
+ size_t range_length() const;
+
+ void set_range(size_t offset, size_t length);
+
+ sp<GraphicBuffer> graphicBuffer() const;
+
+ sp<MetaData> meta_data();
+
+ // Clears meta data and resets the range to the full extent.
+ void reset();
+
+ void setObserver(MediaBufferObserver *group);
+
+ // Returns a clone of this MediaBuffer increasing its reference count.
+ // The clone references the same data but has its own range and
+ // MetaData.
+ MediaBuffer *clone();
+
+ int refcount() const;
+
+ bool ensuresize(size_t length);
+
+protected:
+ virtual ~MediaBuffer();
+
+private:
+ friend class MediaBufferGroup;
+ friend class OMXDecoder;
+
+ // For use by OMXDecoder, reference count must be 1, drop reference
+ // count to 0 without signalling the observer.
+ void claim();
+
+ MediaBufferObserver *mObserver;
+ MediaBuffer *mNextBuffer;
+ int mRefCount;
+
+ void *mData;
+ size_t mSize, mRangeOffset, mRangeLength;
+ sp<GraphicBuffer> mGraphicBuffer;
+ sp<ABuffer> mBuffer;
+
+ bool mOwnsData;
+
+ sp<MetaData> mMetaData;
+
+ MediaBuffer *mOriginal;
+
+ void setNextBuffer(MediaBuffer *buffer);
+ MediaBuffer *nextBuffer();
+
+ MediaBuffer(const MediaBuffer &);
+ MediaBuffer &operator=(const MediaBuffer &);
+
+ FallibleTArray<uint8_t> mBufferBackend;
+};
+
+} // namespace stagefright
+
+#endif // MEDIA_BUFFER_H_
diff --git a/media/libstagefright/frameworks/av/include/media/stagefright/MediaDefs.h b/media/libstagefright/frameworks/av/include/media/stagefright/MediaDefs.h
new file mode 100644
index 000000000..7ac6db8d5
--- /dev/null
+++ b/media/libstagefright/frameworks/av/include/media/stagefright/MediaDefs.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef MEDIA_DEFS_H_
+
+#define MEDIA_DEFS_H_
+
+namespace stagefright {
+
+extern const char *MEDIA_MIMETYPE_IMAGE_JPEG;
+
+extern const char *MEDIA_MIMETYPE_VIDEO_VP6;
+extern const char *MEDIA_MIMETYPE_VIDEO_VP8;
+extern const char *MEDIA_MIMETYPE_VIDEO_VP9;
+extern const char *MEDIA_MIMETYPE_VIDEO_AVC;
+extern const char *MEDIA_MIMETYPE_VIDEO_MPEG4;
+extern const char *MEDIA_MIMETYPE_VIDEO_H263;
+extern const char *MEDIA_MIMETYPE_VIDEO_MPEG2;
+extern const char *MEDIA_MIMETYPE_VIDEO_RAW;
+
+extern const char *MEDIA_MIMETYPE_AUDIO_AMR_NB;
+extern const char *MEDIA_MIMETYPE_AUDIO_AMR_WB;
+extern const char *MEDIA_MIMETYPE_AUDIO_MPEG; // layer III
+extern const char *MEDIA_MIMETYPE_AUDIO_MPEG_LAYER_I;
+extern const char *MEDIA_MIMETYPE_AUDIO_MPEG_LAYER_II;
+extern const char *MEDIA_MIMETYPE_AUDIO_AAC;
+extern const char *MEDIA_MIMETYPE_AUDIO_QCELP;
+extern const char *MEDIA_MIMETYPE_AUDIO_VORBIS;
+extern const char *MEDIA_MIMETYPE_AUDIO_G711_ALAW;
+extern const char *MEDIA_MIMETYPE_AUDIO_G711_MLAW;
+extern const char *MEDIA_MIMETYPE_AUDIO_RAW;
+extern const char *MEDIA_MIMETYPE_AUDIO_FLAC;
+extern const char *MEDIA_MIMETYPE_AUDIO_AAC_ADTS;
+extern const char *MEDIA_MIMETYPE_AUDIO_MSGSM;
+
+extern const char *MEDIA_MIMETYPE_CONTAINER_MPEG4;
+extern const char *MEDIA_MIMETYPE_CONTAINER_WAV;
+extern const char *MEDIA_MIMETYPE_CONTAINER_OGG;
+extern const char *MEDIA_MIMETYPE_CONTAINER_MATROSKA;
+extern const char *MEDIA_MIMETYPE_CONTAINER_MPEG2TS;
+extern const char *MEDIA_MIMETYPE_CONTAINER_AVI;
+extern const char *MEDIA_MIMETYPE_CONTAINER_MPEG2PS;
+
+extern const char *MEDIA_MIMETYPE_CONTAINER_WVM;
+
+extern const char *MEDIA_MIMETYPE_TEXT_3GPP;
+extern const char *MEDIA_MIMETYPE_TEXT_SUBRIP;
+
+} // namespace stagefright
+
+#endif // MEDIA_DEFS_H_
diff --git a/media/libstagefright/frameworks/av/include/media/stagefright/MediaErrors.h b/media/libstagefright/frameworks/av/include/media/stagefright/MediaErrors.h
new file mode 100644
index 000000000..df2c1a50b
--- /dev/null
+++ b/media/libstagefright/frameworks/av/include/media/stagefright/MediaErrors.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef MEDIA_ERRORS_H_
+
+#define MEDIA_ERRORS_H_
+
+#include <utils/Errors.h>
+
+namespace stagefright {
+
+#define MEDIA_ERROR_BASE (-1000)
+
+#define ERROR_ALREADY_CONNECTED (MEDIA_ERROR_BASE)
+#define ERROR_NOT_CONNECTED (MEDIA_ERROR_BASE - 1)
+#define ERROR_UNKNOWN_HOST (MEDIA_ERROR_BASE - 2)
+#define ERROR_CANNOT_CONNECT (MEDIA_ERROR_BASE - 3)
+#define ERROR_IO (MEDIA_ERROR_BASE - 4)
+#define ERROR_CONNECTION_LOST (MEDIA_ERROR_BASE - 5)
+#define ERROR_MALFORMED (MEDIA_ERROR_BASE - 7)
+#define ERROR_OUT_OF_RANGE (MEDIA_ERROR_BASE - 8)
+#define ERROR_BUFFER_TOO_SMALL (MEDIA_ERROR_BASE - 9)
+#define ERROR_UNSUPPORTED (MEDIA_ERROR_BASE - 10)
+#define ERROR_END_OF_STREAM (MEDIA_ERROR_BASE - 11)
+
+// Not technically an error.
+#define INFO_FORMAT_CHANGED (MEDIA_ERROR_BASE - 12)
+#define INFO_DISCONTINUITY (MEDIA_ERROR_BASE - 13)
+#define INFO_OUTPUT_BUFFERS_CHANGED (MEDIA_ERROR_BASE - 14)
+
+// The following constant values should be in sync with
+// drm/drm_framework_common.h
+#define DRM_ERROR_BASE (-2000)
+
+#define ERROR_DRM_UNKNOWN (DRM_ERROR_BASE)
+#define ERROR_DRM_NO_LICENSE (DRM_ERROR_BASE - 1)
+#define ERROR_DRM_LICENSE_EXPIRED (DRM_ERROR_BASE - 2)
+#define ERROR_DRM_SESSION_NOT_OPENED (DRM_ERROR_BASE - 3)
+#define ERROR_DRM_DECRYPT_UNIT_NOT_INITIALIZED (DRM_ERROR_BASE - 4)
+#define ERROR_DRM_DECRYPT (DRM_ERROR_BASE - 5)
+#define ERROR_DRM_CANNOT_HANDLE (DRM_ERROR_BASE - 6)
+#define ERROR_DRM_TAMPER_DETECTED (DRM_ERROR_BASE - 7)
+#define ERROR_DRM_NOT_PROVISIONED (DRM_ERROR_BASE - 8)
+#define ERROR_DRM_DEVICE_REVOKED (DRM_ERROR_BASE - 9)
+#define ERROR_DRM_RESOURCE_BUSY (DRM_ERROR_BASE - 10)
+
+#define ERROR_DRM_VENDOR_MAX (DRM_ERROR_BASE - 500)
+#define ERROR_DRM_VENDOR_MIN (DRM_ERROR_BASE - 999)
+
+// Heartbeat Error Codes
+#define HEARTBEAT_ERROR_BASE (-3000)
+#define ERROR_HEARTBEAT_TERMINATE_REQUESTED (HEARTBEAT_ERROR_BASE)
+
+} // namespace stagefright
+
+#endif // MEDIA_ERRORS_H_
diff --git a/media/libstagefright/frameworks/av/include/media/stagefright/MediaExtractor.h b/media/libstagefright/frameworks/av/include/media/stagefright/MediaExtractor.h
new file mode 100644
index 000000000..25ed8e18b
--- /dev/null
+++ b/media/libstagefright/frameworks/av/include/media/stagefright/MediaExtractor.h
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef MEDIA_EXTRACTOR_H_
+
+#define MEDIA_EXTRACTOR_H_
+
+#include <utils/RefBase.h>
+
+namespace stagefright {
+
+class DataSource;
+struct MediaSource;
+class MetaData;
+
+class MediaExtractor : public RefBase {
+public:
+ static sp<MediaExtractor> Create(
+ const sp<DataSource> &source, const char *mime = NULL);
+
+ virtual size_t countTracks() = 0;
+ virtual sp<MediaSource> getTrack(size_t index) = 0;
+
+ enum GetTrackMetaDataFlags {
+ kIncludeExtensiveMetaData = 1
+ };
+ virtual sp<MetaData> getTrackMetaData(
+ size_t index, uint32_t flags = 0) = 0;
+
+ // Return container specific meta-data. The default implementation
+ // returns an empty metadata object.
+ virtual sp<MetaData> getMetaData() = 0;
+
+ enum Flags {
+ CAN_SEEK_BACKWARD = 1, // the "seek 10secs back button"
+ CAN_SEEK_FORWARD = 2, // the "seek 10secs forward button"
+ CAN_PAUSE = 4,
+ CAN_SEEK = 8, // the "seek bar"
+ };
+
+ // If subclasses do _not_ override this, the default is
+ // CAN_SEEK_BACKWARD | CAN_SEEK_FORWARD | CAN_SEEK | CAN_PAUSE
+ virtual uint32_t flags() const = 0;
+
+ // for DRM
+ void setDrmFlag(bool flag) {
+ mIsDrm = flag;
+ };
+ bool getDrmFlag() {
+ return mIsDrm;
+ }
+ virtual char* getDrmTrackInfo(size_t trackID, int *len) {
+ return NULL;
+ }
+
+protected:
+ MediaExtractor() : mIsDrm(false) {}
+ virtual ~MediaExtractor() {}
+
+private:
+ bool mIsDrm;
+
+ MediaExtractor(const MediaExtractor &);
+ MediaExtractor &operator=(const MediaExtractor &);
+};
+
+} // namespace stagefright
+
+#endif // MEDIA_EXTRACTOR_H_
diff --git a/media/libstagefright/frameworks/av/include/media/stagefright/MediaSource.h b/media/libstagefright/frameworks/av/include/media/stagefright/MediaSource.h
new file mode 100644
index 000000000..913fca6d9
--- /dev/null
+++ b/media/libstagefright/frameworks/av/include/media/stagefright/MediaSource.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef MEDIA_SOURCE_H_
+
+#define MEDIA_SOURCE_H_
+
+#include <sys/types.h>
+
+#include <media/stagefright/MediaErrors.h>
+#include <utils/RefBase.h>
+#include <utils/Vector.h>
+#include "nsTArray.h"
+
+namespace stagefright {
+
+class MediaBuffer;
+class MetaData;
+
+struct MediaSource : public virtual RefBase {
+ MediaSource();
+
+ // Returns the format of the data output by this media source.
+ virtual sp<MetaData> getFormat() = 0;
+
+ struct Indice
+ {
+ uint64_t start_offset;
+ uint64_t end_offset;
+ uint64_t start_composition;
+ uint64_t end_composition;
+ uint64_t start_decode;
+ bool sync;
+ };
+
+ virtual nsTArray<Indice> exportIndex() = 0;
+
+protected:
+ virtual ~MediaSource();
+
+private:
+ MediaSource(const MediaSource &) = delete;
+ MediaSource &operator=(const MediaSource &) = delete;
+};
+
+} // namespace stagefright
+
+#endif // MEDIA_SOURCE_H_
diff --git a/media/libstagefright/frameworks/av/include/media/stagefright/MetaData.h b/media/libstagefright/frameworks/av/include/media/stagefright/MetaData.h
new file mode 100644
index 000000000..3cd6a22b4
--- /dev/null
+++ b/media/libstagefright/frameworks/av/include/media/stagefright/MetaData.h
@@ -0,0 +1,275 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef META_DATA_H_
+
+#define META_DATA_H_
+
+#include <sys/types.h>
+
+#include <stdint.h>
+
+#include <utils/RefBase.h>
+#include <utils/KeyedVector.h>
+#include <utils/String8.h>
+
+namespace stagefright {
+
+// The following keys map to int32_t data unless indicated otherwise.
+enum {
+ kKeyMIMEType = 'mime', // cstring
+ kKeyWidth = 'widt', // int32_t, image pixel
+ kKeyHeight = 'heig', // int32_t, image pixel
+ kKeyDisplayWidth = 'dWid', // int32_t, display/presentation
+ kKeyDisplayHeight = 'dHgt', // int32_t, display/presentation
+ kKeySARWidth = 'sarW', // int32_t, sampleAspectRatio width
+ kKeySARHeight = 'sarH', // int32_t, sampleAspectRatio height
+
+ // a rectangle, if absent assumed to be (0, 0, width - 1, height - 1)
+ kKeyCropRect = 'crop',
+
+ kKeyRotation = 'rotA', // int32_t (angle in degrees)
+ kKeyIFramesInterval = 'ifiv', // int32_t
+ kKeyStride = 'strd', // int32_t
+ kKeySliceHeight = 'slht', // int32_t
+ kKeyChannelCount = '#chn', // int32_t
+ kKeyChannelMask = 'chnm', // int32_t
+ kKeySampleRate = 'srte', // int32_t (audio sampling rate Hz)
+ kKeySampleSize = 'ssiz', // int32_t (sample size in bits)
+ kKeyFrameRate = 'frmR', // int32_t (video frame rate fps)
+ kKeyBitRate = 'brte', // int32_t (bps)
+ kKeyESDS = 'esds', // raw data
+ kKeyAACProfile = 'aacp', // int32_t
+ kKeyAVCC = 'avcc', // raw data
+ kKeyD263 = 'd263', // raw data
+ kKeyVorbisInfo = 'vinf', // raw data
+ kKeyVorbisBooks = 'vboo', // raw data
+ kKeyWantsNALFragments = 'NALf',
+ kKeyIsSyncFrame = 'sync', // int32_t (bool)
+ kKeyIsCodecConfig = 'conf', // int32_t (bool)
+ kKeyTime = 'time', // int64_t (usecs)
+ kKeyDecodingTime = 'decT', // int64_t (decoding timestamp in usecs)
+ kKeyNTPTime = 'ntpT', // uint64_t (ntp-timestamp)
+ kKeyTargetTime = 'tarT', // int64_t (usecs)
+ kKeyDriftTime = 'dftT', // int64_t (usecs)
+ kKeyAnchorTime = 'ancT', // int64_t (usecs)
+ kKeyDuration = 'dura', // int64_t (usecs)
+ kKeyMovieDuration = 'mdur', // int64_t (usecs)
+ kKeyColorFormat = 'colf',
+ kKeyPlatformPrivate = 'priv', // pointer
+ kKeyDecoderComponent = 'decC', // cstring
+ kKeyBufferID = 'bfID',
+ kKeyMaxInputSize = 'inpS',
+ kKeyThumbnailTime = 'thbT', // int64_t (usecs)
+ kKeyTrackID = 'trID',
+ kKeyIsDRM = 'idrm', // int32_t (bool)
+ kKeyEncoderDelay = 'encd', // int32_t (frames)
+ kKeyEncoderPadding = 'encp', // int32_t (frames)
+ kKeyMediaTime = 'mtme', // int64_t (usecs)
+
+ kKeyAlbum = 'albu', // cstring
+ kKeyArtist = 'arti', // cstring
+ kKeyAlbumArtist = 'aart', // cstring
+ kKeyComposer = 'comp', // cstring
+ kKeyGenre = 'genr', // cstring
+ kKeyTitle = 'titl', // cstring
+ kKeyYear = 'year', // cstring
+ kKeyAlbumArt = 'albA', // compressed image data
+ kKeyAlbumArtMIME = 'alAM', // cstring
+ kKeyAuthor = 'auth', // cstring
+ kKeyCDTrackNumber = 'cdtr', // cstring
+ kKeyDiscNumber = 'dnum', // cstring
+ kKeyDate = 'date', // cstring
+ kKeyWriter = 'writ', // cstring
+ kKeyCompilation = 'cpil', // cstring
+ kKeyLocation = 'loc ', // cstring
+ kKeyTimeScale = 'tmsl', // int32_t
+
+ // video profile and level
+ kKeyVideoProfile = 'vprf', // int32_t
+ kKeyVideoLevel = 'vlev', // int32_t
+
+ // Set this key to enable authoring files in 64-bit offset
+ kKey64BitFileOffset = 'fobt', // int32_t (bool)
+ kKey2ByteNalLength = '2NAL', // int32_t (bool)
+
+ // Identify the file output format for authoring
+ // Please see <media/mediarecorder.h> for the supported
+ // file output formats.
+ kKeyFileType = 'ftyp', // int32_t
+
+ // Track authoring progress status
+ // kKeyTrackTimeStatus is used to track progress in elapsed time
+ kKeyTrackTimeStatus = 'tktm', // int64_t
+
+ kKeyRealTimeRecording = 'rtrc', // bool (int32_t)
+ kKeyNumBuffers = 'nbbf', // int32_t
+
+ // Ogg files can be tagged to be automatically looping...
+ kKeyAutoLoop = 'autL', // bool (int32_t)
+
+ kKeyValidSamples = 'valD', // int32_t
+
+ kKeyIsUnreadable = 'unre', // bool (int32_t)
+
+ // An indication that a video buffer has been rendered.
+ kKeyRendered = 'rend', // bool (int32_t)
+
+ // The language code for this media
+ kKeyMediaLanguage = 'lang', // cstring
+
+ // To store the timed text format data
+ kKeyTextFormatData = 'text', // raw data
+
+ kKeyRequiresSecureBuffers = 'secu', // bool (int32_t)
+
+ kKeyIsADTS = 'adts', // bool (int32_t)
+ kKeyAACAOT = 'aaot', // int32_t
+
+ // If a MediaBuffer's data represents (at least partially) encrypted
+ // data, the following fields aid in decryption.
+ // The data can be thought of as pairs of plain and encrypted data
+ // fragments, i.e. plain and encrypted data alternate.
+ // The first fragment is by convention plain data (if that's not the
+ // case, simply specify plain fragment size of 0).
+ // kKeyEncryptedSizes and kKeyPlainSizes each map to an array of
+ // size_t values. The sum total of all size_t values of both arrays
+ // must equal the amount of data (i.e. MediaBuffer's range_length()).
+ // If both arrays are present, they must be of the same size.
+ // If only encrypted sizes are present it is assumed that all
+ // plain sizes are 0, i.e. all fragments are encrypted.
+ // To programmatically set these array, use the MetaData::setData API, i.e.
+ // const size_t encSizes[];
+ // meta->setData(
+ // kKeyEncryptedSizes, 0 /* type */, encSizes, sizeof(encSizes));
+ // A plain sizes array by itself makes no sense.
+ kKeyEncryptedSizes = 'encr', // size_t[]
+ kKeyPlainSizes = 'plai', // size_t[]
+ kKeyCryptoKey = 'cryK', // uint8_t[16]
+ kKeyCryptoIV = 'cryI', // uint8_t[16]
+ kKeyCryptoMode = 'cryM', // int32_t
+
+ kKeyCryptoDefaultIVSize = 'cryS', // int32_t
+
+ kKeyPssh = 'pssh', // raw data
+};
+
+enum {
+ kTypeESDS = 'esds',
+ kTypeAVCC = 'avcc',
+ kTypeD263 = 'd263',
+};
+
+class MetaData : public RefBase {
+public:
+ MetaData();
+ MetaData(const MetaData &from);
+
+ enum Type {
+ TYPE_NONE = 'none',
+ TYPE_C_STRING = 'cstr',
+ TYPE_INT32 = 'in32',
+ TYPE_INT64 = 'in64',
+ TYPE_FLOAT = 'floa',
+ TYPE_POINTER = 'ptr ',
+ TYPE_RECT = 'rect',
+ };
+
+ void clear();
+ bool remove(uint32_t key);
+
+ bool setCString(uint32_t key, const char *value);
+ bool setInt32(uint32_t key, int32_t value);
+ bool setInt64(uint32_t key, int64_t value);
+ bool setFloat(uint32_t key, float value);
+ bool setPointer(uint32_t key, void *value);
+
+ bool setRect(
+ uint32_t key,
+ int32_t left, int32_t top,
+ int32_t right, int32_t bottom);
+
+ bool findCString(uint32_t key, const char **value) const;
+ bool findInt32(uint32_t key, int32_t *value) const;
+ bool findInt64(uint32_t key, int64_t *value) const;
+ bool findFloat(uint32_t key, float *value) const;
+ bool findPointer(uint32_t key, void **value) const;
+
+ bool findRect(
+ uint32_t key,
+ int32_t *left, int32_t *top,
+ int32_t *right, int32_t *bottom) const;
+
+ bool setData(uint32_t key, uint32_t type, const void *data, size_t size);
+
+ bool findData(uint32_t key, uint32_t *type,
+ const void **data, size_t *size) const;
+
+ void dumpToLog() const;
+
+protected:
+ virtual ~MetaData();
+
+private:
+ struct typed_data {
+ typed_data();
+ ~typed_data();
+
+ typed_data(const MetaData::typed_data &);
+ typed_data &operator=(const MetaData::typed_data &);
+
+ void clear();
+ void setData(uint32_t type, const void *data, size_t size);
+ void getData(uint32_t *type, const void **data, size_t *size) const;
+ String8 asString() const;
+
+ private:
+ uint32_t mType;
+ size_t mSize;
+
+ union {
+ void *ext_data;
+ float reservoir;
+ } u;
+
+ bool usesReservoir() const {
+ return mSize <= sizeof(u.reservoir);
+ }
+
+ bool allocateStorage(size_t size);
+ void freeStorage();
+
+ void *storage() {
+ return usesReservoir() ? &u.reservoir : u.ext_data;
+ }
+
+ const void *storage() const {
+ return usesReservoir() ? &u.reservoir : u.ext_data;
+ }
+ };
+
+ struct Rect {
+ int32_t mLeft, mTop, mRight, mBottom;
+ };
+
+ KeyedVector<uint32_t, typed_data> mItems;
+
+ // MetaData &operator=(const MetaData &);
+};
+
+} // namespace stagefright
+
+#endif // META_DATA_H_
diff --git a/media/libstagefright/frameworks/av/include/media/stagefright/Utils.h b/media/libstagefright/frameworks/av/include/media/stagefright/Utils.h
new file mode 100644
index 000000000..ae2e6efa8
--- /dev/null
+++ b/media/libstagefright/frameworks/av/include/media/stagefright/Utils.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef UTILS_H_
+
+#define UTILS_H_
+
+#include <media/stagefright/foundation/AString.h>
+#include <stdint.h>
+#include <utils/Errors.h>
+#include <utils/RefBase.h>
+#include <system/audio.h>
+#include <media/MediaPlayerInterface.h>
+
+namespace stagefright {
+
+#define FOURCC(c1, c2, c3, c4) \
+ ((uint32_t) c1 << 24 | c2 << 16 | c3 << 8 | c4)
+
+uint16_t U16_AT(const uint8_t *ptr);
+uint32_t U32_AT(const uint8_t *ptr);
+uint64_t U64_AT(const uint8_t *ptr);
+
+uint16_t U16LE_AT(const uint8_t *ptr);
+uint32_t U32LE_AT(const uint8_t *ptr);
+uint64_t U64LE_AT(const uint8_t *ptr);
+
+uint64_t ntoh64(uint64_t x);
+uint64_t hton64(uint64_t x);
+
+class MetaData;
+struct AMessage;
+status_t convertMetaDataToMessage(
+ const sp<MetaData> &meta, sp<AMessage> *format);
+void convertMessageToMetaData(
+ const sp<AMessage> &format, sp<MetaData> &meta);
+
+AString MakeUserAgent();
+
+} // namespace stagefright
+
+#endif // UTILS_H_
diff --git a/media/libstagefright/frameworks/av/include/media/stagefright/foundation/AAtomizer.h b/media/libstagefright/frameworks/av/include/media/stagefright/foundation/AAtomizer.h
new file mode 100644
index 000000000..c20ea07b0
--- /dev/null
+++ b/media/libstagefright/frameworks/av/include/media/stagefright/foundation/AAtomizer.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef A_ATOMIZER_H_
+
+#define A_ATOMIZER_H_
+
+#include <stdint.h>
+
+#include <media/stagefright/foundation/ABase.h>
+#include <media/stagefright/foundation/AString.h>
+#include <utils/List.h>
+#include <utils/Vector.h>
+#include <utils/threads.h>
+
+namespace stagefright {
+
+struct AAtomizer {
+ static const char *Atomize(const char *name);
+
+private:
+ static AAtomizer gAtomizer;
+
+ Mutex mLock;
+ Vector<List<AString> > mAtoms;
+
+ AAtomizer();
+
+ const char *atomize(const char *name);
+
+ static uint32_t Hash(const char *s);
+
+ DISALLOW_EVIL_CONSTRUCTORS(AAtomizer);
+};
+
+} // namespace stagefright
+
+#endif // A_ATOMIZER_H_
diff --git a/media/libstagefright/frameworks/av/include/media/stagefright/foundation/ABase.h b/media/libstagefright/frameworks/av/include/media/stagefright/foundation/ABase.h
new file mode 100644
index 000000000..9eceea3fd
--- /dev/null
+++ b/media/libstagefright/frameworks/av/include/media/stagefright/foundation/ABase.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef A_BASE_H_
+
+#define A_BASE_H_
+
+#define DISALLOW_EVIL_CONSTRUCTORS(name) \
+ name(const name &); \
+ name &operator=(const name &)
+
+#endif // A_BASE_H_
diff --git a/media/libstagefright/frameworks/av/include/media/stagefright/foundation/ABitReader.h b/media/libstagefright/frameworks/av/include/media/stagefright/foundation/ABitReader.h
new file mode 100644
index 000000000..589e5fe92
--- /dev/null
+++ b/media/libstagefright/frameworks/av/include/media/stagefright/foundation/ABitReader.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef A_BIT_READER_H_
+
+#define A_BIT_READER_H_
+
+#include <media/stagefright/foundation/ABase.h>
+
+#include <sys/types.h>
+#include <stdint.h>
+
+namespace stagefright {
+
+struct ABitReader {
+ ABitReader(const uint8_t *data, size_t size);
+
+ uint32_t getBits(size_t n);
+ void skipBits(size_t n);
+
+ void putBits(uint32_t x, size_t n);
+
+ size_t numBitsLeft() const;
+
+ const uint8_t *data() const;
+
+private:
+ const uint8_t *mData;
+ size_t mSize;
+
+ uint32_t mReservoir; // left-aligned bits
+ size_t mNumBitsLeft;
+
+ void fillReservoir();
+
+ DISALLOW_EVIL_CONSTRUCTORS(ABitReader);
+};
+
+} // namespace stagefright
+
+#endif // A_BIT_READER_H_
diff --git a/media/libstagefright/frameworks/av/include/media/stagefright/foundation/ABuffer.h b/media/libstagefright/frameworks/av/include/media/stagefright/foundation/ABuffer.h
new file mode 100644
index 000000000..4fa908e93
--- /dev/null
+++ b/media/libstagefright/frameworks/av/include/media/stagefright/foundation/ABuffer.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef A_BUFFER_H_
+
+#define A_BUFFER_H_
+
+#include <sys/types.h>
+#include <stdint.h>
+
+#include <media/stagefright/foundation/ABase.h>
+#include <utils/RefBase.h>
+
+namespace stagefright {
+
+struct AMessage;
+
+struct ABuffer : public RefBase {
+ ABuffer(size_t capacity);
+ ABuffer(void *data, size_t capacity);
+
+ void setFarewellMessage(const sp<AMessage> msg);
+
+ uint8_t *base() { return (uint8_t *)mData; }
+ uint8_t *data() { return (uint8_t *)mData + mRangeOffset; }
+ size_t capacity() const { return mCapacity; }
+ size_t size() const { return mRangeLength; }
+ size_t offset() const { return mRangeOffset; }
+
+ void setRange(size_t offset, size_t size);
+
+ void setInt32Data(int32_t data) { mInt32Data = data; }
+ int32_t int32Data() const { return mInt32Data; }
+
+ sp<AMessage> meta();
+
+protected:
+ virtual ~ABuffer();
+
+private:
+ sp<AMessage> mFarewell;
+ sp<AMessage> mMeta;
+
+ void *mData;
+ size_t mCapacity;
+ size_t mRangeOffset;
+ size_t mRangeLength;
+
+ int32_t mInt32Data;
+
+ bool mOwnsData;
+
+ DISALLOW_EVIL_CONSTRUCTORS(ABuffer);
+};
+
+} // namespace stagefright
+
+#endif // A_BUFFER_H_
diff --git a/media/libstagefright/frameworks/av/include/media/stagefright/foundation/ADebug.h b/media/libstagefright/frameworks/av/include/media/stagefright/foundation/ADebug.h
new file mode 100644
index 000000000..a58d951d5
--- /dev/null
+++ b/media/libstagefright/frameworks/av/include/media/stagefright/foundation/ADebug.h
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef A_DEBUG_H_
+
+#define A_DEBUG_H_
+
+#include <string.h>
+
+#include <media/stagefright/foundation/ABase.h>
+#include <media/stagefright/foundation/AString.h>
+#include <utils/Log.h>
+
+namespace stagefright {
+
+#define LITERAL_TO_STRING_INTERNAL(x) #x
+#define LITERAL_TO_STRING(x) LITERAL_TO_STRING_INTERNAL(x)
+
+#define CHECK(condition) \
+ LOG_ALWAYS_FATAL_IF( \
+ !(condition), \
+ "%s", \
+ __FILE__ ":" LITERAL_TO_STRING(__LINE__) \
+ " CHECK(" #condition ") failed.")
+
+#define MAKE_COMPARATOR(suffix,op) \
+ template<class A, class B> \
+ AString Compare_##suffix(const A &a, const B &b) { \
+ AString res; \
+ if (!(a op b)) { \
+ res.append(a); \
+ res.append(" vs. "); \
+ res.append(b); \
+ } \
+ return res; \
+ }
+
+MAKE_COMPARATOR(EQ,==)
+MAKE_COMPARATOR(NE,!=)
+MAKE_COMPARATOR(LE,<=)
+MAKE_COMPARATOR(GE,>=)
+MAKE_COMPARATOR(LT,<)
+MAKE_COMPARATOR(GT,>)
+
+#define CHECK_OP(x,y,suffix,op) \
+ do { \
+ AString ___res = Compare_##suffix(x, y); \
+ if (!___res.empty()) { \
+ AString ___full = \
+ __FILE__ ":" LITERAL_TO_STRING(__LINE__) \
+ " CHECK_" #suffix "( " #x "," #y ") failed: "; \
+ ___full.append(___res); \
+ \
+ LOG_ALWAYS_FATAL("%s", ___full.c_str()); \
+ } \
+ } while (false)
+
+#define CHECK_EQ(x,y) CHECK_OP(x,y,EQ,==)
+#define CHECK_NE(x,y) CHECK_OP(x,y,NE,!=)
+#define CHECK_LE(x,y) CHECK_OP(x,y,LE,<=)
+#define CHECK_LT(x,y) CHECK_OP(x,y,LT,<)
+#define CHECK_GE(x,y) CHECK_OP(x,y,GE,>=)
+#define CHECK_GT(x,y) CHECK_OP(x,y,GT,>)
+
+#define TRESPASS() \
+ LOG_ALWAYS_FATAL( \
+ __FILE__ ":" LITERAL_TO_STRING(__LINE__) \
+ " Should not be here.");
+
+} // namespace stagefright
+
+#endif // A_DEBUG_H_
+
diff --git a/media/libstagefright/frameworks/av/include/media/stagefright/foundation/AHandler.h b/media/libstagefright/frameworks/av/include/media/stagefright/foundation/AHandler.h
new file mode 100644
index 000000000..5a5ecc203
--- /dev/null
+++ b/media/libstagefright/frameworks/av/include/media/stagefright/foundation/AHandler.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef A_HANDLER_H_
+
+#define A_HANDLER_H_
+
+#include <media/stagefright/foundation/ALooper.h>
+#include <utils/RefBase.h>
+
+namespace stagefright {
+
+struct AMessage;
+
+struct AHandler : public RefBase {
+ AHandler()
+ : mID(0) {
+ }
+
+ ALooper::handler_id id() const {
+ return mID;
+ }
+
+ sp<ALooper> looper();
+
+protected:
+ virtual void onMessageReceived(const sp<AMessage> &msg) = 0;
+
+private:
+ friend struct ALooperRoster;
+
+ ALooper::handler_id mID;
+
+ void setID(ALooper::handler_id id) {
+ mID = id;
+ }
+
+ DISALLOW_EVIL_CONSTRUCTORS(AHandler);
+};
+
+} // namespace stagefright
+
+#endif // A_HANDLER_H_
diff --git a/media/libstagefright/frameworks/av/include/media/stagefright/foundation/AString.h b/media/libstagefright/frameworks/av/include/media/stagefright/foundation/AString.h
new file mode 100644
index 000000000..8f97ed4e2
--- /dev/null
+++ b/media/libstagefright/frameworks/av/include/media/stagefright/foundation/AString.h
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef A_STRING_H_
+
+#define A_STRING_H_
+
+#include <sys/types.h>
+
+namespace stagefright {
+
+struct AString {
+ AString();
+ AString(const char *s);
+ AString(const char *s, size_t size);
+ AString(const AString &from);
+ AString(const AString &from, size_t offset, size_t n);
+ ~AString();
+
+ AString &operator=(const AString &from);
+ void setTo(const char *s);
+ void setTo(const char *s, size_t size);
+ void setTo(const AString &from, size_t offset, size_t n);
+
+ size_t size() const;
+ const char *c_str() const;
+
+ bool empty() const;
+
+ void clear();
+ void trim();
+ void erase(size_t start, size_t n);
+
+ void append(char c) { append(&c, 1); }
+ void append(const char *s);
+ void append(const char *s, size_t size);
+ void append(const AString &from);
+ void append(const AString &from, size_t offset, size_t n);
+ void append(int x);
+ void append(unsigned x);
+ void append(long x);
+ void append(unsigned long x);
+ void append(long long x);
+ void append(unsigned long long x);
+ void append(float x);
+ void append(double x);
+ void append(void *x);
+
+ void insert(const AString &from, size_t insertionPos);
+ void insert(const char *from, size_t size, size_t insertionPos);
+
+ ssize_t find(const char *substring, size_t start = 0) const;
+
+ size_t hash() const;
+
+ bool operator==(const AString &other) const;
+ bool operator<(const AString &other) const;
+ bool operator>(const AString &other) const;
+
+ int compare(const AString &other) const;
+
+ bool startsWith(const char *prefix) const;
+ bool endsWith(const char *suffix) const;
+
+ void tolower();
+
+private:
+ static const char *kEmptyString;
+
+ char *mData;
+ size_t mSize;
+ size_t mAllocSize;
+
+ void makeMutable();
+};
+
+AString StringPrintf(const char *format, ...);
+
+} // namespace stagefright
+
+#endif // A_STRING_H_
+
diff --git a/media/libstagefright/frameworks/av/include/media/stagefright/foundation/hexdump.h b/media/libstagefright/frameworks/av/include/media/stagefright/foundation/hexdump.h
new file mode 100644
index 000000000..18172f7eb
--- /dev/null
+++ b/media/libstagefright/frameworks/av/include/media/stagefright/foundation/hexdump.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef HEXDUMP_H_
+
+#define HEXDUMP_H_
+
+#include <sys/types.h>
+
+namespace stagefright {
+
+struct AString;
+
+void hexdump(
+ const void *_data, size_t size,
+ size_t indent = 0, AString *appendTo = NULL);
+
+} // namespace stagefright
+
+#endif // HEXDUMP_H_
diff --git a/media/libstagefright/frameworks/av/media/libstagefright/DataSource.cpp b/media/libstagefright/frameworks/av/media/libstagefright/DataSource.cpp
new file mode 100644
index 000000000..c65a69e48
--- /dev/null
+++ b/media/libstagefright/frameworks/av/media/libstagefright/DataSource.cpp
@@ -0,0 +1,219 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "include/AMRExtractor.h"
+
+#if CHROMIUM_AVAILABLE
+#include "include/chromium_http_stub.h"
+#endif
+
+#include "include/MPEG4Extractor.h"
+
+#include <media/stagefright/DataSource.h>
+#include <media/stagefright/MediaErrors.h>
+
+namespace stagefright {
+
+bool DataSource::getUInt16(off64_t offset, uint16_t *x) {
+ *x = 0;
+
+ uint8_t byte[2];
+ if (readAt(offset, byte, 2) != 2) {
+ return false;
+ }
+
+ *x = (byte[0] << 8) | byte[1];
+
+ return true;
+}
+
+bool DataSource::getUInt24(off64_t offset, uint32_t *x) {
+ *x = 0;
+
+ uint8_t byte[3];
+ if (readAt(offset, byte, 3) != 3) {
+ return false;
+ }
+
+ *x = (byte[0] << 16) | (byte[1] << 8) | byte[2];
+
+ return true;
+}
+
+bool DataSource::getUInt32(off64_t offset, uint32_t *x) {
+ *x = 0;
+
+ uint32_t tmp;
+ if (readAt(offset, &tmp, 4) != 4) {
+ return false;
+ }
+
+ *x = ntohl(tmp);
+
+ return true;
+}
+
+bool DataSource::getUInt64(off64_t offset, uint64_t *x) {
+ *x = 0;
+
+ uint64_t tmp;
+ if (readAt(offset, &tmp, 8) != 8) {
+ return false;
+ }
+
+ *x = ntoh64(tmp);
+
+ return true;
+}
+
+status_t DataSource::getSize(off64_t *size) {
+ *size = 0;
+
+ return ERROR_UNSUPPORTED;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+#if 0
+
+Mutex DataSource::gSnifferMutex;
+List<DataSource::SnifferFunc> DataSource::gSniffers;
+
+bool DataSource::sniff(
+ String8 *mimeType, float *confidence, sp<AMessage> *meta) {
+ *mimeType = "";
+ *confidence = 0.0f;
+ meta->clear();
+
+ Mutex::Autolock autoLock(gSnifferMutex);
+ for (List<SnifferFunc>::iterator it = gSniffers.begin();
+ it != gSniffers.end(); ++it) {
+ String8 newMimeType;
+ float newConfidence;
+ sp<AMessage> newMeta;
+ if ((*it)(this, &newMimeType, &newConfidence, &newMeta)) {
+ if (newConfidence > *confidence) {
+ *mimeType = newMimeType;
+ *confidence = newConfidence;
+ *meta = newMeta;
+ }
+ }
+ }
+
+ return *confidence > 0.0;
+}
+
+// static
+void DataSource::RegisterSniffer(SnifferFunc func) {
+ Mutex::Autolock autoLock(gSnifferMutex);
+
+ for (List<SnifferFunc>::iterator it = gSniffers.begin();
+ it != gSniffers.end(); ++it) {
+ if (*it == func) {
+ return;
+ }
+ }
+
+ gSniffers.push_back(func);
+}
+
+// static
+void DataSource::RegisterDefaultSniffers() {
+ RegisterSniffer(SniffMPEG4);
+ RegisterSniffer(SniffMatroska);
+ RegisterSniffer(SniffOgg);
+ RegisterSniffer(SniffWAV);
+ RegisterSniffer(SniffFLAC);
+ RegisterSniffer(SniffAMR);
+ RegisterSniffer(SniffMPEG2TS);
+ RegisterSniffer(SniffMP3);
+ RegisterSniffer(SniffAAC);
+ RegisterSniffer(SniffMPEG2PS);
+ RegisterSniffer(SniffWVM);
+
+ char value[PROPERTY_VALUE_MAX];
+ if (property_get("drm.service.enabled", value, NULL)
+ && (!strcmp(value, "1") || !strcasecmp(value, "true"))) {
+ RegisterSniffer(SniffDRM);
+ }
+}
+
+// static
+sp<DataSource> DataSource::CreateFromURI(
+ const char *uri, const KeyedVector<String8, String8> *headers) {
+ bool isWidevine = !strncasecmp("widevine://", uri, 11);
+
+ sp<DataSource> source;
+ if (!strncasecmp("file://", uri, 7)) {
+ source = new FileSource(uri + 7);
+ } else if (!strncasecmp("http://", uri, 7)
+ || !strncasecmp("https://", uri, 8)
+ || isWidevine) {
+ sp<HTTPBase> httpSource = HTTPBase::Create();
+
+ String8 tmp;
+ if (isWidevine) {
+ tmp = String8("http://");
+ tmp.append(uri + 11);
+
+ uri = tmp.string();
+ }
+
+ if (httpSource->connect(uri, headers) != OK) {
+ return NULL;
+ }
+
+ if (!isWidevine) {
+ String8 cacheConfig;
+ bool disconnectAtHighwatermark;
+ if (headers != NULL) {
+ KeyedVector<String8, String8> copy = *headers;
+ NuCachedSource2::RemoveCacheSpecificHeaders(
+ &copy, &cacheConfig, &disconnectAtHighwatermark);
+ }
+
+ source = new NuCachedSource2(
+ httpSource,
+ cacheConfig.isEmpty() ? NULL : cacheConfig.string());
+ } else {
+ // We do not want that prefetching, caching, datasource wrapper
+ // in the widevine:// case.
+ source = httpSource;
+ }
+
+# if CHROMIUM_AVAILABLE
+ } else if (!strncasecmp("data:", uri, 5)) {
+ source = createDataUriSource(uri);
+#endif
+ } else {
+ // Assume it's a filename.
+ source = new FileSource(uri);
+ }
+
+ if (source == NULL || source->initCheck() != OK) {
+ return NULL;
+ }
+
+ return source;
+}
+
+#endif
+
+String8 DataSource::getMIMEType() const {
+ return String8("application/octet-stream");
+}
+
+} // namespace stagefright
diff --git a/media/libstagefright/frameworks/av/media/libstagefright/ESDS.cpp b/media/libstagefright/frameworks/av/media/libstagefright/ESDS.cpp
new file mode 100644
index 000000000..1e64fd505
--- /dev/null
+++ b/media/libstagefright/frameworks/av/media/libstagefright/ESDS.cpp
@@ -0,0 +1,244 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#undef LOG_TAG
+#define LOG_TAG "ESDS"
+#include <utils/Log.h>
+
+#include "include/ESDS.h"
+
+#include <string.h>
+
+namespace stagefright {
+
+ESDS::ESDS(const void *data, size_t size)
+ : mData(new (mozilla::fallible) uint8_t[size]),
+ mSize(size),
+ mInitCheck(NO_INIT),
+ mDecoderSpecificOffset(0),
+ mDecoderSpecificLength(0),
+ mObjectTypeIndication(0) {
+ if (!mData) {
+ mInitCheck = ERROR_BUFFER_TOO_SMALL;
+ return;
+ }
+
+ memcpy(mData, data, size);
+
+ mInitCheck = parse();
+}
+
+ESDS::~ESDS() {
+ delete[] mData;
+ mData = NULL;
+}
+
+status_t ESDS::InitCheck() const {
+ return mInitCheck;
+}
+
+status_t ESDS::getObjectTypeIndication(uint8_t *objectTypeIndication) const {
+ if (mInitCheck != OK) {
+ return mInitCheck;
+ }
+
+ *objectTypeIndication = mObjectTypeIndication;
+
+ return OK;
+}
+
+status_t ESDS::getCodecSpecificInfo(const void **data, size_t *size) const {
+ if (mInitCheck != OK) {
+ return mInitCheck;
+ }
+
+ *data = &mData[mDecoderSpecificOffset];
+ *size = mDecoderSpecificLength;
+
+ return OK;
+}
+
+status_t ESDS::skipDescriptorHeader(
+ size_t offset, size_t size,
+ uint8_t *tag, size_t *data_offset, size_t *data_size) const {
+ if (size == 0) {
+ return ERROR_MALFORMED;
+ }
+
+ *tag = mData[offset++];
+ --size;
+
+ *data_size = 0;
+ bool more;
+ do {
+ if (size == 0) {
+ return ERROR_MALFORMED;
+ }
+
+ uint8_t x = mData[offset++];
+ --size;
+
+ *data_size = (*data_size << 7) | (x & 0x7f);
+ more = (x & 0x80) != 0;
+ }
+ while (more);
+
+ ALOGV("tag=0x%02x data_size=%d", *tag, *data_size);
+
+ if (*data_size > size) {
+ return ERROR_MALFORMED;
+ }
+
+ *data_offset = offset;
+
+ return OK;
+}
+
+status_t ESDS::parse() {
+ uint8_t tag;
+ size_t data_offset;
+ size_t data_size;
+ status_t err =
+ skipDescriptorHeader(0, mSize, &tag, &data_offset, &data_size);
+
+ if (err != OK) {
+ return err;
+ }
+
+ if (tag != kTag_ESDescriptor) {
+ return ERROR_MALFORMED;
+ }
+
+ return parseESDescriptor(data_offset, data_size);
+}
+
+status_t ESDS::parseESDescriptor(size_t offset, size_t size) {
+ if (size < 3) {
+ return ERROR_MALFORMED;
+ }
+
+ offset += 2; // skip ES_ID
+ size -= 2;
+
+ unsigned streamDependenceFlag = mData[offset] & 0x80;
+ unsigned URL_Flag = mData[offset] & 0x40;
+ unsigned OCRstreamFlag = mData[offset] & 0x20;
+
+ ++offset;
+ --size;
+
+ if (streamDependenceFlag) {
+ offset += 2;
+ if (size <= 2) {
+ return ERROR_MALFORMED;
+ }
+ size -= 2;
+ }
+
+ if (URL_Flag) {
+ if (offset >= size) {
+ return ERROR_MALFORMED;
+ }
+ unsigned URLlength = mData[offset];
+ offset += URLlength + 1;
+ if (size <= URLlength + 1) {
+ return ERROR_MALFORMED;
+ }
+ size -= URLlength + 1;
+ }
+
+ if (OCRstreamFlag) {
+ offset += 2;
+ if (size <= 2) {
+ return ERROR_MALFORMED;
+ }
+ size -= 2;
+
+ if ((offset >= size || mData[offset] != kTag_DecoderConfigDescriptor)
+ && offset >= 2
+ && offset - 2 < size
+ && mData[offset - 2] == kTag_DecoderConfigDescriptor) {
+ // Content found "in the wild" had OCRstreamFlag set but was
+ // missing OCR_ES_Id, the decoder config descriptor immediately
+ // followed instead.
+ offset -= 2;
+ size += 2;
+
+ ALOGW("Found malformed 'esds' atom, ignoring missing OCR_ES_Id.");
+ }
+ }
+
+ if (offset >= size) {
+ return ERROR_MALFORMED;
+ }
+
+ uint8_t tag;
+ size_t sub_offset, sub_size;
+ status_t err = skipDescriptorHeader(
+ offset, size, &tag, &sub_offset, &sub_size);
+
+ if (err != OK) {
+ return err;
+ }
+
+ if (tag != kTag_DecoderConfigDescriptor) {
+ return ERROR_MALFORMED;
+ }
+
+ err = parseDecoderConfigDescriptor(sub_offset, sub_size);
+
+ return err;
+}
+
+status_t ESDS::parseDecoderConfigDescriptor(size_t offset, size_t size) {
+ if (size < 13) {
+ return ERROR_MALFORMED;
+ }
+
+ mObjectTypeIndication = mData[offset];
+
+ offset += 13;
+ size -= 13;
+
+ if (size == 0) {
+ mDecoderSpecificOffset = 0;
+ mDecoderSpecificLength = 0;
+ return OK;
+ }
+
+ uint8_t tag;
+ size_t sub_offset, sub_size;
+ status_t err = skipDescriptorHeader(
+ offset, size, &tag, &sub_offset, &sub_size);
+
+ if (err != OK) {
+ return err;
+ }
+
+ if (tag != kTag_DecoderSpecificInfo) {
+ return ERROR_MALFORMED;
+ }
+
+ mDecoderSpecificOffset = sub_offset;
+ mDecoderSpecificLength = sub_size;
+
+ return OK;
+}
+
+} // namespace stagefright
+
+#undef LOG_TAG
diff --git a/media/libstagefright/frameworks/av/media/libstagefright/MPEG4Extractor.cpp b/media/libstagefright/frameworks/av/media/libstagefright/MPEG4Extractor.cpp
new file mode 100644
index 000000000..e881e0262
--- /dev/null
+++ b/media/libstagefright/frameworks/av/media/libstagefright/MPEG4Extractor.cpp
@@ -0,0 +1,2972 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#undef LOG_TAG
+#define LOG_TAG "MPEG4Extractor"
+#include <utils/Log.h>
+
+#include "include/MPEG4Extractor.h"
+#include "include/SampleTable.h"
+#include "include/ESDS.h"
+
+#include <algorithm>
+#include <ctype.h>
+#include <limits>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <type_traits>
+
+#include <media/stagefright/foundation/ABitReader.h>
+#include <media/stagefright/foundation/ABuffer.h>
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/foundation/AMessage.h>
+#include <media/stagefright/MediaBuffer.h>
+#include <media/stagefright/MediaDefs.h>
+#include <media/stagefright/MediaSource.h>
+#include <media/stagefright/MetaData.h>
+
+static const uint32_t kMAX_ALLOCATION =
+ (SIZE_MAX < INT32_MAX ? SIZE_MAX : INT32_MAX) - 128;
+
+namespace stagefright {
+
+static const int64_t OVERFLOW_ERROR = -INT64_MAX;
+
+// Calculate units*1,000,000/hz, trying to avoid overflow.
+// Return OVERFLOW_ERROR in case of unavoidable overflow, or div by hz==0.
+int64_t unitsToUs(int64_t units, int64_t hz) {
+ if (hz == 0) {
+ return OVERFLOW_ERROR;
+ }
+ const int64_t MAX_S = INT64_MAX / 1000000;
+ if (std::abs(units) <= MAX_S) {
+ return units * 1000000 / hz;
+ }
+ // Hard case, avoid overflow-inducing 'units*1M' by calculating:
+ // (units / hz) * 1M + ((units % hz) * 1M) / hz.
+ // ^-- ^-- ^-- overflows still possible
+ int64_t units_div_hz = units / hz;
+ int64_t units_rem_hz = units % hz;
+ if (std::abs(units_div_hz) > MAX_S || std::abs(units_rem_hz) > MAX_S) {
+ return OVERFLOW_ERROR;
+ }
+ int64_t quot_us = units_div_hz * 1000000;
+ int64_t rem_us = (units_rem_hz * 1000000) / hz;
+ if (std::abs(quot_us) > INT64_MAX - std::abs(rem_us)) {
+ return OVERFLOW_ERROR;
+ }
+ return quot_us + rem_us;
+}
+
+class MPEG4Source : public MediaSource {
+public:
+ MPEG4Source(const sp<MetaData> &format,
+ uint32_t timeScale,
+ const sp<SampleTable> &sampleTable);
+
+ sp<MetaData> getFormat() override;
+
+ nsTArray<Indice> exportIndex() override;
+
+protected:
+ virtual ~MPEG4Source();
+
+private:
+ sp<MetaData> mFormat;
+ uint32_t mTimescale;
+ sp<SampleTable> mSampleTable;
+
+ MPEG4Source(const MPEG4Source &) = delete;
+ MPEG4Source &operator=(const MPEG4Source &) = delete;
+};
+
+// This custom data source wraps an existing one and satisfies requests
+// falling entirely within a cached range from the cache while forwarding
+// all remaining requests to the wrapped datasource.
+// This is used to cache the full sampletable metadata for a single track,
+// possibly wrapping multiple times to cover all tracks, i.e.
+// Each MPEG4DataSource caches the sampletable metadata for a single track.
+
+struct MPEG4DataSource : public DataSource {
+ MPEG4DataSource(const sp<DataSource> &source);
+
+ status_t initCheck() const override;
+ ssize_t readAt(off64_t offset, void *data, size_t size) override;
+ status_t getSize(off64_t *size) override;
+ uint32_t flags() override;
+
+ status_t setCachedRange(off64_t offset, size_t size);
+
+protected:
+ virtual ~MPEG4DataSource();
+
+private:
+ Mutex mLock;
+
+ sp<DataSource> mSource;
+ off64_t mCachedOffset;
+ size_t mCachedSize;
+ uint8_t *mCache;
+
+ void clearCache();
+
+ MPEG4DataSource(const MPEG4DataSource &) = delete;
+ MPEG4DataSource &operator=(const MPEG4DataSource &) = delete;
+};
+
+MPEG4DataSource::MPEG4DataSource(const sp<DataSource> &source)
+ : mSource(source),
+ mCachedOffset(0),
+ mCachedSize(0),
+ mCache(NULL) {
+}
+
+MPEG4DataSource::~MPEG4DataSource() {
+ clearCache();
+}
+
+void MPEG4DataSource::clearCache() {
+ if (mCache) {
+ free(mCache);
+ mCache = NULL;
+ }
+
+ mCachedOffset = 0;
+ mCachedSize = 0;
+}
+
+status_t MPEG4DataSource::initCheck() const {
+ return mSource->initCheck();
+}
+
+ssize_t MPEG4DataSource::readAt(off64_t offset, void *data, size_t size) {
+ Mutex::Autolock autoLock(mLock);
+
+ if (offset >= mCachedOffset
+ && offset + size <= mCachedOffset + mCachedSize) {
+ memcpy(data, &mCache[offset - mCachedOffset], size);
+ return size;
+ }
+
+ return mSource->readAt(offset, data, size);
+}
+
+status_t MPEG4DataSource::getSize(off64_t *size) {
+ return mSource->getSize(size);
+}
+
+uint32_t MPEG4DataSource::flags() {
+ return mSource->flags();
+}
+
+status_t MPEG4DataSource::setCachedRange(off64_t offset, size_t size) {
+ Mutex::Autolock autoLock(mLock);
+
+ clearCache();
+
+ mCache = (uint8_t *)malloc(size);
+
+ if (mCache == NULL) {
+ return -ENOMEM;
+ }
+
+ mCachedOffset = offset;
+ mCachedSize = size;
+
+ ssize_t err = mSource->readAt(mCachedOffset, mCache, mCachedSize);
+
+ if (err < (ssize_t)size) {
+ clearCache();
+
+ return ERROR_IO;
+ }
+
+ return OK;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+static void hexdump(const void *_data, size_t size) {
+ const uint8_t *data = (const uint8_t *)_data;
+ size_t offset = 0;
+ while (offset < size) {
+ printf("0x%04x ", offset);
+
+ size_t n = size - offset;
+ if (n > 16) {
+ n = 16;
+ }
+
+ for (size_t i = 0; i < 16; ++i) {
+ if (i == 8) {
+ printf(" ");
+ }
+
+ if (offset + i < size) {
+ printf("%02x ", data[offset + i]);
+ } else {
+ printf(" ");
+ }
+ }
+
+ printf(" ");
+
+ for (size_t i = 0; i < n; ++i) {
+ if (isprint(data[offset + i])) {
+ printf("%c", data[offset + i]);
+ } else {
+ printf(".");
+ }
+ }
+
+ printf("\n");
+
+ offset += 16;
+ }
+}
+
+static const char *FourCC2MIME(uint32_t fourcc) {
+ switch (fourcc) {
+ case FOURCC('m', 'p', '4', 'a'):
+ return MEDIA_MIMETYPE_AUDIO_AAC;
+
+ case FOURCC('s', 'a', 'm', 'r'):
+ return MEDIA_MIMETYPE_AUDIO_AMR_NB;
+
+ case FOURCC('s', 'a', 'w', 'b'):
+ return MEDIA_MIMETYPE_AUDIO_AMR_WB;
+
+ case FOURCC('.', 'm', 'p', '3'):
+ return MEDIA_MIMETYPE_AUDIO_MPEG;
+
+ case FOURCC('m', 'p', '4', 'v'):
+ return MEDIA_MIMETYPE_VIDEO_MPEG4;
+
+ case FOURCC('s', '2', '6', '3'):
+ case FOURCC('h', '2', '6', '3'):
+ case FOURCC('H', '2', '6', '3'):
+ return MEDIA_MIMETYPE_VIDEO_H263;
+
+ case FOURCC('a', 'v', 'c', '1'):
+ case FOURCC('a', 'v', 'c', '3'):
+ return MEDIA_MIMETYPE_VIDEO_AVC;
+
+ case FOURCC('V', 'P', '6', 'F'):
+ return MEDIA_MIMETYPE_VIDEO_VP6;
+
+ default:
+ ALOGE("Unknown MIME type %08x", fourcc);
+ return NULL;
+ }
+}
+
+static bool AdjustChannelsAndRate(uint32_t fourcc, uint32_t *channels, uint32_t *rate) {
+ const char* mime = FourCC2MIME(fourcc);
+ if (!mime) {
+ return false;
+ }
+ if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AMR_NB, mime)) {
+ // AMR NB audio is always mono, 8kHz
+ *channels = 1;
+ *rate = 8000;
+ return true;
+ } else if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AMR_WB, mime)) {
+ // AMR WB audio is always mono, 16kHz
+ *channels = 1;
+ *rate = 16000;
+ return true;
+ }
+ return false;
+}
+
+MPEG4Extractor::MPEG4Extractor(const sp<DataSource> &source)
+ : mSidxDuration(0),
+ mDataSource(source),
+ mInitCheck(NO_INIT),
+ mHasVideo(false),
+ mHeaderTimescale(0),
+ mFirstTrack(NULL),
+ mLastTrack(NULL),
+ mFileMetaData(new MetaData),
+ mFirstSINF(NULL),
+ mIsDrm(false),
+ mDrmScheme(0)
+{
+}
+
+MPEG4Extractor::~MPEG4Extractor() {
+ Track *track = mFirstTrack;
+ while (track) {
+ Track *next = track->next;
+
+ delete track;
+ track = next;
+ }
+ mFirstTrack = mLastTrack = NULL;
+
+ SINF *sinf = mFirstSINF;
+ while (sinf) {
+ SINF *next = sinf->next;
+ delete[] sinf->IPMPData;
+ delete sinf;
+ sinf = next;
+ }
+ mFirstSINF = NULL;
+
+ for (size_t i = 0; i < mPssh.Length(); i++) {
+ delete [] mPssh[i].data;
+ }
+}
+
+uint32_t MPEG4Extractor::flags() const {
+ return CAN_PAUSE | CAN_SEEK_BACKWARD | CAN_SEEK_FORWARD | CAN_SEEK;
+}
+
+sp<MetaData> MPEG4Extractor::getMetaData() {
+ status_t err;
+ if ((err = readMetaData()) != OK) {
+ return NULL;
+ }
+
+ return mFileMetaData;
+}
+
+size_t MPEG4Extractor::countTracks() {
+ status_t err;
+ if ((err = readMetaData()) != OK) {
+ ALOGV("MPEG4Extractor::countTracks: no tracks");
+ return 0;
+ }
+
+ size_t n = 0;
+ Track *track = mFirstTrack;
+ while (track) {
+ ++n;
+ track = track->next;
+ }
+
+ ALOGV("MPEG4Extractor::countTracks: %d tracks", n);
+ return n;
+}
+
+sp<MetaData> MPEG4Extractor::getTrackMetaData(
+ size_t index, uint32_t flags) {
+ status_t err;
+ if ((err = readMetaData()) != OK) {
+ return NULL;
+ }
+
+ Track *track = mFirstTrack;
+ while (index > 0) {
+ if (track == NULL) {
+ return NULL;
+ }
+
+ track = track->next;
+ --index;
+ }
+
+ if (track == NULL) {
+ return NULL;
+ }
+
+ return track->meta;
+}
+
+static void MakeFourCCString(uint32_t x, char *s) {
+ s[0] = x >> 24;
+ s[1] = (x >> 16) & 0xff;
+ s[2] = (x >> 8) & 0xff;
+ s[3] = x & 0xff;
+ s[4] = '\0';
+}
+
+status_t MPEG4Extractor::readMetaData() {
+ if (mInitCheck != NO_INIT) {
+ return mInitCheck;
+ }
+
+ off64_t offset = 0;
+ status_t err = NO_INIT;
+ while (!mFirstTrack) {
+ err = parseChunk(&offset, 0);
+ // The parseChunk function returns UNKNOWN_ERROR to skip
+ // some boxes we don't want to handle. Filter that error
+ // code but return others so e.g. I/O errors propagate.
+ if (err != OK && err != (status_t) UNKNOWN_ERROR) {
+ ALOGW("Error %d parsing chunck at offset %lld looking for first track",
+ err, (long long)offset);
+ break;
+ }
+ }
+
+ if (mInitCheck == OK) {
+ if (mHasVideo) {
+ mFileMetaData->setCString(
+ kKeyMIMEType, MEDIA_MIMETYPE_CONTAINER_MPEG4);
+ } else {
+ mFileMetaData->setCString(kKeyMIMEType, "audio/mp4");
+ }
+
+ mInitCheck = OK;
+ } else {
+ mInitCheck = err;
+ }
+
+ CHECK_NE(err, (status_t)NO_INIT);
+
+ // copy pssh data into file metadata
+ uint64_t psshsize = 0;
+ for (size_t i = 0; i < mPssh.Length(); i++) {
+ psshsize += 20 + mPssh[i].datalen;
+ if (mPssh[i].datalen > kMAX_ALLOCATION - 20 ||
+ psshsize > kMAX_ALLOCATION) {
+ return ERROR_MALFORMED;
+ }
+ }
+ if (psshsize) {
+ char *buf = (char*)malloc(psshsize);
+ if (!buf) {
+ return -ENOMEM;
+ }
+ char *ptr = buf;
+ for (size_t i = 0; i < mPssh.Length(); i++) {
+ memcpy(ptr, mPssh[i].uuid, 20); // uuid + length
+ memcpy(ptr + 20, mPssh[i].data, mPssh[i].datalen);
+ ptr += (20 + mPssh[i].datalen);
+ }
+ mFileMetaData->setData(kKeyPssh, 'pssh', buf, psshsize);
+ free(buf);
+ }
+ return mInitCheck;
+}
+
+char* MPEG4Extractor::getDrmTrackInfo(size_t trackID, int *len) {
+ if (mFirstSINF == NULL) {
+ return NULL;
+ }
+
+ SINF *sinf = mFirstSINF;
+ while (sinf && (trackID != sinf->trackID)) {
+ sinf = sinf->next;
+ }
+
+ if (sinf == NULL) {
+ return NULL;
+ }
+
+ *len = sinf->len;
+ return sinf->IPMPData;
+}
+
+// Reads an encoded integer 7 bits at a time until it encounters the high bit clear.
+static int32_t readSize(off64_t offset,
+ const sp<DataSource> DataSource, uint8_t *numOfBytes) {
+ uint32_t size = 0;
+ uint8_t data;
+ bool moreData = true;
+ *numOfBytes = 0;
+
+ while (moreData) {
+ if (DataSource->readAt(offset, &data, 1) < 1) {
+ return -1;
+ }
+ offset ++;
+ moreData = (data >= 128) ? true : false;
+ size = (size << 7) | (data & 0x7f); // Take last 7 bits
+ (*numOfBytes) ++;
+ }
+
+ return size;
+}
+
+status_t MPEG4Extractor::parseDrmSINF(off64_t *offset, off64_t data_offset) {
+ uint8_t updateIdTag;
+ if (mDataSource->readAt(data_offset, &updateIdTag, 1) < 1) {
+ return ERROR_IO;
+ }
+ data_offset ++;
+
+ if (0x01/*OBJECT_DESCRIPTOR_UPDATE_ID_TAG*/ != updateIdTag) {
+ return ERROR_MALFORMED;
+ }
+
+ uint8_t numOfBytes;
+ int32_t size = readSize(data_offset, mDataSource, &numOfBytes);
+ if (size < 0) {
+ return ERROR_IO;
+ }
+ int32_t classSize = size;
+ data_offset += numOfBytes;
+
+ while(size >= 11 ) {
+ uint8_t descriptorTag;
+ if (mDataSource->readAt(data_offset, &descriptorTag, 1) < 1) {
+ return ERROR_IO;
+ }
+ data_offset ++;
+
+ if (0x11/*OBJECT_DESCRIPTOR_ID_TAG*/ != descriptorTag) {
+ return ERROR_MALFORMED;
+ }
+
+ uint8_t buffer[8];
+ //ObjectDescriptorID and ObjectDescriptor url flag
+ if (mDataSource->readAt(data_offset, buffer, 2) < 2) {
+ return ERROR_IO;
+ }
+ data_offset += 2;
+
+ if ((buffer[1] >> 5) & 0x0001) { //url flag is set
+ return ERROR_MALFORMED;
+ }
+
+ if (mDataSource->readAt(data_offset, buffer, 8) < 8) {
+ return ERROR_IO;
+ }
+ data_offset += 8;
+
+ if ((0x0F/*ES_ID_REF_TAG*/ != buffer[1])
+ || ( 0x0A/*IPMP_DESCRIPTOR_POINTER_ID_TAG*/ != buffer[5])) {
+ return ERROR_MALFORMED;
+ }
+
+ SINF *sinf = new SINF;
+ sinf->trackID = U16_AT(&buffer[3]);
+ sinf->IPMPDescriptorID = buffer[7];
+ sinf->next = mFirstSINF;
+ mFirstSINF = sinf;
+
+ size -= (8 + 2 + 1);
+ }
+
+ if (size != 0) {
+ return ERROR_MALFORMED;
+ }
+
+ if (mDataSource->readAt(data_offset, &updateIdTag, 1) < 1) {
+ return ERROR_IO;
+ }
+ data_offset ++;
+
+ if(0x05/*IPMP_DESCRIPTOR_UPDATE_ID_TAG*/ != updateIdTag) {
+ return ERROR_MALFORMED;
+ }
+
+ size = readSize(data_offset, mDataSource, &numOfBytes);
+ if (size < 0) {
+ return ERROR_IO;
+ }
+ classSize = size;
+ data_offset += numOfBytes;
+
+ while (size > 0) {
+ uint8_t tag;
+ int32_t dataLen;
+ if (mDataSource->readAt(data_offset, &tag, 1) < 1) {
+ return ERROR_IO;
+ }
+ data_offset ++;
+
+ if (0x0B/*IPMP_DESCRIPTOR_ID_TAG*/ == tag) {
+ uint8_t id;
+ dataLen = readSize(data_offset, mDataSource, &numOfBytes);
+ if (dataLen < 0) {
+ return ERROR_IO;
+ } else if (dataLen < 4) {
+ return ERROR_MALFORMED;
+ }
+ data_offset += numOfBytes;
+
+ if (mDataSource->readAt(data_offset, &id, 1) < 1) {
+ return ERROR_IO;
+ }
+ data_offset ++;
+
+ SINF *sinf = mFirstSINF;
+ while (sinf && (sinf->IPMPDescriptorID != id)) {
+ sinf = sinf->next;
+ }
+ if (sinf == NULL) {
+ return ERROR_MALFORMED;
+ }
+ sinf->len = dataLen - 3;
+ sinf->IPMPData = new (fallible) char[sinf->len];
+ if (!sinf->IPMPData) {
+ return -ENOMEM;
+ }
+
+ if (mDataSource->readAt(data_offset + 2, sinf->IPMPData, sinf->len) < sinf->len) {
+ return ERROR_IO;
+ }
+ data_offset += sinf->len;
+
+ size -= (dataLen + numOfBytes + 1);
+ }
+ }
+
+ if (size != 0) {
+ return ERROR_MALFORMED;
+ }
+
+ return UNKNOWN_ERROR; // Return a dummy error.
+}
+
+struct PathAdder {
+ PathAdder(nsTArray<uint32_t> *path, uint32_t chunkType)
+ : mPath(path) {
+ mPath->AppendElement(chunkType);
+ }
+
+ ~PathAdder() {
+ mPath->RemoveElementAt(mPath->Length() - 1);
+ }
+
+private:
+ nsTArray<uint32_t> *mPath;
+
+ PathAdder(const PathAdder &);
+ PathAdder &operator=(const PathAdder &);
+};
+
+static bool underMetaDataPath(const nsTArray<uint32_t> &path) {
+ return path.Length() >= 5
+ && path[0] == FOURCC('m', 'o', 'o', 'v')
+ && path[1] == FOURCC('u', 'd', 't', 'a')
+ && path[2] == FOURCC('m', 'e', 't', 'a')
+ && path[3] == FOURCC('i', 'l', 's', 't');
+}
+
+static bool ValidInputSize(int32_t size) {
+ // Reject compressed samples larger than an uncompressed UHD
+ // frame. This is a reasonable cut-off for a lossy codec,
+ // combined with the current Firefox limit to 5k video.
+ return (size > 0 && size <= 4 * (1920 * 1080) * 3 / 2);
+}
+
+status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) {
+ ALOGV("entering parseChunk %lld/%d", *offset, depth);
+ uint32_t hdr[2];
+ if (mDataSource->readAt(*offset, hdr, 4) < 4) {
+ return ERROR_IO;
+ }
+ if (!hdr[0]) {
+ *offset += 4;
+ return OK;
+ }
+ if (mDataSource->readAt(*offset + 4, hdr + 1, 4) < 4) {
+ return ERROR_IO;
+ }
+ uint64_t chunk_size = ntohl(hdr[0]);
+ uint32_t chunk_type = ntohl(hdr[1]);
+ off64_t data_offset = *offset + 8;
+
+ if (chunk_size == 1) {
+ if (mDataSource->readAt(*offset + 8, &chunk_size, 8) < 8) {
+ return ERROR_IO;
+ }
+ chunk_size = ntoh64(chunk_size);
+ data_offset += 8;
+
+ if (chunk_size < 16) {
+ // The smallest valid chunk is 16 bytes long in this case.
+ return ERROR_MALFORMED;
+ }
+ } else if (chunk_size < 8) {
+ // The smallest valid chunk is 8 bytes long.
+ return ERROR_MALFORMED;
+ }
+
+ if (chunk_size >= kMAX_ALLOCATION) {
+ // Could cause an overflow later. Abort.
+ return ERROR_MALFORMED;
+ }
+
+ char chunk[5];
+ MakeFourCCString(chunk_type, chunk);
+ ALOGV("chunk: %s @ %lld, %d", chunk, *offset, depth);
+
+#if 0
+ static const char kWhitespace[] = " ";
+ const char *indent = &kWhitespace[sizeof(kWhitespace) - 1 - 2 * depth];
+ printf("%sfound chunk '%s' of size %lld\n", indent, chunk, chunk_size);
+
+ char buffer[256];
+ size_t n = chunk_size;
+ if (n > sizeof(buffer)) {
+ n = sizeof(buffer);
+ }
+ if (mDataSource->readAt(*offset, buffer, n)
+ < (ssize_t)n) {
+ return ERROR_IO;
+ }
+
+ hexdump(buffer, n);
+#endif
+
+ PathAdder autoAdder(&mPath, chunk_type);
+
+ off64_t chunk_data_size = *offset + chunk_size - data_offset;
+
+ if (chunk_type != FOURCC('c', 'p', 'r', 't')
+ && chunk_type != FOURCC('c', 'o', 'v', 'r')
+ && mPath.Length() == 5 && underMetaDataPath(mPath)) {
+ off64_t stop_offset = *offset + chunk_size;
+ *offset = data_offset;
+ while (*offset < stop_offset) {
+ status_t err = parseChunk(offset, depth + 1);
+ if (err != OK) {
+ return err;
+ }
+ }
+
+ if (*offset != stop_offset) {
+ return ERROR_MALFORMED;
+ }
+
+ return OK;
+ }
+
+ switch(chunk_type) {
+ case FOURCC('m', 'o', 'o', 'v'):
+ case FOURCC('t', 'r', 'a', 'k'):
+ case FOURCC('m', 'd', 'i', 'a'):
+ case FOURCC('m', 'i', 'n', 'f'):
+ case FOURCC('d', 'i', 'n', 'f'):
+ case FOURCC('s', 't', 'b', 'l'):
+ case FOURCC('m', 'v', 'e', 'x'):
+ case FOURCC('m', 'o', 'o', 'f'):
+ case FOURCC('t', 'r', 'a', 'f'):
+ case FOURCC('m', 'f', 'r', 'a'):
+ case FOURCC('u', 'd', 't', 'a'):
+ case FOURCC('i', 'l', 's', 't'):
+ case FOURCC('s', 'i', 'n', 'f'):
+ case FOURCC('s', 'c', 'h', 'i'):
+ case FOURCC('e', 'd', 't', 's'):
+ {
+ if (chunk_type == FOURCC('s', 't', 'b', 'l')) {
+ ALOGV("sampleTable chunk is %d bytes long.", (size_t)chunk_size);
+
+ if (mDataSource->flags()
+ & (DataSource::kWantsPrefetching
+ | DataSource::kIsCachingDataSource)) {
+ sp<MPEG4DataSource> cachedSource =
+ new MPEG4DataSource(mDataSource);
+
+ if (cachedSource->setCachedRange(*offset, chunk_size) == OK) {
+ mDataSource = cachedSource;
+ }
+ }
+
+ if (!mLastTrack) {
+ return ERROR_MALFORMED;
+ }
+ mLastTrack->sampleTable = new SampleTable(mDataSource);
+ }
+
+ bool isTrack = false;
+ if (chunk_type == FOURCC('t', 'r', 'a', 'k')) {
+ isTrack = true;
+
+ Track *track = new Track;
+ track->next = NULL;
+ if (mLastTrack) {
+ mLastTrack->next = track;
+ } else {
+ mFirstTrack = track;
+ }
+ mLastTrack = track;
+
+ track->meta = new MetaData;
+ track->includes_expensive_metadata = false;
+ track->skipTrack = false;
+ track->timescale = 0;
+ track->empty_duration = 0;
+ track->segment_duration = 0;
+ track->media_time = 0;
+ track->meta->setCString(kKeyMIMEType, "application/octet-stream");
+ }
+
+ off64_t stop_offset = *offset + chunk_size;
+ *offset = data_offset;
+ while (*offset < stop_offset) {
+ status_t err = parseChunk(offset, depth + 1);
+ if (err != OK) {
+ return err;
+ }
+ }
+
+ if (*offset != stop_offset) {
+ return ERROR_MALFORMED;
+ }
+
+ if (isTrack) {
+ if (mLastTrack->skipTrack) {
+ Track *cur = mFirstTrack;
+
+ if (cur == mLastTrack) {
+ delete cur;
+ mFirstTrack = mLastTrack = NULL;
+ } else {
+ while (cur && cur->next != mLastTrack) {
+ cur = cur->next;
+ }
+ cur->next = NULL;
+ delete mLastTrack;
+ mLastTrack = cur;
+ }
+
+ return OK;
+ }
+
+ status_t err = verifyTrack(mLastTrack);
+
+ if (err != OK) {
+ return err;
+ }
+ } else if (chunk_type == FOURCC('m', 'o', 'o', 'v')) {
+ mInitCheck = OK;
+
+ if (!mIsDrm) {
+ return UNKNOWN_ERROR; // Return a dummy error.
+ } else {
+ return OK;
+ }
+ }
+ break;
+ }
+
+ case FOURCC('e', 'l', 's', 't'):
+ {
+ // See 14496-12 8.6.6
+ uint8_t version;
+ if (mDataSource->readAt(data_offset, &version, 1) < 1) {
+ return ERROR_IO;
+ }
+
+ uint32_t entry_count;
+ if (!mDataSource->getUInt32(data_offset + 4, &entry_count)) {
+ return ERROR_IO;
+ }
+
+ off64_t entriesoffset = data_offset + 8;
+ bool nonEmptyCount = false;
+ for (uint32_t i = 0; i < entry_count; i++) {
+ if (mHeaderTimescale == 0) {
+ ALOGW("ignoring edit list because timescale is 0");
+ break;
+ }
+ if (entriesoffset - data_offset > chunk_size) {
+ ALOGW("invalid edit list size");
+ break;
+ }
+ uint64_t segment_duration;
+ int64_t media_time;
+ if (version == 1) {
+ if (!mDataSource->getUInt64(entriesoffset, &segment_duration) ||
+ !mDataSource->getUInt64(entriesoffset + 8, (uint64_t*)&media_time)) {
+ return ERROR_IO;
+ }
+ entriesoffset += 16;
+ } else if (version == 0) {
+ uint32_t sd;
+ int32_t mt;
+ if (!mDataSource->getUInt32(entriesoffset, &sd) ||
+ !mDataSource->getUInt32(entriesoffset + 4, (uint32_t*)&mt)) {
+ return ERROR_IO;
+ }
+ entriesoffset += 8;
+ segment_duration = sd;
+ media_time = mt;
+ } else {
+ return ERROR_IO;
+ }
+ entriesoffset += 4; // ignore media_rate_integer and media_rate_fraction.
+ if (media_time == -1 && i) {
+ ALOGW("ignoring invalid empty edit", i);
+ break;
+ } else if (media_time == -1) {
+ // Starting offsets for tracks (streams) are represented by an initial empty edit.
+ if (!mLastTrack) {
+ return ERROR_MALFORMED;
+ }
+ mLastTrack->empty_duration = segment_duration;
+ continue;
+ } else if (nonEmptyCount) {
+ // we only support a single non-empty entry at the moment, for gapless playback
+ ALOGW("multiple edit list entries, A/V sync will be wrong");
+ break;
+ } else {
+ nonEmptyCount = true;
+ }
+
+ if (!mLastTrack) {
+ return ERROR_MALFORMED;
+ }
+ mLastTrack->segment_duration = segment_duration;
+ mLastTrack->media_time = media_time;
+ }
+ storeEditList();
+ *offset += chunk_size;
+ break;
+ }
+
+ case FOURCC('f', 'r', 'm', 'a'):
+ {
+ uint32_t original_fourcc;
+ if (mDataSource->readAt(data_offset, &original_fourcc, 4) < 4) {
+ return ERROR_IO;
+ }
+ original_fourcc = ntohl(original_fourcc);
+ ALOGV("read original format: %d", original_fourcc);
+ if (!mLastTrack) {
+ return ERROR_MALFORMED;
+ }
+ const char* mime = FourCC2MIME(original_fourcc);
+ if (!mime) {
+ return ERROR_UNSUPPORTED;
+ }
+ mLastTrack->meta->setCString(kKeyMIMEType, mime);
+ uint32_t num_channels = 0;
+ uint32_t sample_rate = 0;
+ if (AdjustChannelsAndRate(original_fourcc, &num_channels, &sample_rate)) {
+ mLastTrack->meta->setInt32(kKeyChannelCount, num_channels);
+ mLastTrack->meta->setInt32(kKeySampleRate, sample_rate);
+ }
+ *offset += chunk_size;
+ break;
+ }
+
+ case FOURCC('s', 'c', 'h', 'm'):
+ {
+ if (!mDataSource->getUInt32(data_offset, &mDrmScheme)) {
+ return ERROR_IO;
+ }
+
+ *offset += chunk_size;
+ break;
+ }
+
+ case FOURCC('t', 'e', 'n', 'c'):
+ {
+ if (chunk_size < 32) {
+ return ERROR_MALFORMED;
+ }
+
+ // tenc box contains 1 byte version, 3 byte flags, 3 byte default algorithm id, one byte
+ // default IV size, 16 bytes default KeyID
+ // (ISO 23001-7)
+ char buf[4];
+ memset(buf, 0, 4);
+ if (mDataSource->readAt(data_offset + 4, buf + 1, 3) < 3) {
+ return ERROR_IO;
+ }
+ uint32_t defaultAlgorithmId = ntohl(*((int32_t*)buf));
+ if (defaultAlgorithmId > 1) {
+ // only 0 (clear) and 1 (AES-128) are valid
+ return ERROR_MALFORMED;
+ }
+
+ memset(buf, 0, 4);
+ if (mDataSource->readAt(data_offset + 7, buf + 3, 1) < 1) {
+ return ERROR_IO;
+ }
+ uint32_t defaultIVSize = ntohl(*((int32_t*)buf));
+
+ if ((defaultAlgorithmId == 0 && defaultIVSize != 0) ||
+ (defaultAlgorithmId != 0 && defaultIVSize == 0)) {
+ // only unencrypted data must have 0 IV size
+ return ERROR_MALFORMED;
+ } else if (defaultIVSize != 0 &&
+ defaultIVSize != 8 &&
+ defaultIVSize != 16) {
+ // only supported sizes are 0, 8 and 16
+ return ERROR_MALFORMED;
+ }
+
+ uint8_t defaultKeyId[16];
+
+ if (mDataSource->readAt(data_offset + 8, &defaultKeyId, 16) < 16) {
+ return ERROR_IO;
+ }
+
+ if (!mLastTrack) {
+ return ERROR_MALFORMED;
+ }
+ mLastTrack->meta->setInt32(kKeyCryptoMode, defaultAlgorithmId);
+ mLastTrack->meta->setInt32(kKeyCryptoDefaultIVSize, defaultIVSize);
+ mLastTrack->meta->setData(kKeyCryptoKey, 'tenc', defaultKeyId, 16);
+ *offset += chunk_size;
+ break;
+ }
+
+ case FOURCC('t', 'k', 'h', 'd'):
+ {
+ status_t err;
+ if ((err = parseTrackHeader(data_offset, chunk_data_size)) != OK) {
+ return err;
+ }
+
+ *offset += chunk_size;
+ break;
+ }
+
+ case FOURCC('p', 's', 's', 'h'):
+ {
+ PsshInfo pssh;
+
+ // We need the contents of the box header before data_offset. Make
+ // sure we don't underflow somehow.
+ CHECK(data_offset >= 8);
+
+ uint32_t version = 0;
+ if (mDataSource->readAt(data_offset, &version, 4) < 4) {
+ return ERROR_IO;
+ }
+
+ if (mDataSource->readAt(data_offset + 4, &pssh.uuid, 16) < 16) {
+ return ERROR_IO;
+ }
+
+ // Copy the contents of the box (including header) verbatim.
+ pssh.datalen = chunk_data_size + 8;
+ pssh.data = new (fallible) uint8_t[pssh.datalen];
+ if (!pssh.data) {
+ return -ENOMEM;
+ }
+ if (mDataSource->readAt(data_offset - 8, pssh.data, pssh.datalen) < pssh.datalen) {
+ return ERROR_IO;
+ }
+
+ mPssh.AppendElement(pssh);
+
+ *offset += chunk_size;
+ break;
+ }
+
+ case FOURCC('m', 'd', 'h', 'd'):
+ {
+ if (chunk_data_size < 4) {
+ return ERROR_MALFORMED;
+ }
+
+ uint8_t version;
+ if (mDataSource->readAt(
+ data_offset, &version, sizeof(version))
+ < (ssize_t)sizeof(version)) {
+ return ERROR_IO;
+ }
+
+ off64_t timescale_offset;
+
+ if (version == 1) {
+ timescale_offset = data_offset + 4 + 16;
+ } else if (version == 0) {
+ timescale_offset = data_offset + 4 + 8;
+ } else {
+ return ERROR_IO;
+ }
+
+ uint32_t timescale;
+ if (mDataSource->readAt(
+ timescale_offset, &timescale, sizeof(timescale))
+ < (ssize_t)sizeof(timescale)) {
+ return ERROR_IO;
+ }
+
+ if (!mLastTrack) {
+ return ERROR_MALFORMED;
+ }
+ mLastTrack->timescale = ntohl(timescale);
+ if (!mLastTrack->timescale) {
+ return ERROR_MALFORMED;
+ }
+
+ // Now that we've parsed the media timescale, we can interpret
+ // the edit list data.
+ storeEditList();
+
+ int64_t duration = 0;
+ if (version == 1) {
+ if (mDataSource->readAt(
+ timescale_offset + 4, &duration, sizeof(duration))
+ < (ssize_t)sizeof(duration)) {
+ return ERROR_IO;
+ }
+ // Avoid duration sets to -1, which is incorrect.
+ if (duration != -1) {
+ duration = ntoh64(duration);
+ } else {
+ duration = 0;
+ }
+ } else {
+ uint32_t duration32;
+ if (mDataSource->readAt(
+ timescale_offset + 4, &duration32, sizeof(duration32))
+ < (ssize_t)sizeof(duration32)) {
+ return ERROR_IO;
+ }
+ // ffmpeg sets duration to -1, which is incorrect.
+ if (duration32 != 0xffffffff) {
+ duration = ntohl(duration32);
+ } else {
+ duration = 0;
+ }
+ }
+ if (duration < 0) {
+ return ERROR_MALFORMED;
+ }
+ int64_t duration_us = unitsToUs(duration, mLastTrack->timescale);
+ if (duration_us == OVERFLOW_ERROR) {
+ return ERROR_MALFORMED;
+ }
+ mLastTrack->meta->setInt64(kKeyDuration, duration_us);
+
+ uint8_t lang[2];
+ off64_t lang_offset;
+ if (version == 1) {
+ lang_offset = timescale_offset + 4 + 8;
+ } else if (version == 0) {
+ lang_offset = timescale_offset + 4 + 4;
+ } else {
+ return ERROR_IO;
+ }
+
+ if (mDataSource->readAt(lang_offset, &lang, sizeof(lang))
+ < (ssize_t)sizeof(lang)) {
+ return ERROR_IO;
+ }
+
+ // To get the ISO-639-2/T three character language code
+ // 1 bit pad followed by 3 5-bits characters. Each character
+ // is packed as the difference between its ASCII value and 0x60.
+ char lang_code[4];
+ lang_code[0] = ((lang[0] >> 2) & 0x1f) + 0x60;
+ lang_code[1] = ((lang[0] & 0x3) << 3 | (lang[1] >> 5)) + 0x60;
+ lang_code[2] = (lang[1] & 0x1f) + 0x60;
+ lang_code[3] = '\0';
+
+ mLastTrack->meta->setCString(
+ kKeyMediaLanguage, lang_code);
+
+ *offset += chunk_size;
+ break;
+ }
+
+ case FOURCC('s', 't', 's', 'd'):
+ {
+ if (chunk_data_size < 8) {
+ return ERROR_MALFORMED;
+ }
+
+ uint8_t buffer[8];
+ if (chunk_data_size < (off64_t)sizeof(buffer)) {
+ return ERROR_MALFORMED;
+ }
+
+ if (mDataSource->readAt(
+ data_offset, buffer, 8) < 8) {
+ return ERROR_IO;
+ }
+
+ if (U32_AT(buffer) != 0) {
+ // Should be version 0, flags 0.
+ return ERROR_MALFORMED;
+ }
+
+ uint32_t entry_count = U32_AT(&buffer[4]);
+
+ if (entry_count > 1) {
+ // For 3GPP timed text, there could be multiple tx3g boxes contain
+ // multiple text display formats. These formats will be used to
+ // display the timed text.
+ // For encrypted files, there may also be more than one entry.
+ const char *mime;
+ if (!mLastTrack) {
+ return ERROR_MALFORMED;
+ }
+ CHECK(mLastTrack->meta->findCString(kKeyMIMEType, &mime));
+ if (strcasecmp(mime, MEDIA_MIMETYPE_TEXT_3GPP) &&
+ strcasecmp(mime, "application/octet-stream")) {
+ // For now we only support a single type of media per track.
+ mLastTrack->skipTrack = true;
+ *offset += chunk_size;
+ break;
+ }
+ }
+ off64_t stop_offset = *offset + chunk_size;
+ *offset = data_offset + 8;
+ for (uint32_t i = 0; i < entry_count; ++i) {
+ status_t err = parseChunk(offset, depth + 1);
+ if (err != OK) {
+ return err;
+ }
+ }
+
+ // Some muxers add some padding after the stsd content. Skip it.
+ *offset = stop_offset;
+ break;
+ }
+
+ case FOURCC('m', 'p', '4', 'a'):
+ case FOURCC('.', 'm', 'p', '3'):
+ case FOURCC('e', 'n', 'c', 'a'):
+ case FOURCC('s', 'a', 'm', 'r'):
+ case FOURCC('s', 'a', 'w', 'b'):
+ {
+ // QT's MP4 may have an empty MP4A atom within a MP4A atom.
+ // Ignore it.
+ if (chunk_data_size == 4) {
+ *offset += chunk_size;
+ break;
+ }
+ uint8_t buffer[8 + 20];
+ if (chunk_data_size < (ssize_t)sizeof(buffer)) {
+ // Basic AudioSampleEntry size.
+ return ERROR_MALFORMED;
+ }
+
+ if (mDataSource->readAt(
+ data_offset, buffer, sizeof(buffer)) < (ssize_t)sizeof(buffer)) {
+ return ERROR_IO;
+ }
+
+ uint16_t data_ref_index = U16_AT(&buffer[6]);
+ uint16_t qt_version = U16_AT(&buffer[8]);
+ uint32_t num_channels = U16_AT(&buffer[16]);
+
+ uint16_t sample_size = U16_AT(&buffer[18]);
+ uint32_t sample_rate = U32_AT(&buffer[24]) >> 16;
+
+ if (!mLastTrack) {
+ return ERROR_MALFORMED;
+ }
+ if (chunk_type != FOURCC('e', 'n', 'c', 'a')) {
+ // if the chunk type is enca, we'll get the type from the sinf/frma box later
+ const char* mime = FourCC2MIME(chunk_type);
+ if (!mime) {
+ return ERROR_UNSUPPORTED;
+ }
+ mLastTrack->meta->setCString(kKeyMIMEType, mime);
+ AdjustChannelsAndRate(chunk_type, &num_channels, &sample_rate);
+ }
+
+ uint64_t skip = 0;
+ if (qt_version == 1) {
+ // Skip QTv1 extension
+ // uint32_t SamplesPerPacket
+ // uint32_t BytesPerPacket
+ // uint32_t BytesPerFrame
+ // uint32_t BytesPerSample
+ skip = 16;
+ } else if (qt_version == 2) {
+ // Skip QTv2 extension
+ // uint32_t Qt V2 StructSize
+ // double SampleRate
+ // uint32_t ChannelCount
+ // uint32_t Reserved
+ // uint32_t BitsPerChannel
+ // uint32_t LPCMFormatSpecificFlags
+ // uint32_t BytesPerAudioPacket
+ // uint32_t LPCMFramesPerAudioPacket
+ // if (Qt V2 StructSize > 72) {
+ // StructSize-72: Qt V2 extension
+ // }
+ uint32_t structSize32;
+ if (mDataSource->readAt(
+ data_offset + 28, &structSize32, sizeof(structSize32))
+ < (ssize_t)sizeof(structSize32)) {
+ return ERROR_IO;
+ }
+ uint32_t structSize = ntohl(structSize32);
+ // Read SampleRate.
+ uint64_t sample_rate64;
+ if (mDataSource->readAt(
+ data_offset + 32, &sample_rate64, sizeof(sample_rate64))
+ < (ssize_t)sizeof(sample_rate64)) {
+ return ERROR_IO;
+ }
+ uint64_t i_value = ntoh64(sample_rate64);
+ void* v_value = reinterpret_cast<void*>(&i_value);
+ sample_rate = uint32_t(*reinterpret_cast<double*>(v_value));
+ // Read ChannelCount.
+ uint32_t channel_count32;
+ if (mDataSource->readAt(
+ data_offset + 40, &channel_count32, sizeof(channel_count32))
+ < (ssize_t)sizeof(channel_count32)) {
+ return ERROR_IO;
+ }
+ num_channels = ntohl(channel_count32);
+
+ skip += 36;
+ if (structSize > 72) {
+ skip += structSize - 72;
+ }
+ }
+ ALOGV("*** coding='%s' %d channels, size %d, rate %d\n",
+ chunk, num_channels, sample_size, sample_rate);
+ mLastTrack->meta->setInt32(kKeyChannelCount, num_channels);
+ mLastTrack->meta->setInt32(kKeySampleSize, sample_size);
+ mLastTrack->meta->setInt32(kKeySampleRate, sample_rate);
+
+ off64_t stop_offset = *offset + chunk_size;
+ *offset = data_offset + sizeof(buffer) + skip;
+ while (*offset < stop_offset) {
+ status_t err = parseChunk(offset, depth + 1);
+ if (err != OK) {
+ return err;
+ }
+ }
+
+ if (*offset != stop_offset) {
+ return ERROR_MALFORMED;
+ }
+ break;
+ }
+
+ case FOURCC('m', 'p', '4', 'v'):
+ case FOURCC('e', 'n', 'c', 'v'):
+ case FOURCC('s', '2', '6', '3'):
+ case FOURCC('H', '2', '6', '3'):
+ case FOURCC('h', '2', '6', '3'):
+ case FOURCC('a', 'v', 'c', '1'):
+ case FOURCC('a', 'v', 'c', '3'):
+ case FOURCC('V', 'P', '6', 'F'):
+ {
+ mHasVideo = true;
+
+ uint8_t buffer[78];
+ if (chunk_data_size < (ssize_t)sizeof(buffer)) {
+ // Basic VideoSampleEntry size.
+ return ERROR_MALFORMED;
+ }
+
+ if (mDataSource->readAt(
+ data_offset, buffer, sizeof(buffer)) < (ssize_t)sizeof(buffer)) {
+ return ERROR_IO;
+ }
+
+ uint16_t data_ref_index = U16_AT(&buffer[6]);
+ uint16_t width = U16_AT(&buffer[6 + 18]);
+ uint16_t height = U16_AT(&buffer[6 + 20]);
+
+ // The video sample is not standard-compliant if it has invalid dimension.
+ // Use some default width and height value, and
+ // let the decoder figure out the actual width and height (and thus
+ // be prepared for INFO_FOMRAT_CHANGED event).
+ if (width == 0) width = 352;
+ if (height == 0) height = 288;
+
+ // printf("*** coding='%s' width=%d height=%d\n",
+ // chunk, width, height);
+
+ if (!mLastTrack) {
+ return ERROR_MALFORMED;
+ }
+ if (chunk_type != FOURCC('e', 'n', 'c', 'v')) {
+ // if the chunk type is encv, we'll get the type from the sinf/frma box later
+ const char* mime = FourCC2MIME(chunk_type);
+ if (!mime) {
+ return ERROR_UNSUPPORTED;
+ }
+ mLastTrack->meta->setCString(kKeyMIMEType, mime);
+ }
+ mLastTrack->meta->setInt32(kKeyWidth, width);
+ mLastTrack->meta->setInt32(kKeyHeight, height);
+
+ off64_t stop_offset = *offset + chunk_size;
+ *offset = data_offset + sizeof(buffer);
+ while (*offset < stop_offset) {
+ status_t err = parseChunk(offset, depth + 1);
+ if (err != OK) {
+ return err;
+ }
+ // Some Apple QuickTime muxed videos appear to have some padding.
+ // Ignore it and assume we've reached the end.
+ if (stop_offset - *offset < 8) {
+ *offset = stop_offset;
+ }
+ }
+
+ if (*offset != stop_offset) {
+ return ERROR_MALFORMED;
+ }
+ break;
+ }
+
+ case FOURCC('s', 't', 'c', 'o'):
+ case FOURCC('c', 'o', '6', '4'):
+ {
+ if (!mLastTrack || !mLastTrack->sampleTable.get()) {
+ return ERROR_MALFORMED;
+ }
+ status_t err =
+ mLastTrack->sampleTable->setChunkOffsetParams(
+ chunk_type, data_offset, chunk_data_size);
+
+ if (err != OK) {
+ return err;
+ }
+
+ *offset += chunk_size;
+ break;
+ }
+
+ case FOURCC('s', 't', 's', 'c'):
+ {
+ if (!mLastTrack || !mLastTrack->sampleTable.get()) {
+ return ERROR_MALFORMED;
+ }
+ status_t err =
+ mLastTrack->sampleTable->setSampleToChunkParams(
+ data_offset, chunk_data_size);
+
+ if (err != OK) {
+ return err;
+ }
+
+ *offset += chunk_size;
+ break;
+ }
+
+ case FOURCC('s', 't', 's', 'z'):
+ case FOURCC('s', 't', 'z', '2'):
+ {
+ if (!mLastTrack || !mLastTrack->sampleTable.get()) {
+ return ERROR_MALFORMED;
+ }
+ status_t err =
+ mLastTrack->sampleTable->setSampleSizeParams(
+ chunk_type, data_offset, chunk_data_size);
+
+ if (err != OK) {
+ return err;
+ }
+
+ size_t max_size;
+ err = mLastTrack->sampleTable->getMaxSampleSize(&max_size);
+
+ if (err != OK) {
+ return err;
+ }
+
+ if (max_size != 0) {
+ // Assume that a given buffer only contains at most 10 chunks,
+ // each chunk originally prefixed with a 2 byte length will
+ // have a 4 byte header (0x00 0x00 0x00 0x01) after conversion,
+ // and thus will grow by 2 bytes per chunk.
+ mLastTrack->meta->setInt32(kKeyMaxInputSize, max_size + 10 * 2);
+ } else {
+ // No size was specified. Pick a conservatively large size.
+ int32_t width, height;
+ if (mLastTrack->meta->findInt32(kKeyWidth, &width) &&
+ mLastTrack->meta->findInt32(kKeyHeight, &height)) {
+ mLastTrack->meta->setInt32(kKeyMaxInputSize, width * height * 3 / 2);
+ } else {
+ ALOGV("No width or height, assuming worst case 1080p");
+ mLastTrack->meta->setInt32(kKeyMaxInputSize, 3110400);
+ }
+ }
+ *offset += chunk_size;
+
+ // Calculate average frame rate.
+ const char *mime;
+ CHECK(mLastTrack->meta->findCString(kKeyMIMEType, &mime));
+ if (!strncasecmp("video/", mime, 6)) {
+ size_t nSamples = mLastTrack->sampleTable->countSamples();
+ int64_t durationUs;
+ if (mLastTrack->meta->findInt64(kKeyDuration, &durationUs)) {
+ if (durationUs > 0) {
+ int32_t frameRate = (nSamples * 1000000LL +
+ (durationUs >> 1)) / durationUs;
+ mLastTrack->meta->setInt32(kKeyFrameRate, frameRate);
+ }
+ }
+ }
+
+ break;
+ }
+
+ case FOURCC('s', 't', 't', 's'):
+ {
+ if (!mLastTrack || !mLastTrack->sampleTable.get()) {
+ return ERROR_MALFORMED;
+ }
+ status_t err =
+ mLastTrack->sampleTable->setTimeToSampleParams(
+ data_offset, chunk_data_size);
+
+ if (err != OK) {
+ return err;
+ }
+
+ *offset += chunk_size;
+ break;
+ }
+
+ case FOURCC('c', 't', 't', 's'):
+ {
+ if (!mLastTrack || !mLastTrack->sampleTable.get()) {
+ return ERROR_MALFORMED;
+ }
+ status_t err =
+ mLastTrack->sampleTable->setCompositionTimeToSampleParams(
+ data_offset, chunk_data_size);
+
+ if (err != OK) {
+ return err;
+ }
+
+ *offset += chunk_size;
+ break;
+ }
+
+ case FOURCC('s', 't', 's', 's'):
+ {
+ if (!mLastTrack || !mLastTrack->sampleTable.get()) {
+ return ERROR_MALFORMED;
+ }
+ status_t err =
+ mLastTrack->sampleTable->setSyncSampleParams(
+ data_offset, chunk_data_size);
+
+ if (err != OK) {
+ return err;
+ }
+
+ *offset += chunk_size;
+ break;
+ }
+
+ case FOURCC('s', 'a', 'i', 'z'):
+ {
+ if (!mLastTrack || !mLastTrack->sampleTable.get()) {
+ return ERROR_MALFORMED;
+ }
+ status_t err =
+ mLastTrack->sampleTable->setSampleAuxiliaryInformationSizeParams(
+ data_offset, chunk_data_size, mDrmScheme);
+
+ if (err != OK) {
+ return err;
+ }
+
+ *offset += chunk_size;
+ break;
+ }
+
+ case FOURCC('s', 'a', 'i', 'o'):
+ {
+ if (!mLastTrack || !mLastTrack->sampleTable.get()) {
+ return ERROR_MALFORMED;
+ }
+ status_t err =
+ mLastTrack->sampleTable->setSampleAuxiliaryInformationOffsetParams(
+ data_offset, chunk_data_size, mDrmScheme);
+
+ if (err != OK) {
+ return err;
+ }
+
+ *offset += chunk_size;
+ break;
+ }
+
+ // @xyz
+ case FOURCC('\xA9', 'x', 'y', 'z'):
+ {
+ // Best case the total data length inside "@xyz" box
+ // would be 8, for instance "@xyz" + "\x00\x04\x15\xc7" + "0+0/",
+ // where "\x00\x04" is the text string length with value = 4,
+ // "\0x15\xc7" is the language code = en, and "0+0" is a
+ // location (string) value with longitude = 0 and latitude = 0.
+ if (chunk_data_size < 8) {
+ return ERROR_MALFORMED;
+ }
+
+ // Worst case the location string length would be 18,
+ // for instance +90.0000-180.0000, without the trailing "/" and
+ // the string length + language code.
+ char buffer[18];
+
+ // Substracting 5 from the data size is because the text string length +
+ // language code takes 4 bytes, and the trailing slash "/" takes 1 byte.
+ off64_t location_length = chunk_data_size - 5;
+ if (location_length >= (off64_t) sizeof(buffer)) {
+ return ERROR_MALFORMED;
+ }
+
+ if (mDataSource->readAt(
+ data_offset + 4, buffer, location_length) < location_length) {
+ return ERROR_IO;
+ }
+
+ buffer[location_length] = '\0';
+ mFileMetaData->setCString(kKeyLocation, buffer);
+ *offset += chunk_size;
+ break;
+ }
+
+ case FOURCC('e', 's', 'd', 's'):
+ {
+ if (chunk_data_size < 4) {
+ return ERROR_MALFORMED;
+ }
+
+ uint8_t buffer[256];
+ if (chunk_data_size > (off64_t)sizeof(buffer)) {
+ return ERROR_BUFFER_TOO_SMALL;
+ }
+
+ if (mDataSource->readAt(
+ data_offset, buffer, chunk_data_size) < chunk_data_size) {
+ return ERROR_IO;
+ }
+
+ if (U32_AT(buffer) != 0) {
+ // Should be version 0, flags 0.
+ return ERROR_MALFORMED;
+ }
+
+ if (!mLastTrack) {
+ return ERROR_MALFORMED;
+ }
+ mLastTrack->meta->setData(
+ kKeyESDS, kTypeESDS, &buffer[4], chunk_data_size - 4);
+
+ if (mPath.Length() >= 2
+ && (mPath[mPath.Length() - 2] == FOURCC('m', 'p', '4', 'a') ||
+ (mPath[mPath.Length() - 2] == FOURCC('e', 'n', 'c', 'a')))) {
+ // Information from the ESDS must be relied on for proper
+ // setup of sample rate and channel count for MPEG4 Audio.
+ // The generic header appears to only contain generic
+ // information...
+
+ status_t err = updateAudioTrackInfoFromESDS_MPEG4Audio(
+ &buffer[4], chunk_data_size - 4);
+
+ if (err != OK) {
+ return err;
+ }
+ }
+
+ *offset += chunk_size;
+ break;
+ }
+
+ case FOURCC('a', 'v', 'c', 'C'):
+ {
+ if (chunk_data_size < 7) {
+ ALOGE("short avcC chunk (%d bytes)", chunk_data_size);
+ return ERROR_MALFORMED;
+ }
+
+ sp<ABuffer> buffer = new (fallible) ABuffer(chunk_data_size);
+ if (!buffer.get() || !buffer->data()) {
+ return -ENOMEM;
+ }
+
+ if (mDataSource->readAt(
+ data_offset, buffer->data(), chunk_data_size) < chunk_data_size) {
+ return ERROR_IO;
+ }
+
+ if (!mLastTrack) {
+ return ERROR_MALFORMED;
+ }
+ mLastTrack->meta->setData(
+ kKeyAVCC, kTypeAVCC, buffer->data(), chunk_data_size);
+
+ *offset += chunk_size;
+ break;
+ }
+
+ case FOURCC('d', '2', '6', '3'):
+ {
+ /*
+ * d263 contains a fixed 7 bytes part:
+ * vendor - 4 bytes
+ * version - 1 byte
+ * level - 1 byte
+ * profile - 1 byte
+ * optionally, "d263" box itself may contain a 16-byte
+ * bit rate box (bitr)
+ * average bit rate - 4 bytes
+ * max bit rate - 4 bytes
+ */
+ char buffer[23];
+ if (chunk_data_size != 7 &&
+ chunk_data_size != 23) {
+ ALOGE("Incorrect D263 box size %lld", chunk_data_size);
+ return ERROR_MALFORMED;
+ }
+
+ if (mDataSource->readAt(
+ data_offset, buffer, chunk_data_size) < chunk_data_size) {
+ return ERROR_IO;
+ }
+
+ if (!mLastTrack) {
+ return ERROR_MALFORMED;
+ }
+ mLastTrack->meta->setData(kKeyD263, kTypeD263, buffer, chunk_data_size);
+
+ *offset += chunk_size;
+ break;
+ }
+
+ case FOURCC('m', 'e', 't', 'a'):
+ {
+ uint8_t buffer[4];
+ if (chunk_data_size < (off64_t)sizeof(buffer)) {
+ return ERROR_MALFORMED;
+ }
+
+ if (mDataSource->readAt(
+ data_offset, buffer, 4) < 4) {
+ return ERROR_IO;
+ }
+
+ if (U32_AT(buffer) != 0) {
+ // Should be version 0, flags 0.
+
+ // If it's not, let's assume this is one of those
+ // apparently malformed chunks that don't have flags
+ // and completely different semantics than what's
+ // in the MPEG4 specs and skip it.
+ *offset += chunk_size;
+ return OK;
+ }
+
+ off64_t stop_offset = *offset + chunk_size;
+ *offset = data_offset + sizeof(buffer);
+ while (*offset < stop_offset) {
+ status_t err = parseChunk(offset, depth + 1);
+ if (err != OK) {
+ return err;
+ }
+ }
+
+ if (*offset != stop_offset) {
+ return ERROR_MALFORMED;
+ }
+ break;
+ }
+
+ case FOURCC('m', 'e', 'a', 'n'):
+ case FOURCC('n', 'a', 'm', 'e'):
+ case FOURCC('d', 'a', 't', 'a'):
+ {
+ if (mPath.Length() == 6 && underMetaDataPath(mPath)) {
+ status_t err = parseMetaData(data_offset, chunk_data_size);
+
+ if (err != OK) {
+ return err;
+ }
+ }
+
+ *offset += chunk_size;
+ break;
+ }
+
+ case FOURCC('m', 'v', 'h', 'd'):
+ {
+ if (chunk_data_size < 24) {
+ return ERROR_MALFORMED;
+ }
+
+ uint8_t header[24];
+ if (mDataSource->readAt(
+ data_offset, header, sizeof(header))
+ < (ssize_t)sizeof(header)) {
+ return ERROR_IO;
+ }
+
+ if (header[0] == 1) {
+ mHeaderTimescale = U32_AT(&header[20]);
+ } else if (header[0] != 0) {
+ return ERROR_MALFORMED;
+ } else {
+ mHeaderTimescale = U32_AT(&header[12]);
+ }
+
+ *offset += chunk_size;
+ break;
+ }
+
+ case FOURCC('m', 'e', 'h', 'd'):
+ {
+ if (chunk_data_size < 8) {
+ return ERROR_MALFORMED;
+ }
+
+ uint8_t version;
+ if (mDataSource->readAt(
+ data_offset, &version, sizeof(version))
+ < (ssize_t)sizeof(version)) {
+ return ERROR_IO;
+ }
+ if (version > 1) {
+ break;
+ }
+ int64_t duration = 0;
+ if (version == 1) {
+ if (mDataSource->readAt(
+ data_offset + 4, &duration, sizeof(duration))
+ < (ssize_t)sizeof(duration)) {
+ return ERROR_IO;
+ }
+ duration = ntoh64(duration);
+ } else {
+ uint32_t duration32;
+ if (mDataSource->readAt(
+ data_offset + 4, &duration32, sizeof(duration32))
+ < (ssize_t)sizeof(duration32)) {
+ return ERROR_IO;
+ }
+ duration = ntohl(duration32);
+ }
+ if (duration < 0) {
+ return ERROR_MALFORMED;
+ }
+ int64_t duration_us = unitsToUs(duration, mHeaderTimescale);
+ if (duration_us == OVERFLOW_ERROR) {
+ return ERROR_MALFORMED;
+ }
+ if (duration && mHeaderTimescale) {
+ mFileMetaData->setInt64(kKeyMovieDuration, duration_us);
+ }
+
+ *offset += chunk_size;
+ break;
+ }
+
+ case FOURCC('m', 'd', 'a', 't'):
+ {
+ ALOGV("mdat chunk, drm: %d", mIsDrm);
+ if (!mIsDrm) {
+ *offset += chunk_size;
+ break;
+ }
+
+ if (chunk_size < 8) {
+ return ERROR_MALFORMED;
+ }
+
+ return parseDrmSINF(offset, data_offset);
+ }
+
+ case FOURCC('h', 'd', 'l', 'r'):
+ {
+ uint32_t buffer;
+ if (mDataSource->readAt(
+ data_offset + 8, &buffer, 4) < 4) {
+ return ERROR_IO;
+ }
+
+ uint32_t type = ntohl(buffer);
+ // For the 3GPP file format, the handler-type within the 'hdlr' box
+ // shall be 'text'. We also want to support 'sbtl' handler type
+ // for a practical reason as various MPEG4 containers use it.
+ if (type == FOURCC('t', 'e', 'x', 't') || type == FOURCC('s', 'b', 't', 'l')) {
+ if (!mLastTrack) {
+ return ERROR_MALFORMED;
+ }
+ mLastTrack->meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_TEXT_3GPP);
+ }
+
+ *offset += chunk_size;
+ break;
+ }
+
+ case FOURCC('t', 'x', '3', 'g'):
+ {
+ if (!mLastTrack) {
+ return ERROR_MALFORMED;
+ }
+ uint32_t type;
+ const void *data;
+ size_t size = 0;
+ if (!mLastTrack->meta->findData(
+ kKeyTextFormatData, &type, &data, &size)) {
+ size = 0;
+ }
+
+ // Make sure (size + chunk_size) isn't going to overflow.
+ if (size >= kMAX_ALLOCATION - chunk_size) {
+ return ERROR_MALFORMED;
+ }
+ uint8_t *buffer = new (fallible) uint8_t[size + chunk_size];
+ if (!buffer) {
+ return -ENOMEM;
+ }
+
+ if (size > 0) {
+ memcpy(buffer, data, size);
+ }
+
+ if ((size_t)(mDataSource->readAt(*offset, buffer + size, chunk_size))
+ < chunk_size) {
+ delete[] buffer;
+ buffer = NULL;
+
+ return ERROR_IO;
+ }
+
+ mLastTrack->meta->setData(
+ kKeyTextFormatData, 0, buffer, size + chunk_size);
+
+ delete[] buffer;
+
+ *offset += chunk_size;
+ break;
+ }
+
+ case FOURCC('c', 'o', 'v', 'r'):
+ {
+ if (mFileMetaData != NULL) {
+ ALOGV("chunk_data_size = %lld and data_offset = %lld",
+ chunk_data_size, data_offset);
+ const int kSkipBytesOfDataBox = 16;
+ if (chunk_data_size <= kSkipBytesOfDataBox) {
+ return ERROR_MALFORMED;
+ }
+ sp<ABuffer> buffer = new (fallible) ABuffer(chunk_data_size + 1);
+ if (!buffer.get() || !buffer->data()) {
+ return -ENOMEM;
+ }
+ if (mDataSource->readAt(
+ data_offset, buffer->data(), chunk_data_size) != (ssize_t)chunk_data_size) {
+ return ERROR_IO;
+ }
+ mFileMetaData->setData(
+ kKeyAlbumArt, MetaData::TYPE_NONE,
+ buffer->data() + kSkipBytesOfDataBox, chunk_data_size - kSkipBytesOfDataBox);
+ }
+
+ *offset += chunk_size;
+ break;
+ }
+
+ case FOURCC('-', '-', '-', '-'):
+ {
+ mLastCommentMean.clear();
+ mLastCommentName.clear();
+ mLastCommentData.clear();
+ *offset += chunk_size;
+ break;
+ }
+
+ case FOURCC('s', 'i', 'd', 'x'):
+ {
+ parseSegmentIndex(data_offset, chunk_data_size);
+ *offset += chunk_size;
+ return UNKNOWN_ERROR; // stop parsing after sidx
+ }
+
+ case FOURCC('w', 'a', 'v', 'e'):
+ {
+ off64_t stop_offset = *offset + chunk_size;
+ *offset = data_offset;
+ while (*offset < stop_offset) {
+ status_t err = parseChunk(offset, depth + 1);
+ if (err != OK) {
+ return err;
+ }
+ }
+
+ if (*offset != stop_offset) {
+ return ERROR_MALFORMED;
+ }
+ break;
+ }
+
+ default:
+ {
+ *offset += chunk_size;
+ break;
+ }
+ }
+
+ return OK;
+}
+
+void MPEG4Extractor::storeEditList()
+{
+ if (mHeaderTimescale == 0 ||
+ !mLastTrack ||
+ mLastTrack->timescale == 0) {
+ return;
+ }
+
+ if (mLastTrack->segment_duration > uint64_t(INT64_MAX) ||
+ mLastTrack->empty_duration > uint64_t(INT64_MAX)) {
+ return;
+ }
+ uint64_t segment_duration =
+ uint64_t(unitsToUs(mLastTrack->segment_duration, mHeaderTimescale));
+ // media_time is measured in media time scale units.
+ int64_t media_time = unitsToUs(mLastTrack->media_time, mLastTrack->timescale);
+ // empty_duration is in the Movie Header Box's timescale.
+ int64_t empty_duration = unitsToUs(mLastTrack->empty_duration, mHeaderTimescale);
+ if (segment_duration == OVERFLOW_ERROR ||
+ media_time == OVERFLOW_ERROR ||
+ empty_duration == OVERFLOW_ERROR) {
+ return;
+ }
+ media_time -= empty_duration;
+ mLastTrack->meta->setInt64(kKeyMediaTime, media_time);
+
+ int64_t duration;
+ int32_t samplerate;
+ if (mLastTrack->meta->findInt64(kKeyDuration, &duration) &&
+ mLastTrack->meta->findInt32(kKeySampleRate, &samplerate)) {
+
+ int64_t delay = (media_time * samplerate + 500000) / 1000000;
+ mLastTrack->meta->setInt32(kKeyEncoderDelay, delay);
+
+ int64_t paddingus = duration - (segment_duration + media_time);
+ int64_t paddingsamples = (paddingus * samplerate + 500000) / 1000000;
+ mLastTrack->meta->setInt32(kKeyEncoderPadding, paddingsamples);
+ }
+}
+
+status_t MPEG4Extractor::parseSegmentIndex(off64_t offset, size_t size) {
+ ALOGV("MPEG4Extractor::parseSegmentIndex");
+
+ if (size < 12) {
+ return -EINVAL;
+ }
+
+ uint32_t flags;
+ if (!mDataSource->getUInt32(offset, &flags)) {
+ return ERROR_MALFORMED;
+ }
+
+ uint32_t version = flags >> 24;
+ flags &= 0xffffff;
+
+ ALOGV("sidx version %d", version);
+
+ uint32_t referenceId;
+ if (!mDataSource->getUInt32(offset + 4, &referenceId)) {
+ return ERROR_MALFORMED;
+ }
+
+ uint32_t timeScale;
+ if (!mDataSource->getUInt32(offset + 8, &timeScale)) {
+ return ERROR_MALFORMED;
+ }
+ if (!timeScale) {
+ return ERROR_MALFORMED;
+ }
+ ALOGV("sidx refid/timescale: %d/%d", referenceId, timeScale);
+
+ uint64_t earliestPresentationTime;
+ uint64_t firstOffset;
+
+ offset += 12;
+ size -= 12;
+
+ if (version == 0) {
+ if (size < 8) {
+ return -EINVAL;
+ }
+ uint32_t tmp;
+ if (!mDataSource->getUInt32(offset, &tmp)) {
+ return ERROR_MALFORMED;
+ }
+ earliestPresentationTime = tmp;
+ if (!mDataSource->getUInt32(offset + 4, &tmp)) {
+ return ERROR_MALFORMED;
+ }
+ firstOffset = tmp;
+ offset += 8;
+ size -= 8;
+ } else {
+ if (size < 16) {
+ return -EINVAL;
+ }
+ if (!mDataSource->getUInt64(offset, &earliestPresentationTime)) {
+ return ERROR_MALFORMED;
+ }
+ if (!mDataSource->getUInt64(offset + 8, &firstOffset)) {
+ return ERROR_MALFORMED;
+ }
+ offset += 16;
+ size -= 16;
+ }
+ ALOGV("sidx pres/off: %Ld/%Ld", earliestPresentationTime, firstOffset);
+
+ if (size < 4) {
+ return -EINVAL;
+ }
+
+ uint16_t referenceCount;
+ if (!mDataSource->getUInt16(offset + 2, &referenceCount)) {
+ return ERROR_MALFORMED;
+ }
+ offset += 4;
+ size -= 4;
+ ALOGV("refcount: %d", referenceCount);
+
+ if (size < referenceCount * 12) {
+ return -EINVAL;
+ }
+
+ int64_t total_duration = 0;
+ for (unsigned int i = 0; i < referenceCount; i++) {
+ uint32_t d1, d2, d3;
+
+ if (!mDataSource->getUInt32(offset, &d1) || // size
+ !mDataSource->getUInt32(offset + 4, &d2) || // duration
+ !mDataSource->getUInt32(offset + 8, &d3)) { // flags
+ return ERROR_MALFORMED;
+ }
+
+ if (d1 & 0x80000000) {
+ ALOGW("sub-sidx boxes not supported yet");
+ }
+ bool sap = d3 & 0x80000000;
+ uint32_t saptype = (d3 >> 28) & 0x3;
+ if (!sap || saptype > 2) {
+ ALOGW("not a stream access point, or unsupported type");
+ }
+ total_duration += d2;
+ offset += 12;
+ ALOGV(" item %d, %08x %08x %08x", i, d1, d2, d3);
+ SidxEntry se;
+ se.mSize = d1 & 0x7fffffff;
+ int64_t durationUs = unitsToUs(d2, timeScale);
+ if (durationUs == OVERFLOW_ERROR || durationUs > int64_t(UINT32_MAX)) {
+ return ERROR_MALFORMED;
+ }
+ se.mDurationUs = uint32_t(durationUs);
+ mSidxEntries.AppendElement(se);
+ }
+
+ mSidxDuration = unitsToUs(total_duration, timeScale);
+ if (mSidxDuration == OVERFLOW_ERROR) {
+ return ERROR_MALFORMED;
+ }
+ ALOGV("duration: %lld", mSidxDuration);
+
+ if (!mLastTrack) {
+ return ERROR_MALFORMED;
+ }
+ int64_t metaDuration;
+ if (!mLastTrack->meta->findInt64(kKeyDuration, &metaDuration) || metaDuration == 0) {
+ mLastTrack->meta->setInt64(kKeyDuration, mSidxDuration);
+ }
+ return OK;
+}
+
+status_t MPEG4Extractor::parseTrackHeader(
+ off64_t data_offset, off64_t data_size) {
+ if (data_size < 4) {
+ return ERROR_MALFORMED;
+ }
+
+ uint8_t version;
+ if (mDataSource->readAt(data_offset, &version, 1) < 1) {
+ return ERROR_IO;
+ }
+
+ size_t dynSize = (version == 1) ? 36 : 24;
+
+ uint8_t buffer[36 + 60];
+
+ if (data_size != (off64_t)dynSize + 60) {
+ return ERROR_MALFORMED;
+ }
+
+ if (mDataSource->readAt(
+ data_offset, buffer, data_size) < (ssize_t)data_size) {
+ return ERROR_IO;
+ }
+
+ uint64_t ctime, mtime, duration;
+ int32_t id;
+
+ if (version == 1) {
+ ctime = U64_AT(&buffer[4]);
+ mtime = U64_AT(&buffer[12]);
+ id = U32_AT(&buffer[20]);
+ duration = U64_AT(&buffer[28]);
+ } else if (version == 0) {
+ ctime = U32_AT(&buffer[4]);
+ mtime = U32_AT(&buffer[8]);
+ id = U32_AT(&buffer[12]);
+ duration = U32_AT(&buffer[20]);
+ } else {
+ return ERROR_UNSUPPORTED;
+ }
+
+ if (!mLastTrack) {
+ return ERROR_MALFORMED;
+ }
+ mLastTrack->meta->setInt32(kKeyTrackID, id);
+
+ size_t matrixOffset = dynSize + 16;
+ int32_t a00 = U32_AT(&buffer[matrixOffset]);
+ int32_t a01 = U32_AT(&buffer[matrixOffset + 4]);
+ int32_t dx = U32_AT(&buffer[matrixOffset + 8]);
+ int32_t a10 = U32_AT(&buffer[matrixOffset + 12]);
+ int32_t a11 = U32_AT(&buffer[matrixOffset + 16]);
+ int32_t dy = U32_AT(&buffer[matrixOffset + 20]);
+
+#if 0
+ ALOGI("x' = %.2f * x + %.2f * y + %.2f",
+ a00 / 65536.0f, a01 / 65536.0f, dx / 65536.0f);
+ ALOGI("y' = %.2f * x + %.2f * y + %.2f",
+ a10 / 65536.0f, a11 / 65536.0f, dy / 65536.0f);
+#endif
+
+ uint32_t rotationDegrees;
+
+ static const int32_t kFixedOne = 0x10000;
+ if (a00 == kFixedOne && a01 == 0 && a10 == 0 && a11 == kFixedOne) {
+ // Identity, no rotation
+ rotationDegrees = 0;
+ } else if (a00 == 0 && a01 == kFixedOne && a10 == -kFixedOne && a11 == 0) {
+ rotationDegrees = 90;
+ } else if (a00 == 0 && a01 == -kFixedOne && a10 == kFixedOne && a11 == 0) {
+ rotationDegrees = 270;
+ } else if (a00 == -kFixedOne && a01 == 0 && a10 == 0 && a11 == -kFixedOne) {
+ rotationDegrees = 180;
+ } else {
+ ALOGW("We only support 0,90,180,270 degree rotation matrices");
+ rotationDegrees = 0;
+ }
+
+ if (rotationDegrees != 0) {
+ mLastTrack->meta->setInt32(kKeyRotation, rotationDegrees);
+ }
+
+ // Handle presentation display size, which could be different
+ // from the image size indicated by kKeyWidth and kKeyHeight.
+ uint32_t width = U32_AT(&buffer[dynSize + 52]);
+ uint32_t height = U32_AT(&buffer[dynSize + 56]);
+ mLastTrack->meta->setInt32(kKeyDisplayWidth, width >> 16);
+ mLastTrack->meta->setInt32(kKeyDisplayHeight, height >> 16);
+
+ return OK;
+}
+
+status_t MPEG4Extractor::parseMetaData(off64_t offset, size_t size) {
+ if (size < 4) {
+ return ERROR_MALFORMED;
+ }
+
+ FallibleTArray<uint8_t> bufferBackend;
+ if (!bufferBackend.SetLength(size + 1, mozilla::fallible)) {
+ // OOM ignore metadata.
+ return OK;
+ }
+
+ uint8_t *buffer = bufferBackend.Elements();
+ if (mDataSource->readAt(
+ offset, buffer, size) != (ssize_t)size) {
+ return ERROR_IO;
+ }
+
+ uint32_t flags = U32_AT(buffer);
+
+ uint32_t metadataKey = 0;
+ char chunk[5];
+ MakeFourCCString(mPath[4], chunk);
+ ALOGV("meta: %s @ %lld", chunk, offset);
+ switch (mPath[4]) {
+ case FOURCC(0xa9, 'a', 'l', 'b'):
+ {
+ metadataKey = kKeyAlbum;
+ break;
+ }
+ case FOURCC(0xa9, 'A', 'R', 'T'):
+ {
+ metadataKey = kKeyArtist;
+ break;
+ }
+ case FOURCC('a', 'A', 'R', 'T'):
+ {
+ metadataKey = kKeyAlbumArtist;
+ break;
+ }
+ case FOURCC(0xa9, 'd', 'a', 'y'):
+ {
+ metadataKey = kKeyYear;
+ break;
+ }
+ case FOURCC(0xa9, 'n', 'a', 'm'):
+ {
+ metadataKey = kKeyTitle;
+ break;
+ }
+ case FOURCC(0xa9, 'w', 'r', 't'):
+ {
+ metadataKey = kKeyWriter;
+ break;
+ }
+ case FOURCC('c', 'o', 'v', 'r'):
+ {
+ metadataKey = kKeyAlbumArt;
+ break;
+ }
+ case FOURCC('g', 'n', 'r', 'e'):
+ {
+ metadataKey = kKeyGenre;
+ break;
+ }
+ case FOURCC(0xa9, 'g', 'e', 'n'):
+ {
+ metadataKey = kKeyGenre;
+ break;
+ }
+ case FOURCC('c', 'p', 'i', 'l'):
+ {
+ if (size == 9 && flags == 21) {
+ char tmp[16];
+ sprintf(tmp, "%d",
+ (int)buffer[size - 1]);
+
+ mFileMetaData->setCString(kKeyCompilation, tmp);
+ }
+ break;
+ }
+ case FOURCC('t', 'r', 'k', 'n'):
+ {
+ if (size == 16 && flags == 0) {
+ char tmp[16];
+ uint16_t* pTrack = (uint16_t*)&buffer[10];
+ uint16_t* pTotalTracks = (uint16_t*)&buffer[12];
+ sprintf(tmp, "%d/%d", ntohs(*pTrack), ntohs(*pTotalTracks));
+
+ mFileMetaData->setCString(kKeyCDTrackNumber, tmp);
+ }
+ break;
+ }
+ case FOURCC('d', 'i', 's', 'k'):
+ {
+ if ((size == 14 || size == 16) && flags == 0) {
+ char tmp[16];
+ uint16_t* pDisc = (uint16_t*)&buffer[10];
+ uint16_t* pTotalDiscs = (uint16_t*)&buffer[12];
+ sprintf(tmp, "%d/%d", ntohs(*pDisc), ntohs(*pTotalDiscs));
+
+ mFileMetaData->setCString(kKeyDiscNumber, tmp);
+ }
+ break;
+ }
+ case FOURCC('-', '-', '-', '-'):
+ {
+ buffer[size] = '\0';
+ switch (mPath[5]) {
+ case FOURCC('m', 'e', 'a', 'n'):
+ mLastCommentMean.setTo((const char *)buffer + 4);
+ break;
+ case FOURCC('n', 'a', 'm', 'e'):
+ mLastCommentName.setTo((const char *)buffer + 4);
+ break;
+ case FOURCC('d', 'a', 't', 'a'):
+ mLastCommentData.setTo((const char *)buffer + 8);
+ break;
+ }
+
+ // Once we have a set of mean/name/data info, go ahead and process
+ // it to see if its something we are interested in. Whether or not
+ // were are interested in the specific tag, make sure to clear out
+ // the set so we can be ready to process another tuple should one
+ // show up later in the file.
+ if ((mLastCommentMean.length() != 0) &&
+ (mLastCommentName.length() != 0) &&
+ (mLastCommentData.length() != 0)) {
+
+ if (mLastCommentMean == "com.apple.iTunes"
+ && mLastCommentName == "iTunSMPB") {
+ int32_t delay, padding;
+ if (sscanf(mLastCommentData,
+ " %*x %x %x %*x", &delay, &padding) == 2) {
+ if (!mLastTrack) {
+ return ERROR_MALFORMED;
+ }
+ mLastTrack->meta->setInt32(kKeyEncoderDelay, delay);
+ mLastTrack->meta->setInt32(kKeyEncoderPadding, padding);
+ }
+ }
+
+ mLastCommentMean.clear();
+ mLastCommentName.clear();
+ mLastCommentData.clear();
+ }
+ break;
+ }
+
+ default:
+ break;
+ }
+
+ if (size >= 8 && metadataKey) {
+ if (metadataKey == kKeyAlbumArt) {
+ mFileMetaData->setData(
+ kKeyAlbumArt, MetaData::TYPE_NONE,
+ buffer + 8, size - 8);
+ } else if (metadataKey == kKeyGenre) {
+ if (flags == 0) {
+ // uint8_t genre code, iTunes genre codes are
+ // the standard id3 codes, except they start
+ // at 1 instead of 0 (e.g. Pop is 14, not 13)
+ // We use standard id3 numbering, so subtract 1.
+ int genrecode = (int)buffer[size - 1];
+ genrecode--;
+ if (genrecode < 0) {
+ genrecode = 255; // reserved for 'unknown genre'
+ }
+ char genre[10];
+ sprintf(genre, "%d", genrecode);
+
+ mFileMetaData->setCString(metadataKey, genre);
+ } else if (flags == 1) {
+ // custom genre string
+ buffer[size] = '\0';
+
+ mFileMetaData->setCString(
+ metadataKey, (const char *)buffer + 8);
+ }
+ } else {
+ buffer[size] = '\0';
+
+ mFileMetaData->setCString(
+ metadataKey, (const char *)buffer + 8);
+ }
+ }
+
+ return OK;
+}
+
+sp<MediaSource> MPEG4Extractor::getTrack(size_t index) {
+ status_t err;
+ if ((err = readMetaData()) != OK) {
+ return NULL;
+ }
+
+ Track *track = mFirstTrack;
+ while (index > 0) {
+ if (track == NULL) {
+ return NULL;
+ }
+
+ track = track->next;
+ --index;
+ }
+
+ if (track == NULL) {
+ return NULL;
+ }
+
+ ALOGV("getTrack called, pssh: %d", mPssh.Length());
+
+ return new MPEG4Source(track->meta, track->timescale, track->sampleTable);
+}
+
+// static
+status_t MPEG4Extractor::verifyTrack(Track *track) {
+ int32_t trackId;
+ if (!track->meta->findInt32(kKeyTrackID, &trackId)) {
+ return ERROR_MALFORMED;
+ }
+
+ const char *mime;
+ if (!track->meta->findCString(kKeyMIMEType, &mime)) {
+ return ERROR_MALFORMED;
+ }
+
+ uint32_t type;
+ const void *data;
+ size_t size;
+ if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AVC)) {
+ if (!track->meta->findData(kKeyAVCC, &type, &data, &size)
+ || type != kTypeAVCC
+ || size < 7
+ // configurationVersion == 1?
+ || reinterpret_cast<const uint8_t*>(data)[0] != 1) {
+ return ERROR_MALFORMED;
+ }
+ } else if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_MPEG4)
+ || !strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AAC)) {
+ if (!track->meta->findData(kKeyESDS, &type, &data, &size)
+ || type != kTypeESDS) {
+ return ERROR_MALFORMED;
+ }
+ }
+
+ if (!track->sampleTable.get() || !track->sampleTable->isValid()) {
+ // Make sure we have all the metadata we need.
+ return ERROR_MALFORMED;
+ }
+
+ uint32_t keytype;
+ const void *key;
+ size_t keysize;
+ if (track->meta->findData(kKeyCryptoKey, &keytype, &key, &keysize)) {
+ if (keysize > 16) {
+ return ERROR_MALFORMED;
+ }
+ }
+
+ return OK;
+}
+
+typedef enum {
+ //AOT_NONE = -1,
+ //AOT_NULL_OBJECT = 0,
+ //AOT_AAC_MAIN = 1, /**< Main profile */
+ AOT_AAC_LC = 2, /**< Low Complexity object */
+ //AOT_AAC_SSR = 3,
+ //AOT_AAC_LTP = 4,
+ AOT_SBR = 5,
+ //AOT_AAC_SCAL = 6,
+ //AOT_TWIN_VQ = 7,
+ //AOT_CELP = 8,
+ //AOT_HVXC = 9,
+ //AOT_RSVD_10 = 10, /**< (reserved) */
+ //AOT_RSVD_11 = 11, /**< (reserved) */
+ //AOT_TTSI = 12, /**< TTSI Object */
+ //AOT_MAIN_SYNTH = 13, /**< Main Synthetic object */
+ //AOT_WAV_TAB_SYNTH = 14, /**< Wavetable Synthesis object */
+ //AOT_GEN_MIDI = 15, /**< General MIDI object */
+ //AOT_ALG_SYNTH_AUD_FX = 16, /**< Algorithmic Synthesis and Audio FX object */
+ AOT_ER_AAC_LC = 17, /**< Error Resilient(ER) AAC Low Complexity */
+ //AOT_RSVD_18 = 18, /**< (reserved) */
+ //AOT_ER_AAC_LTP = 19, /**< Error Resilient(ER) AAC LTP object */
+ AOT_ER_AAC_SCAL = 20, /**< Error Resilient(ER) AAC Scalable object */
+ //AOT_ER_TWIN_VQ = 21, /**< Error Resilient(ER) TwinVQ object */
+ AOT_ER_BSAC = 22, /**< Error Resilient(ER) BSAC object */
+ AOT_ER_AAC_LD = 23, /**< Error Resilient(ER) AAC LowDelay object */
+ //AOT_ER_CELP = 24, /**< Error Resilient(ER) CELP object */
+ //AOT_ER_HVXC = 25, /**< Error Resilient(ER) HVXC object */
+ //AOT_ER_HILN = 26, /**< Error Resilient(ER) HILN object */
+ //AOT_ER_PARA = 27, /**< Error Resilient(ER) Parametric object */
+ //AOT_RSVD_28 = 28, /**< might become SSC */
+ AOT_PS = 29, /**< PS, Parametric Stereo (includes SBR) */
+ //AOT_MPEGS = 30, /**< MPEG Surround */
+
+ AOT_ESCAPE = 31, /**< Signal AOT uses more than 5 bits */
+
+ //AOT_MP3ONMP4_L1 = 32, /**< MPEG-Layer1 in mp4 */
+ //AOT_MP3ONMP4_L2 = 33, /**< MPEG-Layer2 in mp4 */
+ //AOT_MP3ONMP4_L3 = 34, /**< MPEG-Layer3 in mp4 */
+ //AOT_RSVD_35 = 35, /**< might become DST */
+ //AOT_RSVD_36 = 36, /**< might become ALS */
+ //AOT_AAC_SLS = 37, /**< AAC + SLS */
+ //AOT_SLS = 38, /**< SLS */
+ //AOT_ER_AAC_ELD = 39, /**< AAC Enhanced Low Delay */
+
+ //AOT_USAC = 42, /**< USAC */
+ //AOT_SAOC = 43, /**< SAOC */
+ //AOT_LD_MPEGS = 44, /**< Low Delay MPEG Surround */
+
+ //AOT_RSVD50 = 50, /**< Interim AOT for Rsvd50 */
+} AUDIO_OBJECT_TYPE;
+
+status_t MPEG4Extractor::updateAudioTrackInfoFromESDS_MPEG4Audio(
+ const void *esds_data, size_t esds_size) {
+ ESDS esds(esds_data, esds_size);
+
+ uint8_t objectTypeIndication;
+ if (esds.getObjectTypeIndication(&objectTypeIndication) != OK) {
+ return ERROR_MALFORMED;
+ }
+
+ if (objectTypeIndication == 0xe1) {
+ // This isn't MPEG4 audio at all, it's QCELP 14k...
+ if (mLastTrack == NULL)
+ return ERROR_MALFORMED;
+
+ mLastTrack->meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_QCELP);
+ return OK;
+ }
+
+ if (objectTypeIndication == 0x6b || objectTypeIndication == 0x69) {
+ // The media subtype is MP3 audio
+ if (!mLastTrack) {
+ return ERROR_MALFORMED;
+ }
+ mLastTrack->meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_MPEG);
+ }
+
+ const uint8_t *csd;
+ size_t csd_size;
+ if (esds.getCodecSpecificInfo(
+ (const void **)&csd, &csd_size) != OK) {
+ return ERROR_MALFORMED;
+ }
+
+#if 0
+ if (kUseHexDump) {
+ printf("ESD of size %zu\n", csd_size);
+ hexdump(csd, csd_size);
+ }
+#endif
+
+ if (csd_size == 0) {
+ // There's no further information, i.e. no codec specific data
+ // Let's assume that the information provided in the mpeg4 headers
+ // is accurate and hope for the best.
+
+ return OK;
+ }
+
+ if (csd_size < 2) {
+ return ERROR_MALFORMED;
+ }
+
+ static uint32_t kSamplingRate[] = {
+ 96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050,
+ 16000, 12000, 11025, 8000, 7350
+ };
+
+ ABitReader br(csd, csd_size);
+ if (br.numBitsLeft() < 5) {
+ return ERROR_MALFORMED;
+ }
+ uint32_t objectType = br.getBits(5);
+
+ if (objectType == 31) { // AAC-ELD => additional 6 bits
+ if (br.numBitsLeft() < 6) {
+ return ERROR_MALFORMED;
+ }
+ objectType = 32 + br.getBits(6);
+ }
+
+ if (mLastTrack == NULL)
+ return ERROR_MALFORMED;
+
+ if (objectType >= 1 && objectType <= 4) {
+ mLastTrack->meta->setInt32(kKeyAACProfile, objectType);
+ }
+
+ //keep AOT type
+ mLastTrack->meta->setInt32(kKeyAACAOT, objectType);
+
+ if (br.numBitsLeft() < 4) {
+ return ERROR_MALFORMED;
+ }
+ uint32_t freqIndex = br.getBits(4);
+
+ int32_t sampleRate = 0;
+ int32_t numChannels = 0;
+ if (freqIndex == 15) {
+ if (br.numBitsLeft() < 28) return ERROR_MALFORMED;
+ sampleRate = br.getBits(24);
+ numChannels = br.getBits(4);
+ } else {
+ if (br.numBitsLeft() < 4) return ERROR_MALFORMED;
+ numChannels = br.getBits(4);
+
+ if (freqIndex == 13 || freqIndex == 14) {
+ return ERROR_MALFORMED;
+ }
+
+ sampleRate = kSamplingRate[freqIndex];
+ }
+
+ if (objectType == AOT_SBR || objectType == AOT_PS) {//SBR specific config per 14496-3 table 1.13
+ if (br.numBitsLeft() < 4) return ERROR_MALFORMED;
+ uint32_t extFreqIndex = br.getBits(4);
+ int32_t extSampleRate;
+ if (extFreqIndex == 15) {
+ if (csd_size < 8) {
+ return ERROR_MALFORMED;
+ }
+ if (br.numBitsLeft() < 24) return ERROR_MALFORMED;
+ extSampleRate = br.getBits(24);
+ } else {
+ if (extFreqIndex == 13 || extFreqIndex == 14) {
+ return ERROR_MALFORMED;
+ }
+ extSampleRate = kSamplingRate[extFreqIndex];
+ }
+ //TODO: save the extension sampling rate value in meta data =>
+ // mLastTrack->meta->setInt32(kKeyExtSampleRate, extSampleRate);
+ }
+
+ switch (numChannels) {
+ // values defined in 14496-3_2009 amendment-4 Table 1.19 - Channel Configuration
+ case 0:
+ case 1:// FC
+ case 2:// FL FR
+ case 3:// FC, FL FR
+ case 4:// FC, FL FR, RC
+ case 5:// FC, FL FR, SL SR
+ case 6:// FC, FL FR, SL SR, LFE
+ //numChannels already contains the right value
+ break;
+ case 11:// FC, FL FR, SL SR, RC, LFE
+ numChannels = 7;
+ break;
+ case 7: // FC, FCL FCR, FL FR, SL SR, LFE
+ case 12:// FC, FL FR, SL SR, RL RR, LFE
+ case 14:// FC, FL FR, SL SR, LFE, FHL FHR
+ numChannels = 8;
+ break;
+ default:
+ return ERROR_UNSUPPORTED;
+ }
+
+ {
+ if (objectType == AOT_SBR || objectType == AOT_PS) {
+ if (br.numBitsLeft() < 5) return ERROR_MALFORMED;
+ objectType = br.getBits(5);
+
+ if (objectType == AOT_ESCAPE) {
+ if (br.numBitsLeft() < 6) return ERROR_MALFORMED;
+ objectType = 32 + br.getBits(6);
+ }
+ }
+ if (objectType == AOT_AAC_LC || objectType == AOT_ER_AAC_LC ||
+ objectType == AOT_ER_AAC_LD || objectType == AOT_ER_AAC_SCAL ||
+ objectType == AOT_ER_BSAC) {
+ if (br.numBitsLeft() < 2) return ERROR_MALFORMED;
+ const int32_t frameLengthFlag = br.getBits(1);
+
+ const int32_t dependsOnCoreCoder = br.getBits(1);
+
+ if (dependsOnCoreCoder ) {
+ if (br.numBitsLeft() < 14) return ERROR_MALFORMED;
+ const int32_t coreCoderDelay = br.getBits(14);
+ }
+
+ int32_t extensionFlag = -1;
+ if (br.numBitsLeft() > 0) {
+ extensionFlag = br.getBits(1);
+ } else {
+ switch (objectType) {
+ // 14496-3 4.5.1.1 extensionFlag
+ case AOT_AAC_LC:
+ extensionFlag = 0;
+ break;
+ case AOT_ER_AAC_LC:
+ case AOT_ER_AAC_SCAL:
+ case AOT_ER_BSAC:
+ case AOT_ER_AAC_LD:
+ extensionFlag = 1;
+ break;
+ default:
+ return ERROR_MALFORMED;
+ break;
+ }
+ ALOGW("csd missing extension flag; assuming %d for object type %u.",
+ extensionFlag, objectType);
+ }
+
+ if (numChannels == 0) {
+ int32_t channelsEffectiveNum = 0;
+ int32_t channelsNum = 0;
+ if (br.numBitsLeft() < 32) {
+ return ERROR_MALFORMED;
+ }
+ const int32_t ElementInstanceTag = br.getBits(4);
+ const int32_t Profile = br.getBits(2);
+ const int32_t SamplingFrequencyIndex = br.getBits(4);
+ const int32_t NumFrontChannelElements = br.getBits(4);
+ const int32_t NumSideChannelElements = br.getBits(4);
+ const int32_t NumBackChannelElements = br.getBits(4);
+ const int32_t NumLfeChannelElements = br.getBits(2);
+ const int32_t NumAssocDataElements = br.getBits(3);
+ const int32_t NumValidCcElements = br.getBits(4);
+
+ const int32_t MonoMixdownPresent = br.getBits(1);
+
+ if (MonoMixdownPresent != 0) {
+ if (br.numBitsLeft() < 4) return ERROR_MALFORMED;
+ const int32_t MonoMixdownElementNumber = br.getBits(4);
+ }
+
+ if (br.numBitsLeft() < 1) return ERROR_MALFORMED;
+ const int32_t StereoMixdownPresent = br.getBits(1);
+ if (StereoMixdownPresent != 0) {
+ if (br.numBitsLeft() < 4) return ERROR_MALFORMED;
+ const int32_t StereoMixdownElementNumber = br.getBits(4);
+ }
+
+ if (br.numBitsLeft() < 1) return ERROR_MALFORMED;
+ const int32_t MatrixMixdownIndexPresent = br.getBits(1);
+ if (MatrixMixdownIndexPresent != 0) {
+ if (br.numBitsLeft() < 3) return ERROR_MALFORMED;
+ const int32_t MatrixMixdownIndex = br.getBits(2);
+ const int32_t PseudoSurroundEnable = br.getBits(1);
+ }
+
+ int i;
+ for (i=0; i < NumFrontChannelElements; i++) {
+ if (br.numBitsLeft() < 5) return ERROR_MALFORMED;
+ const int32_t FrontElementIsCpe = br.getBits(1);
+ const int32_t FrontElementTagSelect = br.getBits(4);
+ channelsNum += FrontElementIsCpe ? 2 : 1;
+ }
+
+ for (i=0; i < NumSideChannelElements; i++) {
+ if (br.numBitsLeft() < 5) return ERROR_MALFORMED;
+ const int32_t SideElementIsCpe = br.getBits(1);
+ const int32_t SideElementTagSelect = br.getBits(4);
+ channelsNum += SideElementIsCpe ? 2 : 1;
+ }
+
+ for (i=0; i < NumBackChannelElements; i++) {
+ if (br.numBitsLeft() < 5) return ERROR_MALFORMED;
+ const int32_t BackElementIsCpe = br.getBits(1);
+ const int32_t BackElementTagSelect = br.getBits(4);
+ channelsNum += BackElementIsCpe ? 2 : 1;
+ }
+ channelsEffectiveNum = channelsNum;
+
+ for (i=0; i < NumLfeChannelElements; i++) {
+ if (br.numBitsLeft() < 4) return ERROR_MALFORMED;
+ const int32_t LfeElementTagSelect = br.getBits(4);
+ channelsNum += 1;
+ }
+ ALOGV("mpeg4 audio channelsNum = %d", channelsNum);
+ ALOGV("mpeg4 audio channelsEffectiveNum = %d", channelsEffectiveNum);
+ numChannels = channelsNum;
+ }
+ }
+ }
+
+ if (numChannels == 0) {
+ return ERROR_UNSUPPORTED;
+ }
+
+ if (mLastTrack == NULL)
+ return ERROR_MALFORMED;
+
+ int32_t prevSampleRate;
+ CHECK(mLastTrack->meta->findInt32(kKeySampleRate, &prevSampleRate));
+
+ if (prevSampleRate != sampleRate) {
+ ALOGV("mpeg4 audio sample rate different from previous setting. "
+ "was: %d, now: %d", prevSampleRate, sampleRate);
+ }
+
+ mLastTrack->meta->setInt32(kKeySampleRate, sampleRate);
+
+ int32_t prevChannelCount;
+ CHECK(mLastTrack->meta->findInt32(kKeyChannelCount, &prevChannelCount));
+
+ if (prevChannelCount != numChannels) {
+ ALOGV("mpeg4 audio channel count different from previous setting. "
+ "was: %d, now: %d", prevChannelCount, numChannels);
+ }
+
+ mLastTrack->meta->setInt32(kKeyChannelCount, numChannels);
+
+ return OK;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+MPEG4Source::MPEG4Source(
+ const sp<MetaData> &format,
+ uint32_t timeScale,
+ const sp<SampleTable> &sampleTable)
+ : mFormat(format),
+ mTimescale(timeScale),
+ mSampleTable(sampleTable) {
+}
+
+MPEG4Source::~MPEG4Source() {
+}
+
+sp<MetaData> MPEG4Source::getFormat() {
+ return mFormat;
+}
+
+class CompositionSorter
+{
+public:
+ bool LessThan(MediaSource::Indice* aFirst, MediaSource::Indice* aSecond) const
+ {
+ return aFirst->start_composition < aSecond->start_composition;
+ }
+
+ bool Equals(MediaSource::Indice* aFirst, MediaSource::Indice* aSecond) const
+ {
+ return aFirst->start_composition == aSecond->start_composition;
+ }
+};
+
+nsTArray<MediaSource::Indice> MPEG4Source::exportIndex()
+{
+ nsTArray<MediaSource::Indice> index;
+ if (!mTimescale || !mSampleTable.get()) {
+ return index;
+ }
+
+ if (!index.SetCapacity(mSampleTable->countSamples(), mozilla::fallible)) {
+ return index;
+ }
+ for (uint32_t sampleIndex = 0; sampleIndex < mSampleTable->countSamples();
+ sampleIndex++) {
+ off64_t offset;
+ size_t size;
+ uint32_t compositionTime;
+ uint32_t duration;
+ bool isSyncSample;
+ uint32_t decodeTime;
+ if (mSampleTable->getMetaDataForSample(sampleIndex, &offset, &size,
+ &compositionTime, &duration,
+ &isSyncSample, &decodeTime) != OK) {
+ ALOGE("Unexpected sample table problem");
+ continue;
+ }
+
+ Indice indice;
+ indice.start_offset = offset;
+ indice.end_offset = offset + size;
+ indice.start_composition = (compositionTime * 1000000ll) / mTimescale;
+ // end_composition is overwritten everywhere except the last frame, where
+ // the presentation duration is equal to the sample duration.
+ indice.end_composition =
+ (compositionTime * 1000000ll + duration * 1000000ll) / mTimescale;
+ indice.sync = isSyncSample;
+ indice.start_decode = (decodeTime * 1000000ll) / mTimescale;
+ index.AppendElement(indice);
+ }
+
+ // Fix up composition durations so we don't end up with any unsightly gaps.
+ if (index.Length() != 0) {
+ nsTArray<Indice*> composition_order;
+ if (!composition_order.SetCapacity(index.Length(), mozilla::fallible)) {
+ return index;
+ }
+ for (uint32_t i = 0; i < index.Length(); i++) {
+ composition_order.AppendElement(&index[i]);
+ }
+
+ composition_order.Sort(CompositionSorter());
+ for (uint32_t i = 0; i + 1 < composition_order.Length(); i++) {
+ composition_order[i]->end_composition =
+ composition_order[i + 1]->start_composition;
+ }
+ }
+
+ return index;
+}
+
+} // namespace stagefright
+
+#undef LOG_TAG
diff --git a/media/libstagefright/frameworks/av/media/libstagefright/MediaBuffer.cpp b/media/libstagefright/frameworks/av/media/libstagefright/MediaBuffer.cpp
new file mode 100644
index 000000000..e41afcc76
--- /dev/null
+++ b/media/libstagefright/frameworks/av/media/libstagefright/MediaBuffer.cpp
@@ -0,0 +1,215 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "MediaBuffer"
+#include <utils/Log.h>
+
+#include <errno.h>
+#include <pthread.h>
+#include <stdlib.h>
+
+#include <media/stagefright/foundation/ABuffer.h>
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/MediaBuffer.h>
+#include <media/stagefright/MetaData.h>
+
+#include <ui/GraphicBuffer.h>
+#include <sys/atomics.h>
+
+namespace stagefright {
+
+MediaBuffer::MediaBuffer(void *data, size_t size)
+ : mObserver(NULL),
+ mNextBuffer(NULL),
+ mRefCount(0),
+ mData(data),
+ mSize(size),
+ mRangeOffset(0),
+ mRangeLength(size),
+ mOwnsData(false),
+ mMetaData(new MetaData),
+ mOriginal(NULL) {
+}
+
+MediaBuffer::MediaBuffer(size_t size)
+ : mObserver(NULL),
+ mNextBuffer(NULL),
+ mRefCount(0),
+ mData(NULL),
+ mSize(size),
+ mRangeOffset(0),
+ mRangeLength(size),
+ mOwnsData(true),
+ mMetaData(new MetaData),
+ mOriginal(NULL) {
+ ensuresize(size);
+}
+
+MediaBuffer::MediaBuffer(const sp<GraphicBuffer>& graphicBuffer)
+ : mObserver(NULL),
+ mNextBuffer(NULL),
+ mRefCount(0),
+ mData(NULL),
+ mSize(1),
+ mRangeOffset(0),
+ mRangeLength(mSize),
+ mGraphicBuffer(graphicBuffer),
+ mOwnsData(false),
+ mMetaData(new MetaData),
+ mOriginal(NULL) {
+}
+
+MediaBuffer::MediaBuffer(const sp<ABuffer> &buffer)
+ : mObserver(NULL),
+ mNextBuffer(NULL),
+ mRefCount(0),
+ mData(buffer->data()),
+ mSize(buffer->size()),
+ mRangeOffset(0),
+ mRangeLength(mSize),
+ mBuffer(buffer),
+ mOwnsData(false),
+ mMetaData(new MetaData),
+ mOriginal(NULL) {
+}
+
+void MediaBuffer::release() {
+ if (mObserver == NULL) {
+ CHECK_EQ(mRefCount, 0);
+ delete this;
+ return;
+ }
+
+ int prevCount = __atomic_dec(&mRefCount);
+ if (prevCount == 1) {
+ if (mObserver == NULL) {
+ delete this;
+ return;
+ }
+
+ mObserver->signalBufferReturned(this);
+ }
+ CHECK(prevCount > 0);
+}
+
+void MediaBuffer::claim() {
+ CHECK(mObserver != NULL);
+ CHECK_EQ(mRefCount, 1);
+
+ mRefCount = 0;
+}
+
+void MediaBuffer::add_ref() {
+ (void) __atomic_inc(&mRefCount);
+}
+
+void *MediaBuffer::data() const {
+ CHECK(mGraphicBuffer == NULL);
+ return mData;
+}
+
+size_t MediaBuffer::size() const {
+ CHECK(mGraphicBuffer == NULL);
+ return mSize;
+}
+
+size_t MediaBuffer::range_offset() const {
+ return mRangeOffset;
+}
+
+size_t MediaBuffer::range_length() const {
+ return mRangeLength;
+}
+
+void MediaBuffer::set_range(size_t offset, size_t length) {
+ if ((mGraphicBuffer == NULL) && (offset + length > mSize)) {
+ ALOGE("offset = %d, length = %d, mSize = %d", offset, length, mSize);
+ }
+ CHECK((mGraphicBuffer != NULL) || (offset + length <= mSize));
+
+ mRangeOffset = offset;
+ mRangeLength = length;
+}
+
+sp<GraphicBuffer> MediaBuffer::graphicBuffer() const {
+ return mGraphicBuffer;
+}
+
+sp<MetaData> MediaBuffer::meta_data() {
+ return mMetaData;
+}
+
+void MediaBuffer::reset() {
+ mMetaData->clear();
+ set_range(0, mSize);
+}
+
+MediaBuffer::~MediaBuffer() {
+ CHECK(mObserver == NULL);
+
+ if (mOriginal != NULL) {
+ mOriginal->release();
+ mOriginal = NULL;
+ }
+}
+
+void MediaBuffer::setObserver(MediaBufferObserver *observer) {
+ CHECK(observer == NULL || mObserver == NULL);
+ mObserver = observer;
+}
+
+void MediaBuffer::setNextBuffer(MediaBuffer *buffer) {
+ mNextBuffer = buffer;
+}
+
+MediaBuffer *MediaBuffer::nextBuffer() {
+ return mNextBuffer;
+}
+
+int MediaBuffer::refcount() const {
+ return mRefCount;
+}
+
+MediaBuffer *MediaBuffer::clone() {
+ CHECK(mGraphicBuffer == NULL);
+
+ MediaBuffer *buffer = new MediaBuffer(mData, mSize);
+ buffer->set_range(mRangeOffset, mRangeLength);
+ buffer->mMetaData = new MetaData(*mMetaData.get());
+
+ add_ref();
+ buffer->mOriginal = this;
+
+ return buffer;
+}
+
+bool MediaBuffer::ensuresize(size_t length) {
+ if (mBufferBackend.Length() >= length) {
+ return true;
+ }
+ // Can't reallocate data we don't owned or shared with another.
+ if (!mOwnsData || refcount()) {
+ return false;
+ }
+ if (!mBufferBackend.SetLength(length, mozilla::fallible)) {
+ return false;
+ }
+ mData = mBufferBackend.Elements();
+ mSize = length;
+ return true;
+}
+
+} // namespace stagefright
diff --git a/media/libstagefright/frameworks/av/media/libstagefright/MediaDefs.cpp b/media/libstagefright/frameworks/av/media/libstagefright/MediaDefs.cpp
new file mode 100644
index 000000000..a1b520b10
--- /dev/null
+++ b/media/libstagefright/frameworks/av/media/libstagefright/MediaDefs.cpp
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <media/stagefright/MediaDefs.h>
+
+namespace stagefright {
+
+const char *MEDIA_MIMETYPE_IMAGE_JPEG = "image/jpeg";
+
+const char *MEDIA_MIMETYPE_VIDEO_VP6 = "video/x-vnd.on2.vp6";
+const char *MEDIA_MIMETYPE_VIDEO_VP8 = "video/x-vnd.on2.vp8";
+const char *MEDIA_MIMETYPE_VIDEO_VP9 = "video/x-vnd.on2.vp9";
+const char *MEDIA_MIMETYPE_VIDEO_AVC = "video/avc";
+const char *MEDIA_MIMETYPE_VIDEO_MPEG4 = "video/mp4v-es";
+const char *MEDIA_MIMETYPE_VIDEO_H263 = "video/3gpp";
+const char *MEDIA_MIMETYPE_VIDEO_MPEG2 = "video/mpeg2";
+const char *MEDIA_MIMETYPE_VIDEO_RAW = "video/raw";
+
+const char *MEDIA_MIMETYPE_AUDIO_AMR_NB = "audio/3gpp";
+const char *MEDIA_MIMETYPE_AUDIO_AMR_WB = "audio/amr-wb";
+const char *MEDIA_MIMETYPE_AUDIO_MPEG = "audio/mpeg";
+const char *MEDIA_MIMETYPE_AUDIO_MPEG_LAYER_I = "audio/mpeg-L1";
+const char *MEDIA_MIMETYPE_AUDIO_MPEG_LAYER_II = "audio/mpeg-L2";
+const char *MEDIA_MIMETYPE_AUDIO_AAC = "audio/mp4a-latm";
+const char *MEDIA_MIMETYPE_AUDIO_QCELP = "audio/qcelp";
+const char *MEDIA_MIMETYPE_AUDIO_VORBIS = "audio/vorbis";
+const char *MEDIA_MIMETYPE_AUDIO_G711_ALAW = "audio/g711-alaw";
+const char *MEDIA_MIMETYPE_AUDIO_G711_MLAW = "audio/g711-mlaw";
+const char *MEDIA_MIMETYPE_AUDIO_RAW = "audio/raw";
+const char *MEDIA_MIMETYPE_AUDIO_FLAC = "audio/flac";
+const char *MEDIA_MIMETYPE_AUDIO_AAC_ADTS = "audio/aac-adts";
+const char *MEDIA_MIMETYPE_AUDIO_MSGSM = "audio/gsm";
+
+const char *MEDIA_MIMETYPE_CONTAINER_MPEG4 = "video/mp4";
+const char *MEDIA_MIMETYPE_CONTAINER_WAV = "audio/x-wav";
+const char *MEDIA_MIMETYPE_CONTAINER_OGG = "application/ogg";
+const char *MEDIA_MIMETYPE_CONTAINER_MATROSKA = "video/x-matroska";
+const char *MEDIA_MIMETYPE_CONTAINER_MPEG2TS = "video/mp2ts";
+const char *MEDIA_MIMETYPE_CONTAINER_AVI = "video/avi";
+const char *MEDIA_MIMETYPE_CONTAINER_MPEG2PS = "video/mp2p";
+
+const char *MEDIA_MIMETYPE_CONTAINER_WVM = "video/wvm";
+
+const char *MEDIA_MIMETYPE_TEXT_3GPP = "text/3gpp-tt";
+const char *MEDIA_MIMETYPE_TEXT_SUBRIP = "application/x-subrip";
+
+} // namespace stagefright
diff --git a/media/libstagefright/frameworks/av/media/libstagefright/MediaSource.cpp b/media/libstagefright/frameworks/av/media/libstagefright/MediaSource.cpp
new file mode 100644
index 000000000..581a26472
--- /dev/null
+++ b/media/libstagefright/frameworks/av/media/libstagefright/MediaSource.cpp
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <media/stagefright/MediaSource.h>
+
+namespace stagefright {
+
+MediaSource::MediaSource() {}
+
+MediaSource::~MediaSource() {}
+
+} // namespace stagefright
diff --git a/media/libstagefright/frameworks/av/media/libstagefright/MetaData.cpp b/media/libstagefright/frameworks/av/media/libstagefright/MetaData.cpp
new file mode 100644
index 000000000..987541758
--- /dev/null
+++ b/media/libstagefright/frameworks/av/media/libstagefright/MetaData.cpp
@@ -0,0 +1,367 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#undef LOG_TAG
+#define LOG_TAG "MetaData"
+#include <utils/Log.h>
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/foundation/AString.h>
+#include <media/stagefright/foundation/hexdump.h>
+#include <media/stagefright/MetaData.h>
+
+namespace stagefright {
+
+MetaData::MetaData() {
+}
+
+MetaData::MetaData(const MetaData &from)
+ : RefBase(),
+ mItems(from.mItems) {
+}
+
+MetaData::~MetaData() {
+ clear();
+}
+
+void MetaData::clear() {
+ mItems.clear();
+}
+
+bool MetaData::remove(uint32_t key) {
+ ssize_t i = mItems.indexOfKey(key);
+
+ if (i < 0) {
+ return false;
+ }
+
+ mItems.removeItemsAt(i);
+
+ return true;
+}
+
+bool MetaData::setCString(uint32_t key, const char *value) {
+ return setData(key, TYPE_C_STRING, value, strlen(value) + 1);
+}
+
+bool MetaData::setInt32(uint32_t key, int32_t value) {
+ return setData(key, TYPE_INT32, &value, sizeof(value));
+}
+
+bool MetaData::setInt64(uint32_t key, int64_t value) {
+ return setData(key, TYPE_INT64, &value, sizeof(value));
+}
+
+bool MetaData::setFloat(uint32_t key, float value) {
+ return setData(key, TYPE_FLOAT, &value, sizeof(value));
+}
+
+bool MetaData::setPointer(uint32_t key, void *value) {
+ return setData(key, TYPE_POINTER, &value, sizeof(value));
+}
+
+bool MetaData::setRect(
+ uint32_t key,
+ int32_t left, int32_t top,
+ int32_t right, int32_t bottom) {
+ Rect r;
+ r.mLeft = left;
+ r.mTop = top;
+ r.mRight = right;
+ r.mBottom = bottom;
+
+ return setData(key, TYPE_RECT, &r, sizeof(r));
+}
+
+bool MetaData::findCString(uint32_t key, const char **value) const {
+ uint32_t type;
+ const void *data;
+ size_t size;
+ if (!findData(key, &type, &data, &size) || type != TYPE_C_STRING) {
+ return false;
+ }
+
+ *value = (const char *)data;
+
+ return true;
+}
+
+bool MetaData::findInt32(uint32_t key, int32_t *value) const {
+ uint32_t type;
+ const void *data;
+ size_t size;
+ if (!findData(key, &type, &data, &size) || type != TYPE_INT32) {
+ return false;
+ }
+
+ CHECK_EQ(size, sizeof(*value));
+
+ *value = *(int32_t *)data;
+
+ return true;
+}
+
+bool MetaData::findInt64(uint32_t key, int64_t *value) const {
+ uint32_t type;
+ const void *data;
+ size_t size;
+ if (!findData(key, &type, &data, &size) || type != TYPE_INT64) {
+ return false;
+ }
+
+ CHECK_EQ(size, sizeof(*value));
+
+ *value = *(int64_t *)data;
+
+ return true;
+}
+
+bool MetaData::findFloat(uint32_t key, float *value) const {
+ uint32_t type;
+ const void *data;
+ size_t size;
+ if (!findData(key, &type, &data, &size) || type != TYPE_FLOAT) {
+ return false;
+ }
+
+ CHECK_EQ(size, sizeof(*value));
+
+ *value = *(float *)data;
+
+ return true;
+}
+
+bool MetaData::findPointer(uint32_t key, void **value) const {
+ uint32_t type;
+ const void *data;
+ size_t size;
+ if (!findData(key, &type, &data, &size) || type != TYPE_POINTER) {
+ return false;
+ }
+
+ CHECK_EQ(size, sizeof(*value));
+
+ *value = *(void **)data;
+
+ return true;
+}
+
+bool MetaData::findRect(
+ uint32_t key,
+ int32_t *left, int32_t *top,
+ int32_t *right, int32_t *bottom) const {
+ uint32_t type;
+ const void *data;
+ size_t size;
+ if (!findData(key, &type, &data, &size) || type != TYPE_RECT) {
+ return false;
+ }
+
+ CHECK_EQ(size, sizeof(Rect));
+
+ const Rect *r = (const Rect *)data;
+ *left = r->mLeft;
+ *top = r->mTop;
+ *right = r->mRight;
+ *bottom = r->mBottom;
+
+ return true;
+}
+
+bool MetaData::setData(
+ uint32_t key, uint32_t type, const void *data, size_t size) {
+ bool overwrote_existing = true;
+
+ ssize_t i = mItems.indexOfKey(key);
+ if (i < 0) {
+ typed_data item;
+ i = mItems.add(key, item);
+
+ overwrote_existing = false;
+ }
+
+ typed_data &item = mItems.editValueAt(i);
+
+ item.setData(type, data, size);
+
+ return overwrote_existing;
+}
+
+bool MetaData::findData(uint32_t key, uint32_t *type,
+ const void **data, size_t *size) const {
+ ssize_t i = mItems.indexOfKey(key);
+
+ if (i < 0) {
+ return false;
+ }
+
+ const typed_data &item = mItems.valueAt(i);
+
+ item.getData(type, data, size);
+
+ return true;
+}
+
+MetaData::typed_data::typed_data()
+ : mType(TYPE_NONE),
+ mSize(0) {
+}
+
+MetaData::typed_data::~typed_data() {
+ clear();
+}
+
+MetaData::typed_data::typed_data(const typed_data &from)
+ : mType(from.mType),
+ mSize(0) {
+ if (allocateStorage(from.mSize)) {
+ memcpy(storage(), from.storage(), mSize);
+ }
+}
+
+MetaData::typed_data &MetaData::typed_data::operator=(
+ const MetaData::typed_data &from) {
+ if (this != &from) {
+ clear();
+ if (allocateStorage(from.mSize)) {
+ mType = from.mType;
+ memcpy(storage(), from.storage(), mSize);
+ }
+ }
+
+ return *this;
+}
+
+void MetaData::typed_data::clear() {
+ freeStorage();
+
+ mType = TYPE_NONE;
+}
+
+void MetaData::typed_data::setData(
+ uint32_t type, const void *data, size_t size) {
+ clear();
+
+ if (allocateStorage(size)) {
+ mType = type;
+ memcpy(storage(), data, size);
+ }
+}
+
+void MetaData::typed_data::getData(
+ uint32_t *type, const void **data, size_t *size) const {
+ *type = mType;
+ *size = mSize;
+ *data = storage();
+}
+
+bool MetaData::typed_data::allocateStorage(size_t size) {
+ // Update mSize now, as it is needed by usesReservoir() below.
+ // (mSize will be reset if the allocation fails further below.)
+ mSize = size;
+
+ if (usesReservoir()) {
+ return true;
+ }
+
+ u.ext_data = malloc(mSize);
+ if (!u.ext_data) {
+ mType = TYPE_NONE;
+ mSize = 0;
+ return false;
+ }
+ return true;
+}
+
+void MetaData::typed_data::freeStorage() {
+ if (!usesReservoir()) {
+ if (u.ext_data) {
+ free(u.ext_data);
+ u.ext_data = NULL;
+ }
+ }
+
+ mSize = 0;
+}
+
+String8 MetaData::typed_data::asString() const {
+ String8 out;
+ const void *data = storage();
+ switch(mType) {
+ case TYPE_NONE:
+ out = String8::format("no type, size %d)", mSize);
+ break;
+ case TYPE_C_STRING:
+ out = String8::format("(char*) %s", (const char *)data);
+ break;
+ case TYPE_INT32:
+ out = String8::format("(int32_t) %d", *(int32_t *)data);
+ break;
+ case TYPE_INT64:
+ out = String8::format("(int64_t) %lld", *(int64_t *)data);
+ break;
+ case TYPE_FLOAT:
+ out = String8::format("(float) %f", *(float *)data);
+ break;
+ case TYPE_POINTER:
+ out = String8::format("(void*) %p", *(void **)data);
+ break;
+ case TYPE_RECT:
+ {
+ const Rect *r = (const Rect *)data;
+ out = String8::format("Rect(%d, %d, %d, %d)",
+ r->mLeft, r->mTop, r->mRight, r->mBottom);
+ break;
+ }
+
+ default:
+ out = String8::format("(unknown type %d, size %d)", mType, mSize);
+ if (mSize <= 48) { // if it's less than three lines of hex data, dump it
+ AString foo;
+ hexdump(data, mSize, 0, &foo);
+ out.append("\n");
+ out.append(foo.c_str());
+ }
+ break;
+ }
+ return out;
+}
+
+static void MakeFourCCString(uint32_t x, char *s) {
+ s[0] = x >> 24;
+ s[1] = (x >> 16) & 0xff;
+ s[2] = (x >> 8) & 0xff;
+ s[3] = x & 0xff;
+ s[4] = '\0';
+}
+
+void MetaData::dumpToLog() const {
+ for (int i = mItems.size(); --i >= 0;) {
+ int32_t key = mItems.keyAt(i);
+ char cc[5];
+ MakeFourCCString(key, cc);
+ const typed_data &item = mItems.valueAt(i);
+ ALOGI("%s: %s", cc, item.asString().string());
+ }
+}
+
+} // namespace stagefright
+
+#undef LOG_TAG
diff --git a/media/libstagefright/frameworks/av/media/libstagefright/SampleIterator.cpp b/media/libstagefright/frameworks/av/media/libstagefright/SampleIterator.cpp
new file mode 100644
index 000000000..37bb2b7a5
--- /dev/null
+++ b/media/libstagefright/frameworks/av/media/libstagefright/SampleIterator.cpp
@@ -0,0 +1,331 @@
+/*
+ * Copyright (C) 2010 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.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "SampleIterator"
+//#define LOG_NDEBUG 0
+#include <utils/Log.h>
+
+#include "include/SampleIterator.h"
+
+#include <arpa/inet.h>
+
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/DataSource.h>
+#include <media/stagefright/Utils.h>
+
+#include "include/SampleTable.h"
+
+namespace stagefright {
+
+SampleIterator::SampleIterator(SampleTable *table)
+ : mTable(table),
+ mInitialized(false),
+ mTimeToSampleIndex(0),
+ mTTSSampleIndex(0),
+ mTTSSampleTime(0),
+ mTTSCount(0),
+ mTTSDuration(0) {
+ reset();
+}
+
+void SampleIterator::reset() {
+ mSampleToChunkIndex = 0;
+ mFirstChunk = 0;
+ mFirstChunkSampleIndex = 0;
+ mStopChunk = 0;
+ mStopChunkSampleIndex = 0;
+ mSamplesPerChunk = 0;
+ mChunkDesc = 0;
+}
+
+status_t SampleIterator::seekTo(uint32_t sampleIndex) {
+ ALOGV("seekTo(%d)", sampleIndex);
+
+ if (sampleIndex >= mTable->mNumSampleSizes) {
+ return ERROR_END_OF_STREAM;
+ }
+
+ if (mTable->mSampleToChunkOffset < 0
+ || mTable->mChunkOffsetOffset < 0
+ || mTable->mSampleSizeOffset < 0
+ || mTable->mTimeToSampleCount == 0) {
+
+ return ERROR_MALFORMED;
+ }
+
+ if (mInitialized && mCurrentSampleIndex == sampleIndex) {
+ return OK;
+ }
+
+ if (!mInitialized || sampleIndex < mFirstChunkSampleIndex) {
+ reset();
+ }
+
+ if (sampleIndex >= mStopChunkSampleIndex) {
+ status_t err;
+ if ((err = findChunkRange(sampleIndex)) != OK) {
+ ALOGE("findChunkRange failed");
+ return err;
+ }
+ }
+
+ if (sampleIndex >= mStopChunkSampleIndex) {
+ return ERROR_MALFORMED;
+ }
+
+ uint32_t chunk =
+ (sampleIndex - mFirstChunkSampleIndex) / mSamplesPerChunk
+ + mFirstChunk;
+
+ if (!mInitialized || chunk != mCurrentChunkIndex) {
+ mCurrentChunkIndex = chunk;
+
+ status_t err;
+ if ((err = getChunkOffset(chunk, &mCurrentChunkOffset)) != OK) {
+ ALOGE("getChunkOffset return error");
+ return err;
+ }
+
+ mCurrentChunkSampleSizes.clear();
+
+ uint32_t firstChunkSampleIndex =
+ mFirstChunkSampleIndex
+ + mSamplesPerChunk * (mCurrentChunkIndex - mFirstChunk);
+
+ for (uint32_t i = 0; i < mSamplesPerChunk; ++i) {
+ size_t sampleSize;
+ if ((err = getSampleSizeDirect(
+ firstChunkSampleIndex + i, &sampleSize)) != OK) {
+ ALOGE("getSampleSizeDirect return error");
+ return err;
+ }
+
+ mCurrentChunkSampleSizes.push(sampleSize);
+ }
+ }
+
+ if (mCurrentChunkSampleSizes.size() != mSamplesPerChunk) {
+ return ERROR_MALFORMED;
+ }
+
+ uint32_t chunkRelativeSampleIndex =
+ (sampleIndex - mFirstChunkSampleIndex) % mSamplesPerChunk;
+
+ // This can never happen unless % operator is buggy.
+ CHECK(chunkRelativeSampleIndex < mSamplesPerChunk);
+
+ mCurrentSampleOffset = mCurrentChunkOffset;
+ for (uint32_t i = 0; i < chunkRelativeSampleIndex; ++i) {
+ mCurrentSampleOffset += mCurrentChunkSampleSizes[i];
+ }
+
+ mCurrentSampleSize = mCurrentChunkSampleSizes[chunkRelativeSampleIndex];
+ if (sampleIndex < mTTSSampleIndex) {
+ mTimeToSampleIndex = 0;
+ mTTSSampleIndex = 0;
+ mTTSSampleTime = 0;
+ mTTSCount = 0;
+ mTTSDuration = 0;
+ }
+
+ status_t err;
+ if ((err = findSampleTime(sampleIndex, &mCurrentSampleTime)) != OK) {
+ ALOGE("findSampleTime return error");
+ return err;
+ }
+
+ // mTTSDuration is set by findSampleTime()
+ mCurrentSampleDuration = mTTSDuration;
+ mCurrentSampleDecodeTime = mTTSSampleTime + mTTSDuration * (sampleIndex -
+ mTTSSampleIndex);
+ mCurrentSampleIndex = sampleIndex;
+
+ mInitialized = true;
+
+ return OK;
+}
+
+status_t SampleIterator::findChunkRange(uint32_t sampleIndex) {
+ CHECK(sampleIndex >= mFirstChunkSampleIndex);
+
+ while (sampleIndex >= mStopChunkSampleIndex) {
+ if (mSampleToChunkIndex == mTable->mNumSampleToChunkOffsets) {
+ return ERROR_OUT_OF_RANGE;
+ }
+
+ mFirstChunkSampleIndex = mStopChunkSampleIndex;
+
+ const SampleTable::SampleToChunkEntry *entry =
+ &mTable->mSampleToChunkEntries[mSampleToChunkIndex];
+
+ mFirstChunk = entry->startChunk;
+ mSamplesPerChunk = entry->samplesPerChunk;
+ mChunkDesc = entry->chunkDesc;
+
+ if (mSampleToChunkIndex + 1 < mTable->mNumSampleToChunkOffsets) {
+ mStopChunk = entry[1].startChunk;
+
+ mStopChunkSampleIndex =
+ mFirstChunkSampleIndex
+ + (mStopChunk - mFirstChunk) * mSamplesPerChunk;
+ } else if (mSamplesPerChunk) {
+ mStopChunk = 0xffffffff;
+ mStopChunkSampleIndex = 0xffffffff;
+ }
+
+ ++mSampleToChunkIndex;
+ }
+
+ return OK;
+}
+
+status_t SampleIterator::getChunkOffset(uint32_t chunk, off64_t *offset) {
+ *offset = 0;
+
+ if (chunk >= mTable->mNumChunkOffsets) {
+ return ERROR_OUT_OF_RANGE;
+ }
+
+ if (mTable->mChunkOffsetType == SampleTable::kChunkOffsetType32) {
+ uint32_t offset32;
+
+ if (mTable->mDataSource->readAt(
+ mTable->mChunkOffsetOffset + 8 + 4 * chunk,
+ &offset32,
+ sizeof(offset32)) < (ssize_t)sizeof(offset32)) {
+ return ERROR_IO;
+ }
+
+ *offset = ntohl(offset32);
+ } else {
+ CHECK_EQ(mTable->mChunkOffsetType, SampleTable::kChunkOffsetType64);
+
+ uint64_t offset64;
+ if (mTable->mDataSource->readAt(
+ mTable->mChunkOffsetOffset + 8 + 8 * chunk,
+ &offset64,
+ sizeof(offset64)) < (ssize_t)sizeof(offset64)) {
+ return ERROR_IO;
+ }
+
+ *offset = ntoh64(offset64);
+ }
+
+ return OK;
+}
+
+status_t SampleIterator::getSampleSizeDirect(
+ uint32_t sampleIndex, size_t *size) {
+ *size = 0;
+
+ if (sampleIndex >= mTable->mNumSampleSizes) {
+ return ERROR_OUT_OF_RANGE;
+ }
+
+ if (mTable->mDefaultSampleSize > 0) {
+ *size = mTable->mDefaultSampleSize;
+ return OK;
+ }
+
+ switch (mTable->mSampleSizeFieldSize) {
+ case 32:
+ {
+ if (mTable->mDataSource->readAt(
+ mTable->mSampleSizeOffset + 12 + 4 * sampleIndex,
+ size, sizeof(*size)) < (ssize_t)sizeof(*size)) {
+ return ERROR_IO;
+ }
+
+ *size = ntohl(*size);
+ break;
+ }
+
+ case 16:
+ {
+ uint16_t x;
+ if (mTable->mDataSource->readAt(
+ mTable->mSampleSizeOffset + 12 + 2 * sampleIndex,
+ &x, sizeof(x)) < (ssize_t)sizeof(x)) {
+ return ERROR_IO;
+ }
+
+ *size = ntohs(x);
+ break;
+ }
+
+ case 8:
+ {
+ uint8_t x;
+ if (mTable->mDataSource->readAt(
+ mTable->mSampleSizeOffset + 12 + sampleIndex,
+ &x, sizeof(x)) < (ssize_t)sizeof(x)) {
+ return ERROR_IO;
+ }
+
+ *size = x;
+ break;
+ }
+
+ default:
+ {
+ CHECK_EQ(mTable->mSampleSizeFieldSize, 4);
+
+ uint8_t x;
+ if (mTable->mDataSource->readAt(
+ mTable->mSampleSizeOffset + 12 + sampleIndex / 2,
+ &x, sizeof(x)) < (ssize_t)sizeof(x)) {
+ return ERROR_IO;
+ }
+
+ *size = (sampleIndex & 1) ? x & 0x0f : x >> 4;
+ break;
+ }
+ }
+
+ return OK;
+}
+
+status_t SampleIterator::findSampleTime(
+ uint32_t sampleIndex, uint32_t *time) {
+ if (sampleIndex >= mTable->mNumSampleSizes) {
+ return ERROR_OUT_OF_RANGE;
+ }
+
+ while (sampleIndex >= mTTSSampleIndex + mTTSCount) {
+ if (mTimeToSampleIndex == mTable->mTimeToSampleCount) {
+ return ERROR_OUT_OF_RANGE;
+ }
+
+ mTTSSampleIndex += mTTSCount;
+ mTTSSampleTime += mTTSCount * mTTSDuration;
+
+ mTTSCount = mTable->mTimeToSample[2 * mTimeToSampleIndex];
+ mTTSDuration = mTable->mTimeToSample[2 * mTimeToSampleIndex + 1];
+
+ ++mTimeToSampleIndex;
+ }
+
+ *time = mTTSSampleTime + mTTSDuration * (sampleIndex - mTTSSampleIndex);
+
+ *time += mTable->getCompositionTimeOffset(sampleIndex);
+
+ return OK;
+}
+
+} // namespace stagefright
+
+#undef LOG_TAG
diff --git a/media/libstagefright/frameworks/av/media/libstagefright/SampleTable.cpp b/media/libstagefright/frameworks/av/media/libstagefright/SampleTable.cpp
new file mode 100644
index 000000000..bbb2227e7
--- /dev/null
+++ b/media/libstagefright/frameworks/av/media/libstagefright/SampleTable.cpp
@@ -0,0 +1,1170 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "SampleTable"
+//#define LOG_NDEBUG 0
+#include <utils/Log.h>
+
+#include "include/SampleTable.h"
+#include "include/SampleIterator.h"
+
+#include <arpa/inet.h>
+
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/DataSource.h>
+#include <media/stagefright/Utils.h>
+
+#include <stdint.h>
+
+namespace stagefright {
+
+// static
+const uint32_t SampleTable::kChunkOffsetType32 = FOURCC('s', 't', 'c', 'o');
+// static
+const uint32_t SampleTable::kChunkOffsetType64 = FOURCC('c', 'o', '6', '4');
+// static
+const uint32_t SampleTable::kSampleSizeType32 = FOURCC('s', 't', 's', 'z');
+// static
+const uint32_t SampleTable::kSampleSizeTypeCompact = FOURCC('s', 't', 'z', '2');
+
+const uint32_t kAuxTypeCenc = FOURCC('c', 'e', 'n', 'c');
+
+static const uint32_t kMAX_ALLOCATION =
+ (SIZE_MAX < INT32_MAX ? SIZE_MAX : INT32_MAX) - 128;
+
+////////////////////////////////////////////////////////////////////////////////
+
+struct SampleTable::CompositionDeltaLookup {
+ CompositionDeltaLookup();
+
+ void setEntries(
+ const uint32_t *deltaEntries, size_t numDeltaEntries);
+
+ uint32_t getCompositionTimeOffset(uint32_t sampleIndex);
+
+private:
+ Mutex mLock;
+
+ const uint32_t *mDeltaEntries;
+ size_t mNumDeltaEntries;
+
+ size_t mCurrentDeltaEntry;
+ size_t mCurrentEntrySampleIndex;
+
+ DISALLOW_EVIL_CONSTRUCTORS(CompositionDeltaLookup);
+};
+
+SampleTable::CompositionDeltaLookup::CompositionDeltaLookup()
+ : mDeltaEntries(NULL),
+ mNumDeltaEntries(0),
+ mCurrentDeltaEntry(0),
+ mCurrentEntrySampleIndex(0) {
+}
+
+void SampleTable::CompositionDeltaLookup::setEntries(
+ const uint32_t *deltaEntries, size_t numDeltaEntries) {
+ Mutex::Autolock autolock(mLock);
+
+ mDeltaEntries = deltaEntries;
+ mNumDeltaEntries = numDeltaEntries;
+ mCurrentDeltaEntry = 0;
+ mCurrentEntrySampleIndex = 0;
+}
+
+uint32_t SampleTable::CompositionDeltaLookup::getCompositionTimeOffset(
+ uint32_t sampleIndex) {
+ Mutex::Autolock autolock(mLock);
+
+ if (mDeltaEntries == NULL) {
+ return 0;
+ }
+
+ if (sampleIndex < mCurrentEntrySampleIndex) {
+ mCurrentDeltaEntry = 0;
+ mCurrentEntrySampleIndex = 0;
+ }
+
+ while (mCurrentDeltaEntry < mNumDeltaEntries) {
+ uint32_t sampleCount = mDeltaEntries[2 * mCurrentDeltaEntry];
+ if (sampleIndex < mCurrentEntrySampleIndex + sampleCount) {
+ return mDeltaEntries[2 * mCurrentDeltaEntry + 1];
+ }
+
+ mCurrentEntrySampleIndex += sampleCount;
+ ++mCurrentDeltaEntry;
+ }
+
+ return 0;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+SampleTable::SampleTable(const sp<DataSource> &source)
+ : mDataSource(source),
+ mChunkOffsetOffset(-1),
+ mChunkOffsetType(0),
+ mNumChunkOffsets(0),
+ mSampleToChunkOffset(-1),
+ mNumSampleToChunkOffsets(0),
+ mSampleSizeOffset(-1),
+ mSampleSizeFieldSize(0),
+ mDefaultSampleSize(0),
+ mNumSampleSizes(0),
+ mTimeToSampleCount(0),
+ mTimeToSample(NULL),
+ mSampleTimeEntries(NULL),
+ mCompositionTimeDeltaEntries(NULL),
+ mNumCompositionTimeDeltaEntries(0),
+ mCompositionDeltaLookup(new CompositionDeltaLookup),
+ mSyncSampleOffset(-1),
+ mNumSyncSamples(0),
+ mSyncSamples(NULL),
+ mLastSyncSampleIndex(0),
+ mSampleToChunkEntries(NULL),
+ mCencInfo(NULL),
+ mCencInfoCount(0),
+ mCencDefaultSize(0)
+{
+ mSampleIterator = new SampleIterator(this);
+}
+
+SampleTable::~SampleTable() {
+ delete[] mSampleToChunkEntries;
+ mSampleToChunkEntries = NULL;
+
+ delete[] mSyncSamples;
+ mSyncSamples = NULL;
+
+ delete mCompositionDeltaLookup;
+ mCompositionDeltaLookup = NULL;
+
+ delete[] mCompositionTimeDeltaEntries;
+ mCompositionTimeDeltaEntries = NULL;
+
+ delete[] mSampleTimeEntries;
+ mSampleTimeEntries = NULL;
+
+ delete[] mTimeToSample;
+ mTimeToSample = NULL;
+
+ if (mCencInfo) {
+ for (uint32_t i = 0; i < mCencInfoCount; i++) {
+ if (mCencInfo[i].mSubsamples) {
+ delete[] mCencInfo[i].mSubsamples;
+ }
+ }
+ delete[] mCencInfo;
+ }
+
+ delete mSampleIterator;
+ mSampleIterator = NULL;
+}
+
+bool SampleTable::isValid() const {
+ return mChunkOffsetOffset >= 0
+ && mSampleToChunkOffset >= 0
+ && mSampleSizeOffset >= 0
+ && mTimeToSample != NULL;
+}
+
+status_t SampleTable::setChunkOffsetParams(
+ uint32_t type, off64_t data_offset, size_t data_size) {
+ if (mChunkOffsetOffset >= 0) {
+ return ERROR_MALFORMED;
+ }
+
+ CHECK(type == kChunkOffsetType32 || type == kChunkOffsetType64);
+
+ mChunkOffsetOffset = data_offset;
+ mChunkOffsetType = type;
+
+ if (data_size < 8) {
+ return ERROR_MALFORMED;
+ }
+
+ uint8_t header[8];
+ if (mDataSource->readAt(
+ data_offset, header, sizeof(header)) < (ssize_t)sizeof(header)) {
+ return ERROR_IO;
+ }
+
+ if (U32_AT(header) != 0) {
+ // Expected version = 0, flags = 0.
+ return ERROR_MALFORMED;
+ }
+
+ mNumChunkOffsets = U32_AT(&header[4]);
+
+ if (mChunkOffsetType == kChunkOffsetType32) {
+ if (data_size < 8 + (uint64_t)mNumChunkOffsets * 4) {
+ return ERROR_MALFORMED;
+ }
+ } else {
+ if (data_size < 8 + (uint64_t)mNumChunkOffsets * 8) {
+ return ERROR_MALFORMED;
+ }
+ }
+
+ return OK;
+}
+
+status_t SampleTable::setSampleToChunkParams(
+ off64_t data_offset, size_t data_size) {
+ if (mSampleToChunkOffset >= 0) {
+ return ERROR_MALFORMED;
+ }
+
+ mSampleToChunkOffset = data_offset;
+
+ if (data_size < 8) {
+ return ERROR_MALFORMED;
+ }
+
+ uint8_t header[8];
+ if (mDataSource->readAt(
+ data_offset, header, sizeof(header)) < (ssize_t)sizeof(header)) {
+ return ERROR_IO;
+ }
+
+ if (U32_AT(header) != 0) {
+ // Expected version = 0, flags = 0.
+ return ERROR_MALFORMED;
+ }
+
+ mNumSampleToChunkOffsets = U32_AT(&header[4]);
+
+ if (data_size < 8 + (uint64_t)mNumSampleToChunkOffsets * 12) {
+ return ERROR_MALFORMED;
+ }
+
+ mSampleToChunkEntries =
+ new (mozilla::fallible) SampleToChunkEntry[mNumSampleToChunkOffsets];
+ if (!mSampleToChunkEntries) {
+ return ERROR_BUFFER_TOO_SMALL;
+ }
+
+ for (uint32_t i = 0; i < mNumSampleToChunkOffsets; ++i) {
+ uint8_t buffer[12];
+ if (mDataSource->readAt(
+ mSampleToChunkOffset + 8 + i * 12, buffer, sizeof(buffer))
+ != (ssize_t)sizeof(buffer)) {
+ return ERROR_IO;
+ }
+
+ if (!U32_AT(buffer)) {
+ ALOGE("error reading sample to chunk table");
+ return ERROR_MALFORMED; // chunk index is 1 based in the spec.
+ }
+
+ // We want the chunk index to be 0-based.
+ mSampleToChunkEntries[i].startChunk = U32_AT(buffer) - 1;
+ mSampleToChunkEntries[i].samplesPerChunk = U32_AT(&buffer[4]);
+ mSampleToChunkEntries[i].chunkDesc = U32_AT(&buffer[8]);
+ }
+
+ return OK;
+}
+
+status_t SampleTable::setSampleSizeParams(
+ uint32_t type, off64_t data_offset, size_t data_size) {
+ if (mSampleSizeOffset >= 0) {
+ return ERROR_MALFORMED;
+ }
+
+ CHECK(type == kSampleSizeType32 || type == kSampleSizeTypeCompact);
+
+ mSampleSizeOffset = data_offset;
+
+ if (data_size < 12) {
+ return ERROR_MALFORMED;
+ }
+
+ uint8_t header[12];
+ if (mDataSource->readAt(
+ data_offset, header, sizeof(header)) < (ssize_t)sizeof(header)) {
+ return ERROR_IO;
+ }
+
+ if (U32_AT(header) != 0) {
+ // Expected version = 0, flags = 0.
+ return ERROR_MALFORMED;
+ }
+
+ mDefaultSampleSize = U32_AT(&header[4]);
+ mNumSampleSizes = U32_AT(&header[8]);
+
+ if (type == kSampleSizeType32) {
+ mSampleSizeFieldSize = 32;
+
+ if (mDefaultSampleSize != 0) {
+ return OK;
+ }
+
+ if (data_size < 12 + (uint64_t)mNumSampleSizes * 4) {
+ return ERROR_MALFORMED;
+ }
+ } else {
+ if ((mDefaultSampleSize & 0xffffff00) != 0) {
+ // The high 24 bits are reserved and must be 0.
+ return ERROR_MALFORMED;
+ }
+
+ mSampleSizeFieldSize = mDefaultSampleSize & 0xff;
+ mDefaultSampleSize = 0;
+
+ if (mSampleSizeFieldSize != 4 && mSampleSizeFieldSize != 8
+ && mSampleSizeFieldSize != 16) {
+ return ERROR_MALFORMED;
+ }
+
+ if (data_size < 12 + ((uint64_t)mNumSampleSizes * mSampleSizeFieldSize + 4) / 8) {
+ return ERROR_MALFORMED;
+ }
+ }
+
+ return OK;
+}
+
+status_t SampleTable::setTimeToSampleParams(
+ off64_t data_offset, size_t data_size) {
+ if (mTimeToSample != NULL || data_size < 8) {
+ return ERROR_MALFORMED;
+ }
+
+ uint8_t header[8];
+ if (mDataSource->readAt(
+ data_offset, header, sizeof(header)) < (ssize_t)sizeof(header)) {
+ return ERROR_IO;
+ }
+
+ if (U32_AT(header) != 0) {
+ // Expected version = 0, flags = 0.
+ return ERROR_MALFORMED;
+ }
+
+ mTimeToSampleCount = U32_AT(&header[4]);
+ if (mTimeToSampleCount > kMAX_ALLOCATION / 2 / sizeof(uint32_t)) {
+ // Avoid later overflow.
+ return ERROR_MALFORMED;
+ }
+
+ size_t size = sizeof(uint32_t) * mTimeToSampleCount * 2;
+
+ mTimeToSample = new (mozilla::fallible) uint32_t[mTimeToSampleCount * 2];
+ if (!mTimeToSample) {
+ return ERROR_BUFFER_TOO_SMALL;
+ }
+
+ if (mDataSource->readAt(
+ data_offset + 8, mTimeToSample, size) < (ssize_t)size) {
+ return ERROR_IO;
+ }
+
+ for (uint32_t i = 0; i < mTimeToSampleCount * 2; ++i) {
+ mTimeToSample[i] = ntohl(mTimeToSample[i]);
+ }
+
+ return OK;
+}
+
+status_t SampleTable::setCompositionTimeToSampleParams(
+ off64_t data_offset, size_t data_size) {
+ ALOGV("There are reordered frames present.");
+
+ if (mCompositionTimeDeltaEntries != NULL || data_size < 8) {
+ return ERROR_MALFORMED;
+ }
+
+ uint8_t header[8];
+ if (mDataSource->readAt(
+ data_offset, header, sizeof(header))
+ < (ssize_t)sizeof(header)) {
+ return ERROR_IO;
+ }
+
+ uint32_t numEntries = U32_AT(&header[4]);
+
+ if (U32_AT(header) != 0 && numEntries) {
+ // Expected version = 0, flags = 0.
+ return ERROR_MALFORMED;
+ }
+
+ if (data_size < ((uint64_t)numEntries + 1) * 8) {
+ return ERROR_MALFORMED;
+ }
+
+ mNumCompositionTimeDeltaEntries = numEntries;
+ mCompositionTimeDeltaEntries = new (mozilla::fallible) uint32_t[2 * numEntries];
+ if (!mCompositionTimeDeltaEntries) {
+ return ERROR_BUFFER_TOO_SMALL;
+ }
+
+ if (mDataSource->readAt(
+ data_offset + 8, mCompositionTimeDeltaEntries, numEntries * 8)
+ < (ssize_t)numEntries * 8) {
+ delete[] mCompositionTimeDeltaEntries;
+ mCompositionTimeDeltaEntries = NULL;
+
+ return ERROR_IO;
+ }
+
+ for (size_t i = 0; i < 2 * numEntries; ++i) {
+ mCompositionTimeDeltaEntries[i] = ntohl(mCompositionTimeDeltaEntries[i]);
+ }
+
+ mCompositionDeltaLookup->setEntries(
+ mCompositionTimeDeltaEntries, mNumCompositionTimeDeltaEntries);
+
+ return OK;
+}
+
+status_t SampleTable::setSyncSampleParams(off64_t data_offset, size_t data_size) {
+ if (mSyncSampleOffset >= 0 || data_size < 8) {
+ return ERROR_MALFORMED;
+ }
+
+ mSyncSampleOffset = data_offset;
+
+ uint8_t header[8];
+ if (mDataSource->readAt(
+ data_offset, header, sizeof(header)) < (ssize_t)sizeof(header)) {
+ return ERROR_IO;
+ }
+
+ if (U32_AT(header) != 0) {
+ // Expected version = 0, flags = 0.
+ return ERROR_MALFORMED;
+ }
+
+ mNumSyncSamples = U32_AT(&header[4]);
+ if (mNumSyncSamples > kMAX_ALLOCATION / sizeof(uint32_t)) {
+ // Avoid later overflow.
+ return ERROR_MALFORMED;
+ }
+
+ if (mNumSyncSamples < 2) {
+ ALOGV("Table of sync samples is empty or has only a single entry!");
+ }
+
+ mSyncSamples = new (mozilla::fallible) uint32_t[mNumSyncSamples];
+ if (!mSyncSamples) {
+ return ERROR_BUFFER_TOO_SMALL;
+ }
+ size_t size = mNumSyncSamples * sizeof(uint32_t);
+ if (mDataSource->readAt(mSyncSampleOffset + 8, mSyncSamples, size)
+ != (ssize_t)size) {
+ return ERROR_IO;
+ }
+
+ for (size_t i = 0; i < mNumSyncSamples; ++i) {
+ mSyncSamples[i] = ntohl(mSyncSamples[i]) - 1;
+ }
+
+ return OK;
+}
+
+static status_t
+validateCencBoxHeader(
+ sp<DataSource>& data_source, off64_t& data_offset,
+ uint8_t* out_version, uint32_t* out_aux_type) {
+ *out_aux_type = 0;
+
+ if (data_source->readAt(data_offset++, out_version, 1) < 1) {
+ ALOGE("error reading sample aux info header");
+ return ERROR_IO;
+ }
+
+ uint32_t flags;
+ if (!data_source->getUInt24(data_offset, &flags)) {
+ ALOGE("error reading sample aux info flags");
+ return ERROR_IO;
+ }
+ data_offset += 3;
+
+ if (flags & 1) {
+ uint32_t aux_type;
+ uint32_t aux_param;
+ if (!data_source->getUInt32(data_offset, &aux_type) ||
+ !data_source->getUInt32(data_offset + 4, &aux_param)) {
+ ALOGE("error reading aux info type");
+ return ERROR_IO;
+ }
+ data_offset += 8;
+ *out_aux_type = aux_type;
+ }
+
+ return OK;
+}
+
+status_t
+SampleTable::setSampleAuxiliaryInformationSizeParams(
+ off64_t data_offset, size_t data_size, uint32_t drm_scheme) {
+ off64_t data_end = data_offset + data_size;
+
+ uint8_t version;
+ uint32_t aux_type;
+ status_t err = validateCencBoxHeader(
+ mDataSource, data_offset, &version, &aux_type);
+ if (err != OK) {
+ return err;
+ }
+
+ if (aux_type && aux_type != kAuxTypeCenc && drm_scheme != kAuxTypeCenc) {
+ // Quietly skip aux types we don't care about.
+ return OK;
+ }
+
+ if (!mCencSizes.IsEmpty() || mCencDefaultSize) {
+ ALOGE("duplicate cenc saiz box");
+ return ERROR_MALFORMED;
+ }
+
+ if (version) {
+ ALOGV("unsupported cenc saiz version");
+ return ERROR_UNSUPPORTED;
+ }
+
+ if (mDataSource->readAt(
+ data_offset++, &mCencDefaultSize, sizeof(mCencDefaultSize))
+ < sizeof(mCencDefaultSize)) {
+ return ERROR_IO;
+ }
+
+ if (!mDataSource->getUInt32(data_offset, &mCencInfoCount)) {
+ return ERROR_IO;
+ }
+ data_offset += 4;
+
+ if (!mCencDefaultSize) {
+ if (!mCencSizes.InsertElementsAt(0, mCencInfoCount, mozilla::fallible)) {
+ return ERROR_IO;
+ }
+ if (mDataSource->readAt(
+ data_offset, mCencSizes.Elements(), mCencInfoCount)
+ < mCencInfoCount) {
+ return ERROR_IO;
+ }
+ data_offset += mCencInfoCount;
+ }
+
+ if (data_offset != data_end) {
+ ALOGW("wrong saiz data size, expected %lu, actual %lu",
+ data_size, data_offset - (data_end - data_size));
+ // Continue, assume extra data is not important.
+ // Parser will skip past the box end.
+ }
+
+ return parseSampleCencInfo();
+}
+
+status_t
+SampleTable::setSampleAuxiliaryInformationOffsetParams(
+ off64_t data_offset, size_t data_size, uint32_t drm_scheme) {
+ off64_t data_end = data_offset + data_size;
+
+ uint8_t version;
+ uint32_t aux_type;
+ status_t err = validateCencBoxHeader(mDataSource, data_offset,
+ &version, &aux_type);
+ if (err != OK) {
+ return err;
+ }
+
+ if (aux_type && aux_type != kAuxTypeCenc && drm_scheme != kAuxTypeCenc) {
+ // Quietly skip aux types we don't care about.
+ return OK;
+ }
+
+ if (!mCencOffsets.IsEmpty()) {
+ ALOGE("duplicate cenc saio box");
+ return ERROR_MALFORMED;
+ }
+
+ uint32_t cencOffsetCount;
+ if (!mDataSource->getUInt32(data_offset, &cencOffsetCount)) {
+ ALOGE("error reading cenc aux info offset count");
+ return ERROR_IO;
+ }
+ data_offset += 4;
+
+ if (cencOffsetCount >= kMAX_ALLOCATION) {
+ return ERROR_MALFORMED;
+ }
+ if (!version) {
+ if (!mCencOffsets.SetCapacity(cencOffsetCount, mozilla::fallible)) {
+ return ERROR_MALFORMED;
+ }
+ for (uint32_t i = 0; i < cencOffsetCount; i++) {
+ uint32_t tmp;
+ if (!mDataSource->getUInt32(data_offset, &tmp)) {
+ ALOGE("error reading cenc aux info offsets");
+ return ERROR_IO;
+ }
+ // FIXME: Make this infallible after bug 968520 is done.
+ MOZ_ALWAYS_TRUE(mCencOffsets.AppendElement(tmp, mozilla::fallible));
+ data_offset += 4;
+ }
+ } else {
+ if (!mCencOffsets.SetLength(cencOffsetCount, mozilla::fallible)) {
+ return ERROR_MALFORMED;
+ }
+ for (uint32_t i = 0; i < cencOffsetCount; i++) {
+ if (!mDataSource->getUInt64(data_offset, &mCencOffsets[i])) {
+ ALOGE("error reading cenc aux info offsets");
+ return ERROR_IO;
+ }
+ data_offset += 8;
+ }
+ }
+
+ if (data_offset != data_end) {
+ ALOGW("wrong saio data size, expected %lu, actual %lu",
+ data_size, data_offset - (data_end - data_size));
+ // Continue, assume extra data is not important.
+ // Parser will skip past the box end.
+ }
+
+ return parseSampleCencInfo();
+}
+
+status_t
+SampleTable::parseSampleCencInfo() {
+ if ((!mCencDefaultSize && !mCencInfoCount) || mCencOffsets.IsEmpty()) {
+ // We don't have all the cenc information we need yet. Quietly fail and
+ // hope we get the data we need later in the track header.
+ ALOGV("Got half of cenc saio/saiz pair. Deferring parse until we get the other half.");
+ return OK;
+ }
+
+ if ((mCencOffsets.Length() > 1 && mCencOffsets.Length() < mCencInfoCount) ||
+ (!mCencDefaultSize && mCencSizes.Length() < mCencInfoCount)) {
+ return ERROR_MALFORMED;
+ }
+
+ if (mCencInfoCount > kMAX_ALLOCATION / sizeof(SampleCencInfo)) {
+ // Avoid future OOM.
+ return ERROR_MALFORMED;
+ }
+
+ mCencInfo = new (mozilla::fallible) SampleCencInfo[mCencInfoCount];
+ if (!mCencInfo) {
+ return ERROR_BUFFER_TOO_SMALL;
+ }
+ for (uint32_t i = 0; i < mCencInfoCount; i++) {
+ mCencInfo[i].mSubsamples = NULL;
+ }
+
+ uint64_t nextOffset = mCencOffsets[0];
+ for (uint32_t i = 0; i < mCencInfoCount; i++) {
+ uint8_t size = mCencDefaultSize ? mCencDefaultSize : mCencSizes[i];
+ uint64_t offset = mCencOffsets.Length() == 1 ? nextOffset : mCencOffsets[i];
+ nextOffset = offset + size;
+
+ auto& info = mCencInfo[i];
+
+ if (size < IV_BYTES) {
+ ALOGE("cenc aux info too small");
+ return ERROR_MALFORMED;
+ }
+
+ if (mDataSource->readAt(offset, info.mIV, IV_BYTES) < IV_BYTES) {
+ ALOGE("couldn't read init vector");
+ return ERROR_IO;
+ }
+ offset += IV_BYTES;
+
+ if (size == IV_BYTES) {
+ info.mSubsampleCount = 0;
+ continue;
+ }
+
+ if (size < IV_BYTES + sizeof(info.mSubsampleCount)) {
+ ALOGE("subsample count overflows sample aux info buffer");
+ return ERROR_MALFORMED;
+ }
+
+ if (!mDataSource->getUInt16(offset, &info.mSubsampleCount)) {
+ ALOGE("error reading sample cenc info subsample count");
+ return ERROR_IO;
+ }
+ offset += sizeof(info.mSubsampleCount);
+
+ if (size < IV_BYTES + sizeof(info.mSubsampleCount) + info.mSubsampleCount * 6) {
+ ALOGE("subsample descriptions overflow sample aux info buffer");
+ return ERROR_MALFORMED;
+ }
+
+ info.mSubsamples = new (mozilla::fallible) SampleCencInfo::SubsampleSizes[info.mSubsampleCount];
+ if (!info.mSubsamples) {
+ return ERROR_BUFFER_TOO_SMALL;
+ }
+ for (uint16_t j = 0; j < info.mSubsampleCount; j++) {
+ auto& subsample = info.mSubsamples[j];
+ if (!mDataSource->getUInt16(offset, &subsample.mClearBytes) ||
+ !mDataSource->getUInt32(offset + sizeof(subsample.mClearBytes),
+ &subsample.mCipherBytes)) {
+ ALOGE("error reading cenc subsample aux info");
+ return ERROR_IO;
+ }
+ offset += 6;
+ }
+ }
+
+ return OK;
+}
+
+uint32_t SampleTable::countChunkOffsets() const {
+ return mNumChunkOffsets;
+}
+
+uint32_t SampleTable::countSamples() const {
+ return mNumSampleSizes;
+}
+
+status_t SampleTable::getMaxSampleSize(size_t *max_size) {
+ Mutex::Autolock autoLock(mLock);
+
+ *max_size = 0;
+
+ for (uint32_t i = 0; i < mNumSampleSizes; ++i) {
+ size_t sample_size;
+ status_t err = getSampleSize_l(i, &sample_size);
+
+ if (err != OK) {
+ return err;
+ }
+
+ if (sample_size > *max_size) {
+ *max_size = sample_size;
+ }
+ }
+
+ return OK;
+}
+
+uint32_t abs_difference(uint32_t time1, uint32_t time2) {
+ return time1 > time2 ? time1 - time2 : time2 - time1;
+}
+
+// static
+int SampleTable::CompareIncreasingTime(const void *_a, const void *_b) {
+ const SampleTimeEntry *a = (const SampleTimeEntry *)_a;
+ const SampleTimeEntry *b = (const SampleTimeEntry *)_b;
+
+ if (a->mCompositionTime < b->mCompositionTime) {
+ return -1;
+ } else if (a->mCompositionTime > b->mCompositionTime) {
+ return 1;
+ }
+
+ return 0;
+}
+
+status_t SampleTable::buildSampleEntriesTable() {
+ Mutex::Autolock autoLock(mLock);
+
+ if (mSampleTimeEntries != NULL) {
+ return OK;
+ }
+
+ mSampleTimeEntries = new (mozilla::fallible) SampleTimeEntry[mNumSampleSizes];
+ if (!mSampleTimeEntries) {
+ return ERROR_BUFFER_TOO_SMALL;
+ }
+
+ uint32_t sampleIndex = 0;
+ uint32_t sampleTime = 0;
+
+ for (uint32_t i = 0; i < mTimeToSampleCount; ++i) {
+ uint32_t n = mTimeToSample[2 * i];
+ uint32_t delta = mTimeToSample[2 * i + 1];
+
+ for (uint32_t j = 0; j < n; ++j) {
+ if (sampleIndex < mNumSampleSizes) {
+ // Technically this should always be the case if the file
+ // is well-formed, but you know... there's (gasp) malformed
+ // content out there.
+
+ mSampleTimeEntries[sampleIndex].mSampleIndex = sampleIndex;
+
+ uint32_t compTimeDelta =
+ mCompositionDeltaLookup->getCompositionTimeOffset(
+ sampleIndex);
+
+ mSampleTimeEntries[sampleIndex].mCompositionTime =
+ sampleTime + compTimeDelta;
+ }
+
+ ++sampleIndex;
+ sampleTime += delta;
+ }
+ }
+
+ qsort(mSampleTimeEntries, mNumSampleSizes, sizeof(SampleTimeEntry),
+ CompareIncreasingTime);
+ return OK;
+}
+
+status_t SampleTable::findSampleAtTime(
+ uint32_t req_time, uint32_t *sample_index, uint32_t flags) {
+ status_t err = buildSampleEntriesTable();
+ if (err != OK) {
+ return err;
+ }
+
+ uint32_t left = 0;
+ uint32_t right = mNumSampleSizes;
+ while (left < right) {
+ uint32_t center = (left + right) / 2;
+ uint32_t centerTime = mSampleTimeEntries[center].mCompositionTime;
+
+ if (req_time < centerTime) {
+ right = center;
+ } else if (req_time > centerTime) {
+ left = center + 1;
+ } else {
+ left = center;
+ break;
+ }
+ }
+
+ if (left == mNumSampleSizes) {
+ if (flags == kFlagAfter) {
+ return ERROR_OUT_OF_RANGE;
+ }
+
+ --left;
+ }
+
+ uint32_t closestIndex = left;
+
+ switch (flags) {
+ case kFlagBefore:
+ {
+ while (closestIndex > 0
+ && mSampleTimeEntries[closestIndex].mCompositionTime
+ > req_time) {
+ --closestIndex;
+ }
+ break;
+ }
+
+ case kFlagAfter:
+ {
+ while (closestIndex + 1 < mNumSampleSizes
+ && mSampleTimeEntries[closestIndex].mCompositionTime
+ < req_time) {
+ ++closestIndex;
+ }
+ break;
+ }
+
+ default:
+ {
+ CHECK(flags == kFlagClosest);
+
+ if (closestIndex > 0) {
+ // Check left neighbour and pick closest.
+ uint32_t absdiff1 =
+ abs_difference(
+ mSampleTimeEntries[closestIndex].mCompositionTime,
+ req_time);
+
+ uint32_t absdiff2 =
+ abs_difference(
+ mSampleTimeEntries[closestIndex - 1].mCompositionTime,
+ req_time);
+
+ if (absdiff1 > absdiff2) {
+ closestIndex = closestIndex - 1;
+ }
+ }
+
+ break;
+ }
+ }
+
+ *sample_index = mSampleTimeEntries[closestIndex].mSampleIndex;
+
+ return OK;
+}
+
+status_t SampleTable::findSyncSampleNear(
+ uint32_t start_sample_index, uint32_t *sample_index, uint32_t flags) {
+ Mutex::Autolock autoLock(mLock);
+
+ *sample_index = 0;
+
+ if (mSyncSampleOffset < 0) {
+ // All samples are sync-samples.
+ *sample_index = start_sample_index;
+ return OK;
+ }
+
+ if (mNumSyncSamples == 0) {
+ *sample_index = 0;
+ return OK;
+ }
+
+ uint32_t left = 0;
+ uint32_t right = mNumSyncSamples;
+ while (left < right) {
+ uint32_t center = left + (right - left) / 2;
+ uint32_t x = mSyncSamples[center];
+
+ if (start_sample_index < x) {
+ right = center;
+ } else if (start_sample_index > x) {
+ left = center + 1;
+ } else {
+ left = center;
+ break;
+ }
+ }
+ if (left == mNumSyncSamples) {
+ if (flags == kFlagAfter) {
+ ALOGE("tried to find a sync frame after the last one: %d", left);
+ return ERROR_OUT_OF_RANGE;
+ }
+ left = left - 1;
+ }
+
+ // Now ssi[left] is the sync sample index just before (or at)
+ // start_sample_index.
+ // Also start_sample_index < ssi[left + 1], if left + 1 < mNumSyncSamples.
+
+ uint32_t x = mSyncSamples[left];
+
+ if (left + 1 < mNumSyncSamples) {
+ uint32_t y = mSyncSamples[left + 1];
+
+ // our sample lies between sync samples x and y.
+
+ status_t err = mSampleIterator->seekTo(start_sample_index);
+ if (err != OK) {
+ return err;
+ }
+
+ uint32_t sample_time = mSampleIterator->getSampleTime();
+
+ err = mSampleIterator->seekTo(x);
+ if (err != OK) {
+ return err;
+ }
+ uint32_t x_time = mSampleIterator->getSampleTime();
+
+ err = mSampleIterator->seekTo(y);
+ if (err != OK) {
+ return err;
+ }
+
+ uint32_t y_time = mSampleIterator->getSampleTime();
+
+ if (abs_difference(x_time, sample_time)
+ > abs_difference(y_time, sample_time)) {
+ // Pick the sync sample closest (timewise) to the start-sample.
+ x = y;
+ ++left;
+ }
+ }
+
+ switch (flags) {
+ case kFlagBefore:
+ {
+ if (x > start_sample_index) {
+ CHECK(left > 0);
+
+ x = mSyncSamples[left - 1];
+
+ if (x > start_sample_index) {
+ // The table of sync sample indices was not sorted
+ // properly.
+ return ERROR_MALFORMED;
+ }
+ }
+ break;
+ }
+
+ case kFlagAfter:
+ {
+ if (x < start_sample_index) {
+ if (left + 1 >= mNumSyncSamples) {
+ return ERROR_OUT_OF_RANGE;
+ }
+
+ x = mSyncSamples[left + 1];
+
+ if (x < start_sample_index) {
+ // The table of sync sample indices was not sorted
+ // properly.
+ return ERROR_MALFORMED;
+ }
+ }
+
+ break;
+ }
+
+ default:
+ break;
+ }
+
+ *sample_index = x;
+
+ return OK;
+}
+
+status_t SampleTable::findThumbnailSample(uint32_t *sample_index) {
+ Mutex::Autolock autoLock(mLock);
+
+ if (mSyncSampleOffset < 0) {
+ // All samples are sync-samples.
+ *sample_index = 0;
+ return OK;
+ }
+
+ uint32_t bestSampleIndex = 0;
+ size_t maxSampleSize = 0;
+
+ static const size_t kMaxNumSyncSamplesToScan = 20;
+
+ // Consider the first kMaxNumSyncSamplesToScan sync samples and
+ // pick the one with the largest (compressed) size as the thumbnail.
+
+ size_t numSamplesToScan = mNumSyncSamples;
+ if (numSamplesToScan > kMaxNumSyncSamplesToScan) {
+ numSamplesToScan = kMaxNumSyncSamplesToScan;
+ }
+
+ for (size_t i = 0; i < numSamplesToScan; ++i) {
+ uint32_t x = mSyncSamples[i];
+
+ // Now x is a sample index.
+ size_t sampleSize;
+ status_t err = getSampleSize_l(x, &sampleSize);
+ if (err != OK) {
+ return err;
+ }
+
+ if (i == 0 || sampleSize > maxSampleSize) {
+ bestSampleIndex = x;
+ maxSampleSize = sampleSize;
+ }
+ }
+
+ *sample_index = bestSampleIndex;
+
+ return OK;
+}
+
+status_t SampleTable::getSampleSize_l(
+ uint32_t sampleIndex, size_t *sampleSize) {
+ return mSampleIterator->getSampleSizeDirect(
+ sampleIndex, sampleSize);
+}
+
+status_t SampleTable::getMetaDataForSample(
+ uint32_t sampleIndex,
+ off64_t *offset,
+ size_t *size,
+ uint32_t *compositionTime,
+ uint32_t *duration,
+ bool *isSyncSample,
+ uint32_t *decodeTime) {
+ Mutex::Autolock autoLock(mLock);
+
+ status_t err;
+ if ((err = mSampleIterator->seekTo(sampleIndex)) != OK) {
+ return err;
+ }
+
+ if (offset) {
+ *offset = mSampleIterator->getSampleOffset();
+ }
+
+ if (size) {
+ *size = mSampleIterator->getSampleSize();
+ }
+
+ if (compositionTime) {
+ *compositionTime = mSampleIterator->getSampleTime();
+ }
+
+ if (decodeTime) {
+ *decodeTime = mSampleIterator->getSampleDecodeTime();
+ }
+
+ if (duration) {
+ *duration = mSampleIterator->getSampleDuration();
+ }
+
+ if (isSyncSample) {
+ *isSyncSample = false;
+ if (mSyncSampleOffset < 0) {
+ // Every sample is a sync sample.
+ *isSyncSample = true;
+ } else {
+ size_t i = (mLastSyncSampleIndex < mNumSyncSamples)
+ && (mSyncSamples[mLastSyncSampleIndex] <= sampleIndex)
+ ? mLastSyncSampleIndex : 0;
+
+ while (i < mNumSyncSamples && mSyncSamples[i] < sampleIndex) {
+ ++i;
+ }
+
+ if (i < mNumSyncSamples && mSyncSamples[i] == sampleIndex) {
+ *isSyncSample = true;
+ }
+
+ mLastSyncSampleIndex = i;
+ }
+ }
+
+ return OK;
+}
+
+uint32_t SampleTable::getCompositionTimeOffset(uint32_t sampleIndex) {
+ return mCompositionDeltaLookup->getCompositionTimeOffset(sampleIndex);
+}
+
+status_t
+SampleTable::getSampleCencInfo(
+ uint32_t sample_index, nsTArray<uint16_t>& clear_sizes,
+ nsTArray<uint32_t>& cipher_sizes, uint8_t iv[]) {
+ CHECK(clear_sizes.IsEmpty() && cipher_sizes.IsEmpty());
+
+ if (sample_index >= mCencInfoCount) {
+ ALOGE("cenc info requested for out of range sample index");
+ return ERROR_MALFORMED;
+ }
+
+ auto& info = mCencInfo[sample_index];
+ clear_sizes.SetCapacity(info.mSubsampleCount);
+ cipher_sizes.SetCapacity(info.mSubsampleCount);
+
+ for (uint32_t i = 0; i < info.mSubsampleCount; i++) {
+ clear_sizes.AppendElement(info.mSubsamples[i].mClearBytes);
+ cipher_sizes.AppendElement(info.mSubsamples[i].mCipherBytes);
+ }
+
+ memcpy(iv, info.mIV, IV_BYTES);
+
+ return OK;
+}
+
+} // namespace stagefright
+
+#undef LOG_TAG
diff --git a/media/libstagefright/frameworks/av/media/libstagefright/Utils.cpp b/media/libstagefright/frameworks/av/media/libstagefright/Utils.cpp
new file mode 100644
index 000000000..abc926239
--- /dev/null
+++ b/media/libstagefright/frameworks/av/media/libstagefright/Utils.cpp
@@ -0,0 +1,619 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#undef LOG_TAG
+#define LOG_TAG "Utils"
+#include <utils/Log.h>
+
+#include "include/ESDS.h"
+
+#include <arpa/inet.h>
+#include <cutils/properties.h>
+#include <media/stagefright/foundation/ABuffer.h>
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/foundation/AMessage.h>
+#include <media/stagefright/MetaData.h>
+#include <media/stagefright/MediaDefs.h>
+#include <media/AudioSystem.h>
+#include <media/MediaPlayerInterface.h>
+#include <hardware/audio.h>
+#include <media/stagefright/Utils.h>
+#include <media/AudioParameter.h>
+
+namespace stagefright {
+
+uint16_t U16_AT(const uint8_t *ptr) {
+ return ptr[0] << 8 | ptr[1];
+}
+
+uint32_t U32_AT(const uint8_t *ptr) {
+ return ptr[0] << 24 | ptr[1] << 16 | ptr[2] << 8 | ptr[3];
+}
+
+uint64_t U64_AT(const uint8_t *ptr) {
+ return ((uint64_t)U32_AT(ptr)) << 32 | U32_AT(ptr + 4);
+}
+
+uint16_t U16LE_AT(const uint8_t *ptr) {
+ return ptr[0] | (ptr[1] << 8);
+}
+
+uint32_t U32LE_AT(const uint8_t *ptr) {
+ return ptr[3] << 24 | ptr[2] << 16 | ptr[1] << 8 | ptr[0];
+}
+
+uint64_t U64LE_AT(const uint8_t *ptr) {
+ return ((uint64_t)U32LE_AT(ptr + 4)) << 32 | U32LE_AT(ptr);
+}
+
+uint64_t ntoh64(uint64_t x) {
+ return ((x & 0xFF00000000000000ull) >> 56) |
+ ((x & 0x00FF000000000000ull) >> 40) |
+ ((x & 0x0000FF0000000000ull) >> 24) |
+ ((x & 0x000000FF00000000ull) >> 8) |
+ ((x & 0x00000000FF000000ull) << 8) |
+ ((x & 0x0000000000FF0000ull) << 24) |
+ ((x & 0x000000000000FF00ull) << 40) |
+ ((x & 0x00000000000000FFull) << 56);
+}
+
+// XXX warning: this won't work on big-endian host.
+uint64_t hton64(uint64_t x) {
+ return ((uint64_t)htonl(x & 0xffffffff) << 32) | htonl(x >> 32);
+}
+
+#if 0
+status_t convertMetaDataToMessage(
+ const sp<MetaData> &meta, sp<AMessage> *format) {
+ format->clear();
+
+ const char *mime;
+ CHECK(meta->findCString(kKeyMIMEType, &mime));
+
+ sp<AMessage> msg = new AMessage;
+ msg->setString("mime", mime);
+
+ int64_t durationUs;
+ if (meta->findInt64(kKeyDuration, &durationUs)) {
+ msg->setInt64("durationUs", durationUs);
+ }
+
+ int32_t isSync;
+ if (meta->findInt32(kKeyIsSyncFrame, &isSync) && isSync != 0) {
+ msg->setInt32("is-sync-frame", 1);
+ }
+
+ if (!strncasecmp("video/", mime, 6)) {
+ int32_t width, height;
+ CHECK(meta->findInt32(kKeyWidth, &width));
+ CHECK(meta->findInt32(kKeyHeight, &height));
+
+ msg->setInt32("width", width);
+ msg->setInt32("height", height);
+
+ int32_t sarWidth, sarHeight;
+ if (meta->findInt32(kKeySARWidth, &sarWidth)
+ && meta->findInt32(kKeySARHeight, &sarHeight)) {
+ msg->setInt32("sar-width", sarWidth);
+ msg->setInt32("sar-height", sarHeight);
+ }
+ } else if (!strncasecmp("audio/", mime, 6)) {
+ int32_t numChannels, sampleRate;
+ CHECK(meta->findInt32(kKeyChannelCount, &numChannels));
+ CHECK(meta->findInt32(kKeySampleRate, &sampleRate));
+
+ msg->setInt32("channel-count", numChannels);
+ msg->setInt32("sample-rate", sampleRate);
+
+ int32_t channelMask;
+ if (meta->findInt32(kKeyChannelMask, &channelMask)) {
+ msg->setInt32("channel-mask", channelMask);
+ }
+
+ int32_t delay = 0;
+ if (meta->findInt32(kKeyEncoderDelay, &delay)) {
+ msg->setInt32("encoder-delay", delay);
+ }
+ int32_t padding = 0;
+ if (meta->findInt32(kKeyEncoderPadding, &padding)) {
+ msg->setInt32("encoder-padding", padding);
+ }
+
+ int32_t isADTS;
+ if (meta->findInt32(kKeyIsADTS, &isADTS)) {
+ msg->setInt32("is-adts", true);
+ }
+ }
+
+ int32_t maxInputSize;
+ if (meta->findInt32(kKeyMaxInputSize, &maxInputSize)) {
+ msg->setInt32("max-input-size", maxInputSize);
+ }
+
+ uint32_t type;
+ const void *data;
+ size_t size;
+ if (meta->findData(kKeyAVCC, &type, &data, &size)) {
+ // Parse the AVCDecoderConfigurationRecord
+
+ const uint8_t *ptr = (const uint8_t *)data;
+
+ CHECK(size >= 7);
+ CHECK_EQ((unsigned)ptr[0], 1u); // configurationVersion == 1
+ uint8_t profile = ptr[1];
+ uint8_t level = ptr[3];
+
+ // There is decodable content out there that fails the following
+ // assertion, let's be lenient for now...
+ // CHECK((ptr[4] >> 2) == 0x3f); // reserved
+
+ size_t lengthSize = 1 + (ptr[4] & 3);
+
+ // commented out check below as H264_QVGA_500_NO_AUDIO.3gp
+ // violates it...
+ // CHECK((ptr[5] >> 5) == 7); // reserved
+
+ size_t numSeqParameterSets = ptr[5] & 31;
+
+ ptr += 6;
+ size -= 6;
+
+ sp<ABuffer> buffer = new ABuffer(1024);
+ buffer->setRange(0, 0);
+
+ for (size_t i = 0; i < numSeqParameterSets; ++i) {
+ CHECK(size >= 2);
+ size_t length = U16_AT(ptr);
+
+ ptr += 2;
+ size -= 2;
+
+ CHECK(size >= length);
+
+ memcpy(buffer->data() + buffer->size(), "\x00\x00\x00\x01", 4);
+ memcpy(buffer->data() + buffer->size() + 4, ptr, length);
+ buffer->setRange(0, buffer->size() + 4 + length);
+
+ ptr += length;
+ size -= length;
+ }
+
+ buffer->meta()->setInt32("csd", true);
+ buffer->meta()->setInt64("timeUs", 0);
+
+ msg->setBuffer("csd-0", buffer);
+
+ buffer = new ABuffer(1024);
+ buffer->setRange(0, 0);
+
+ CHECK(size >= 1);
+ size_t numPictureParameterSets = *ptr;
+ ++ptr;
+ --size;
+
+ for (size_t i = 0; i < numPictureParameterSets; ++i) {
+ CHECK(size >= 2);
+ size_t length = U16_AT(ptr);
+
+ ptr += 2;
+ size -= 2;
+
+ CHECK(size >= length);
+
+ memcpy(buffer->data() + buffer->size(), "\x00\x00\x00\x01", 4);
+ memcpy(buffer->data() + buffer->size() + 4, ptr, length);
+ buffer->setRange(0, buffer->size() + 4 + length);
+
+ ptr += length;
+ size -= length;
+ }
+
+ buffer->meta()->setInt32("csd", true);
+ buffer->meta()->setInt64("timeUs", 0);
+ msg->setBuffer("csd-1", buffer);
+ } else if (meta->findData(kKeyESDS, &type, &data, &size)) {
+ ESDS esds((const char *)data, size);
+ CHECK_EQ(esds.InitCheck(), (status_t)OK);
+
+ const void *codec_specific_data;
+ size_t codec_specific_data_size;
+ esds.getCodecSpecificInfo(
+ &codec_specific_data, &codec_specific_data_size);
+
+ sp<ABuffer> buffer = new ABuffer(codec_specific_data_size);
+
+ memcpy(buffer->data(), codec_specific_data,
+ codec_specific_data_size);
+
+ buffer->meta()->setInt32("csd", true);
+ buffer->meta()->setInt64("timeUs", 0);
+ msg->setBuffer("csd-0", buffer);
+ } else if (meta->findData(kKeyVorbisInfo, &type, &data, &size)) {
+ sp<ABuffer> buffer = new ABuffer(size);
+ memcpy(buffer->data(), data, size);
+
+ buffer->meta()->setInt32("csd", true);
+ buffer->meta()->setInt64("timeUs", 0);
+ msg->setBuffer("csd-0", buffer);
+
+ if (!meta->findData(kKeyVorbisBooks, &type, &data, &size)) {
+ return -EINVAL;
+ }
+
+ buffer = new ABuffer(size);
+ memcpy(buffer->data(), data, size);
+
+ buffer->meta()->setInt32("csd", true);
+ buffer->meta()->setInt64("timeUs", 0);
+ msg->setBuffer("csd-1", buffer);
+ }
+
+ *format = msg;
+
+ return OK;
+}
+
+static size_t reassembleAVCC(const sp<ABuffer> &csd0, const sp<ABuffer> csd1, char *avcc) {
+
+ avcc[0] = 1; // version
+ avcc[1] = 0x64; // profile
+ avcc[2] = 0; // unused (?)
+ avcc[3] = 0xd; // level
+ avcc[4] = 0xff; // reserved+size
+
+ size_t i = 0;
+ int numparams = 0;
+ int lastparamoffset = 0;
+ int avccidx = 6;
+ do {
+ if (i >= csd0->size() - 4 ||
+ memcmp(csd0->data() + i, "\x00\x00\x00\x01", 4) == 0) {
+ if (i >= csd0->size() - 4) {
+ // there can't be another param here, so use all the rest
+ i = csd0->size();
+ }
+ ALOGV("block at %d, last was %d", i, lastparamoffset);
+ if (lastparamoffset > 0) {
+ int size = i - lastparamoffset;
+ avcc[avccidx++] = size >> 8;
+ avcc[avccidx++] = size & 0xff;
+ memcpy(avcc+avccidx, csd0->data() + lastparamoffset, size);
+ avccidx += size;
+ numparams++;
+ }
+ i += 4;
+ lastparamoffset = i;
+ } else {
+ i++;
+ }
+ } while(i < csd0->size());
+ ALOGV("csd0 contains %d params", numparams);
+
+ avcc[5] = 0xe0 | numparams;
+ //and now csd-1
+ i = 0;
+ numparams = 0;
+ lastparamoffset = 0;
+ int numpicparamsoffset = avccidx;
+ avccidx++;
+ do {
+ if (i >= csd1->size() - 4 ||
+ memcmp(csd1->data() + i, "\x00\x00\x00\x01", 4) == 0) {
+ if (i >= csd1->size() - 4) {
+ // there can't be another param here, so use all the rest
+ i = csd1->size();
+ }
+ ALOGV("block at %d, last was %d", i, lastparamoffset);
+ if (lastparamoffset > 0) {
+ int size = i - lastparamoffset;
+ avcc[avccidx++] = size >> 8;
+ avcc[avccidx++] = size & 0xff;
+ memcpy(avcc+avccidx, csd1->data() + lastparamoffset, size);
+ avccidx += size;
+ numparams++;
+ }
+ i += 4;
+ lastparamoffset = i;
+ } else {
+ i++;
+ }
+ } while(i < csd1->size());
+ avcc[numpicparamsoffset] = numparams;
+ return avccidx;
+}
+
+static void reassembleESDS(const sp<ABuffer> &csd0, char *esds) {
+ int csd0size = csd0->size();
+ esds[0] = 3; // kTag_ESDescriptor;
+ int esdescriptorsize = 26 + csd0size;
+ CHECK(esdescriptorsize < 268435456); // 7 bits per byte, so max is 2^28-1
+ esds[1] = 0x80 | (esdescriptorsize >> 21);
+ esds[2] = 0x80 | ((esdescriptorsize >> 14) & 0x7f);
+ esds[3] = 0x80 | ((esdescriptorsize >> 7) & 0x7f);
+ esds[4] = (esdescriptorsize & 0x7f);
+ esds[5] = esds[6] = 0; // es id
+ esds[7] = 0; // flags
+ esds[8] = 4; // kTag_DecoderConfigDescriptor
+ int configdescriptorsize = 18 + csd0size;
+ esds[9] = 0x80 | (configdescriptorsize >> 21);
+ esds[10] = 0x80 | ((configdescriptorsize >> 14) & 0x7f);
+ esds[11] = 0x80 | ((configdescriptorsize >> 7) & 0x7f);
+ esds[12] = (configdescriptorsize & 0x7f);
+ esds[13] = 0x40; // objectTypeIndication
+ esds[14] = 0x15; // not sure what 14-25 mean, they are ignored by ESDS.cpp,
+ esds[15] = 0x00; // but the actual values here were taken from a real file.
+ esds[16] = 0x18;
+ esds[17] = 0x00;
+ esds[18] = 0x00;
+ esds[19] = 0x00;
+ esds[20] = 0xfa;
+ esds[21] = 0x00;
+ esds[22] = 0x00;
+ esds[23] = 0x00;
+ esds[24] = 0xfa;
+ esds[25] = 0x00;
+ esds[26] = 5; // kTag_DecoderSpecificInfo;
+ esds[27] = 0x80 | (csd0size >> 21);
+ esds[28] = 0x80 | ((csd0size >> 14) & 0x7f);
+ esds[29] = 0x80 | ((csd0size >> 7) & 0x7f);
+ esds[30] = (csd0size & 0x7f);
+ memcpy((void*)&esds[31], csd0->data(), csd0size);
+ // data following this is ignored, so don't bother appending it
+
+}
+
+void convertMessageToMetaData(const sp<AMessage> &msg, sp<MetaData> &meta) {
+ AString mime;
+ if (msg->findString("mime", &mime)) {
+ meta->setCString(kKeyMIMEType, mime.c_str());
+ } else {
+ ALOGW("did not find mime type");
+ }
+
+ int64_t durationUs;
+ if (msg->findInt64("durationUs", &durationUs)) {
+ meta->setInt64(kKeyDuration, durationUs);
+ }
+
+ int32_t isSync;
+ if (msg->findInt32("is-sync-frame", &isSync) && isSync != 0) {
+ meta->setInt32(kKeyIsSyncFrame, 1);
+ }
+
+ if (mime.startsWith("video/")) {
+ int32_t width;
+ int32_t height;
+ if (msg->findInt32("width", &width) && msg->findInt32("height", &height)) {
+ meta->setInt32(kKeyWidth, width);
+ meta->setInt32(kKeyHeight, height);
+ } else {
+ ALOGW("did not find width and/or height");
+ }
+
+ int32_t sarWidth, sarHeight;
+ if (msg->findInt32("sar-width", &sarWidth)
+ && msg->findInt32("sar-height", &sarHeight)) {
+ meta->setInt32(kKeySARWidth, sarWidth);
+ meta->setInt32(kKeySARHeight, sarHeight);
+ }
+ } else if (mime.startsWith("audio/")) {
+ int32_t numChannels;
+ if (msg->findInt32("channel-count", &numChannels)) {
+ meta->setInt32(kKeyChannelCount, numChannels);
+ }
+ int32_t sampleRate;
+ if (msg->findInt32("sample-rate", &sampleRate)) {
+ meta->setInt32(kKeySampleRate, sampleRate);
+ }
+ int32_t channelMask;
+ if (msg->findInt32("channel-mask", &channelMask)) {
+ meta->setInt32(kKeyChannelMask, channelMask);
+ }
+ int32_t delay = 0;
+ if (msg->findInt32("encoder-delay", &delay)) {
+ meta->setInt32(kKeyEncoderDelay, delay);
+ }
+ int32_t padding = 0;
+ if (msg->findInt32("encoder-padding", &padding)) {
+ meta->setInt32(kKeyEncoderPadding, padding);
+ }
+
+ int32_t isADTS;
+ if (msg->findInt32("is-adts", &isADTS)) {
+ meta->setInt32(kKeyIsADTS, isADTS);
+ }
+ }
+
+ int32_t maxInputSize;
+ if (msg->findInt32("max-input-size", &maxInputSize)) {
+ meta->setInt32(kKeyMaxInputSize, maxInputSize);
+ }
+
+ // reassemble the csd data into its original form
+ sp<ABuffer> csd0;
+ if (msg->findBuffer("csd-0", &csd0)) {
+ if (mime.startsWith("video/")) { // do we need to be stricter than this?
+ sp<ABuffer> csd1;
+ if (msg->findBuffer("csd-1", &csd1)) {
+ char avcc[1024]; // that oughta be enough, right?
+ size_t outsize = reassembleAVCC(csd0, csd1, avcc);
+ meta->setData(kKeyAVCC, kKeyAVCC, avcc, outsize);
+ }
+ } else if (mime.startsWith("audio/")) {
+ int csd0size = csd0->size();
+ char esds[csd0size + 31];
+ reassembleESDS(csd0, esds);
+ meta->setData(kKeyESDS, kKeyESDS, esds, sizeof(esds));
+ }
+ }
+
+ // XXX TODO add whatever other keys there are
+
+#if 0
+ ALOGI("converted %s to:", msg->debugString(0).c_str());
+ meta->dumpToLog();
+#endif
+}
+
+AString MakeUserAgent() {
+ AString ua;
+ ua.append("stagefright/1.2 (Linux;Android ");
+
+#if (PROPERTY_VALUE_MAX < 8)
+#error "PROPERTY_VALUE_MAX must be at least 8"
+#endif
+
+ char value[PROPERTY_VALUE_MAX];
+ property_get("ro.build.version.release", value, "Unknown");
+ ua.append(value);
+ ua.append(")");
+
+ return ua;
+}
+
+status_t sendMetaDataToHal(sp<MediaPlayerBase::AudioSink>& sink,
+ const sp<MetaData>& meta)
+{
+ int32_t sampleRate = 0;
+ int32_t bitRate = 0;
+ int32_t channelMask = 0;
+ int32_t delaySamples = 0;
+ int32_t paddingSamples = 0;
+
+ AudioParameter param = AudioParameter();
+
+ if (meta->findInt32(kKeySampleRate, &sampleRate)) {
+ param.addInt(String8(AUDIO_OFFLOAD_CODEC_SAMPLE_RATE), sampleRate);
+ }
+ if (meta->findInt32(kKeyChannelMask, &channelMask)) {
+ param.addInt(String8(AUDIO_OFFLOAD_CODEC_NUM_CHANNEL), channelMask);
+ }
+ if (meta->findInt32(kKeyBitRate, &bitRate)) {
+ param.addInt(String8(AUDIO_OFFLOAD_CODEC_AVG_BIT_RATE), bitRate);
+ }
+ if (meta->findInt32(kKeyEncoderDelay, &delaySamples)) {
+ param.addInt(String8(AUDIO_OFFLOAD_CODEC_DELAY_SAMPLES), delaySamples);
+ }
+ if (meta->findInt32(kKeyEncoderPadding, &paddingSamples)) {
+ param.addInt(String8(AUDIO_OFFLOAD_CODEC_PADDING_SAMPLES), paddingSamples);
+ }
+
+ ALOGV("sendMetaDataToHal: bitRate %d, sampleRate %d, chanMask %d,"
+ "delaySample %d, paddingSample %d", bitRate, sampleRate,
+ channelMask, delaySamples, paddingSamples);
+
+ sink->setParameters(param.toString());
+ return OK;
+}
+
+struct mime_conv_t {
+ const char* mime;
+ audio_format_t format;
+};
+
+static const struct mime_conv_t mimeLookup[] = {
+ { MEDIA_MIMETYPE_AUDIO_MPEG, AUDIO_FORMAT_MP3 },
+ { MEDIA_MIMETYPE_AUDIO_RAW, AUDIO_FORMAT_PCM_16_BIT },
+ { MEDIA_MIMETYPE_AUDIO_AMR_NB, AUDIO_FORMAT_AMR_NB },
+ { MEDIA_MIMETYPE_AUDIO_AMR_WB, AUDIO_FORMAT_AMR_WB },
+ { MEDIA_MIMETYPE_AUDIO_AAC, AUDIO_FORMAT_AAC },
+ { MEDIA_MIMETYPE_AUDIO_VORBIS, AUDIO_FORMAT_VORBIS },
+ { 0, AUDIO_FORMAT_INVALID }
+};
+
+status_t mapMimeToAudioFormat( audio_format_t& format, const char* mime )
+{
+const struct mime_conv_t* p = &mimeLookup[0];
+ while (p->mime != NULL) {
+ if (0 == strcasecmp(mime, p->mime)) {
+ format = p->format;
+ return OK;
+ }
+ ++p;
+ }
+
+ return BAD_VALUE;
+}
+
+bool canOffloadStream(const sp<MetaData>& meta, bool hasVideo, bool isStreaming)
+{
+ const char *mime;
+ CHECK(meta->findCString(kKeyMIMEType, &mime));
+
+ audio_offload_info_t info = AUDIO_INFO_INITIALIZER;
+
+ info.format = AUDIO_FORMAT_INVALID;
+ if (mapMimeToAudioFormat(info.format, mime) != OK) {
+ ALOGE(" Couldn't map mime type \"%s\" to a valid AudioSystem::audio_format !", mime);
+ return false;
+ } else {
+ ALOGV("Mime type \"%s\" mapped to audio_format %d", mime, info.format);
+ }
+
+ if (AUDIO_FORMAT_INVALID == info.format) {
+ // can't offload if we don't know what the source format is
+ ALOGE("mime type \"%s\" not a known audio format", mime);
+ return false;
+ }
+
+ int32_t srate = -1;
+ if (!meta->findInt32(kKeySampleRate, &srate)) {
+ ALOGV("track of type '%s' does not publish sample rate", mime);
+ }
+ info.sample_rate = srate;
+
+ int32_t cmask = 0;
+ if (!meta->findInt32(kKeyChannelMask, &cmask)) {
+ ALOGV("track of type '%s' does not publish channel mask", mime);
+
+ // Try a channel count instead
+ int32_t channelCount;
+ if (!meta->findInt32(kKeyChannelCount, &channelCount)) {
+ ALOGV("track of type '%s' does not publish channel count", mime);
+ } else {
+ cmask = audio_channel_out_mask_from_count(channelCount);
+ }
+ }
+ info.channel_mask = cmask;
+
+ int64_t duration = 0;
+ if (!meta->findInt64(kKeyDuration, &duration)) {
+ ALOGV("track of type '%s' does not publish duration", mime);
+ }
+ info.duration_us = duration;
+
+ int32_t brate = -1;
+ if (!meta->findInt32(kKeyBitRate, &brate)) {
+ ALOGV("track of type '%s' does not publish bitrate", mime);
+ }
+ info.bit_rate = brate;
+
+
+ info.stream_type = AUDIO_STREAM_MUSIC;
+ info.has_video = hasVideo;
+ info.is_streaming = isStreaming;
+
+ // Check if offload is possible for given format, stream type, sample rate,
+ // bit rate, duration, video and streaming
+ return AudioSystem::isOffloadSupported(info);
+}
+
+#endif
+
+} // namespace stagefright
+
+#undef LOG_TAG
diff --git a/media/libstagefright/frameworks/av/media/libstagefright/foundation/AAtomizer.cpp b/media/libstagefright/frameworks/av/media/libstagefright/foundation/AAtomizer.cpp
new file mode 100644
index 000000000..4210cdaa6
--- /dev/null
+++ b/media/libstagefright/frameworks/av/media/libstagefright/foundation/AAtomizer.cpp
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <sys/types.h>
+
+#include "AAtomizer.h"
+
+namespace stagefright {
+
+// static
+AAtomizer AAtomizer::gAtomizer;
+
+// static
+const char *AAtomizer::Atomize(const char *name) {
+ return gAtomizer.atomize(name);
+}
+
+AAtomizer::AAtomizer() {
+ for (size_t i = 0; i < 128; ++i) {
+ mAtoms.push(List<AString>());
+ }
+}
+
+const char *AAtomizer::atomize(const char *name) {
+ Mutex::Autolock autoLock(mLock);
+
+ const size_t n = mAtoms.size();
+ size_t index = AAtomizer::Hash(name) % n;
+ List<AString> &entry = mAtoms.editItemAt(index);
+ List<AString>::iterator it = entry.begin();
+ while (it != entry.end()) {
+ if ((*it) == name) {
+ return (*it).c_str();
+ }
+ ++it;
+ }
+
+ entry.push_back(AString(name));
+
+ return (*--entry.end()).c_str();
+}
+
+// static
+uint32_t AAtomizer::Hash(const char *s) {
+ uint32_t sum = 0;
+ while (*s != '\0') {
+ sum = (sum * 31) + *s;
+ ++s;
+ }
+
+ return sum;
+}
+
+} // namespace stagefright
diff --git a/media/libstagefright/frameworks/av/media/libstagefright/foundation/ABitReader.cpp b/media/libstagefright/frameworks/av/media/libstagefright/foundation/ABitReader.cpp
new file mode 100644
index 000000000..22850ef02
--- /dev/null
+++ b/media/libstagefright/frameworks/av/media/libstagefright/foundation/ABitReader.cpp
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "ABitReader.h"
+
+#include <log/log.h>
+#include <media/stagefright/foundation/ADebug.h>
+
+namespace stagefright {
+
+ABitReader::ABitReader(const uint8_t *data, size_t size)
+ : mData(data),
+ mSize(size),
+ mReservoir(0),
+ mNumBitsLeft(0) {
+}
+
+void ABitReader::fillReservoir() {
+ CHECK_GT(mSize, 0u);
+
+ mReservoir = 0;
+ size_t i;
+ for (i = 0; mSize > 0 && i < 4; ++i) {
+ mReservoir = (mReservoir << 8) | *mData;
+
+ ++mData;
+ --mSize;
+ }
+
+ mNumBitsLeft = 8 * i;
+ mReservoir <<= 32 - mNumBitsLeft;
+}
+
+uint32_t ABitReader::getBits(size_t n) {
+ CHECK_LE(n, 32u);
+
+ uint32_t result = 0;
+ while (n > 0) {
+ if (mNumBitsLeft == 0) {
+ fillReservoir();
+ }
+
+ size_t m = n;
+ if (m > mNumBitsLeft) {
+ m = mNumBitsLeft;
+ }
+
+ result = (result << m) | (mReservoir >> (32 - m));
+ mReservoir <<= m;
+ mNumBitsLeft -= m;
+
+ n -= m;
+ }
+
+ return result;
+}
+
+void ABitReader::skipBits(size_t n) {
+ while (n > 32) {
+ getBits(32);
+ n -= 32;
+ }
+
+ if (n > 0) {
+ getBits(n);
+ }
+}
+
+void ABitReader::putBits(uint32_t x, size_t n) {
+ CHECK_LE(n, 32u);
+
+ while (mNumBitsLeft + n > 32) {
+ mNumBitsLeft -= 8;
+ --mData;
+ ++mSize;
+ }
+
+ mReservoir = (mReservoir >> n) | (x << (32 - n));
+ mNumBitsLeft += n;
+}
+
+size_t ABitReader::numBitsLeft() const {
+ return mSize * 8 + mNumBitsLeft;
+}
+
+const uint8_t *ABitReader::data() const {
+ return mData - (mNumBitsLeft + 7) / 8;
+}
+
+} // namespace stagefright
diff --git a/media/libstagefright/frameworks/av/media/libstagefright/foundation/ABuffer.cpp b/media/libstagefright/frameworks/av/media/libstagefright/foundation/ABuffer.cpp
new file mode 100644
index 000000000..53f5ebc69
--- /dev/null
+++ b/media/libstagefright/frameworks/av/media/libstagefright/foundation/ABuffer.cpp
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "ABuffer.h"
+
+#include "ADebug.h"
+#include "ALooper.h"
+#include "AMessage.h"
+
+namespace stagefright {
+
+ABuffer::ABuffer(size_t capacity)
+ : mData(malloc(capacity)),
+ mCapacity(capacity),
+ mRangeOffset(0),
+ mRangeLength(capacity),
+ mInt32Data(0),
+ mOwnsData(true) {
+}
+
+ABuffer::ABuffer(void *data, size_t capacity)
+ : mData(data),
+ mCapacity(capacity),
+ mRangeOffset(0),
+ mRangeLength(capacity),
+ mInt32Data(0),
+ mOwnsData(false) {
+}
+
+ABuffer::~ABuffer() {
+ if (mOwnsData) {
+ if (mData != NULL) {
+ free(mData);
+ mData = NULL;
+ }
+ }
+
+ if (mFarewell != NULL) {
+ mFarewell->post();
+ }
+}
+
+void ABuffer::setRange(size_t offset, size_t size) {
+ CHECK_LE(offset, mCapacity);
+ CHECK_LE(offset + size, mCapacity);
+
+ mRangeOffset = offset;
+ mRangeLength = size;
+}
+
+void ABuffer::setFarewellMessage(const sp<AMessage> msg) {
+ mFarewell = msg;
+}
+
+sp<AMessage> ABuffer::meta() {
+ if (mMeta == NULL) {
+ mMeta = new AMessage;
+ }
+ return mMeta;
+}
+
+} // namespace stagefright
+
diff --git a/media/libstagefright/frameworks/av/media/libstagefright/foundation/AString.cpp b/media/libstagefright/frameworks/av/media/libstagefright/foundation/AString.cpp
new file mode 100644
index 000000000..79c944b58
--- /dev/null
+++ b/media/libstagefright/frameworks/av/media/libstagefright/foundation/AString.cpp
@@ -0,0 +1,347 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <ctype.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "ADebug.h"
+#include "AString.h"
+
+namespace stagefright {
+
+// static
+const char *AString::kEmptyString = "";
+
+AString::AString()
+ : mData((char *)kEmptyString),
+ mSize(0),
+ mAllocSize(1) {
+}
+
+AString::AString(const char *s)
+ : mData(NULL),
+ mSize(0),
+ mAllocSize(1) {
+ setTo(s);
+}
+
+AString::AString(const char *s, size_t size)
+ : mData(NULL),
+ mSize(0),
+ mAllocSize(1) {
+ setTo(s, size);
+}
+
+AString::AString(const AString &from)
+ : mData(NULL),
+ mSize(0),
+ mAllocSize(1) {
+ setTo(from, 0, from.size());
+}
+
+AString::AString(const AString &from, size_t offset, size_t n)
+ : mData(NULL),
+ mSize(0),
+ mAllocSize(1) {
+ setTo(from, offset, n);
+}
+
+AString::~AString() {
+ clear();
+}
+
+AString &AString::operator=(const AString &from) {
+ if (&from != this) {
+ setTo(from, 0, from.size());
+ }
+
+ return *this;
+}
+
+size_t AString::size() const {
+ return mSize;
+}
+
+const char *AString::c_str() const {
+ return mData;
+}
+
+bool AString::empty() const {
+ return mSize == 0;
+}
+
+void AString::setTo(const char *s) {
+ setTo(s, strlen(s));
+}
+
+void AString::setTo(const char *s, size_t size) {
+ clear();
+ append(s, size);
+}
+
+void AString::setTo(const AString &from, size_t offset, size_t n) {
+ CHECK(&from != this);
+
+ clear();
+ setTo(from.mData + offset, n);
+}
+
+void AString::clear() {
+ if (mData && mData != kEmptyString) {
+ free(mData);
+ mData = NULL;
+ }
+
+ mData = (char *)kEmptyString;
+ mSize = 0;
+ mAllocSize = 1;
+}
+
+size_t AString::hash() const {
+ size_t x = 0;
+ for (size_t i = 0; i < mSize; ++i) {
+ x = (x * 31) + mData[i];
+ }
+
+ return x;
+}
+
+bool AString::operator==(const AString &other) const {
+ return mSize == other.mSize && !memcmp(mData, other.mData, mSize);
+}
+
+void AString::trim() {
+ makeMutable();
+
+ size_t i = 0;
+ while (i < mSize && isspace(mData[i])) {
+ ++i;
+ }
+
+ size_t j = mSize;
+ while (j > i && isspace(mData[j - 1])) {
+ --j;
+ }
+
+ memmove(mData, &mData[i], j - i);
+ mSize = j - i;
+ mData[mSize] = '\0';
+}
+
+void AString::erase(size_t start, size_t n) {
+ CHECK_LT(start, mSize);
+ CHECK_LE(start + n, mSize);
+
+ makeMutable();
+
+ memmove(&mData[start], &mData[start + n], mSize - start - n);
+ mSize -= n;
+ mData[mSize] = '\0';
+}
+
+void AString::makeMutable() {
+ if (mData == kEmptyString) {
+ mData = strdup(kEmptyString);
+ }
+}
+
+void AString::append(const char *s) {
+ append(s, strlen(s));
+}
+
+void AString::append(const char *s, size_t size) {
+ makeMutable();
+
+ if (mSize + size + 1 > mAllocSize) {
+ mAllocSize = (mAllocSize + size + 31) & -32;
+ mData = (char *)realloc(mData, mAllocSize);
+ CHECK(mData != NULL);
+ }
+
+ memcpy(&mData[mSize], s, size);
+ mSize += size;
+ mData[mSize] = '\0';
+}
+
+void AString::append(const AString &from) {
+ append(from.c_str(), from.size());
+}
+
+void AString::append(const AString &from, size_t offset, size_t n) {
+ append(from.c_str() + offset, n);
+}
+
+void AString::append(int x) {
+ char s[16];
+ sprintf(s, "%d", x);
+
+ append(s);
+}
+
+void AString::append(unsigned x) {
+ char s[16];
+ sprintf(s, "%u", x);
+
+ append(s);
+}
+
+void AString::append(long x) {
+ char s[16];
+ sprintf(s, "%ld", x);
+
+ append(s);
+}
+
+void AString::append(unsigned long x) {
+ char s[16];
+ sprintf(s, "%lu", x);
+
+ append(s);
+}
+
+void AString::append(long long x) {
+ char s[32];
+ sprintf(s, "%lld", x);
+
+ append(s);
+}
+
+void AString::append(unsigned long long x) {
+ char s[32];
+ sprintf(s, "%llu", x);
+
+ append(s);
+}
+
+void AString::append(float x) {
+ char s[16];
+ sprintf(s, "%f", x);
+
+ append(s);
+}
+
+void AString::append(double x) {
+ char s[16];
+ sprintf(s, "%f", x);
+
+ append(s);
+}
+
+void AString::append(void *x) {
+ char s[16];
+ sprintf(s, "%p", x);
+
+ append(s);
+}
+
+ssize_t AString::find(const char *substring, size_t start) const {
+ CHECK_LE(start, size());
+
+ const char *match = strstr(mData + start, substring);
+
+ if (match == NULL) {
+ return -1;
+ }
+
+ return match - mData;
+}
+
+void AString::insert(const AString &from, size_t insertionPos) {
+ insert(from.c_str(), from.size(), insertionPos);
+}
+
+void AString::insert(const char *from, size_t size, size_t insertionPos) {
+ CHECK_GE(insertionPos, 0u);
+ CHECK_LE(insertionPos, mSize);
+
+ makeMutable();
+
+ if (mSize + size + 1 > mAllocSize) {
+ mAllocSize = (mAllocSize + size + 31) & -32;
+ mData = (char *)realloc(mData, mAllocSize);
+ CHECK(mData != NULL);
+ }
+
+ memmove(&mData[insertionPos + size],
+ &mData[insertionPos], mSize - insertionPos + 1);
+
+ memcpy(&mData[insertionPos], from, size);
+
+ mSize += size;
+}
+
+bool AString::operator<(const AString &other) const {
+ return compare(other) < 0;
+}
+
+bool AString::operator>(const AString &other) const {
+ return compare(other) > 0;
+}
+
+int AString::compare(const AString &other) const {
+ return strcmp(mData, other.mData);
+}
+
+void AString::tolower() {
+ makeMutable();
+
+ for (size_t i = 0; i < mSize; ++i) {
+ mData[i] = ::tolower(mData[i]);
+ }
+}
+
+bool AString::startsWith(const char *prefix) const {
+ return !strncmp(mData, prefix, strlen(prefix));
+}
+
+bool AString::endsWith(const char *suffix) const {
+ size_t suffixLen = strlen(suffix);
+
+ if (mSize < suffixLen) {
+ return false;
+ }
+
+ return !strcmp(mData + mSize - suffixLen, suffix);
+}
+
+AString StringPrintf(const char *format, ...) {
+ va_list ap;
+ va_start(ap, format);
+
+ char *buffer;
+#ifdef WIN32
+ int n = vsnprintf(NULL, 0, format, ap);
+ buffer = new char[n+1];
+ vsnprintf(buffer, n+1, format, ap);
+#else
+ vasprintf(&buffer, format, ap);
+#endif
+
+ va_end(ap);
+
+ AString result(buffer);
+
+ free(buffer);
+ buffer = NULL;
+
+ return result;
+}
+
+} // namespace stagefright
+
diff --git a/media/libstagefright/frameworks/av/media/libstagefright/foundation/hexdump.cpp b/media/libstagefright/frameworks/av/media/libstagefright/foundation/hexdump.cpp
new file mode 100644
index 000000000..7b566eb23
--- /dev/null
+++ b/media/libstagefright/frameworks/av/media/libstagefright/foundation/hexdump.cpp
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#undef LOG_TAG
+#define LOG_TAG "hexdump"
+#include <utils/Log.h>
+
+#include "hexdump.h"
+
+#include "ADebug.h"
+#include "AString.h"
+
+#include <ctype.h>
+#include <stdint.h>
+#include <stdio.h>
+
+namespace stagefright {
+
+static void appendIndent(AString *s, int32_t indent) {
+ static const char kWhitespace[] =
+ " "
+ " ";
+
+ CHECK_LT((size_t)indent, sizeof(kWhitespace));
+
+ s->append(kWhitespace, indent);
+}
+
+void hexdump(const void *_data, size_t size, size_t indent, AString *appendTo) {
+ const uint8_t *data = (const uint8_t *)_data;
+
+ size_t offset = 0;
+ while (offset < size) {
+ AString line;
+
+ appendIndent(&line, indent);
+
+ char tmp[32];
+ sprintf(tmp, "%08lx: ", (unsigned long)offset);
+
+ line.append(tmp);
+
+ for (size_t i = 0; i < 16; ++i) {
+ if (i == 8) {
+ line.append(' ');
+ }
+ if (offset + i >= size) {
+ line.append(" ");
+ } else {
+ sprintf(tmp, "%02x ", data[offset + i]);
+ line.append(tmp);
+ }
+ }
+
+ line.append(' ');
+
+ for (size_t i = 0; i < 16; ++i) {
+ if (offset + i >= size) {
+ break;
+ }
+
+ if (isprint(data[offset + i])) {
+ line.append((char)data[offset + i]);
+ } else {
+ line.append('.');
+ }
+ }
+
+ if (appendTo != NULL) {
+ appendTo->append(line);
+ appendTo->append("\n");
+ } else {
+ ALOGI("%s", line.c_str());
+ }
+
+ offset += 16;
+ }
+}
+
+} // namespace stagefright
+
+#undef LOG_TAG
diff --git a/media/libstagefright/frameworks/av/media/libstagefright/include/AMRExtractor.h b/media/libstagefright/frameworks/av/media/libstagefright/include/AMRExtractor.h
new file mode 100644
index 000000000..cdfc98e88
--- /dev/null
+++ b/media/libstagefright/frameworks/av/media/libstagefright/include/AMRExtractor.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef AMR_EXTRACTOR_H_
+
+#define AMR_EXTRACTOR_H_
+
+#include <utils/Errors.h>
+#include <media/stagefright/MediaExtractor.h>
+
+namespace stagefright {
+
+struct AMessage;
+class String8;
+#define OFFSET_TABLE_LEN 300
+
+class AMRExtractor : public MediaExtractor {
+public:
+ AMRExtractor(const sp<DataSource> &source);
+
+ virtual size_t countTracks();
+ virtual sp<MediaSource> getTrack(size_t index);
+ virtual sp<MetaData> getTrackMetaData(size_t index, uint32_t flags);
+
+ virtual sp<MetaData> getMetaData();
+
+protected:
+ virtual ~AMRExtractor();
+
+private:
+ sp<DataSource> mDataSource;
+ sp<MetaData> mMeta;
+ status_t mInitCheck;
+ bool mIsWide;
+
+ off64_t mOffsetTable[OFFSET_TABLE_LEN]; //5 min
+ size_t mOffsetTableLength;
+
+ AMRExtractor(const AMRExtractor &);
+ AMRExtractor &operator=(const AMRExtractor &);
+};
+
+bool SniffAMR(
+ const sp<DataSource> &source, String8 *mimeType, float *confidence,
+ sp<AMessage> *);
+
+} // namespace stagefright
+
+#endif // AMR_EXTRACTOR_H_
diff --git a/media/libstagefright/frameworks/av/media/libstagefright/include/ESDS.h b/media/libstagefright/frameworks/av/media/libstagefright/include/ESDS.h
new file mode 100644
index 000000000..bc4c5a6e5
--- /dev/null
+++ b/media/libstagefright/frameworks/av/media/libstagefright/include/ESDS.h
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ESDS_H_
+
+#define ESDS_H_
+
+#include <stdint.h>
+
+#include <media/stagefright/MediaErrors.h>
+
+namespace stagefright {
+
+class ESDS {
+public:
+ ESDS(const void *data, size_t size);
+ ~ESDS();
+
+ status_t InitCheck() const;
+
+ status_t getObjectTypeIndication(uint8_t *objectTypeIndication) const;
+ status_t getCodecSpecificInfo(const void **data, size_t *size) const;
+ status_t getCodecSpecificOffset(size_t *offset, size_t *size) const;
+ status_t getBitRate(uint32_t *brateMax, uint32_t *brateAvg) const;
+ status_t getStreamType(uint8_t *streamType) const;
+
+private:
+ enum {
+ kTag_ESDescriptor = 0x03,
+ kTag_DecoderConfigDescriptor = 0x04,
+ kTag_DecoderSpecificInfo = 0x05
+ };
+
+ uint8_t *mData;
+ size_t mSize;
+
+ status_t mInitCheck;
+
+ size_t mDecoderSpecificOffset;
+ size_t mDecoderSpecificLength;
+ uint8_t mObjectTypeIndication;
+ uint8_t mStreamType;
+ uint32_t mBitRateMax;
+ uint32_t mBitRateAvg;
+
+ status_t skipDescriptorHeader(
+ size_t offset, size_t size,
+ uint8_t *tag, size_t *data_offset, size_t *data_size) const;
+
+ status_t parse();
+ status_t parseESDescriptor(size_t offset, size_t size);
+ status_t parseDecoderConfigDescriptor(size_t offset, size_t size);
+
+ ESDS(const ESDS &);
+ ESDS &operator=(const ESDS &);
+};
+
+} // namespace stagefright
+#endif // ESDS_H_
diff --git a/media/libstagefright/frameworks/av/media/libstagefright/include/MPEG4Extractor.h b/media/libstagefright/frameworks/av/media/libstagefright/include/MPEG4Extractor.h
new file mode 100644
index 000000000..86579d39b
--- /dev/null
+++ b/media/libstagefright/frameworks/av/media/libstagefright/include/MPEG4Extractor.h
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef MPEG4_EXTRACTOR_H_
+
+#define MPEG4_EXTRACTOR_H_
+
+#include <arpa/inet.h>
+
+#include <media/stagefright/DataSource.h>
+#include <media/stagefright/MediaExtractor.h>
+#include <media/stagefright/Utils.h>
+#include <utils/List.h>
+#include <utils/String8.h>
+#include "nsTArray.h"
+
+namespace stagefright {
+
+struct AMessage;
+class DataSource;
+class SampleTable;
+class String8;
+
+struct SidxEntry {
+ size_t mSize;
+ uint32_t mDurationUs;
+};
+
+class MPEG4Extractor : public MediaExtractor {
+public:
+ MPEG4Extractor(const sp<DataSource> &source);
+
+ size_t countTracks() override;
+ sp<MediaSource> getTrack(size_t index) override;
+ sp<MetaData> getTrackMetaData(size_t index, uint32_t flags) override;
+
+ sp<MetaData> getMetaData() override;
+ uint32_t flags() const override;
+
+ // for DRM
+ char* getDrmTrackInfo(size_t trackID, int *len) override;
+
+protected:
+ virtual ~MPEG4Extractor();
+
+private:
+
+ struct PsshInfo {
+ uint8_t uuid[16];
+ uint32_t datalen;
+ uint8_t *data;
+ };
+ struct Track {
+ Track *next;
+ sp<MetaData> meta;
+ uint32_t timescale;
+ // Temporary storage for elst until we've
+ // parsed mdhd and can interpret them.
+ uint64_t empty_duration;
+ uint64_t segment_duration;
+ int64_t media_time;
+
+ sp<SampleTable> sampleTable;
+ bool includes_expensive_metadata;
+ bool skipTrack;
+ };
+
+ nsTArray<SidxEntry> mSidxEntries;
+ uint64_t mSidxDuration;
+
+ nsTArray<PsshInfo> mPssh;
+
+ sp<DataSource> mDataSource;
+ status_t mInitCheck;
+ bool mHasVideo;
+ uint32_t mHeaderTimescale;
+
+ Track *mFirstTrack, *mLastTrack;
+
+ sp<MetaData> mFileMetaData;
+
+ nsTArray<uint32_t> mPath;
+ String8 mLastCommentMean;
+ String8 mLastCommentName;
+ String8 mLastCommentData;
+
+ status_t readMetaData();
+ status_t parseChunk(off64_t *offset, int depth);
+ status_t parseMetaData(off64_t offset, size_t size);
+
+ status_t updateAudioTrackInfoFromESDS_MPEG4Audio(
+ const void *esds_data, size_t esds_size);
+
+ static status_t verifyTrack(Track *track);
+
+ struct SINF {
+ SINF *next;
+ uint16_t trackID;
+ uint8_t IPMPDescriptorID;
+ ssize_t len;
+ char *IPMPData;
+ };
+
+ SINF *mFirstSINF;
+
+ bool mIsDrm;
+ uint32_t mDrmScheme;
+
+ status_t parseDrmSINF(off64_t *offset, off64_t data_offset);
+
+ status_t parseTrackHeader(off64_t data_offset, off64_t data_size);
+
+ status_t parseSegmentIndex(off64_t data_offset, size_t data_size);
+
+ void storeEditList();
+
+ MPEG4Extractor(const MPEG4Extractor &);
+ MPEG4Extractor &operator=(const MPEG4Extractor &);
+};
+
+} // namespace stagefright
+
+#endif // MPEG4_EXTRACTOR_H_
diff --git a/media/libstagefright/frameworks/av/media/libstagefright/include/SampleIterator.h b/media/libstagefright/frameworks/av/media/libstagefright/include/SampleIterator.h
new file mode 100644
index 000000000..907ea39f1
--- /dev/null
+++ b/media/libstagefright/frameworks/av/media/libstagefright/include/SampleIterator.h
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SAMPLE_ITERATOR_H_
+#define SAMPLE_ITERATOR_H_
+
+#include <utils/Vector.h>
+
+namespace stagefright {
+
+class SampleTable;
+
+struct SampleIterator {
+ SampleIterator(SampleTable *table);
+
+ status_t seekTo(uint32_t sampleIndex);
+
+ uint32_t getChunkIndex() const { return mCurrentChunkIndex; }
+ uint32_t getDescIndex() const { return mChunkDesc; }
+ off64_t getSampleOffset() const { return mCurrentSampleOffset; }
+ size_t getSampleSize() const { return mCurrentSampleSize; }
+ uint32_t getSampleTime() const { return mCurrentSampleTime; }
+ uint32_t getSampleDecodeTime() const { return mCurrentSampleDecodeTime; }
+ uint32_t getSampleDuration() const { return mCurrentSampleDuration; }
+
+ status_t getSampleSizeDirect(
+ uint32_t sampleIndex, size_t *size);
+
+private:
+ SampleTable *mTable;
+
+ bool mInitialized;
+
+ uint32_t mSampleToChunkIndex;
+ uint32_t mFirstChunk;
+ uint32_t mFirstChunkSampleIndex;
+ uint32_t mStopChunk;
+ uint32_t mStopChunkSampleIndex;
+ uint32_t mSamplesPerChunk;
+ uint32_t mChunkDesc;
+
+ uint32_t mCurrentChunkIndex;
+ off64_t mCurrentChunkOffset;
+ Vector<size_t> mCurrentChunkSampleSizes;
+
+ uint32_t mTimeToSampleIndex;
+ uint32_t mTTSSampleIndex;
+ uint32_t mTTSSampleTime;
+ uint32_t mTTSCount;
+ uint32_t mTTSDuration;
+
+ uint32_t mCurrentSampleIndex;
+ off64_t mCurrentSampleOffset;
+ size_t mCurrentSampleSize;
+ uint32_t mCurrentSampleTime;
+ uint32_t mCurrentSampleDecodeTime;
+ uint32_t mCurrentSampleDuration;
+
+ void reset();
+ status_t findChunkRange(uint32_t sampleIndex);
+ status_t getChunkOffset(uint32_t chunk, off64_t *offset);
+ status_t findSampleTime(uint32_t sampleIndex, uint32_t *time);
+
+ SampleIterator(const SampleIterator &);
+ SampleIterator &operator=(const SampleIterator &);
+};
+
+} // namespace stagefright
+
+#endif
diff --git a/media/libstagefright/frameworks/av/media/libstagefright/include/SampleTable.h b/media/libstagefright/frameworks/av/media/libstagefright/include/SampleTable.h
new file mode 100644
index 000000000..e115c92bb
--- /dev/null
+++ b/media/libstagefright/frameworks/av/media/libstagefright/include/SampleTable.h
@@ -0,0 +1,188 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SAMPLE_TABLE_H_
+
+#define SAMPLE_TABLE_H_
+
+#include <sys/types.h>
+#include <stdint.h>
+
+#include <media/stagefright/MediaErrors.h>
+#include <utils/RefBase.h>
+#include <utils/threads.h>
+
+namespace stagefright {
+
+class DataSource;
+struct SampleIterator;
+
+class SampleTable : public RefBase {
+public:
+ SampleTable(const sp<DataSource> &source);
+
+ bool isValid() const;
+
+ // type can be 'stco' or 'co64'.
+ status_t setChunkOffsetParams(
+ uint32_t type, off64_t data_offset, size_t data_size);
+
+ status_t setSampleToChunkParams(off64_t data_offset, size_t data_size);
+
+ // type can be 'stsz' or 'stz2'.
+ status_t setSampleSizeParams(
+ uint32_t type, off64_t data_offset, size_t data_size);
+
+ status_t setTimeToSampleParams(off64_t data_offset, size_t data_size);
+
+ status_t setCompositionTimeToSampleParams(
+ off64_t data_offset, size_t data_size);
+
+ status_t setSyncSampleParams(off64_t data_offset, size_t data_size);
+
+ status_t setSampleAuxiliaryInformationSizeParams(off64_t aDataOffset,
+ size_t aDataSize,
+ uint32_t aDrmScheme);
+
+ status_t setSampleAuxiliaryInformationOffsetParams(off64_t aDataOffset,
+ size_t aDataSize,
+ uint32_t aDrmScheme);
+
+ ////////////////////////////////////////////////////////////////////////////
+
+ uint32_t countChunkOffsets() const;
+
+ uint32_t countSamples() const;
+
+ status_t getMaxSampleSize(size_t *size);
+
+ status_t getMetaDataForSample(
+ uint32_t sampleIndex,
+ off64_t *offset,
+ size_t *size,
+ uint32_t *compositionTime,
+ uint32_t *duration = NULL,
+ bool *isSyncSample = NULL,
+ uint32_t *decodeTime = NULL);
+
+ enum {
+ kFlagBefore,
+ kFlagAfter,
+ kFlagClosest
+ };
+ status_t findSampleAtTime(
+ uint32_t req_time, uint32_t *sample_index, uint32_t flags);
+
+ status_t findSyncSampleNear(
+ uint32_t start_sample_index, uint32_t *sample_index,
+ uint32_t flags);
+
+ status_t findThumbnailSample(uint32_t *sample_index);
+
+ bool hasCencInfo() const { return !!mCencInfo; }
+
+ status_t getSampleCencInfo(uint32_t aSampleIndex,
+ nsTArray<uint16_t>& aClearSizes,
+ nsTArray<uint32_t>& aCipherSizes,
+ uint8_t aIV[]);
+
+protected:
+ ~SampleTable();
+
+private:
+ struct CompositionDeltaLookup;
+
+ static const uint32_t kChunkOffsetType32;
+ static const uint32_t kChunkOffsetType64;
+ static const uint32_t kSampleSizeType32;
+ static const uint32_t kSampleSizeTypeCompact;
+
+ sp<DataSource> mDataSource;
+ Mutex mLock;
+
+ off64_t mChunkOffsetOffset;
+ uint32_t mChunkOffsetType;
+ uint32_t mNumChunkOffsets;
+
+ off64_t mSampleToChunkOffset;
+ uint32_t mNumSampleToChunkOffsets;
+
+ off64_t mSampleSizeOffset;
+ uint32_t mSampleSizeFieldSize;
+ uint32_t mDefaultSampleSize;
+ uint32_t mNumSampleSizes;
+
+ uint32_t mTimeToSampleCount;
+ uint32_t *mTimeToSample;
+
+ struct SampleTimeEntry {
+ uint32_t mSampleIndex;
+ uint32_t mCompositionTime;
+ };
+ SampleTimeEntry *mSampleTimeEntries;
+
+ uint32_t *mCompositionTimeDeltaEntries;
+ size_t mNumCompositionTimeDeltaEntries;
+ CompositionDeltaLookup *mCompositionDeltaLookup;
+
+ off64_t mSyncSampleOffset;
+ uint32_t mNumSyncSamples;
+ uint32_t *mSyncSamples;
+ size_t mLastSyncSampleIndex;
+
+ SampleIterator *mSampleIterator;
+
+ struct SampleToChunkEntry {
+ uint32_t startChunk;
+ uint32_t samplesPerChunk;
+ uint32_t chunkDesc;
+ };
+ SampleToChunkEntry *mSampleToChunkEntries;
+
+ enum { IV_BYTES = 16 };
+ struct SampleCencInfo {
+ uint8_t mIV[IV_BYTES];
+ uint16_t mSubsampleCount;
+
+ struct SubsampleSizes {
+ uint16_t mClearBytes;
+ uint32_t mCipherBytes;
+ } * mSubsamples;
+ } * mCencInfo;
+ uint32_t mCencInfoCount;
+
+ uint8_t mCencDefaultSize;
+ FallibleTArray<uint8_t> mCencSizes;
+ FallibleTArray<uint64_t> mCencOffsets;
+
+ friend struct SampleIterator;
+
+ status_t getSampleSize_l(uint32_t sample_index, size_t *sample_size);
+ uint32_t getCompositionTimeOffset(uint32_t sampleIndex);
+
+ static int CompareIncreasingTime(const void *, const void *);
+
+ status_t buildSampleEntriesTable();
+
+ status_t parseSampleCencInfo();
+
+ SampleTable(const SampleTable &);
+ SampleTable &operator=(const SampleTable &);
+};
+
+} // namespace stagefright
+
+#endif // SAMPLE_TABLE_H_
diff --git a/media/libstagefright/gtest/TestInterval.cpp b/media/libstagefright/gtest/TestInterval.cpp
new file mode 100644
index 000000000..68aa3b126
--- /dev/null
+++ b/media/libstagefright/gtest/TestInterval.cpp
@@ -0,0 +1,89 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "gtest/gtest.h"
+#include "mp4_demuxer/Interval.h"
+
+using namespace mp4_demuxer;
+using namespace mozilla;
+
+TEST(Interval, Length)
+{
+ Interval<int> i(15, 25);
+ EXPECT_EQ(10, i.Length());
+}
+
+TEST(Interval, Intersection)
+{
+ Interval<int> i0(10, 20);
+ Interval<int> i1(15, 25);
+ Interval<int> i = i0.Intersection(i1);
+ EXPECT_EQ(15, i.start);
+ EXPECT_EQ(20, i.end);
+}
+
+TEST(Interval, Equals)
+{
+ Interval<int> i0(10, 20);
+ Interval<int> i1(10, 20);
+ EXPECT_EQ(i0, i1);
+
+ Interval<int> i2(5, 20);
+ EXPECT_NE(i0, i2);
+
+ Interval<int> i3(10, 15);
+ EXPECT_NE(i0, i2);
+}
+
+TEST(Interval, IntersectionVector)
+{
+ nsTArray<Interval<int>> i0;
+ i0.AppendElement(Interval<int>(5, 10));
+ i0.AppendElement(Interval<int>(20, 25));
+ i0.AppendElement(Interval<int>(40, 60));
+
+ nsTArray<Interval<int>> i1;
+ i1.AppendElement(Interval<int>(7, 15));
+ i1.AppendElement(Interval<int>(16, 27));
+ i1.AppendElement(Interval<int>(45, 50));
+ i1.AppendElement(Interval<int>(53, 57));
+
+ nsTArray<Interval<int>> i;
+ Interval<int>::Intersection(i0, i1, &i);
+
+ EXPECT_EQ(4u, i.Length());
+
+ EXPECT_EQ(7, i[0].start);
+ EXPECT_EQ(10, i[0].end);
+
+ EXPECT_EQ(20, i[1].start);
+ EXPECT_EQ(25, i[1].end);
+
+ EXPECT_EQ(45, i[2].start);
+ EXPECT_EQ(50, i[2].end);
+
+ EXPECT_EQ(53, i[3].start);
+ EXPECT_EQ(57, i[3].end);
+}
+
+TEST(Interval, Normalize)
+{
+ nsTArray<Interval<int>> i;
+ i.AppendElement(Interval<int>(20, 30));
+ i.AppendElement(Interval<int>(1, 8));
+ i.AppendElement(Interval<int>(5, 10));
+ i.AppendElement(Interval<int>(2, 7));
+
+ nsTArray<Interval<int>> o;
+ Interval<int>::Normalize(i, &o);
+
+ EXPECT_EQ(2u, o.Length());
+
+ EXPECT_EQ(1, o[0].start);
+ EXPECT_EQ(10, o[0].end);
+
+ EXPECT_EQ(20, o[1].start);
+ EXPECT_EQ(30, o[1].end);
+}
diff --git a/media/libstagefright/gtest/TestMP4Rust.cpp b/media/libstagefright/gtest/TestMP4Rust.cpp
new file mode 100644
index 000000000..a338b5386
--- /dev/null
+++ b/media/libstagefright/gtest/TestMP4Rust.cpp
@@ -0,0 +1,142 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "gtest/gtest.h"
+#include "mp4parse.h"
+
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <algorithm>
+#include <vector>
+
+static intptr_t
+error_reader(uint8_t* buffer, uintptr_t size, void* userdata)
+{
+ return -1;
+}
+
+struct read_vector {
+ explicit read_vector(FILE* file, size_t length);
+ explicit read_vector(size_t length);
+
+ size_t location;
+ std::vector<uint8_t> buffer;
+};
+
+read_vector::read_vector(FILE* file, size_t length)
+ : location(0)
+{
+ buffer.resize(length);
+ size_t read = fread(buffer.data(), sizeof(decltype(buffer)::value_type),
+ buffer.size(), file);
+ buffer.resize(read);
+}
+
+read_vector::read_vector(size_t length)
+ : location(0)
+{
+ buffer.resize(length, 0);
+}
+
+static intptr_t
+vector_reader(uint8_t* buffer, uintptr_t size, void* userdata)
+{
+ if (!buffer || !userdata) {
+ return -1;
+ }
+
+ auto source = reinterpret_cast<read_vector*>(userdata);
+ if (source->location > source->buffer.size()) {
+ return -1;
+ }
+ uintptr_t available = source->buffer.size() - source->location;
+ uintptr_t length = std::min(available, size);
+ memcpy(buffer, source->buffer.data() + source->location, length);
+ source->location += length;
+ return length;
+}
+
+TEST(rust, MP4MetadataEmpty)
+{
+ mp4parse_error rv;
+ mp4parse_io io;
+
+ // Shouldn't be able to read with no context.
+ rv = mp4parse_read(nullptr);
+ EXPECT_EQ(rv, MP4PARSE_ERROR_BADARG);
+
+ // Shouldn't be able to wrap an mp4parse_io with null members.
+ io = { nullptr, nullptr };
+ mp4parse_parser* context = mp4parse_new(&io);
+ EXPECT_EQ(context, nullptr);
+
+ io = { nullptr, &io };
+ context = mp4parse_new(&io);
+ EXPECT_EQ(context, nullptr);
+
+ // FIXME: this should probably be accepted.
+ io = { error_reader, nullptr };
+ context = mp4parse_new(&io);
+ EXPECT_EQ(context, nullptr);
+
+ // Read method errors should propagate.
+ io = { error_reader, &io };
+ context = mp4parse_new(&io);
+ ASSERT_NE(context, nullptr);
+ rv = mp4parse_read(context);
+ EXPECT_EQ(rv, MP4PARSE_ERROR_IO);
+ mp4parse_free(context);
+
+ // Short buffers should fail.
+ read_vector buf(0);
+ io = { vector_reader, &buf };
+ context = mp4parse_new(&io);
+ ASSERT_NE(context, nullptr);
+ rv = mp4parse_read(context);
+ EXPECT_EQ(rv, MP4PARSE_ERROR_INVALID);
+ mp4parse_free(context);
+
+ buf.buffer.reserve(4097);
+ context = mp4parse_new(&io);
+ ASSERT_NE(context, nullptr);
+ rv = mp4parse_read(context);
+ EXPECT_EQ(rv, MP4PARSE_ERROR_INVALID);
+ mp4parse_free(context);
+
+ // Empty buffers should fail.
+ buf.buffer.resize(4097, 0);
+ context = mp4parse_new(&io);
+ rv = mp4parse_read(context);
+ EXPECT_EQ(rv, MP4PARSE_ERROR_UNSUPPORTED);
+ mp4parse_free(context);
+}
+
+TEST(rust, MP4Metadata)
+{
+ FILE* f = fopen("street.mp4", "rb");
+ ASSERT_TRUE(f != nullptr);
+ // Read just the moov header to work around the parser
+ // treating mid-box eof as an error.
+ //read_vector reader = read_vector(f, 1061);
+ struct stat s;
+ ASSERT_EQ(0, fstat(fileno(f), &s));
+ read_vector reader = read_vector(f, s.st_size);
+ fclose(f);
+
+ mp4parse_io io = { vector_reader, &reader };
+ mp4parse_parser* context = mp4parse_new(&io);
+ ASSERT_NE(nullptr, context);
+
+ mp4parse_error rv = mp4parse_read(context);
+ EXPECT_EQ(MP4PARSE_OK, rv);
+
+ uint32_t tracks = 0;
+ rv = mp4parse_get_track_count(context, &tracks);
+ EXPECT_EQ(MP4PARSE_OK, rv);
+ EXPECT_EQ(2U, tracks);
+
+ mp4parse_free(context);
+}
diff --git a/media/libstagefright/gtest/TestParser.cpp b/media/libstagefright/gtest/TestParser.cpp
new file mode 100644
index 000000000..caaca6c70
--- /dev/null
+++ b/media/libstagefright/gtest/TestParser.cpp
@@ -0,0 +1,479 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "gtest/gtest.h"
+#include "MediaData.h"
+#include "mozilla/ArrayUtils.h"
+#include "mp4_demuxer/BufferStream.h"
+#include "mp4_demuxer/MP4Metadata.h"
+#include "mp4_demuxer/MoofParser.h"
+
+using namespace mozilla;
+using namespace mp4_demuxer;
+
+class TestStream : public Stream
+{
+public:
+ TestStream(const uint8_t* aBuffer, size_t aSize)
+ : mHighestSuccessfulEndOffset(0)
+ , mBuffer(aBuffer)
+ , mSize(aSize)
+ {
+ }
+ bool ReadAt(int64_t aOffset, void* aData, size_t aLength,
+ size_t* aBytesRead) override
+ {
+ if (aOffset < 0 || aOffset > static_cast<int64_t>(mSize)) {
+ return false;
+ }
+ // After the test, 0 <= aOffset <= mSize <= SIZE_MAX, so it's safe to cast to size_t.
+ size_t offset = static_cast<size_t>(aOffset);
+ // Don't read past the end (but it's not an error to try).
+ if (aLength > mSize - offset) {
+ aLength = mSize - offset;
+ }
+ // Now, 0 <= offset <= offset + aLength <= mSize <= SIZE_MAX.
+ *aBytesRead = aLength;
+ memcpy(aData, mBuffer + offset, aLength);
+ if (mHighestSuccessfulEndOffset < offset + aLength)
+ {
+ mHighestSuccessfulEndOffset = offset + aLength;
+ }
+ return true;
+ }
+ bool CachedReadAt(int64_t aOffset, void* aData, size_t aLength,
+ size_t* aBytesRead) override
+ {
+ return ReadAt(aOffset, aData, aLength, aBytesRead);
+ }
+ bool Length(int64_t* aLength) override
+ {
+ *aLength = mSize;
+ return true;
+ }
+ void DiscardBefore(int64_t aOffset) override
+ {
+ }
+
+ // Offset past the last character ever read. 0 when nothing read yet.
+ size_t mHighestSuccessfulEndOffset;
+protected:
+ virtual ~TestStream()
+ {
+ }
+
+ const uint8_t* mBuffer;
+ size_t mSize;
+};
+
+TEST(stagefright_MP4Metadata, EmptyStream)
+{
+ RefPtr<Stream> stream = new TestStream(nullptr, 0);
+
+ EXPECT_FALSE(MP4Metadata::HasCompleteMetadata(stream));
+ RefPtr<MediaByteBuffer> metadataBuffer = MP4Metadata::Metadata(stream);
+ EXPECT_FALSE(metadataBuffer);
+
+ MP4Metadata metadata(stream);
+ EXPECT_EQ(0u, metadata.GetNumberTracks(TrackInfo::kUndefinedTrack));
+ EXPECT_EQ(0u, metadata.GetNumberTracks(TrackInfo::kAudioTrack));
+ EXPECT_EQ(0u, metadata.GetNumberTracks(TrackInfo::kVideoTrack));
+ EXPECT_EQ(0u, metadata.GetNumberTracks(TrackInfo::kTextTrack));
+ EXPECT_EQ(0u, metadata.GetNumberTracks(static_cast<TrackInfo::TrackType>(-1)));
+ EXPECT_FALSE(metadata.GetTrackInfo(TrackInfo::kUndefinedTrack, 0));
+ EXPECT_FALSE(metadata.GetTrackInfo(TrackInfo::kAudioTrack, 0));
+ EXPECT_FALSE(metadata.GetTrackInfo(TrackInfo::kVideoTrack, 0));
+ EXPECT_FALSE(metadata.GetTrackInfo(TrackInfo::kTextTrack, 0));
+ EXPECT_FALSE(metadata.GetTrackInfo(static_cast<TrackInfo::TrackType>(-1), 0));
+ // We can seek anywhere in any MPEG4.
+ EXPECT_TRUE(metadata.CanSeek());
+ EXPECT_FALSE(metadata.Crypto().valid);
+}
+
+TEST(stagefright_MoofParser, EmptyStream)
+{
+ RefPtr<Stream> stream = new TestStream(nullptr, 0);
+
+ MoofParser parser(stream, 0, false);
+ EXPECT_EQ(0u, parser.mOffset);
+ EXPECT_TRUE(parser.ReachedEnd());
+
+ MediaByteRangeSet byteRanges;
+ EXPECT_FALSE(parser.RebuildFragmentedIndex(byteRanges));
+
+ EXPECT_TRUE(parser.GetCompositionRange(byteRanges).IsNull());
+ EXPECT_TRUE(parser.mInitRange.IsEmpty());
+ EXPECT_EQ(0u, parser.mOffset);
+ EXPECT_TRUE(parser.ReachedEnd());
+ EXPECT_FALSE(parser.HasMetadata());
+ RefPtr<MediaByteBuffer> metadataBuffer = parser.Metadata();
+ EXPECT_FALSE(metadataBuffer);
+ EXPECT_TRUE(parser.FirstCompleteMediaSegment().IsEmpty());
+ EXPECT_TRUE(parser.FirstCompleteMediaHeader().IsEmpty());
+}
+
+nsTArray<uint8_t>
+ReadTestFile(const char* aFilename)
+{
+ if (!aFilename) {
+ return {};
+ }
+ FILE* f = fopen(aFilename, "rb");
+ if (!f) {
+ return {};
+ }
+
+ if (fseek(f, 0, SEEK_END) != 0) {
+ fclose(f);
+ return {};
+ }
+ long position = ftell(f);
+ // I know EOF==-1, so this test is made obsolete by '<0', but I don't want
+ // the code to rely on that.
+ if (position == 0 || position == EOF || position < 0) {
+ fclose(f);
+ return {};
+ }
+ if (fseek(f, 0, SEEK_SET) != 0) {
+ fclose(f);
+ return {};
+ }
+
+ size_t len = static_cast<size_t>(position);
+ nsTArray<uint8_t> buffer(len);
+ buffer.SetLength(len);
+ size_t read = fread(buffer.Elements(), 1, len, f);
+ fclose(f);
+ if (read != len) {
+ return {};
+ }
+
+ return buffer;
+}
+
+struct TestFileData
+{
+ const char* mFilename;
+ uint32_t mNumberVideoTracks;
+ int64_t mVideoDuration; // For first video track, -1 if N/A.
+ int32_t mWidth;
+ int32_t mHeight;
+ uint32_t mNumberAudioTracks;
+ int64_t mAudioDuration; // For first audio track, -1 if N/A.
+ bool mHasCrypto;
+ uint64_t mMoofReachedOffset; // or 0 for the end.
+ bool mValidMoof;
+ bool mHeader;
+};
+static const TestFileData testFiles[] = {
+ // filename #V dur w h #A dur crypt off moof headr
+ { "test_case_1156505.mp4", 0, -1, 0, 0, 0, -1, false, 152, false, false },
+ { "test_case_1181213.mp4", 0, -1, 0, 0, 0, -1, false, 0, false, false },
+ { "test_case_1181215.mp4", 0, -1, 0, 0, 0, -1, false, 0, false, false },
+ { "test_case_1181220.mp4", 0, -1, 0, 0, 0, -1, false, 0, false, false },
+ { "test_case_1181223.mp4", 0, -1, 0, 0, 0, -1, false, 0, false, false },
+ { "test_case_1181719.mp4", 0, -1, 0, 0, 0, -1, false, 0, false, false },
+ { "test_case_1185230.mp4", 1, 416666,
+ 320, 240, 1, 5, false, 0, false, false },
+ { "test_case_1187067.mp4", 1, 80000,
+ 160, 90, 0, -1, false, 0, false, false },
+ { "test_case_1200326.mp4", 0, -1, 0, 0, 0, -1, false, 0, false, false },
+ { "test_case_1204580.mp4", 1, 502500,
+ 320, 180, 0, -1, false, 0, false, false },
+ { "test_case_1216748.mp4", 0, -1, 0, 0, 0, -1, false, 152, false, false },
+ { "test_case_1296473.mp4", 0, -1, 0, 0, 0, -1, false, 0, false, false },
+ { "test_case_1296532.mp4", 1, 5589333,
+ 560, 320, 1, 5589333,
+ true, 0, true, true },
+ { "test_case_1301065.mp4", 0, -1, 0, 0, 1, 100079991719000000,
+ false, 0, false, false },
+ { "test_case_1301065-u32max.mp4", 0, -1, 0, 0, 1, 97391548639,
+ false, 0, false, false },
+ { "test_case_1301065-max-ez.mp4", 0, -1, 0, 0, 1, 209146758205306,
+ false, 0, false, false },
+ { "test_case_1301065-harder.mp4", 0, -1, 0, 0, 1, 209146758205328,
+ false, 0, false, false },
+ { "test_case_1301065-max-ok.mp4", 0, -1, 0, 0, 1, 9223372036854775804,
+ false, 0, false, false },
+ { "test_case_1301065-overfl.mp4", 0, -1, 0, 0, 0, -1, false, 0, false, false },
+ { "test_case_1301065-i64max.mp4", 0, -1, 0, 0, 0, -1, false, 0, false, false },
+ { "test_case_1301065-i64min.mp4", 0, -1, 0, 0, 0, -1, false, 0, false, false },
+ { "test_case_1301065-u64max.mp4", 0, -1, 0, 0, 1, 0, false, 0, false, false },
+ { "test_case_1351094.mp4", 0, -1, 0, 0, 0, -1, false, 0, true, true },
+};
+
+TEST(stagefright_MPEG4Metadata, test_case_mp4)
+{
+ for (size_t test = 0; test < ArrayLength(testFiles); ++test) {
+ nsTArray<uint8_t> buffer = ReadTestFile(testFiles[test].mFilename);
+ ASSERT_FALSE(buffer.IsEmpty());
+ RefPtr<Stream> stream = new TestStream(buffer.Elements(), buffer.Length());
+
+ EXPECT_TRUE(MP4Metadata::HasCompleteMetadata(stream));
+ RefPtr<MediaByteBuffer> metadataBuffer = MP4Metadata::Metadata(stream);
+ EXPECT_TRUE(metadataBuffer);
+
+ MP4Metadata metadata(stream);
+ EXPECT_EQ(0u, metadata.GetNumberTracks(TrackInfo::kUndefinedTrack));
+ EXPECT_EQ(testFiles[test].mNumberAudioTracks,
+ metadata.GetNumberTracks(TrackInfo::kAudioTrack));
+ EXPECT_EQ(testFiles[test].mNumberVideoTracks,
+ metadata.GetNumberTracks(TrackInfo::kVideoTrack));
+ EXPECT_EQ(0u, metadata.GetNumberTracks(TrackInfo::kTextTrack));
+ EXPECT_EQ(0u, metadata.GetNumberTracks(static_cast<TrackInfo::TrackType>(-1)));
+ EXPECT_FALSE(metadata.GetTrackInfo(TrackInfo::kUndefinedTrack, 0));
+ UniquePtr<TrackInfo> trackInfo = metadata.GetTrackInfo(TrackInfo::kVideoTrack, 0);
+ if (testFiles[test].mNumberVideoTracks == 0) {
+ EXPECT_TRUE(!trackInfo);
+ } else {
+ ASSERT_TRUE(!!trackInfo);
+ const VideoInfo* videoInfo = trackInfo->GetAsVideoInfo();
+ ASSERT_TRUE(!!videoInfo);
+ EXPECT_TRUE(videoInfo->IsValid());
+ EXPECT_TRUE(videoInfo->IsVideo());
+ EXPECT_EQ(testFiles[test].mVideoDuration, videoInfo->mDuration);
+ EXPECT_EQ(testFiles[test].mWidth, videoInfo->mDisplay.width);
+ EXPECT_EQ(testFiles[test].mHeight, videoInfo->mDisplay.height);
+ FallibleTArray<mp4_demuxer::Index::Indice> indices;
+ EXPECT_TRUE(metadata.ReadTrackIndex(indices, videoInfo->mTrackId));
+ for (const mp4_demuxer::Index::Indice& indice : indices) {
+ EXPECT_TRUE(indice.start_offset <= indice.end_offset);
+ EXPECT_TRUE(indice.start_composition <= indice.end_composition);
+ }
+ }
+ trackInfo = metadata.GetTrackInfo(TrackInfo::kAudioTrack, 0);
+ if (testFiles[test].mNumberAudioTracks == 0) {
+ EXPECT_TRUE(!trackInfo);
+ } else {
+ ASSERT_TRUE(!!trackInfo);
+ const AudioInfo* audioInfo = trackInfo->GetAsAudioInfo();
+ ASSERT_TRUE(!!audioInfo);
+ EXPECT_TRUE(audioInfo->IsValid());
+ EXPECT_TRUE(audioInfo->IsAudio());
+ EXPECT_EQ(testFiles[test].mAudioDuration, audioInfo->mDuration);
+ FallibleTArray<mp4_demuxer::Index::Indice> indices;
+ EXPECT_TRUE(metadata.ReadTrackIndex(indices, audioInfo->mTrackId));
+ for (const mp4_demuxer::Index::Indice& indice : indices) {
+ EXPECT_TRUE(indice.start_offset <= indice.end_offset);
+ EXPECT_TRUE(indice.start_composition <= indice.end_composition);
+ }
+ }
+ EXPECT_FALSE(metadata.GetTrackInfo(TrackInfo::kTextTrack, 0));
+ EXPECT_FALSE(metadata.GetTrackInfo(static_cast<TrackInfo::TrackType>(-1), 0));
+ // We can see anywhere in any MPEG4.
+ EXPECT_TRUE(metadata.CanSeek());
+ EXPECT_EQ(testFiles[test].mHasCrypto, metadata.Crypto().valid);
+ }
+}
+
+// Bug 1224019: This test produces way to much output, disabling for now.
+#if 0
+TEST(stagefright_MPEG4Metadata, test_case_mp4_subsets)
+{
+ static const size_t step = 1u;
+ for (size_t test = 0; test < ArrayLength(testFiles); ++test) {
+ nsTArray<uint8_t> buffer = ReadTestFile(testFiles[test].mFilename);
+ ASSERT_FALSE(buffer.IsEmpty());
+ ASSERT_LE(step, buffer.Length());
+ // Just exercizing the parser starting at different points through the file,
+ // making sure it doesn't crash.
+ // No checks because results would differ for each position.
+ for (size_t offset = 0; offset < buffer.Length() - step; offset += step) {
+ size_t size = buffer.Length() - offset;
+ while (size > 0) {
+ RefPtr<TestStream> stream =
+ new TestStream(buffer.Elements() + offset, size);
+
+ MP4Metadata::HasCompleteMetadata(stream);
+ RefPtr<MediaByteBuffer> metadataBuffer = MP4Metadata::Metadata(stream);
+ MP4Metadata metadata(stream);
+
+ if (stream->mHighestSuccessfulEndOffset <= 0) {
+ // No successful reads -> Cutting down the size won't change anything.
+ break;
+ }
+ if (stream->mHighestSuccessfulEndOffset < size) {
+ // Read up to a point before the end -> Resize down to that point.
+ size = stream->mHighestSuccessfulEndOffset;
+ } else {
+ // Read up to the end (or after?!) -> Just cut 1 byte.
+ size -= 1;
+ }
+ }
+ }
+ }
+}
+#endif
+
+TEST(stagefright_MoofParser, test_case_mp4)
+{
+ for (size_t test = 0; test < ArrayLength(testFiles); ++test) {
+ nsTArray<uint8_t> buffer = ReadTestFile(testFiles[test].mFilename);
+ ASSERT_FALSE(buffer.IsEmpty());
+ RefPtr<Stream> stream = new TestStream(buffer.Elements(), buffer.Length());
+
+ MoofParser parser(stream, 0, false);
+ EXPECT_EQ(0u, parser.mOffset);
+ EXPECT_FALSE(parser.ReachedEnd());
+ EXPECT_TRUE(parser.mInitRange.IsEmpty());
+
+ EXPECT_TRUE(parser.HasMetadata());
+ RefPtr<MediaByteBuffer> metadataBuffer = parser.Metadata();
+ EXPECT_TRUE(metadataBuffer);
+
+ EXPECT_FALSE(parser.mInitRange.IsEmpty());
+ const MediaByteRangeSet byteRanges(
+ MediaByteRange(0, int64_t(buffer.Length())));
+ EXPECT_EQ(testFiles[test].mValidMoof,
+ parser.RebuildFragmentedIndex(byteRanges));
+ if (testFiles[test].mMoofReachedOffset == 0) {
+ EXPECT_EQ(buffer.Length(), parser.mOffset);
+ EXPECT_TRUE(parser.ReachedEnd());
+ } else {
+ EXPECT_EQ(testFiles[test].mMoofReachedOffset, parser.mOffset);
+ EXPECT_FALSE(parser.ReachedEnd());
+ }
+
+ EXPECT_FALSE(parser.mInitRange.IsEmpty());
+ EXPECT_TRUE(parser.GetCompositionRange(byteRanges).IsNull());
+ EXPECT_TRUE(parser.FirstCompleteMediaSegment().IsEmpty());
+ EXPECT_EQ(testFiles[test].mHeader,
+ !parser.FirstCompleteMediaHeader().IsEmpty());
+ }
+}
+
+// Bug 1224019: This test produces way to much output, disabling for now.
+#if 0
+TEST(stagefright_MoofParser, test_case_mp4_subsets)
+{
+ const size_t step = 1u;
+ for (size_t test = 0; test < ArrayLength(testFiles); ++test) {
+ nsTArray<uint8_t> buffer = ReadTestFile(testFiles[test].mFilename);
+ ASSERT_FALSE(buffer.IsEmpty());
+ ASSERT_LE(step, buffer.Length());
+ // Just exercizing the parser starting at different points through the file,
+ // making sure it doesn't crash.
+ // No checks because results would differ for each position.
+ for (size_t offset = 0; offset < buffer.Length() - step; offset += step) {
+ size_t size = buffer.Length() - offset;
+ while (size > 0) {
+ RefPtr<TestStream> stream =
+ new TestStream(buffer.Elements() + offset, size);
+
+ MoofParser parser(stream, 0, false);
+ MediaByteRangeSet byteRanges;
+ EXPECT_FALSE(parser.RebuildFragmentedIndex(byteRanges));
+ parser.GetCompositionRange(byteRanges);
+ parser.HasMetadata();
+ RefPtr<MediaByteBuffer> metadataBuffer = parser.Metadata();
+ parser.FirstCompleteMediaSegment();
+ parser.FirstCompleteMediaHeader();
+
+ if (stream->mHighestSuccessfulEndOffset <= 0) {
+ // No successful reads -> Cutting down the size won't change anything.
+ break;
+ }
+ if (stream->mHighestSuccessfulEndOffset < size) {
+ // Read up to a point before the end -> Resize down to that point.
+ size = stream->mHighestSuccessfulEndOffset;
+ } else {
+ // Read up to the end (or after?!) -> Just cut 1 byte.
+ size -= 1;
+ }
+ }
+ }
+ }
+}
+#endif
+
+uint8_t media_libstagefright_gtest_video_init_mp4[] = {
+ 0x00, 0x00, 0x00, 0x18, 0x66, 0x74, 0x79, 0x70, 0x69, 0x73, 0x6f, 0x6d,
+ 0x00, 0x00, 0x00, 0x01, 0x69, 0x73, 0x6f, 0x6d, 0x61, 0x76, 0x63, 0x31,
+ 0x00, 0x00, 0x02, 0xd1, 0x6d, 0x6f, 0x6f, 0x76, 0x00, 0x00, 0x00, 0x6c,
+ 0x6d, 0x76, 0x68, 0x64, 0x00, 0x00, 0x00, 0x00, 0xc8, 0x49, 0x73, 0xf8,
+ 0xc8, 0x4a, 0xc5, 0x7a, 0x00, 0x00, 0x02, 0x58, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x18,
+ 0x69, 0x6f, 0x64, 0x73, 0x00, 0x00, 0x00, 0x00, 0x10, 0x80, 0x80, 0x80,
+ 0x07, 0x00, 0x4f, 0xff, 0xff, 0x29, 0x15, 0xff, 0x00, 0x00, 0x02, 0x0d,
+ 0x74, 0x72, 0x61, 0x6b, 0x00, 0x00, 0x00, 0x5c, 0x74, 0x6b, 0x68, 0x64,
+ 0x00, 0x00, 0x00, 0x01, 0xc8, 0x49, 0x73, 0xf8, 0xc8, 0x49, 0x73, 0xf9,
+ 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x40, 0x00, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x01, 0x68, 0x00, 0x00,
+ 0x00, 0x00, 0x01, 0xa9, 0x6d, 0x64, 0x69, 0x61, 0x00, 0x00, 0x00, 0x20,
+ 0x6d, 0x64, 0x68, 0x64, 0x00, 0x00, 0x00, 0x00, 0xc8, 0x49, 0x73, 0xf8,
+ 0xc8, 0x49, 0x73, 0xf9, 0x00, 0x00, 0x75, 0x30, 0x00, 0x00, 0x00, 0x00,
+ 0x55, 0xc4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x68, 0x64, 0x6c, 0x72,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0x69, 0x64, 0x65,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x47, 0x50, 0x41, 0x43, 0x20, 0x49, 0x53, 0x4f, 0x20, 0x56, 0x69, 0x64,
+ 0x65, 0x6f, 0x20, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x72, 0x00, 0x00,
+ 0x00, 0x00, 0x01, 0x49, 0x6d, 0x69, 0x6e, 0x66, 0x00, 0x00, 0x00, 0x14,
+ 0x76, 0x6d, 0x68, 0x64, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x64, 0x69, 0x6e, 0x66,
+ 0x00, 0x00, 0x00, 0x1c, 0x64, 0x72, 0x65, 0x66, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0c, 0x75, 0x72, 0x6c, 0x20,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x09, 0x73, 0x74, 0x62, 0x6c,
+ 0x00, 0x00, 0x00, 0xad, 0x73, 0x74, 0x73, 0x64, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x9d, 0x61, 0x76, 0x63, 0x31,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x02, 0x80, 0x01, 0x68, 0x00, 0x48, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x18, 0xff, 0xff, 0x00, 0x00, 0x00, 0x33, 0x61, 0x76,
+ 0x63, 0x43, 0x01, 0x64, 0x00, 0x1f, 0xff, 0xe1, 0x00, 0x1b, 0x67, 0x64,
+ 0x00, 0x1f, 0xac, 0x2c, 0xc5, 0x02, 0x80, 0xbf, 0xe5, 0xc0, 0x44, 0x00,
+ 0x00, 0x03, 0x00, 0x04, 0x00, 0x00, 0x03, 0x00, 0xf2, 0x3c, 0x60, 0xc6,
+ 0x58, 0x01, 0x00, 0x05, 0x68, 0xe9, 0x2b, 0x2c, 0x8b, 0x00, 0x00, 0x00,
+ 0x14, 0x62, 0x74, 0x72, 0x74, 0x00, 0x01, 0x5a, 0xc2, 0x00, 0x24, 0x74,
+ 0x38, 0x00, 0x09, 0x22, 0x00, 0x00, 0x00, 0x00, 0x10, 0x73, 0x74, 0x74,
+ 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x10, 0x63, 0x74, 0x74, 0x73, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x10, 0x73, 0x74, 0x73, 0x63, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0x73, 0x74, 0x73,
+ 0x7a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x10, 0x73, 0x74, 0x63, 0x6f, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x6d, 0x76, 0x65,
+ 0x78, 0x00, 0x00, 0x00, 0x10, 0x6d, 0x65, 0x68, 0x64, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x05, 0x76, 0x18, 0x00, 0x00, 0x00, 0x20, 0x74, 0x72, 0x65,
+ 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x03, 0xe8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
+ 0x00
+};
+
+const uint32_t media_libstagefright_gtest_video_init_mp4_len = 745;
+
+TEST(stagefright_MP4Metadata, EmptyCTTS)
+{
+ RefPtr<MediaByteBuffer> buffer = new MediaByteBuffer(media_libstagefright_gtest_video_init_mp4_len);
+ buffer->AppendElements(media_libstagefright_gtest_video_init_mp4, media_libstagefright_gtest_video_init_mp4_len);
+ RefPtr<BufferStream> stream = new BufferStream(buffer);
+
+ EXPECT_TRUE(MP4Metadata::HasCompleteMetadata(stream));
+ RefPtr<MediaByteBuffer> metadataBuffer = MP4Metadata::Metadata(stream);
+ EXPECT_TRUE(metadataBuffer);
+
+ MP4Metadata metadata(stream);
+
+ EXPECT_EQ(1u, metadata.GetNumberTracks(TrackInfo::kVideoTrack));
+ mozilla::UniquePtr<mozilla::TrackInfo> track =
+ metadata.GetTrackInfo(TrackInfo::kVideoTrack, 0);
+ EXPECT_TRUE(track != nullptr);
+ // We can seek anywhere in any MPEG4.
+ EXPECT_TRUE(metadata.CanSeek());
+ EXPECT_FALSE(metadata.Crypto().valid);
+}
+
diff --git a/media/libstagefright/gtest/moz.build b/media/libstagefright/gtest/moz.build
new file mode 100644
index 000000000..15d91dc94
--- /dev/null
+++ b/media/libstagefright/gtest/moz.build
@@ -0,0 +1,49 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+Library('stagefright_gtest')
+
+SOURCES += [
+ 'TestInterval.cpp',
+ 'TestParser.cpp',
+]
+
+TEST_HARNESS_FILES.gtest += [
+ 'test_case_1156505.mp4',
+ 'test_case_1181213.mp4',
+ 'test_case_1181215.mp4',
+ 'test_case_1181220.mp4',
+ 'test_case_1181223.mp4',
+ 'test_case_1181719.mp4',
+ 'test_case_1185230.mp4',
+ 'test_case_1187067.mp4',
+ 'test_case_1200326.mp4',
+ 'test_case_1204580.mp4',
+ 'test_case_1216748.mp4',
+ 'test_case_1296473.mp4',
+ 'test_case_1296532.mp4',
+ 'test_case_1301065-harder.mp4',
+ 'test_case_1301065-i64max.mp4',
+ 'test_case_1301065-i64min.mp4',
+ 'test_case_1301065-max-ez.mp4',
+ 'test_case_1301065-max-ok.mp4',
+ 'test_case_1301065-overfl.mp4',
+ 'test_case_1301065-u32max.mp4',
+ 'test_case_1301065-u64max.mp4',
+ 'test_case_1301065.mp4',
+ 'test_case_1351094.mp4',
+]
+
+if CONFIG['MOZ_RUST']:
+ UNIFIED_SOURCES += ['TestMP4Rust.cpp',]
+ TEST_HARNESS_FILES.gtest += [
+ '../../../dom/media/test/street.mp4',
+ ]
+ LOCAL_INCLUDES += [
+ '../binding/include',
+ ]
+
+FINAL_LIBRARY = 'xul-gtest'
diff --git a/media/libstagefright/gtest/test_case_1156505.mp4 b/media/libstagefright/gtest/test_case_1156505.mp4
new file mode 100644
index 000000000..687b06ee1
--- /dev/null
+++ b/media/libstagefright/gtest/test_case_1156505.mp4
Binary files differ
diff --git a/media/libstagefright/gtest/test_case_1181213.mp4 b/media/libstagefright/gtest/test_case_1181213.mp4
new file mode 100644
index 000000000..e2326edb4
--- /dev/null
+++ b/media/libstagefright/gtest/test_case_1181213.mp4
Binary files differ
diff --git a/media/libstagefright/gtest/test_case_1181215.mp4 b/media/libstagefright/gtest/test_case_1181215.mp4
new file mode 100644
index 000000000..7adba3836
--- /dev/null
+++ b/media/libstagefright/gtest/test_case_1181215.mp4
Binary files differ
diff --git a/media/libstagefright/gtest/test_case_1181220.mp4 b/media/libstagefright/gtest/test_case_1181220.mp4
new file mode 100644
index 000000000..e4a144c8e
--- /dev/null
+++ b/media/libstagefright/gtest/test_case_1181220.mp4
Binary files differ
diff --git a/media/libstagefright/gtest/test_case_1181223.mp4 b/media/libstagefright/gtest/test_case_1181223.mp4
new file mode 100644
index 000000000..2aa2d5abf
--- /dev/null
+++ b/media/libstagefright/gtest/test_case_1181223.mp4
Binary files differ
diff --git a/media/libstagefright/gtest/test_case_1181719.mp4 b/media/libstagefright/gtest/test_case_1181719.mp4
new file mode 100644
index 000000000..6846edd6e
--- /dev/null
+++ b/media/libstagefright/gtest/test_case_1181719.mp4
Binary files differ
diff --git a/media/libstagefright/gtest/test_case_1185230.mp4 b/media/libstagefright/gtest/test_case_1185230.mp4
new file mode 100644
index 000000000..ac5cbdbe8
--- /dev/null
+++ b/media/libstagefright/gtest/test_case_1185230.mp4
Binary files differ
diff --git a/media/libstagefright/gtest/test_case_1187067.mp4 b/media/libstagefright/gtest/test_case_1187067.mp4
new file mode 100644
index 000000000..fdb396eeb
--- /dev/null
+++ b/media/libstagefright/gtest/test_case_1187067.mp4
Binary files differ
diff --git a/media/libstagefright/gtest/test_case_1200326.mp4 b/media/libstagefright/gtest/test_case_1200326.mp4
new file mode 100644
index 000000000..5b8b27d50
--- /dev/null
+++ b/media/libstagefright/gtest/test_case_1200326.mp4
Binary files differ
diff --git a/media/libstagefright/gtest/test_case_1204580.mp4 b/media/libstagefright/gtest/test_case_1204580.mp4
new file mode 100644
index 000000000..4e55b0571
--- /dev/null
+++ b/media/libstagefright/gtest/test_case_1204580.mp4
Binary files differ
diff --git a/media/libstagefright/gtest/test_case_1216748.mp4 b/media/libstagefright/gtest/test_case_1216748.mp4
new file mode 100755
index 000000000..7072f53be
--- /dev/null
+++ b/media/libstagefright/gtest/test_case_1216748.mp4
Binary files differ
diff --git a/media/libstagefright/gtest/test_case_1296473.mp4 b/media/libstagefright/gtest/test_case_1296473.mp4
new file mode 100644
index 000000000..109eb5106
--- /dev/null
+++ b/media/libstagefright/gtest/test_case_1296473.mp4
Binary files differ
diff --git a/media/libstagefright/gtest/test_case_1296532.mp4 b/media/libstagefright/gtest/test_case_1296532.mp4
new file mode 100644
index 000000000..5a5669bb8
--- /dev/null
+++ b/media/libstagefright/gtest/test_case_1296532.mp4
Binary files differ
diff --git a/media/libstagefright/gtest/test_case_1301065-harder.mp4 b/media/libstagefright/gtest/test_case_1301065-harder.mp4
new file mode 100644
index 000000000..7d678b7c6
--- /dev/null
+++ b/media/libstagefright/gtest/test_case_1301065-harder.mp4
Binary files differ
diff --git a/media/libstagefright/gtest/test_case_1301065-i64max.mp4 b/media/libstagefright/gtest/test_case_1301065-i64max.mp4
new file mode 100644
index 000000000..5a3572f88
--- /dev/null
+++ b/media/libstagefright/gtest/test_case_1301065-i64max.mp4
Binary files differ
diff --git a/media/libstagefright/gtest/test_case_1301065-i64min.mp4 b/media/libstagefright/gtest/test_case_1301065-i64min.mp4
new file mode 100644
index 000000000..4d3eb366e
--- /dev/null
+++ b/media/libstagefright/gtest/test_case_1301065-i64min.mp4
Binary files differ
diff --git a/media/libstagefright/gtest/test_case_1301065-max-ez.mp4 b/media/libstagefright/gtest/test_case_1301065-max-ez.mp4
new file mode 100644
index 000000000..17fbf411e
--- /dev/null
+++ b/media/libstagefright/gtest/test_case_1301065-max-ez.mp4
Binary files differ
diff --git a/media/libstagefright/gtest/test_case_1301065-max-ok.mp4 b/media/libstagefright/gtest/test_case_1301065-max-ok.mp4
new file mode 100644
index 000000000..a5e1e4610
--- /dev/null
+++ b/media/libstagefright/gtest/test_case_1301065-max-ok.mp4
Binary files differ
diff --git a/media/libstagefright/gtest/test_case_1301065-overfl.mp4 b/media/libstagefright/gtest/test_case_1301065-overfl.mp4
new file mode 100644
index 000000000..1ef24e932
--- /dev/null
+++ b/media/libstagefright/gtest/test_case_1301065-overfl.mp4
Binary files differ
diff --git a/media/libstagefright/gtest/test_case_1301065-u32max.mp4 b/media/libstagefright/gtest/test_case_1301065-u32max.mp4
new file mode 100644
index 000000000..b1d8b6ce7
--- /dev/null
+++ b/media/libstagefright/gtest/test_case_1301065-u32max.mp4
Binary files differ
diff --git a/media/libstagefright/gtest/test_case_1301065-u64max.mp4 b/media/libstagefright/gtest/test_case_1301065-u64max.mp4
new file mode 100644
index 000000000..419dcba2c
--- /dev/null
+++ b/media/libstagefright/gtest/test_case_1301065-u64max.mp4
Binary files differ
diff --git a/media/libstagefright/gtest/test_case_1301065.mp4 b/media/libstagefright/gtest/test_case_1301065.mp4
new file mode 100644
index 000000000..543a4fba3
--- /dev/null
+++ b/media/libstagefright/gtest/test_case_1301065.mp4
Binary files differ
diff --git a/media/libstagefright/gtest/test_case_1351094.mp4 b/media/libstagefright/gtest/test_case_1351094.mp4
new file mode 100644
index 000000000..2dfd4c35c
--- /dev/null
+++ b/media/libstagefright/gtest/test_case_1351094.mp4
Binary files differ
diff --git a/media/libstagefright/moz.build b/media/libstagefright/moz.build
new file mode 100644
index 000000000..71cc87e5f
--- /dev/null
+++ b/media/libstagefright/moz.build
@@ -0,0 +1,172 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+DEFINES['ANDROID_SMP'] = 0
+DEFINES['LOG_NDEBUG'] = 1
+
+if CONFIG['OS_TARGET'] != 'WINNT':
+ DEFINES['_GLIBCXX_OS_DEFINES'] = True
+
+if CONFIG['OS_TARGET'] == 'WINNT':
+ if CONFIG['_MSC_VER']:
+ DEFINES['ssize_t'] = 'intptr_t'
+ DEFINES['off64_t'] = 'int64_t'
+ DEFINES['strcasecmp'] = 'stricmp'
+ DEFINES['strncasecmp'] = 'strnicmp'
+ DEFINES['HAVE_MS_C_RUNTIME'] = True
+ DEFINES['__PRETTY_FUNCTION__'] = '__FUNCTION__'
+ LOCAL_INCLUDES += [ 'ports/win32/include' ]
+elif CONFIG['OS_TARGET'] == 'Darwin':
+ DEFINES['HAVE_SYS_UIO_H'] = True
+ DEFINES['off64_t'] = 'off_t'
+ LOCAL_INCLUDES += [ 'ports/darwin/include' ]
+elif CONFIG['OS_TARGET'] in ('DragonFly', 'FreeBSD', 'OpenBSD', 'NetBSD',
+ 'GNU/kFreeBSD'):
+ if CONFIG['OS_TARGET'] != 'NetBSD':
+ DEFINES['ENODATA'] = '-0x80000003'
+ if CONFIG['OS_TARGET'] == 'OpenBSD':
+ DEFINES['EBADMSG'] = '-0x80000006'
+ DEFINES['HAVE_SYS_UIO_H'] = True
+ if CONFIG['OS_TARGET'] != 'GNU/kFreeBSD':
+ DEFINES['off64_t'] = 'off_t'
+ LOCAL_INCLUDES += [ 'ports/bsd/include' ]
+else:
+ DEFINES['HAVE_SYS_UIO_H'] = True
+
+if CONFIG['OS_TARGET'] != 'Android':
+ DEFINES['FAKE_LOG_DEVICE'] = True
+ SOURCES += [
+ 'system/core/liblog/fake_log_device.c',
+ ]
+ UNIFIED_SOURCES += [
+ 'system/core/libcutils/strdup16to8.c',
+ 'system/core/liblog/logd_write.c',
+ 'system/core/liblog/logprint.c',
+ ]
+
+EXPORTS.mp4_demuxer += [
+ 'binding/include/mp4_demuxer/Adts.h',
+ 'binding/include/mp4_demuxer/AnnexB.h',
+ 'binding/include/mp4_demuxer/Atom.h',
+ 'binding/include/mp4_demuxer/AtomType.h',
+ 'binding/include/mp4_demuxer/BitReader.h',
+ 'binding/include/mp4_demuxer/BufferStream.h',
+ 'binding/include/mp4_demuxer/ByteReader.h',
+ 'binding/include/mp4_demuxer/ByteWriter.h',
+ 'binding/include/mp4_demuxer/DecoderData.h',
+ 'binding/include/mp4_demuxer/H264.h',
+ 'binding/include/mp4_demuxer/Index.h',
+ 'binding/include/mp4_demuxer/Interval.h',
+ 'binding/include/mp4_demuxer/MoofParser.h',
+ 'binding/include/mp4_demuxer/MP4Metadata.h',
+ 'binding/include/mp4_demuxer/ResourceStream.h',
+ 'binding/include/mp4_demuxer/SinfParser.h',
+ 'binding/include/mp4_demuxer/Stream.h',
+]
+
+EXPORTS.demuxer += [
+ 'binding/include/demuxer/TrackDemuxer.h',
+]
+
+SOURCES += [
+ 'frameworks/av/media/libstagefright/foundation/hexdump.cpp',
+ 'frameworks/av/media/libstagefright/MetaData.cpp',
+ 'system/core/libutils/RefBase.cpp',
+ 'system/core/libutils/String16.cpp',
+ 'system/core/libutils/String8.cpp',
+ 'system/core/libutils/VectorImpl.cpp',
+]
+
+if CONFIG['MOZ_RUST']:
+ EXPORTS += [
+ 'binding/include/mp4parse.h',
+ ]
+
+UNIFIED_SOURCES += [
+ 'binding/Adts.cpp',
+ 'binding/AnnexB.cpp',
+ 'binding/BitReader.cpp',
+ 'binding/Box.cpp',
+ 'binding/BufferStream.cpp',
+ 'binding/DecoderData.cpp',
+ 'binding/H264.cpp',
+ 'binding/Index.cpp',
+ 'binding/MoofParser.cpp',
+ 'binding/MP4Metadata.cpp',
+ 'binding/ResourceStream.cpp',
+ 'binding/SinfParser.cpp',
+ 'frameworks/av/media/libstagefright/DataSource.cpp',
+ 'frameworks/av/media/libstagefright/ESDS.cpp',
+ 'frameworks/av/media/libstagefright/foundation/AAtomizer.cpp',
+ 'frameworks/av/media/libstagefright/foundation/ABitReader.cpp',
+ 'frameworks/av/media/libstagefright/foundation/ABuffer.cpp',
+ 'frameworks/av/media/libstagefright/foundation/AString.cpp',
+ 'frameworks/av/media/libstagefright/MediaBuffer.cpp',
+ 'frameworks/av/media/libstagefright/MediaDefs.cpp',
+ 'frameworks/av/media/libstagefright/MediaSource.cpp',
+ 'frameworks/av/media/libstagefright/MPEG4Extractor.cpp',
+ 'frameworks/av/media/libstagefright/SampleIterator.cpp',
+ 'frameworks/av/media/libstagefright/SampleTable.cpp',
+ 'frameworks/av/media/libstagefright/Utils.cpp',
+ 'system/core/libutils/SharedBuffer.cpp',
+ 'system/core/libutils/Static.cpp',
+ 'system/core/libutils/Unicode.cpp',
+]
+
+LOCAL_INCLUDES += [
+ 'binding/include',
+ 'frameworks/av/include',
+ 'frameworks/av/include/media/stagefright/foundation',
+ 'frameworks/av/media/libstagefright/',
+ 'stubs/empty',
+ 'stubs/include',
+ 'stubs/include/media/stagefright/foundation',
+ 'system/core/include',
+]
+
+TEST_DIRS += [
+ 'gtest',
+]
+
+# We allow warnings for third-party code that can be updated from upstream.
+ALLOW_COMPILER_WARNINGS = True
+
+FINAL_LIBRARY = 'xul'
+
+# Suppress warnings in third-party code.
+if CONFIG['_MSC_VER']:
+ CFLAGS += [
+ '-wd4013', # 'function' undefined; assuming extern returning int
+ '-wd4101', # unreferenced local variable
+ ]
+ CXXFLAGS += [
+ '-wd4018', # '<' : signed/unsigned mismatch
+ '-wd4275', # non dll-interface class used as base for dll-interface class
+ '-wd4305', # '=' : truncation from 'double' to 'float'
+ '-wd4309', # '=' : truncation of constant value
+ '-wd4355', # 'this' : used in base member initializer list
+ '-wd4804', # '>' : unsafe use of type 'bool' in operation
+ '-wd4099', # mismatched class/struct tags
+ ]
+elif CONFIG['GNU_CXX']:
+ CFLAGS += [
+ '-Wno-comment',
+ '-Wno-sign-compare'
+ ]
+ CXXFLAGS += [
+ '-Wno-format',
+ '-Wno-format-security',
+ '-Wno-multichar',
+ '-Wno-sign-compare',
+ '-Wno-unused',
+ ]
+ if CONFIG['CLANG_CXX']:
+ CXXFLAGS += [
+ '-Wno-mismatched-tags',
+ '-Wno-tautological-constant-out-of-range-compare',
+ '-Wno-unreachable-code-return',
+ '-Wno-implicit-fallthrough',
+ ]
diff --git a/media/libstagefright/patches/frameworks/av.patch b/media/libstagefright/patches/frameworks/av.patch
new file mode 100644
index 000000000..22fb6c580
--- /dev/null
+++ b/media/libstagefright/patches/frameworks/av.patch
@@ -0,0 +1,1192 @@
+diff --git a/include/media/stagefright/DataSource.h b/include/media/stagefright/DataSource.h
+index 742bc0e..f6b30b0 100644
+--- a/include/media/stagefright/DataSource.h
++++ b/include/media/stagefright/DataSource.h
+@@ -21,16 +21,10 @@
+ #include <sys/types.h>
+
+ #include <media/stagefright/MediaErrors.h>
+-#include <utils/Errors.h>
+-#include <utils/KeyedVector.h>
+-#include <utils/List.h>
+ #include <utils/RefBase.h>
+-#include <utils/threads.h>
+-#include <drm/DrmManagerClient.h>
+
+-namespace android {
++namespace stagefright {
+
+-struct AMessage;
+ class String8;
+
+ class DataSource : public RefBase {
+@@ -42,10 +36,6 @@ public:
+ kIsHTTPBasedSource = 8,
+ };
+
+- static sp<DataSource> CreateFromURI(
+- const char *uri,
+- const KeyedVector<String8, String8> *headers = NULL);
+-
+ DataSource() {}
+
+ virtual status_t initCheck() const = 0;
+@@ -69,6 +59,7 @@ public:
+ return ERROR_UNSUPPORTED;
+ }
+
++#if 0
+ ////////////////////////////////////////////////////////////////////////////
+
+ bool sniff(String8 *mimeType, float *confidence, sp<AMessage> *meta);
+@@ -92,6 +83,7 @@ public:
+ virtual String8 getUri() {
+ return String8();
+ }
++#endif
+
+ virtual String8 getMIMEType() const;
+
+@@ -99,13 +91,10 @@ protected:
+ virtual ~DataSource() {}
+
+ private:
+- static Mutex gSnifferMutex;
+- static List<SnifferFunc> gSniffers;
+-
+ DataSource(const DataSource &);
+ DataSource &operator=(const DataSource &);
+ };
+
+-} // namespace android
++} // namespace stagefright
+
+ #endif // DATA_SOURCE_H_
+diff --git a/include/media/stagefright/MediaBuffer.h b/include/media/stagefright/MediaBuffer.h
+index 3d79596..4172739 100644
+--- a/include/media/stagefright/MediaBuffer.h
++++ b/include/media/stagefright/MediaBuffer.h
+@@ -23,7 +23,7 @@
+ #include <utils/Errors.h>
+ #include <utils/RefBase.h>
+
+-namespace android {
++namespace stagefright {
+
+ struct ABuffer;
+ class GraphicBuffer;
+@@ -118,6 +118,6 @@ private:
+ MediaBuffer &operator=(const MediaBuffer &);
+ };
+
+-} // namespace android
++} // namespace stagefright
+
+ #endif // MEDIA_BUFFER_H_
+diff --git a/include/media/stagefright/MediaBufferGroup.h b/include/media/stagefright/MediaBufferGroup.h
+index 0488292..4a51351 100644
+--- a/include/media/stagefright/MediaBufferGroup.h
++++ b/include/media/stagefright/MediaBufferGroup.h
+@@ -22,7 +22,7 @@
+ #include <utils/Errors.h>
+ #include <utils/threads.h>
+
+-namespace android {
++namespace stagefright {
+
+ class MediaBuffer;
+ class MetaData;
+@@ -53,6 +53,6 @@ private:
+ MediaBufferGroup &operator=(const MediaBufferGroup &);
+ };
+
+-} // namespace android
++} // namespace stagefright
+
+ #endif // MEDIA_BUFFER_GROUP_H_
+diff --git a/include/media/stagefright/MediaDefs.h b/include/media/stagefright/MediaDefs.h
+index 85693d4..9897caf 100644
+--- a/include/media/stagefright/MediaDefs.h
++++ b/include/media/stagefright/MediaDefs.h
+@@ -18,7 +18,7 @@
+
+ #define MEDIA_DEFS_H_
+
+-namespace android {
++namespace stagefright {
+
+ extern const char *MEDIA_MIMETYPE_IMAGE_JPEG;
+
+@@ -58,6 +58,6 @@ extern const char *MEDIA_MIMETYPE_CONTAINER_WVM;
+ extern const char *MEDIA_MIMETYPE_TEXT_3GPP;
+ extern const char *MEDIA_MIMETYPE_TEXT_SUBRIP;
+
+-} // namespace android
++} // namespace stagefright
+
+ #endif // MEDIA_DEFS_H_
+diff --git a/include/media/stagefright/MediaErrors.h b/include/media/stagefright/MediaErrors.h
+index 686f286..df2c1a5 100644
+--- a/include/media/stagefright/MediaErrors.h
++++ b/include/media/stagefright/MediaErrors.h
+@@ -20,52 +20,50 @@
+
+ #include <utils/Errors.h>
+
+-namespace android {
++namespace stagefright {
+
+-enum {
+- MEDIA_ERROR_BASE = -1000,
++#define MEDIA_ERROR_BASE (-1000)
+
+- ERROR_ALREADY_CONNECTED = MEDIA_ERROR_BASE,
+- ERROR_NOT_CONNECTED = MEDIA_ERROR_BASE - 1,
+- ERROR_UNKNOWN_HOST = MEDIA_ERROR_BASE - 2,
+- ERROR_CANNOT_CONNECT = MEDIA_ERROR_BASE - 3,
+- ERROR_IO = MEDIA_ERROR_BASE - 4,
+- ERROR_CONNECTION_LOST = MEDIA_ERROR_BASE - 5,
+- ERROR_MALFORMED = MEDIA_ERROR_BASE - 7,
+- ERROR_OUT_OF_RANGE = MEDIA_ERROR_BASE - 8,
+- ERROR_BUFFER_TOO_SMALL = MEDIA_ERROR_BASE - 9,
+- ERROR_UNSUPPORTED = MEDIA_ERROR_BASE - 10,
+- ERROR_END_OF_STREAM = MEDIA_ERROR_BASE - 11,
++#define ERROR_ALREADY_CONNECTED (MEDIA_ERROR_BASE)
++#define ERROR_NOT_CONNECTED (MEDIA_ERROR_BASE - 1)
++#define ERROR_UNKNOWN_HOST (MEDIA_ERROR_BASE - 2)
++#define ERROR_CANNOT_CONNECT (MEDIA_ERROR_BASE - 3)
++#define ERROR_IO (MEDIA_ERROR_BASE - 4)
++#define ERROR_CONNECTION_LOST (MEDIA_ERROR_BASE - 5)
++#define ERROR_MALFORMED (MEDIA_ERROR_BASE - 7)
++#define ERROR_OUT_OF_RANGE (MEDIA_ERROR_BASE - 8)
++#define ERROR_BUFFER_TOO_SMALL (MEDIA_ERROR_BASE - 9)
++#define ERROR_UNSUPPORTED (MEDIA_ERROR_BASE - 10)
++#define ERROR_END_OF_STREAM (MEDIA_ERROR_BASE - 11)
+
+- // Not technically an error.
+- INFO_FORMAT_CHANGED = MEDIA_ERROR_BASE - 12,
+- INFO_DISCONTINUITY = MEDIA_ERROR_BASE - 13,
+- INFO_OUTPUT_BUFFERS_CHANGED = MEDIA_ERROR_BASE - 14,
++// Not technically an error.
++#define INFO_FORMAT_CHANGED (MEDIA_ERROR_BASE - 12)
++#define INFO_DISCONTINUITY (MEDIA_ERROR_BASE - 13)
++#define INFO_OUTPUT_BUFFERS_CHANGED (MEDIA_ERROR_BASE - 14)
+
+- // The following constant values should be in sync with
+- // drm/drm_framework_common.h
+- DRM_ERROR_BASE = -2000,
++// The following constant values should be in sync with
++// drm/drm_framework_common.h
++#define DRM_ERROR_BASE (-2000)
+
+- ERROR_DRM_UNKNOWN = DRM_ERROR_BASE,
+- ERROR_DRM_NO_LICENSE = DRM_ERROR_BASE - 1,
+- ERROR_DRM_LICENSE_EXPIRED = DRM_ERROR_BASE - 2,
+- ERROR_DRM_SESSION_NOT_OPENED = DRM_ERROR_BASE - 3,
+- ERROR_DRM_DECRYPT_UNIT_NOT_INITIALIZED = DRM_ERROR_BASE - 4,
+- ERROR_DRM_DECRYPT = DRM_ERROR_BASE - 5,
+- ERROR_DRM_CANNOT_HANDLE = DRM_ERROR_BASE - 6,
+- ERROR_DRM_TAMPER_DETECTED = DRM_ERROR_BASE - 7,
+- ERROR_DRM_NOT_PROVISIONED = DRM_ERROR_BASE - 8,
+- ERROR_DRM_DEVICE_REVOKED = DRM_ERROR_BASE - 9,
+- ERROR_DRM_RESOURCE_BUSY = DRM_ERROR_BASE - 10,
++#define ERROR_DRM_UNKNOWN (DRM_ERROR_BASE)
++#define ERROR_DRM_NO_LICENSE (DRM_ERROR_BASE - 1)
++#define ERROR_DRM_LICENSE_EXPIRED (DRM_ERROR_BASE - 2)
++#define ERROR_DRM_SESSION_NOT_OPENED (DRM_ERROR_BASE - 3)
++#define ERROR_DRM_DECRYPT_UNIT_NOT_INITIALIZED (DRM_ERROR_BASE - 4)
++#define ERROR_DRM_DECRYPT (DRM_ERROR_BASE - 5)
++#define ERROR_DRM_CANNOT_HANDLE (DRM_ERROR_BASE - 6)
++#define ERROR_DRM_TAMPER_DETECTED (DRM_ERROR_BASE - 7)
++#define ERROR_DRM_NOT_PROVISIONED (DRM_ERROR_BASE - 8)
++#define ERROR_DRM_DEVICE_REVOKED (DRM_ERROR_BASE - 9)
++#define ERROR_DRM_RESOURCE_BUSY (DRM_ERROR_BASE - 10)
+
+- ERROR_DRM_VENDOR_MAX = DRM_ERROR_BASE - 500,
+- ERROR_DRM_VENDOR_MIN = DRM_ERROR_BASE - 999,
++#define ERROR_DRM_VENDOR_MAX (DRM_ERROR_BASE - 500)
++#define ERROR_DRM_VENDOR_MIN (DRM_ERROR_BASE - 999)
+
+- // Heartbeat Error Codes
+- HEARTBEAT_ERROR_BASE = -3000,
+- ERROR_HEARTBEAT_TERMINATE_REQUESTED = HEARTBEAT_ERROR_BASE,
+-};
++// Heartbeat Error Codes
++#define HEARTBEAT_ERROR_BASE (-3000)
++#define ERROR_HEARTBEAT_TERMINATE_REQUESTED (HEARTBEAT_ERROR_BASE)
+
+-} // namespace android
++} // namespace stagefright
+
+ #endif // MEDIA_ERRORS_H_
+diff --git a/include/media/stagefright/MediaExtractor.h b/include/media/stagefright/MediaExtractor.h
+index 3076a96..96d9ee1 100644
+--- a/include/media/stagefright/MediaExtractor.h
++++ b/include/media/stagefright/MediaExtractor.h
+@@ -20,7 +20,7 @@
+
+ #include <utils/RefBase.h>
+
+-namespace android {
++namespace stagefright {
+
+ class DataSource;
+ class MediaSource;
+@@ -42,7 +42,7 @@ public:
+
+ // Return container specific meta-data. The default implementation
+ // returns an empty metadata object.
+- virtual sp<MetaData> getMetaData();
++ virtual sp<MetaData> getMetaData() = 0;
+
+ enum Flags {
+ CAN_SEEK_BACKWARD = 1, // the "seek 10secs back button"
+@@ -53,7 +53,7 @@ public:
+
+ // If subclasses do _not_ override this, the default is
+ // CAN_SEEK_BACKWARD | CAN_SEEK_FORWARD | CAN_SEEK | CAN_PAUSE
+- virtual uint32_t flags() const;
++ virtual uint32_t flags() const = 0;
+
+ // for DRM
+ void setDrmFlag(bool flag) {
+@@ -77,6 +77,6 @@ private:
+ MediaExtractor &operator=(const MediaExtractor &);
+ };
+
+-} // namespace android
++} // namespace stagefright
+
+ #endif // MEDIA_EXTRACTOR_H_
+diff --git a/include/media/stagefright/MediaSource.h b/include/media/stagefright/MediaSource.h
+index 3818e63..9aa6669 100644
+--- a/include/media/stagefright/MediaSource.h
++++ b/include/media/stagefright/MediaSource.h
+@@ -24,7 +24,7 @@
+ #include <utils/RefBase.h>
+ #include <utils/Vector.h>
+
+-namespace android {
++namespace stagefright {
+
+ class MediaBuffer;
+ class MetaData;
+@@ -117,6 +117,6 @@ private:
+ MediaSource &operator=(const MediaSource &);
+ };
+
+-} // namespace android
++} // namespace stagefright
+
+ #endif // MEDIA_SOURCE_H_
+diff --git a/include/media/stagefright/MetaData.h b/include/media/stagefright/MetaData.h
+index de3fc36..30b40c8 100644
+--- a/include/media/stagefright/MetaData.h
++++ b/include/media/stagefright/MetaData.h
+@@ -26,7 +26,7 @@
+ #include <utils/KeyedVector.h>
+ #include <utils/String8.h>
+
+-namespace android {
++namespace stagefright {
+
+ // The following keys map to int32_t data unless indicated otherwise.
+ enum {
+@@ -48,6 +48,7 @@ enum {
+ kKeyChannelCount = '#chn', // int32_t
+ kKeyChannelMask = 'chnm', // int32_t
+ kKeySampleRate = 'srte', // int32_t (audio sampling rate Hz)
++ kKeySampleSize = 'ssiz', // int32_t (sample size in bits)
+ kKeyFrameRate = 'frmR', // int32_t (video frame rate fps)
+ kKeyBitRate = 'brte', // int32_t (bps)
+ kKeyESDS = 'esds', // raw data
+@@ -266,6 +267,6 @@ private:
+ // MetaData &operator=(const MetaData &);
+ };
+
+-} // namespace android
++} // namespace stagefright
+
+ #endif // META_DATA_H_
+diff --git a/include/media/stagefright/Utils.h b/include/media/stagefright/Utils.h
+index c24f612..bd70252 100644
+--- a/include/media/stagefright/Utils.h
++++ b/include/media/stagefright/Utils.h
+@@ -25,10 +25,10 @@
+ #include <system/audio.h>
+ #include <media/MediaPlayerInterface.h>
+
+-namespace android {
++namespace stagefright {
+
+ #define FOURCC(c1, c2, c3, c4) \
+- (c1 << 24 | c2 << 16 | c3 << 8 | c4)
++ ((uint32_t) c1 << 24 | c2 << 16 | c3 << 8 | c4)
+
+ uint16_t U16_AT(const uint8_t *ptr);
+ uint32_t U32_AT(const uint8_t *ptr);
+@@ -50,15 +50,6 @@ void convertMessageToMetaData(
+
+ AString MakeUserAgent();
+
+-// Convert a MIME type to a AudioSystem::audio_format
+-status_t mapMimeToAudioFormat(audio_format_t& format, const char* mime);
+-
+-// Send information from MetaData to the HAL via AudioSink
+-status_t sendMetaDataToHal(sp<MediaPlayerBase::AudioSink>& sink, const sp<MetaData>& meta);
+-
+-// Check whether the stream defined by meta can be offloaded to hardware
+-bool canOffloadStream(const sp<MetaData>& meta, bool hasVideo, bool isStreaming);
+-
+-} // namespace android
++} // namespace stagefright
+
+ #endif // UTILS_H_
+diff --git a/include/media/stagefright/foundation/AAtomizer.h b/include/media/stagefright/foundation/AAtomizer.h
+index 5f3a678..c20ea07 100644
+--- a/include/media/stagefright/foundation/AAtomizer.h
++++ b/include/media/stagefright/foundation/AAtomizer.h
+@@ -26,7 +26,7 @@
+ #include <utils/Vector.h>
+ #include <utils/threads.h>
+
+-namespace android {
++namespace stagefright {
+
+ struct AAtomizer {
+ static const char *Atomize(const char *name);
+@@ -46,6 +46,6 @@ private:
+ DISALLOW_EVIL_CONSTRUCTORS(AAtomizer);
+ };
+
+-} // namespace android
++} // namespace stagefright
+
+ #endif // A_ATOMIZER_H_
+diff --git a/include/media/stagefright/foundation/ABitReader.h b/include/media/stagefright/foundation/ABitReader.h
+index 5510b12..589e5fe 100644
+--- a/include/media/stagefright/foundation/ABitReader.h
++++ b/include/media/stagefright/foundation/ABitReader.h
+@@ -23,7 +23,7 @@
+ #include <sys/types.h>
+ #include <stdint.h>
+
+-namespace android {
++namespace stagefright {
+
+ struct ABitReader {
+ ABitReader(const uint8_t *data, size_t size);
+@@ -49,6 +49,6 @@ private:
+ DISALLOW_EVIL_CONSTRUCTORS(ABitReader);
+ };
+
+-} // namespace android
++} // namespace stagefright
+
+ #endif // A_BIT_READER_H_
+diff --git a/include/media/stagefright/foundation/ABuffer.h b/include/media/stagefright/foundation/ABuffer.h
+index 28f0aed..4fa908e 100644
+--- a/include/media/stagefright/foundation/ABuffer.h
++++ b/include/media/stagefright/foundation/ABuffer.h
+@@ -24,7 +24,7 @@
+ #include <media/stagefright/foundation/ABase.h>
+ #include <utils/RefBase.h>
+
+-namespace android {
++namespace stagefright {
+
+ struct AMessage;
+
+@@ -66,6 +66,6 @@ private:
+ DISALLOW_EVIL_CONSTRUCTORS(ABuffer);
+ };
+
+-} // namespace android
++} // namespace stagefright
+
+ #endif // A_BUFFER_H_
+diff --git a/include/media/stagefright/foundation/ADebug.h b/include/media/stagefright/foundation/ADebug.h
+index 450dcfe..a58d951 100644
+--- a/include/media/stagefright/foundation/ADebug.h
++++ b/include/media/stagefright/foundation/ADebug.h
+@@ -24,7 +24,7 @@
+ #include <media/stagefright/foundation/AString.h>
+ #include <utils/Log.h>
+
+-namespace android {
++namespace stagefright {
+
+ #define LITERAL_TO_STRING_INTERNAL(x) #x
+ #define LITERAL_TO_STRING(x) LITERAL_TO_STRING_INTERNAL(x)
+@@ -80,7 +80,7 @@ MAKE_COMPARATOR(GT,>)
+ __FILE__ ":" LITERAL_TO_STRING(__LINE__) \
+ " Should not be here.");
+
+-} // namespace android
++} // namespace stagefright
+
+ #endif // A_DEBUG_H_
+
+diff --git a/include/media/stagefright/foundation/AHandler.h b/include/media/stagefright/foundation/AHandler.h
+index b008b54..5a5ecc2 100644
+--- a/include/media/stagefright/foundation/AHandler.h
++++ b/include/media/stagefright/foundation/AHandler.h
+@@ -21,7 +21,7 @@
+ #include <media/stagefright/foundation/ALooper.h>
+ #include <utils/RefBase.h>
+
+-namespace android {
++namespace stagefright {
+
+ struct AMessage;
+
+@@ -51,6 +51,6 @@ private:
+ DISALLOW_EVIL_CONSTRUCTORS(AHandler);
+ };
+
+-} // namespace android
++} // namespace stagefright
+
+ #endif // A_HANDLER_H_
+diff --git a/include/media/stagefright/foundation/AString.h b/include/media/stagefright/foundation/AString.h
+index 0f8f1e1..8f97ed4 100644
+--- a/include/media/stagefright/foundation/AString.h
++++ b/include/media/stagefright/foundation/AString.h
+@@ -20,7 +20,7 @@
+
+ #include <sys/types.h>
+
+-namespace android {
++namespace stagefright {
+
+ struct AString {
+ AString();
+@@ -89,7 +89,7 @@ private:
+
+ AString StringPrintf(const char *format, ...);
+
+-} // namespace android
++} // namespace stagefright
+
+ #endif // A_STRING_H_
+
+diff --git a/include/media/stagefright/foundation/hexdump.h b/include/media/stagefright/foundation/hexdump.h
+index 8360c5a..18172f7 100644
+--- a/include/media/stagefright/foundation/hexdump.h
++++ b/include/media/stagefright/foundation/hexdump.h
+@@ -20,7 +20,7 @@
+
+ #include <sys/types.h>
+
+-namespace android {
++namespace stagefright {
+
+ struct AString;
+
+@@ -28,6 +28,6 @@ void hexdump(
+ const void *_data, size_t size,
+ size_t indent = 0, AString *appendTo = NULL);
+
+-} // namespace android
++} // namespace stagefright
+
+ #endif // HEXDUMP_H_
+diff --git a/media/libstagefright/DataSource.cpp b/media/libstagefright/DataSource.cpp
+index fc6fd9c..c65a69e 100644
+--- a/media/libstagefright/DataSource.cpp
++++ b/media/libstagefright/DataSource.cpp
+@@ -20,30 +20,12 @@
+ #include "include/chromium_http_stub.h"
+ #endif
+
+-#include "include/AACExtractor.h"
+-#include "include/DRMExtractor.h"
+-#include "include/FLACExtractor.h"
+-#include "include/HTTPBase.h"
+-#include "include/MP3Extractor.h"
+-#include "include/MPEG2PSExtractor.h"
+-#include "include/MPEG2TSExtractor.h"
+ #include "include/MPEG4Extractor.h"
+-#include "include/NuCachedSource2.h"
+-#include "include/OggExtractor.h"
+-#include "include/WAVExtractor.h"
+-#include "include/WVMExtractor.h"
+
+-#include "matroska/MatroskaExtractor.h"
+-
+-#include <media/stagefright/foundation/AMessage.h>
+ #include <media/stagefright/DataSource.h>
+-#include <media/stagefright/FileSource.h>
+ #include <media/stagefright/MediaErrors.h>
+-#include <utils/String8.h>
+-
+-#include <cutils/properties.h>
+
+-namespace android {
++namespace stagefright {
+
+ bool DataSource::getUInt16(off64_t offset, uint16_t *x) {
+ *x = 0;
+@@ -105,6 +87,8 @@ status_t DataSource::getSize(off64_t *size) {
+
+ ////////////////////////////////////////////////////////////////////////////////
+
++#if 0
++
+ Mutex DataSource::gSnifferMutex;
+ List<DataSource::SnifferFunc> DataSource::gSniffers;
+
+@@ -226,8 +210,10 @@ sp<DataSource> DataSource::CreateFromURI(
+ return source;
+ }
+
++#endif
++
+ String8 DataSource::getMIMEType() const {
+ return String8("application/octet-stream");
+ }
+
+-} // namespace android
++} // namespace stagefright
+diff --git a/media/libstagefright/ESDS.cpp b/media/libstagefright/ESDS.cpp
+index 4a0c35c..ccf60e3 100644
+--- a/media/libstagefright/ESDS.cpp
++++ b/media/libstagefright/ESDS.cpp
+@@ -15,6 +15,7 @@
+ */
+
+ //#define LOG_NDEBUG 0
++#undef LOG_TAG
+ #define LOG_TAG "ESDS"
+ #include <utils/Log.h>
+
+@@ -22,7 +23,7 @@
+
+ #include <string.h>
+
+-namespace android {
++namespace stagefright {
+
+ ESDS::ESDS(const void *data, size_t size)
+ : mData(new uint8_t[size]),
+@@ -223,5 +224,6 @@ status_t ESDS::parseDecoderConfigDescriptor(size_t offset, size_t size) {
+ return OK;
+ }
+
+-} // namespace android
++} // namespace stagefright
+
++#undef LOG_TAG
+diff --git a/media/libstagefright/MPEG4Extractor.cpp b/media/libstagefright/MPEG4Extractor.cpp
+index ad985ee..71a0613 100644
+--- a/media/libstagefright/MPEG4Extractor.cpp
++++ b/media/libstagefright/MPEG4Extractor.cpp
+@@ -15,6 +15,7 @@
+ */
+
+ //#define LOG_NDEBUG 0
++#undef LOG_TAG
+ #define LOG_TAG "MPEG4Extractor"
+ #include <utils/Log.h>
+
+@@ -38,7 +39,7 @@
+ #include <media/stagefright/MetaData.h>
+ #include <utils/String8.h>
+
+-namespace android {
++namespace stagefright {
+
+ class MPEG4Source : public MediaSource {
+ public:
+@@ -726,6 +727,11 @@ static bool underMetaDataPath(const Vector<uint32_t> &path) {
+ static void convertTimeToDate(int64_t time_1904, String8 *s) {
+ time_t time_1970 = time_1904 - (((66 * 365 + 17) * 24) * 3600);
+
++ if (time_1970 < 0) {
++ s->clear();
++ return;
++ }
++
+ char tmp[32];
+ strftime(tmp, sizeof(tmp), "%Y%m%dT%H%M%S.000Z", gmtime(&time_1970));
+
+@@ -1248,6 +1254,7 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) {
+ ALOGV("*** coding='%s' %d channels, size %d, rate %d\n",
+ chunk, num_channels, sample_size, sample_rate);
+ mLastTrack->meta->setInt32(kKeyChannelCount, num_channels);
++ mLastTrack->meta->setInt32(kKeySampleSize, sample_size);
+ mLastTrack->meta->setInt32(kKeySampleRate, sample_rate);
+
+ off64_t stop_offset = *offset + chunk_size;
+@@ -1652,8 +1659,9 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) {
+
+ String8 s;
+ convertTimeToDate(creationTime, &s);
+-
+- mFileMetaData->setCString(kKeyDate, s.string());
++ if (s.length()) {
++ mFileMetaData->setCString(kKeyDate, s.string());
++ }
+
+ *offset += chunk_size;
+ break;
+@@ -2278,6 +2286,10 @@ status_t MPEG4Extractor::updateAudioTrackInfoFromESDS_MPEG4Audio(
+ objectType = 32 + br.getBits(6);
+ }
+
++ if (objectType >= 1 && objectType <= 4) {
++ mLastTrack->meta->setInt32(kKeyAACProfile, objectType);
++ }
++
+ uint32_t freqIndex = br.getBits(4);
+
+ int32_t sampleRate = 0;
+@@ -3154,6 +3166,7 @@ status_t MPEG4Source::read(
+ CHECK(mBuffer != NULL);
+ mBuffer->set_range(0, size);
+ mBuffer->meta_data()->clear();
++ mBuffer->meta_data()->setInt64(kKey64BitFileOffset, offset);
+ mBuffer->meta_data()->setInt64(
+ kKeyTime, ((int64_t)cts * 1000000) / mTimescale);
+
+@@ -3276,6 +3289,7 @@ status_t MPEG4Source::read(
+ }
+
+ mBuffer->meta_data()->clear();
++ mBuffer->meta_data()->setInt64(kKey64BitFileOffset, offset);
+ mBuffer->meta_data()->setInt64(
+ kKeyTime, ((int64_t)cts * 1000000) / mTimescale);
+
+@@ -3360,6 +3374,18 @@ status_t MPEG4Source::fragmentedRead(
+ // move to next fragment
+ Sample lastSample = mCurrentSamples[mCurrentSamples.size() - 1];
+ off64_t nextMoof = mNextMoofOffset; // lastSample.offset + lastSample.size;
++
++ // If we're pointing to a sidx box then we skip it.
++ uint32_t hdr[2];
++ if (mDataSource->readAt(nextMoof, hdr, 8) < 8) {
++ return ERROR_END_OF_STREAM;
++ }
++ uint64_t chunk_size = ntohl(hdr[0]);
++ uint32_t chunk_type = ntohl(hdr[1]);
++ if (chunk_type == FOURCC('s', 'i', 'd', 'x')) {
++ nextMoof += chunk_size;
++ }
++
+ mCurrentMoofOffset = nextMoof;
+ mCurrentSamples.clear();
+ mCurrentSampleIndex = 0;
+@@ -3626,6 +3652,7 @@ static bool isCompatibleBrand(uint32_t fourcc) {
+ return false;
+ }
+
++#if 0
+ // Attempt to actually parse the 'ftyp' atom and determine if a suitable
+ // compatible brand is present.
+ // Also try to identify where this file's metadata ends
+@@ -3756,5 +3783,8 @@ bool SniffMPEG4(
+
+ return false;
+ }
++#endif
++
++} // namespace stagefright
+
+-} // namespace android
++#undef LOG_TAG
+diff --git a/media/libstagefright/MediaBuffer.cpp b/media/libstagefright/MediaBuffer.cpp
+index 11b80bf..72a7174 100644
+--- a/media/libstagefright/MediaBuffer.cpp
++++ b/media/libstagefright/MediaBuffer.cpp
+@@ -29,7 +29,7 @@
+ #include <ui/GraphicBuffer.h>
+ #include <sys/atomics.h>
+
+-namespace android {
++namespace stagefright {
+
+ MediaBuffer::MediaBuffer(void *data, size_t size)
+ : mObserver(NULL),
+@@ -200,4 +200,4 @@ MediaBuffer *MediaBuffer::clone() {
+ return buffer;
+ }
+
+-} // namespace android
++} // namespace stagefright
+diff --git a/media/libstagefright/MediaBufferGroup.cpp b/media/libstagefright/MediaBufferGroup.cpp
+index 80aae51..17dc08a 100644
+--- a/media/libstagefright/MediaBufferGroup.cpp
++++ b/media/libstagefright/MediaBufferGroup.cpp
+@@ -14,6 +14,7 @@
+ * limitations under the License.
+ */
+
++#undef LOG_TAG
+ #define LOG_TAG "MediaBufferGroup"
+ #include <utils/Log.h>
+
+@@ -21,7 +22,7 @@
+ #include <media/stagefright/MediaBuffer.h>
+ #include <media/stagefright/MediaBufferGroup.h>
+
+-namespace android {
++namespace stagefright {
+
+ MediaBufferGroup::MediaBufferGroup()
+ : mFirstBuffer(NULL),
+@@ -83,4 +84,6 @@ void MediaBufferGroup::signalBufferReturned(MediaBuffer *) {
+ mCondition.signal();
+ }
+
+-} // namespace android
++} // namespace stagefright
++
++#undef LOG_TAG
+diff --git a/media/libstagefright/MediaDefs.cpp b/media/libstagefright/MediaDefs.cpp
+index b5d4e44..1f17bc0 100644
+--- a/media/libstagefright/MediaDefs.cpp
++++ b/media/libstagefright/MediaDefs.cpp
+@@ -16,7 +16,7 @@
+
+ #include <media/stagefright/MediaDefs.h>
+
+-namespace android {
++namespace stagefright {
+
+ const char *MEDIA_MIMETYPE_IMAGE_JPEG = "image/jpeg";
+
+@@ -56,4 +56,4 @@ const char *MEDIA_MIMETYPE_CONTAINER_WVM = "video/wvm";
+ const char *MEDIA_MIMETYPE_TEXT_3GPP = "text/3gpp-tt";
+ const char *MEDIA_MIMETYPE_TEXT_SUBRIP = "application/x-subrip";
+
+-} // namespace android
++} // namespace stagefright
+diff --git a/media/libstagefright/MediaSource.cpp b/media/libstagefright/MediaSource.cpp
+index fd0e79c..faafe59 100644
+--- a/media/libstagefright/MediaSource.cpp
++++ b/media/libstagefright/MediaSource.cpp
+@@ -16,7 +16,7 @@
+
+ #include <media/stagefright/MediaSource.h>
+
+-namespace android {
++namespace stagefright {
+
+ MediaSource::MediaSource() {}
+
+@@ -61,4 +61,4 @@ int64_t MediaSource::ReadOptions::getLateBy() const {
+ return mLatenessUs;
+ }
+
+-} // namespace android
++} // namespace stagefright
+diff --git a/media/libstagefright/MetaData.cpp b/media/libstagefright/MetaData.cpp
+index ae6ae2d..c832c96 100644
+--- a/media/libstagefright/MetaData.cpp
++++ b/media/libstagefright/MetaData.cpp
+@@ -15,6 +15,7 @@
+ */
+
+ //#define LOG_NDEBUG 0
++#undef LOG_TAG
+ #define LOG_TAG "MetaData"
+ #include <utils/Log.h>
+
+@@ -26,7 +27,7 @@
+ #include <media/stagefright/foundation/hexdump.h>
+ #include <media/stagefright/MetaData.h>
+
+-namespace android {
++namespace stagefright {
+
+ MetaData::MetaData() {
+ }
+@@ -350,5 +351,6 @@ void MetaData::dumpToLog() const {
+ }
+ }
+
+-} // namespace android
++} // namespace stagefright
+
++#undef LOG_TAG
+diff --git a/media/libstagefright/SampleIterator.cpp b/media/libstagefright/SampleIterator.cpp
+index eae721b..767393a 100644
+--- a/media/libstagefright/SampleIterator.cpp
++++ b/media/libstagefright/SampleIterator.cpp
+@@ -14,6 +14,7 @@
+ * limitations under the License.
+ */
+
++#undef LOG_TAG
+ #define LOG_TAG "SampleIterator"
+ //#define LOG_NDEBUG 0
+ #include <utils/Log.h>
+@@ -28,7 +29,7 @@
+
+ #include "include/SampleTable.h"
+
+-namespace android {
++namespace stagefright {
+
+ SampleIterator::SampleIterator(SampleTable *table)
+ : mTable(table),
+@@ -312,5 +313,6 @@ status_t SampleIterator::findSampleTime(
+ return OK;
+ }
+
+-} // namespace android
++} // namespace stagefright
+
++#undef LOG_TAG
+diff --git a/media/libstagefright/SampleTable.cpp b/media/libstagefright/SampleTable.cpp
+index d9858d7..d2099df 100644
+--- a/media/libstagefright/SampleTable.cpp
++++ b/media/libstagefright/SampleTable.cpp
+@@ -14,6 +14,7 @@
+ * limitations under the License.
+ */
+
++#undef LOG_TAG
+ #define LOG_TAG "SampleTable"
+ //#define LOG_NDEBUG 0
+ #include <utils/Log.h>
+@@ -27,7 +28,7 @@
+ #include <media/stagefright/DataSource.h>
+ #include <media/stagefright/Utils.h>
+
+-namespace android {
++namespace stagefright {
+
+ // static
+ const uint32_t SampleTable::kChunkOffsetType32 = FOURCC('s', 't', 'c', 'o');
+@@ -827,5 +828,6 @@ uint32_t SampleTable::getCompositionTimeOffset(uint32_t sampleIndex) {
+ return mCompositionDeltaLookup->getCompositionTimeOffset(sampleIndex);
+ }
+
+-} // namespace android
++} // namespace stagefright
+
++#undef LOG_TAG
+diff --git a/media/libstagefright/Utils.cpp b/media/libstagefright/Utils.cpp
+index 4db8e80..7b7c17a 100644
+--- a/media/libstagefright/Utils.cpp
++++ b/media/libstagefright/Utils.cpp
+@@ -15,6 +15,7 @@
+ */
+
+ //#define LOG_NDEBUG 0
++#undef LOG_TAG
+ #define LOG_TAG "Utils"
+ #include <utils/Log.h>
+
+@@ -33,7 +34,7 @@
+ #include <media/stagefright/Utils.h>
+ #include <media/AudioParameter.h>
+
+-namespace android {
++namespace stagefright {
+
+ uint16_t U16_AT(const uint8_t *ptr) {
+ return ptr[0] << 8 | ptr[1];
+@@ -68,6 +69,7 @@ uint64_t hton64(uint64_t x) {
+ return ((uint64_t)htonl(x & 0xffffffff) << 32) | htonl(x >> 32);
+ }
+
++#if 0
+ status_t convertMetaDataToMessage(
+ const sp<MetaData> &meta, sp<AMessage> *format) {
+ format->clear();
+@@ -603,5 +605,8 @@ bool canOffloadStream(const sp<MetaData>& meta, bool hasVideo, bool isStreaming)
+ return AudioSystem::isOffloadSupported(info);
+ }
+
+-} // namespace android
++#endif
++
++} // namespace stagefright
+
++#undef LOG_TAG
+diff --git a/media/libstagefright/foundation/AAtomizer.cpp b/media/libstagefright/foundation/AAtomizer.cpp
+index b7b9e9f..4210cda 100644
+--- a/media/libstagefright/foundation/AAtomizer.cpp
++++ b/media/libstagefright/foundation/AAtomizer.cpp
+@@ -18,7 +18,7 @@
+
+ #include "AAtomizer.h"
+
+-namespace android {
++namespace stagefright {
+
+ // static
+ AAtomizer AAtomizer::gAtomizer;
+@@ -64,4 +64,4 @@ uint32_t AAtomizer::Hash(const char *s) {
+ return sum;
+ }
+
+-} // namespace android
++} // namespace stagefright
+diff --git a/media/libstagefright/foundation/ABitReader.cpp b/media/libstagefright/foundation/ABitReader.cpp
+index 5499c32..22850ef 100644
+--- a/media/libstagefright/foundation/ABitReader.cpp
++++ b/media/libstagefright/foundation/ABitReader.cpp
+@@ -16,9 +16,10 @@
+
+ #include "ABitReader.h"
+
++#include <log/log.h>
+ #include <media/stagefright/foundation/ADebug.h>
+
+-namespace android {
++namespace stagefright {
+
+ ABitReader::ABitReader(const uint8_t *data, size_t size)
+ : mData(data),
+@@ -99,4 +100,4 @@ const uint8_t *ABitReader::data() const {
+ return mData - (mNumBitsLeft + 7) / 8;
+ }
+
+-} // namespace android
++} // namespace stagefright
+diff --git a/media/libstagefright/foundation/ABuffer.cpp b/media/libstagefright/foundation/ABuffer.cpp
+index 6173db4..53f5ebc 100644
+--- a/media/libstagefright/foundation/ABuffer.cpp
++++ b/media/libstagefright/foundation/ABuffer.cpp
+@@ -20,7 +20,7 @@
+ #include "ALooper.h"
+ #include "AMessage.h"
+
+-namespace android {
++namespace stagefright {
+
+ ABuffer::ABuffer(size_t capacity)
+ : mData(malloc(capacity)),
+@@ -72,5 +72,5 @@ sp<AMessage> ABuffer::meta() {
+ return mMeta;
+ }
+
+-} // namespace android
++} // namespace stagefright
+
+diff --git a/media/libstagefright/foundation/AString.cpp b/media/libstagefright/foundation/AString.cpp
+index dee786d..d06110a 100644
+--- a/media/libstagefright/foundation/AString.cpp
++++ b/media/libstagefright/foundation/AString.cpp
+@@ -23,7 +23,7 @@
+ #include "ADebug.h"
+ #include "AString.h"
+
+-namespace android {
++namespace stagefright {
+
+ // static
+ const char *AString::kEmptyString = "";
+@@ -325,7 +325,13 @@ AString StringPrintf(const char *format, ...) {
+ va_start(ap, format);
+
+ char *buffer;
++#ifdef _MSC_VER
++ int n = vsnprintf(NULL, 0, format, ap);
++ buffer = new char[n+1];
++ vsnprintf(buffer, n+1, format, ap);
++#else
+ vasprintf(&buffer, format, ap);
++#endif
+
+ va_end(ap);
+
+@@ -337,5 +343,5 @@ AString StringPrintf(const char *format, ...) {
+ return result;
+ }
+
+-} // namespace android
++} // namespace stagefright
+
+diff --git a/media/libstagefright/foundation/hexdump.cpp b/media/libstagefright/foundation/hexdump.cpp
+index a44d832..7b566eb 100644
+--- a/media/libstagefright/foundation/hexdump.cpp
++++ b/media/libstagefright/foundation/hexdump.cpp
+@@ -15,6 +15,7 @@
+ */
+
+ //#define LOG_NDEBUG 0
++#undef LOG_TAG
+ #define LOG_TAG "hexdump"
+ #include <utils/Log.h>
+
+@@ -27,7 +28,7 @@
+ #include <stdint.h>
+ #include <stdio.h>
+
+-namespace android {
++namespace stagefright {
+
+ static void appendIndent(AString *s, int32_t indent) {
+ static const char kWhitespace[] =
+@@ -90,5 +91,6 @@ void hexdump(const void *_data, size_t size, size_t indent, AString *appendTo) {
+ }
+ }
+
+-} // namespace android
++} // namespace stagefright
+
++#undef LOG_TAG
+diff --git a/media/libstagefright/id3/ID3.cpp b/media/libstagefright/id3/ID3.cpp
+index 34d671a..3aa395c 100644
+--- a/media/libstagefright/id3/ID3.cpp
++++ b/media/libstagefright/id3/ID3.cpp
+@@ -15,6 +15,7 @@
+ */
+
+ //#define LOG_NDEBUG 0
++#undef LOG_TAG
+ #define LOG_TAG "ID3"
+ #include <utils/Log.h>
+
+@@ -26,7 +27,7 @@
+ #include <utils/String8.h>
+ #include <byteswap.h>
+
+-namespace android {
++namespace stagefright {
+
+ static const size_t kMaxMetadataSize = 3 * 1024 * 1024;
+
+@@ -897,4 +898,6 @@ bool ID3::parseV1(const sp<DataSource> &source) {
+ return true;
+ }
+
+-} // namespace android
++} // namespace stagefright
++
++#undef LOG_TAG
+diff --git a/media/libstagefright/include/AMRExtractor.h b/media/libstagefright/include/AMRExtractor.h
+index 4a1c827..cdfc98e 100644
+--- a/media/libstagefright/include/AMRExtractor.h
++++ b/media/libstagefright/include/AMRExtractor.h
+@@ -21,7 +21,7 @@
+ #include <utils/Errors.h>
+ #include <media/stagefright/MediaExtractor.h>
+
+-namespace android {
++namespace stagefright {
+
+ struct AMessage;
+ class String8;
+@@ -57,6 +57,6 @@ bool SniffAMR(
+ const sp<DataSource> &source, String8 *mimeType, float *confidence,
+ sp<AMessage> *);
+
+-} // namespace android
++} // namespace stagefright
+
+ #endif // AMR_EXTRACTOR_H_
+diff --git a/media/libstagefright/include/ESDS.h b/media/libstagefright/include/ESDS.h
+index 2f40dae..bc4c5a6 100644
+--- a/media/libstagefright/include/ESDS.h
++++ b/media/libstagefright/include/ESDS.h
+@@ -22,7 +22,7 @@
+
+ #include <media/stagefright/MediaErrors.h>
+
+-namespace android {
++namespace stagefright {
+
+ class ESDS {
+ public:
+@@ -68,5 +68,5 @@ private:
+ ESDS &operator=(const ESDS &);
+ };
+
+-} // namespace android
++} // namespace stagefright
+ #endif // ESDS_H_
+diff --git a/media/libstagefright/include/ID3.h b/media/libstagefright/include/ID3.h
+index cca83ab..cbb3bbf 100644
+--- a/media/libstagefright/include/ID3.h
++++ b/media/libstagefright/include/ID3.h
+@@ -20,7 +20,7 @@
+
+ #include <utils/RefBase.h>
+
+-namespace android {
++namespace stagefright {
+
+ struct DataSource;
+ struct String8;
+@@ -96,7 +96,7 @@ private:
+ ID3 &operator=(const ID3 &);
+ };
+
+-} // namespace android
++} // namespace stagefright
+
+ #endif // ID3_H_
+
+diff --git a/media/libstagefright/include/MPEG4Extractor.h b/media/libstagefright/include/MPEG4Extractor.h
+index bbec1c4..98818b8 100644
+--- a/media/libstagefright/include/MPEG4Extractor.h
++++ b/media/libstagefright/include/MPEG4Extractor.h
+@@ -27,7 +27,7 @@
+ #include <utils/Vector.h>
+ #include <utils/String8.h>
+
+-namespace android {
++namespace stagefright {
+
+ struct AMessage;
+ class DataSource;
+@@ -129,6 +129,6 @@ bool SniffMPEG4(
+ const sp<DataSource> &source, String8 *mimeType, float *confidence,
+ sp<AMessage> *);
+
+-} // namespace android
++} // namespace stagefright
+
+ #endif // MPEG4_EXTRACTOR_H_
+diff --git a/media/libstagefright/include/SampleIterator.h b/media/libstagefright/include/SampleIterator.h
+index b5a043c..a4d52fe 100644
+--- a/media/libstagefright/include/SampleIterator.h
++++ b/media/libstagefright/include/SampleIterator.h
+@@ -14,9 +14,12 @@
+ * limitations under the License.
+ */
+
++#ifndef SAMPLE_ITERATOR_H_
++#define SAMPLE_ITERATOR_H_
++
+ #include <utils/Vector.h>
+
+-namespace android {
++namespace stagefright {
+
+ struct SampleTable;
+
+@@ -71,5 +74,6 @@ private:
+ SampleIterator &operator=(const SampleIterator &);
+ };
+
+-} // namespace android
++} // namespace stagefright
+
++#endif
+diff --git a/media/libstagefright/include/SampleTable.h b/media/libstagefright/include/SampleTable.h
+index 847dff7..2a82077 100644
+--- a/media/libstagefright/include/SampleTable.h
++++ b/media/libstagefright/include/SampleTable.h
+@@ -25,7 +25,7 @@
+ #include <utils/RefBase.h>
+ #include <utils/threads.h>
+
+-namespace android {
++namespace stagefright {
+
+ class DataSource;
+ struct SampleIterator;
+@@ -148,6 +148,6 @@ private:
+ SampleTable &operator=(const SampleTable &);
+ };
+
+-} // namespace android
++} // namespace stagefright
+
+ #endif // SAMPLE_TABLE_H_
diff --git a/media/libstagefright/patches/system/core.patch b/media/libstagefright/patches/system/core.patch
new file mode 100644
index 000000000..374afc5f3
--- /dev/null
+++ b/media/libstagefright/patches/system/core.patch
@@ -0,0 +1,1387 @@
+diff --git a/include/cutils/properties.h b/include/cutils/properties.h
+index 2c70165..c380d5d 100644
+--- a/include/cutils/properties.h
++++ b/include/cutils/properties.h
+@@ -19,7 +19,6 @@
+
+ #include <sys/cdefs.h>
+ #include <stddef.h>
+-#include <sys/system_properties.h>
+
+ #ifdef __cplusplus
+ extern "C" {
+diff --git a/include/log/log.h b/include/log/log.h
+index 7faddea..6131f01 100644
+--- a/include/log/log.h
++++ b/include/log/log.h
+@@ -25,6 +25,16 @@
+ // supports O_APPEND. These calls have mutex-protected data structures
+ // and so are NOT reentrant. Do not use LOG in a signal handler.
+ //
++
++/*
++ * This is the local tag used for the following simplified
++ * logging macros. You can change this preprocessor definition
++ * before using the other macros to change the tag.
++ */
++#ifndef LOG_TAG
++#define LOG_TAG NULL
++#endif
++
+ #ifndef _LIBS_LOG_LOG_H
+ #define _LIBS_LOG_LOG_H
+
+@@ -40,6 +50,10 @@
+ #include <log/uio.h>
+ #include <log/logd.h>
+
++#ifdef _MSC_VER
++#define __builtin_expect(X, Y) (X)
++#endif
++
+ #ifdef __cplusplus
+ extern "C" {
+ #endif
+@@ -59,15 +73,6 @@ extern "C" {
+ #endif
+ #endif
+
+-/*
+- * This is the local tag used for the following simplified
+- * logging macros. You can change this preprocessor definition
+- * before using the other macros to change the tag.
+- */
+-#ifndef LOG_TAG
+-#define LOG_TAG NULL
+-#endif
+-
+ // ---------------------------------------------------------------------
+
+ /*
+@@ -498,11 +503,11 @@ typedef enum {
+ * The stuff in the rest of this file should not be used directly.
+ */
+
+-#define android_printLog(prio, tag, fmt...) \
+- __android_log_print(prio, tag, fmt)
++#define android_printLog(prio, tag, ...) \
++ __android_log_print(prio, tag, __VA_ARGS__)
+
+-#define android_vprintLog(prio, cond, tag, fmt...) \
+- __android_log_vprint(prio, tag, fmt)
++#define android_vprintLog(prio, cond, tag, ...) \
++ __android_log_vprint(prio, tag, __VA_ARGS__)
+
+ /* XXX Macros to work around syntax errors in places where format string
+ * arg is not passed to ALOG_ASSERT, LOG_ALWAYS_FATAL or LOG_ALWAYS_FATAL_IF
+@@ -519,9 +524,9 @@ typedef enum {
+ */
+ #define __android_rest(first, ...) , ## __VA_ARGS__
+
+-#define android_printAssert(cond, tag, fmt...) \
++#define android_printAssert(cond, tag, ...) \
+ __android_log_assert(cond, tag, \
+- __android_second(0, ## fmt, NULL) __android_rest(fmt))
++ __android_second(0, ## __VA_ARGS__, NULL) __android_rest(__VA_ARGS__))
+
+ #define android_writeLog(prio, tag, text) \
+ __android_log_write(prio, tag, text)
+diff --git a/include/log/logprint.h b/include/log/logprint.h
+index 481c96e..9b57e0e 100644
+--- a/include/log/logprint.h
++++ b/include/log/logprint.h
+@@ -20,7 +20,6 @@
+ #include <log/log.h>
+ #include <log/logger.h>
+ #include <log/event_tag_map.h>
+-#include <pthread.h>
+
+ #ifdef __cplusplus
+ extern "C" {
+diff --git a/include/sysutils/List.h b/include/sysutils/List.h
+index 31f7b37..72a9c44 100644
+--- a/include/sysutils/List.h
++++ b/include/sysutils/List.h
+@@ -30,7 +30,7 @@
+ #include <stddef.h>
+ #include <stdint.h>
+
+-namespace android {
++namespace stagefright {
+ namespace sysutils {
+
+ /*
+@@ -329,6 +329,6 @@ List<T>& List<T>::operator=(const List<T>& right)
+ }
+
+ }; // namespace sysutils
+-}; // namespace android
++}; // namespace stagefright
+
+ #endif // _SYSUTILS_LIST_H
+diff --git a/include/utils/CallStack.h b/include/utils/CallStack.h
+index 61dc832..eb52c8b 100644
+--- a/include/utils/CallStack.h
++++ b/include/utils/CallStack.h
+@@ -25,7 +25,7 @@
+
+ // ---------------------------------------------------------------------------
+
+-namespace android {
++namespace stagefright {
+
+ class CallStack
+ {
+@@ -68,7 +68,7 @@ private:
+ backtrace_frame_t mStack[MAX_DEPTH];
+ };
+
+-}; // namespace android
++}; // namespace stagefright
+
+
+ // ---------------------------------------------------------------------------
+diff --git a/include/utils/Condition.h b/include/utils/Condition.h
+index e63ba7e..e8e7ae9 100644
+--- a/include/utils/Condition.h
++++ b/include/utils/Condition.h
+@@ -30,7 +30,7 @@
+ #include <utils/Timers.h>
+
+ // ---------------------------------------------------------------------------
+-namespace android {
++namespace stagefright {
+ // ---------------------------------------------------------------------------
+
+ /*
+@@ -138,10 +138,22 @@ inline void Condition::broadcast() {
+ pthread_cond_broadcast(&mCond);
+ }
+
++#else
++
++inline Condition::Condition() {}
++inline Condition::Condition(int type) {}
++inline Condition::~Condition() {}
++inline status_t Condition::wait(Mutex& mutex) { return OK; }
++inline status_t Condition::waitRelative(Mutex& mutex, nsecs_t reltime) {
++ return OK;
++}
++inline void Condition::signal() {}
++inline void Condition::broadcast() {}
++
+ #endif // HAVE_PTHREADS
+
+ // ---------------------------------------------------------------------------
+-}; // namespace android
++}; // namespace stagefright
+ // ---------------------------------------------------------------------------
+
+ #endif // _LIBS_UTILS_CONDITON_H
+diff --git a/include/utils/Debug.h b/include/utils/Debug.h
+index 08893bd..41f09eb 100644
+--- a/include/utils/Debug.h
++++ b/include/utils/Debug.h
+@@ -20,7 +20,7 @@
+ #include <stdint.h>
+ #include <sys/types.h>
+
+-namespace android {
++namespace stagefright {
+ // ---------------------------------------------------------------------------
+
+ #ifdef __cplusplus
+@@ -43,6 +43,6 @@ struct CompileTimeIfElse<false, LHS, RHS> { typedef RHS TYPE; };
+ #endif
+
+ // ---------------------------------------------------------------------------
+-}; // namespace android
++}; // namespace stagefright
+
+ #endif // ANDROID_UTILS_DEBUG_H
+diff --git a/include/utils/Errors.h b/include/utils/Errors.h
+index 0b75b19..0ceda59 100644
+--- a/include/utils/Errors.h
++++ b/include/utils/Errors.h
+@@ -20,7 +20,7 @@
+ #include <sys/types.h>
+ #include <errno.h>
+
+-namespace android {
++namespace stagefright {
+
+ // use this type to return error codes
+ #ifdef HAVE_MS_C_RUNTIME
+@@ -81,7 +81,7 @@ enum {
+ # define NO_ERROR 0L
+ #endif
+
+-}; // namespace android
++}; // namespace stagefright
+
+ // ---------------------------------------------------------------------------
+
+diff --git a/include/utils/KeyedVector.h b/include/utils/KeyedVector.h
+index c4faae0..ca4bfd6 100644
+--- a/include/utils/KeyedVector.h
++++ b/include/utils/KeyedVector.h
+@@ -29,7 +29,7 @@
+
+ // ---------------------------------------------------------------------------
+
+-namespace android {
++namespace stagefright {
+
+ template <typename KEY, typename VALUE>
+ class KeyedVector
+@@ -217,7 +217,7 @@ const VALUE& DefaultKeyedVector<KEY,VALUE>::valueFor(const KEY& key) const {
+ return i >= 0 ? KeyedVector<KEY,VALUE>::valueAt(i) : mDefault;
+ }
+
+-}; // namespace android
++}; // namespace stagefright
+
+ // ---------------------------------------------------------------------------
+
+diff --git a/include/utils/List.h b/include/utils/List.h
+index 403cd7f..f5c110e 100644
+--- a/include/utils/List.h
++++ b/include/utils/List.h
+@@ -30,7 +30,7 @@
+ #include <stddef.h>
+ #include <stdint.h>
+
+-namespace android {
++namespace stagefright {
+
+ /*
+ * Doubly-linked list. Instantiate with "List<MyClass> myList".
+@@ -56,9 +56,11 @@ protected:
+ inline void setVal(const T& val) { mVal = val; }
+ inline void setPrev(_Node* ptr) { mpPrev = ptr; }
+ inline void setNext(_Node* ptr) { mpNext = ptr; }
++#ifndef _MSC_VER
+ private:
+ friend class List;
+ friend class _ListIterator;
++#endif
+ T mVal;
+ _Node* mpPrev;
+ _Node* mpNext;
+@@ -327,6 +329,6 @@ List<T>& List<T>::operator=(const List<T>& right)
+ return *this;
+ }
+
+-}; // namespace android
++}; // namespace stagefright
+
+ #endif // _LIBS_UTILS_LIST_H
+diff --git a/include/utils/Log.h b/include/utils/Log.h
+index 4259c86..97cc4f3 100644
+--- a/include/utils/Log.h
++++ b/include/utils/Log.h
+@@ -33,7 +33,7 @@
+
+ #ifdef __cplusplus
+
+-namespace android {
++namespace stagefright {
+
+ /*
+ * A very simple utility that yells in the log when an operation takes too long.
+@@ -62,9 +62,9 @@ private:
+ * }
+ */
+ #define ALOGD_IF_SLOW(timeoutMillis, message) \
+- android::LogIfSlow _logIfSlow(LOG_TAG, ANDROID_LOG_DEBUG, timeoutMillis, message);
++ stagefright::LogIfSlow _logIfSlow(LOG_TAG, ANDROID_LOG_DEBUG, timeoutMillis, message);
+
+-} // namespace android
++} // namespace stagefright
+
+ #endif // __cplusplus
+
+diff --git a/include/utils/Mutex.h b/include/utils/Mutex.h
+index dd201c8..b33efef 100644
+--- a/include/utils/Mutex.h
++++ b/include/utils/Mutex.h
+@@ -28,7 +28,7 @@
+ #include <utils/Errors.h>
+
+ // ---------------------------------------------------------------------------
+-namespace android {
++namespace stagefright {
+ // ---------------------------------------------------------------------------
+
+ class Condition;
+@@ -118,6 +118,17 @@ inline status_t Mutex::tryLock() {
+ return -pthread_mutex_trylock(&mMutex);
+ }
+
++#else
++
++inline Mutex::Mutex() {}
++inline Mutex::Mutex(const char* name) {}
++inline Mutex::Mutex(int type, const char* name) {}
++inline Mutex::~Mutex() {}
++inline status_t Mutex::lock() { return OK; }
++inline void Mutex::unlock() {}
++inline status_t Mutex::tryLock() { return OK; }
++inline void Mutex::_init() {}
++
+ #endif // HAVE_PTHREADS
+
+ // ---------------------------------------------------------------------------
+@@ -131,7 +142,7 @@ inline status_t Mutex::tryLock() {
+ typedef Mutex::Autolock AutoMutex;
+
+ // ---------------------------------------------------------------------------
+-}; // namespace android
++}; // namespace stagefright
+ // ---------------------------------------------------------------------------
+
+ #endif // _LIBS_UTILS_MUTEX_H
+diff --git a/include/utils/RWLock.h b/include/utils/RWLock.h
+index 90beb5f..4c43827 100644
+--- a/include/utils/RWLock.h
++++ b/include/utils/RWLock.h
+@@ -28,7 +28,7 @@
+ #include <utils/ThreadDefs.h>
+
+ // ---------------------------------------------------------------------------
+-namespace android {
++namespace stagefright {
+ // ---------------------------------------------------------------------------
+
+ #if defined(HAVE_PTHREADS)
+@@ -120,7 +120,7 @@ inline void RWLock::unlock() {
+ #endif // HAVE_PTHREADS
+
+ // ---------------------------------------------------------------------------
+-}; // namespace android
++}; // namespace stagefright
+ // ---------------------------------------------------------------------------
+
+ #endif // _LIBS_UTILS_RWLOCK_H
+diff --git a/include/utils/RefBase.h b/include/utils/RefBase.h
+index cbfe13a..e1f97c9 100644
+--- a/include/utils/RefBase.h
++++ b/include/utils/RefBase.h
+@@ -27,8 +27,12 @@
+ #include <utils/StrongPointer.h>
+ #include <utils/TypeHelpers.h>
+
++#ifdef _MSC_VER
++#define __attribute__(X)
++#endif
++
+ // ---------------------------------------------------------------------------
+-namespace android {
++namespace stagefright {
+
+ class TextOutput;
+ TextOutput& printWeakPointer(TextOutput& to, const void* val);
+@@ -539,7 +543,11 @@ void move_backward_type(wp<TYPE>* d, wp<TYPE> const* s, size_t n) {
+ }
+
+
+-}; // namespace android
++}; // namespace stagefright
++
++#ifdef _MSC_VER
++#undef __attribute__
++#endif
+
+ // ---------------------------------------------------------------------------
+
+diff --git a/include/utils/SharedBuffer.h b/include/utils/SharedBuffer.h
+index b670953..62d3ca4 100644
+--- a/include/utils/SharedBuffer.h
++++ b/include/utils/SharedBuffer.h
+@@ -22,7 +22,7 @@
+
+ // ---------------------------------------------------------------------------
+
+-namespace android {
++namespace stagefright {
+
+ class SharedBuffer
+ {
+@@ -130,7 +130,7 @@ bool SharedBuffer::onlyOwner() const {
+ return (mRefs == 1);
+ }
+
+-}; // namespace android
++}; // namespace stagefright
+
+ // ---------------------------------------------------------------------------
+
+diff --git a/include/utils/SortedVector.h b/include/utils/SortedVector.h
+index 2d3e82a..67bfea8 100644
+--- a/include/utils/SortedVector.h
++++ b/include/utils/SortedVector.h
+@@ -29,7 +29,7 @@
+
+ // ---------------------------------------------------------------------------
+
+-namespace android {
++namespace stagefright {
+
+ template <class TYPE>
+ class SortedVector : private SortedVectorImpl
+@@ -48,7 +48,6 @@ public:
+ virtual ~SortedVector();
+
+ /*! copy operator */
+- const SortedVector<TYPE>& operator = (const SortedVector<TYPE>& rhs) const;
+ SortedVector<TYPE>& operator = (const SortedVector<TYPE>& rhs);
+
+ /*
+@@ -168,12 +167,6 @@ SortedVector<TYPE>& SortedVector<TYPE>::operator = (const SortedVector<TYPE>& rh
+ }
+
+ template<class TYPE> inline
+-const SortedVector<TYPE>& SortedVector<TYPE>::operator = (const SortedVector<TYPE>& rhs) const {
+- SortedVectorImpl::operator = (rhs);
+- return *this;
+-}
+-
+-template<class TYPE> inline
+ const TYPE* SortedVector<TYPE>::array() const {
+ return static_cast<const TYPE *>(arrayImpl());
+ }
+@@ -274,7 +267,7 @@ int SortedVector<TYPE>::do_compare(const void* lhs, const void* rhs) const {
+ return compare_type( *reinterpret_cast<const TYPE*>(lhs), *reinterpret_cast<const TYPE*>(rhs) );
+ }
+
+-}; // namespace android
++}; // namespace stagefright
+
+
+ // ---------------------------------------------------------------------------
+diff --git a/include/utils/String16.h b/include/utils/String16.h
+index d131bfc..40632d7 100644
+--- a/include/utils/String16.h
++++ b/include/utils/String16.h
+@@ -30,7 +30,7 @@ extern "C" {
+
+ // ---------------------------------------------------------------------------
+
+-namespace android {
++namespace stagefright {
+
+ // ---------------------------------------------------------------------------
+
+@@ -243,7 +243,7 @@ inline String16::operator const char16_t*() const
+ return mString;
+ }
+
+-}; // namespace android
++}; // namespace stagefright
+
+ // ---------------------------------------------------------------------------
+
+diff --git a/include/utils/String8.h b/include/utils/String8.h
+index ef59470..3007f21 100644
+--- a/include/utils/String8.h
++++ b/include/utils/String8.h
+@@ -27,7 +27,11 @@
+
+ // ---------------------------------------------------------------------------
+
+-namespace android {
++#ifdef _MSC_VER
++#define __attribute__(X)
++#endif
++
++namespace stagefright {
+
+ class String16;
+ class TextOutput;
+@@ -388,7 +392,11 @@ inline String8::operator const char*() const
+ return mString;
+ }
+
+-} // namespace android
++} // namespace stagefright
++
++#ifdef _MSC_VER
++#undef __attribute__
++#endif
+
+ // ---------------------------------------------------------------------------
+
+diff --git a/include/utils/StrongPointer.h b/include/utils/StrongPointer.h
+index aba9577..db22900 100644
+--- a/include/utils/StrongPointer.h
++++ b/include/utils/StrongPointer.h
+@@ -24,7 +24,7 @@
+ #include <stdlib.h>
+
+ // ---------------------------------------------------------------------------
+-namespace android {
++namespace stagefright {
+
+ template<typename T> class wp;
+
+@@ -204,7 +204,7 @@ void sp<T>::set_pointer(T* ptr) {
+ m_ptr = ptr;
+ }
+
+-}; // namespace android
++}; // namespace stagefright
+
+ // ---------------------------------------------------------------------------
+
+diff --git a/include/utils/TypeHelpers.h b/include/utils/TypeHelpers.h
+index 13c9081..7a19244 100644
+--- a/include/utils/TypeHelpers.h
++++ b/include/utils/TypeHelpers.h
+@@ -24,7 +24,7 @@
+
+ // ---------------------------------------------------------------------------
+
+-namespace android {
++namespace stagefright {
+
+ /*
+ * Types traits
+@@ -201,7 +201,7 @@ void move_backward_type(TYPE* d, const TYPE* s, size_t n = 1) {
+ if ((traits<TYPE>::has_trivial_dtor && traits<TYPE>::has_trivial_copy)
+ || traits<TYPE>::has_trivial_move)
+ {
+- memmove(d,s,n*sizeof(TYPE));
++ memmove((void*)d,(void*)s,n*sizeof(TYPE));
+ } else {
+ while (n--) {
+ if (!traits<TYPE>::has_trivial_copy) {
+@@ -295,7 +295,7 @@ template <typename T> inline hash_t hash_type(T* const & value) {
+ return hash_type(uintptr_t(value));
+ }
+
+-}; // namespace android
++}; // namespace stagefright
+
+ // ---------------------------------------------------------------------------
+
+diff --git a/include/utils/Unicode.h b/include/utils/Unicode.h
+index c8c87c3..b76a5e2 100644
+--- a/include/utils/Unicode.h
++++ b/include/utils/Unicode.h
+@@ -22,9 +22,6 @@
+
+ extern "C" {
+
+-typedef uint32_t char32_t;
+-typedef uint16_t char16_t;
+-
+ // Standard string functions on char16_t strings.
+ int strcmp16(const char16_t *, const char16_t *);
+ int strncmp16(const char16_t *s1, const char16_t *s2, size_t n);
+diff --git a/include/utils/Vector.h b/include/utils/Vector.h
+index ed7b725..2388d06 100644
+--- a/include/utils/Vector.h
++++ b/include/utils/Vector.h
+@@ -28,7 +28,7 @@
+
+ // ---------------------------------------------------------------------------
+
+-namespace android {
++namespace stagefright {
+
+ template <typename TYPE>
+ class SortedVector;
+@@ -55,10 +55,8 @@ public:
+ virtual ~Vector();
+
+ /*! copy operator */
+- const Vector<TYPE>& operator = (const Vector<TYPE>& rhs) const;
+ Vector<TYPE>& operator = (const Vector<TYPE>& rhs);
+
+- const Vector<TYPE>& operator = (const SortedVector<TYPE>& rhs) const;
+ Vector<TYPE>& operator = (const SortedVector<TYPE>& rhs);
+
+ /*
+@@ -171,8 +169,12 @@ public:
+ typedef int (*compar_t)(const TYPE* lhs, const TYPE* rhs);
+ typedef int (*compar_r_t)(const TYPE* lhs, const TYPE* rhs, void* state);
+
+- inline status_t sort(compar_t cmp);
+- inline status_t sort(compar_r_t cmp, void* state);
++ inline status_t sort(compar_t cmp) {
++ return VectorImpl::sort((VectorImpl::compar_t)cmp);
++ }
++ inline status_t sort(compar_r_t cmp, void* state) {
++ return VectorImpl::sort((VectorImpl::compar_r_t)cmp, state);
++ }
+
+ // for debugging only
+ inline size_t getItemSize() const { return itemSize(); }
+@@ -247,24 +249,12 @@ Vector<TYPE>& Vector<TYPE>::operator = (const Vector<TYPE>& rhs) {
+ }
+
+ template<class TYPE> inline
+-const Vector<TYPE>& Vector<TYPE>::operator = (const Vector<TYPE>& rhs) const {
+- VectorImpl::operator = (static_cast<const VectorImpl&>(rhs));
+- return *this;
+-}
+-
+-template<class TYPE> inline
+ Vector<TYPE>& Vector<TYPE>::operator = (const SortedVector<TYPE>& rhs) {
+ VectorImpl::operator = (static_cast<const VectorImpl&>(rhs));
+ return *this;
+ }
+
+ template<class TYPE> inline
+-const Vector<TYPE>& Vector<TYPE>::operator = (const SortedVector<TYPE>& rhs) const {
+- VectorImpl::operator = (rhs);
+- return *this;
+-}
+-
+-template<class TYPE> inline
+ const TYPE* Vector<TYPE>::array() const {
+ return static_cast<const TYPE *>(arrayImpl());
+ }
+@@ -373,16 +363,6 @@ ssize_t Vector<TYPE>::removeItemsAt(size_t index, size_t count) {
+ return VectorImpl::removeItemsAt(index, count);
+ }
+
+-template<class TYPE> inline
+-status_t Vector<TYPE>::sort(Vector<TYPE>::compar_t cmp) {
+- return VectorImpl::sort((VectorImpl::compar_t)cmp);
+-}
+-
+-template<class TYPE> inline
+-status_t Vector<TYPE>::sort(Vector<TYPE>::compar_r_t cmp, void* state) {
+- return VectorImpl::sort((VectorImpl::compar_r_t)cmp, state);
+-}
+-
+ // ---------------------------------------------------------------------------
+
+ template<class TYPE>
+@@ -415,7 +395,7 @@ void Vector<TYPE>::do_move_backward(void* dest, const void* from, size_t num) co
+ move_backward_type( reinterpret_cast<TYPE*>(dest), reinterpret_cast<const TYPE*>(from), num );
+ }
+
+-}; // namespace android
++}; // namespace stagefright
+
+
+ // ---------------------------------------------------------------------------
+diff --git a/include/utils/VectorImpl.h b/include/utils/VectorImpl.h
+index 21ad71c..b83c946 100644
+--- a/include/utils/VectorImpl.h
++++ b/include/utils/VectorImpl.h
+@@ -26,7 +26,7 @@
+ // No user serviceable parts in here...
+ // ---------------------------------------------------------------------------
+
+-namespace android {
++namespace stagefright {
+
+ /*!
+ * Implementation of the guts of the vector<> class
+@@ -175,7 +175,7 @@ private:
+ ssize_t replaceAt(const void* item, size_t index);
+ };
+
+-}; // namespace android
++}; // namespace stagefright
+
+
+ // ---------------------------------------------------------------------------
+diff --git a/liblog/fake_log_device.c b/liblog/fake_log_device.c
+index 5283619..a34838c 100644
+--- a/liblog/fake_log_device.c
++++ b/liblog/fake_log_device.c
+@@ -31,6 +31,34 @@
+ #include <pthread.h>
+ #endif
+
++#ifdef _MSC_VER
++#include <io.h>
++#include <process.h>
++#include <nspr/prprf.h>
++#define snprintf PR_snprintf
++
++/* We don't want to indent large blocks because it causes unnecessary merge
++ * conflicts */
++#define UNINDENTED_BLOCK_START {
++#define UNINDENTED_BLOCK_END }
++#else
++#define UNINDENTED_BLOCK_START
++#define UNINDENTED_BLOCK_END
++#endif
++
++#ifdef _MSC_VER
++#include <io.h>
++#include <process.h>
++
++/* We don't want to indent large blocks because it causes unnecessary merge
++ * conflicts */
++#define UNINDENTED_BLOCK_START {
++#define UNINDENTED_BLOCK_END }
++#else
++#define UNINDENTED_BLOCK_START
++#define UNINDENTED_BLOCK_END
++#endif
++
+ #define kMaxTagLen 16 /* from the long-dead utils/Log.cpp */
+
+ #define kTagSetSize 16 /* arbitrary */
+@@ -191,6 +219,7 @@ static void configureInitialState(const char* pathName, LogState* logState)
+ /*
+ * This is based on the the long-dead utils/Log.cpp code.
+ */
++ UNINDENTED_BLOCK_START
+ const char* tags = getenv("ANDROID_LOG_TAGS");
+ TRACE("Found ANDROID_LOG_TAGS='%s'\n", tags);
+ if (tags != NULL) {
+@@ -264,11 +293,12 @@ static void configureInitialState(const char* pathName, LogState* logState)
+ }
+ }
+ }
+-
++ UNINDENTED_BLOCK_END
+
+ /*
+ * Taken from the long-dead utils/Log.cpp
+ */
++ UNINDENTED_BLOCK_START
+ const char* fstr = getenv("ANDROID_PRINTF_LOG");
+ LogFormat format;
+ if (fstr == NULL) {
+@@ -293,6 +323,7 @@ static void configureInitialState(const char* pathName, LogState* logState)
+ }
+
+ logState->outputFormat = format;
++ UNINDENTED_BLOCK_END
+ }
+
+ /*
+@@ -321,7 +352,7 @@ static const char* getPriorityString(int priority)
+ */
+ static ssize_t fake_writev(int fd, const struct iovec *iov, int iovcnt) {
+ int result = 0;
+- struct iovec* end = iov + iovcnt;
++ const struct iovec* end = iov + iovcnt;
+ for (; iov < end; iov++) {
+ int w = write(fd, iov->iov_base, iov->iov_len);
+ if (w != iov->iov_len) {
+@@ -354,7 +385,11 @@ static void showLog(LogState *state,
+ char prefixBuf[128], suffixBuf[128];
+ char priChar;
+ time_t when;
++#ifdef _MSC_VER
++ int pid, tid;
++#else
+ pid_t pid, tid;
++#endif
+
+ TRACE("LOG %d: %s %s", logPrio, tag, msg);
+
+@@ -382,6 +417,7 @@ static void showLog(LogState *state,
+ /*
+ * Construct a buffer containing the log header and log message.
+ */
++ UNINDENTED_BLOCK_START
+ size_t prefixLen, suffixLen;
+
+ switch (state->outputFormat) {
+@@ -431,6 +467,7 @@ static void showLog(LogState *state,
+ /*
+ * Figure out how many lines there will be.
+ */
++ UNINDENTED_BLOCK_START
+ const char* end = msg + strlen(msg);
+ size_t numLines = 0;
+ const char* p = msg;
+@@ -443,7 +480,8 @@ static void showLog(LogState *state,
+ * Create an array of iovecs large enough to write all of
+ * the lines with a prefix and a suffix.
+ */
+- const size_t INLINE_VECS = 6;
++ UNINDENTED_BLOCK_START
++ #define INLINE_VECS 6
+ const size_t MAX_LINES = ((size_t)~0)/(3*sizeof(struct iovec*));
+ struct iovec stackVec[INLINE_VECS];
+ struct iovec* vec = stackVec;
+@@ -467,6 +505,7 @@ static void showLog(LogState *state,
+ * Fill in the iovec pointers.
+ */
+ p = msg;
++ UNINDENTED_BLOCK_START
+ struct iovec* v = vec;
+ int totalLen = 0;
+ while (numLines > 0 && p < end) {
+@@ -476,6 +515,7 @@ static void showLog(LogState *state,
+ totalLen += prefixLen;
+ v++;
+ }
++ UNINDENTED_BLOCK_START
+ const char* start = p;
+ while (p < end && *p != '\n') p++;
+ if ((p-start) > 0) {
+@@ -492,6 +532,7 @@ static void showLog(LogState *state,
+ v++;
+ }
+ numLines -= 1;
++ UNINDENTED_BLOCK_END
+ }
+
+ /*
+@@ -529,6 +570,10 @@ static void showLog(LogState *state,
+ /* if we allocated storage for the iovecs, free it */
+ if (vec != stackVec)
+ free(vec);
++ UNINDENTED_BLOCK_END
++ UNINDENTED_BLOCK_END
++ UNINDENTED_BLOCK_END
++ UNINDENTED_BLOCK_END
+ }
+
+
+@@ -567,6 +612,7 @@ static ssize_t logWritev(int fd, const struct iovec* vector, int count)
+ }
+
+ /* pull out the three fields */
++ UNINDENTED_BLOCK_START
+ int logPrio = *(const char*)vector[0].iov_base;
+ const char* tag = (const char*) vector[1].iov_base;
+ const char* msg = (const char*) vector[2].iov_base;
+@@ -590,6 +636,7 @@ static ssize_t logWritev(int fd, const struct iovec* vector, int count)
+ } else {
+ //TRACE("+++ NOLOG(%d): %s %s", logPrio, tag, msg);
+ }
++ UNINDENTED_BLOCK_END
+
+ bail:
+ unlock();
+@@ -683,3 +730,6 @@ ssize_t fakeLogWritev(int fd, const struct iovec* vector, int count)
+ /* Assume that open() was called first. */
+ return redirectWritev(fd, vector, count);
+ }
++
++#undef UNINDENTED_BLOCK_START
++#undef UNINDENTED_BLOCK_END
+diff --git a/liblog/logd_write.c b/liblog/logd_write.c
+index fff7cc4..a194a9c 100644
+--- a/liblog/logd_write.c
++++ b/liblog/logd_write.c
+@@ -33,7 +33,19 @@
+
+ #define LOG_BUF_SIZE 1024
+
++#ifdef _MSC_VER
++#include <nspr/prprf.h>
++#define snprintf PR_snprintf
++#define __builtin_trap abort
++static int W_OK = 0;
++static int access(char* c, int i) { return -1; }
++#endif
++
+ #if FAKE_LOG_DEVICE
++int fakeLogOpen(const char *pathName, int flags);
++ssize_t fakeLogWritev(int fd, const struct iovec* vector, int count);
++int fakeLogClose(int fd);
++
+ // This will be defined when building for the host.
+ #define log_open(pathname, flags) fakeLogOpen(pathname, flags)
+ #define log_writev(filedes, vector, count) fakeLogWritev(filedes, vector, count)
+@@ -258,7 +270,11 @@ void __android_log_assert(const char *cond, const char *tag,
+
+ __android_log_write(ANDROID_LOG_FATAL, tag, buf);
+
++#ifdef _MSC_VER
++ abort();
++#else
+ __builtin_trap(); /* trap so we have a chance to debug the situation */
++#endif
+ }
+
+ int __android_log_bwrite(int32_t tag, const void *payload, size_t len)
+diff --git a/liblog/logprint.c b/liblog/logprint.c
+index 508c825..6b229df 100644
+--- a/liblog/logprint.c
++++ b/liblog/logprint.c
+@@ -29,6 +29,35 @@
+ #include <log/logd.h>
+ #include <log/logprint.h>
+
++#ifdef _MSC_VER
++#include <nspr/prprf.h>
++#define snprintf PR_snprintf
++#define inline
++/* We don't want to indent large blocks because it causes unnecessary merge
++ * conflicts */
++#define UNINDENTED_BLOCK_START {
++#define UNINDENTED_BLOCK_END }
++
++static char *
++strsep(char **stringp, const char *delim)
++{
++ char* res = *stringp;
++ while (**stringp) {
++ const char *c;
++ for (c = delim; *c; c++) {
++ if (**stringp == *c) {
++ **stringp++ = 0;
++ return res;
++ }
++ }
++ }
++ return res;
++}
++#else
++#define UNINDENTED_BLOCK_START
++#define UNINDENTED_BLOCK_END
++#endif
++
+ typedef struct FilterInfo_t {
+ char *mTag;
+ android_LogPriority mPri;
+@@ -268,6 +297,7 @@ int android_log_addFilterRule(AndroidLogFormat *p_format,
+ pri = ANDROID_LOG_VERBOSE;
+ }
+
++ UNINDENTED_BLOCK_START
+ char *tagName;
+
+ // Presently HAVE_STRNDUP is never defined, so the second case is always taken
+@@ -280,11 +310,14 @@ int android_log_addFilterRule(AndroidLogFormat *p_format,
+ tagName[tagNameLength] = '\0';
+ #endif /*HAVE_STRNDUP*/
+
++ UNINDENTED_BLOCK_START
+ FilterInfo *p_fi = filterinfo_new(tagName, pri);
+ free(tagName);
+
+ p_fi->p_next = p_format->filters;
+ p_format->filters = p_fi;
++ UNINDENTED_BLOCK_END
++ UNINDENTED_BLOCK_END
+ }
+
+ return 0;
+@@ -373,6 +406,7 @@ int android_log_processLogBuffer(struct logger_entry *buf,
+ return -1;
+ }
+
++ UNINDENTED_BLOCK_START
+ int msgStart = -1;
+ int msgEnd = -1;
+
+@@ -404,6 +438,7 @@ int android_log_processLogBuffer(struct logger_entry *buf,
+ entry->messageLen = msgEnd - msgStart;
+
+ return 0;
++ UNINDENTED_BLOCK_END
+ }
+
+ /*
+@@ -621,11 +656,7 @@ int android_log_processBinaryLogBuffer(struct logger_entry *buf,
+ eventData += 4;
+ inCount -= 4;
+
+- if (map != NULL) {
+- entry->tag = android_lookupEventTag(map, tagIndex);
+- } else {
+- entry->tag = NULL;
+- }
++ entry->tag = NULL;
+
+ /*
+ * If we don't have a map, or didn't find the tag number in the map,
+@@ -644,6 +675,7 @@ int android_log_processBinaryLogBuffer(struct logger_entry *buf,
+ /*
+ * Format the event log data into the buffer.
+ */
++ UNINDENTED_BLOCK_START
+ char* outBuf = messageBuf;
+ size_t outRemaining = messageBufLen-1; /* leave one for nul byte */
+ int result;
+@@ -687,6 +719,7 @@ int android_log_processBinaryLogBuffer(struct logger_entry *buf,
+ entry->message = messageBuf;
+
+ return 0;
++ UNINDENTED_BLOCK_END
+ }
+
+ /**
+@@ -737,6 +770,7 @@ char *android_log_formatLogLine (
+ /*
+ * Construct a buffer containing the log header and log message.
+ */
++ UNINDENTED_BLOCK_START
+ size_t prefixLen, suffixLen;
+
+ switch (p_format->format) {
+@@ -807,6 +841,7 @@ char *android_log_formatLogLine (
+
+ /* the following code is tragically unreadable */
+
++ UNINDENTED_BLOCK_START
+ size_t numLines;
+ size_t i;
+ char *p;
+@@ -882,6 +917,8 @@ char *android_log_formatLogLine (
+ }
+
+ return ret;
++ UNINDENTED_BLOCK_END
++ UNINDENTED_BLOCK_END
+ }
+
+ /**
+@@ -1014,3 +1051,6 @@ void logprint_run_tests()
+ fprintf(stderr, "tests complete\n");
+ #endif
+ }
++
++#undef UNINDENTED_BLOCK_START
++#undef UNINDENTED_BLOCK_END
+diff --git a/libpixelflinger/codeflinger/tinyutils/Errors.h b/libpixelflinger/codeflinger/tinyutils/Errors.h
+index 47ae9d7..98f2190 100644
+--- a/libpixelflinger/codeflinger/tinyutils/Errors.h
++++ b/libpixelflinger/codeflinger/tinyutils/Errors.h
+@@ -20,7 +20,7 @@
+ #include <sys/types.h>
+ #include <errno.h>
+
+-namespace android {
++namespace stagefright {
+ namespace tinyutils {
+
+ // use this type to return error codes
+@@ -41,7 +41,7 @@ enum {
+
+
+ } // namespace tinyutils
+-} // namespace android
++} // namespace stagefright
+
+ // ---------------------------------------------------------------------------
+
+diff --git a/libpixelflinger/codeflinger/tinyutils/KeyedVector.h b/libpixelflinger/codeflinger/tinyutils/KeyedVector.h
+index 9d8668b..62fc760 100644
+--- a/libpixelflinger/codeflinger/tinyutils/KeyedVector.h
++++ b/libpixelflinger/codeflinger/tinyutils/KeyedVector.h
+@@ -27,7 +27,7 @@
+
+ // ---------------------------------------------------------------------------
+
+-namespace android {
++namespace stagefright {
+ namespace tinyutils {
+
+ template <typename KEY, typename VALUE>
+@@ -196,7 +196,7 @@ const VALUE& DefaultKeyedVector<KEY,VALUE>::valueFor(const KEY& key) const {
+ }
+
+ } // namespace tinyutils
+-} // namespace android
++} // namespace stagefright
+
+ // ---------------------------------------------------------------------------
+
+diff --git a/libpixelflinger/codeflinger/tinyutils/SortedVector.h b/libpixelflinger/codeflinger/tinyutils/SortedVector.h
+index a2b7005..71026c8 100644
+--- a/libpixelflinger/codeflinger/tinyutils/SortedVector.h
++++ b/libpixelflinger/codeflinger/tinyutils/SortedVector.h
+@@ -27,7 +27,7 @@
+
+ // ---------------------------------------------------------------------------
+
+-namespace android {
++namespace stagefright {
+ namespace tinyutils {
+
+ template <class TYPE>
+@@ -276,7 +276,7 @@ int SortedVector<TYPE>::do_compare(const void* lhs, const void* rhs) const {
+ }
+
+ } // namespace tinyutils
+-} // namespace android
++} // namespace stagefright
+
+
+ // ---------------------------------------------------------------------------
+diff --git a/libpixelflinger/codeflinger/tinyutils/Vector.h b/libpixelflinger/codeflinger/tinyutils/Vector.h
+index c07a17a..3fe87a2 100644
+--- a/libpixelflinger/codeflinger/tinyutils/Vector.h
++++ b/libpixelflinger/codeflinger/tinyutils/Vector.h
+@@ -29,7 +29,7 @@
+
+ // ---------------------------------------------------------------------------
+
+-namespace android {
++namespace stagefright {
+ namespace tinyutils {
+
+ /*!
+@@ -345,7 +345,7 @@ void Vector<TYPE>::do_move_backward(void* dest, const void* from, size_t num) co
+ }
+
+ } // namespace tinyutils
+-} // namespace android
++} // namespace stagefright
+
+
+ // ---------------------------------------------------------------------------
+diff --git a/libpixelflinger/codeflinger/tinyutils/VectorImpl.h b/libpixelflinger/codeflinger/tinyutils/VectorImpl.h
+index 56089b3..6cc55c4 100644
+--- a/libpixelflinger/codeflinger/tinyutils/VectorImpl.h
++++ b/libpixelflinger/codeflinger/tinyutils/VectorImpl.h
+@@ -25,7 +25,7 @@
+ // No user serviceable parts in here...
+ // ---------------------------------------------------------------------------
+
+-namespace android {
++namespace stagefright {
+ namespace tinyutils {
+
+ /*!
+@@ -187,7 +187,7 @@ private:
+ };
+
+ } // namespace tinyutils
+-} // namespace android
++} // namespace stagefright
+
+
+ // ---------------------------------------------------------------------------
+diff --git a/libutils/RefBase.cpp b/libutils/RefBase.cpp
+index f398a82..259b0a4 100644
+--- a/libutils/RefBase.cpp
++++ b/libutils/RefBase.cpp
+@@ -20,7 +20,14 @@
+ #include <utils/RefBase.h>
+
+ #include <utils/Atomic.h>
++#ifdef _MSC_VER
++class CallStack {
++public:
++ CallStack(int x) {}
++};
++#else
+ #include <utils/CallStack.h>
++#endif
+ #include <utils/Log.h>
+ #include <utils/threads.h>
+
+@@ -40,7 +47,7 @@
+ #define DEBUG_REFS_ENABLED_BY_DEFAULT 0
+
+ // whether callstack are collected (significantly slows things down)
+-#define DEBUG_REFS_CALLSTACK_ENABLED 1
++#define DEBUG_REFS_CALLSTACK_ENABLED 0
+
+ // folder where stack traces are saved when DEBUG_REFS is enabled
+ // this folder needs to exist and be writable
+@@ -51,7 +58,7 @@
+
+ // ---------------------------------------------------------------------------
+
+-namespace android {
++namespace stagefright {
+
+ #define INITIAL_STRONG_VALUE (1<<28)
+
+@@ -647,4 +654,4 @@ void RefBase::renameRefId(RefBase* ref,
+ ref->mRefs->renameWeakRefId(old_id, new_id);
+ }
+
+-}; // namespace android
++}; // namespace stagefright
+diff --git a/libutils/SharedBuffer.cpp b/libutils/SharedBuffer.cpp
+index 3555fb7..7aefe80 100644
+--- a/libutils/SharedBuffer.cpp
++++ b/libutils/SharedBuffer.cpp
+@@ -22,7 +22,7 @@
+
+ // ---------------------------------------------------------------------------
+
+-namespace android {
++namespace stagefright {
+
+ SharedBuffer* SharedBuffer::alloc(size_t size)
+ {
+@@ -110,4 +110,4 @@ int32_t SharedBuffer::release(uint32_t flags) const
+ }
+
+
+-}; // namespace android
++}; // namespace stagefright
+diff --git a/libutils/Static.cpp b/libutils/Static.cpp
+index 3ed07a1..476240f 100644
+--- a/libutils/Static.cpp
++++ b/libutils/Static.cpp
+@@ -17,7 +17,7 @@
+ // All static variables go here, to control initialization and
+ // destruction order in the library.
+
+-namespace android {
++namespace stagefright {
+
+ // For String8.cpp
+ extern void initialize_string8();
+@@ -46,4 +46,4 @@ public:
+ static LibUtilsFirstStatics gFirstStatics;
+ int gDarwinCantLoadAllObjects = 1;
+
+-} // namespace android
++} // namespace stagefright
+diff --git a/libutils/String16.cpp b/libutils/String16.cpp
+index b09b728..998cb94 100644
+--- a/libutils/String16.cpp
++++ b/libutils/String16.cpp
+@@ -27,7 +27,7 @@
+ #include <ctype.h>
+
+
+-namespace android {
++namespace stagefright {
+
+ static SharedBuffer* gEmptyStringBuf = NULL;
+ static char16_t* gEmptyString = NULL;
+@@ -419,4 +419,4 @@ status_t String16::remove(size_t len, size_t begin)
+ return NO_MEMORY;
+ }
+
+-}; // namespace android
++}; // namespace stagefright
+diff --git a/libutils/String8.cpp b/libutils/String8.cpp
+index e852d77..3d4b285 100644
+--- a/libutils/String8.cpp
++++ b/libutils/String8.cpp
+@@ -25,13 +25,13 @@
+ #include <ctype.h>
+
+ /*
+- * Functions outside android is below the namespace android, since they use
++ * Functions outside android is below the namespace stagefright, since they use
+ * functions and constants in android namespace.
+ */
+
+ // ---------------------------------------------------------------------------
+
+-namespace android {
++namespace stagefright {
+
+ // Separator used by resource paths. This is not platform dependent contrary
+ // to OS_PATH_SEPARATOR.
+@@ -138,17 +138,8 @@ static char* allocFromUTF32(const char32_t* in, size_t len)
+ // ---------------------------------------------------------------------------
+
+ String8::String8()
+- : mString(getEmptyString())
+-{
+-}
+-
+-String8::String8(StaticLinkage)
+ : mString(0)
+ {
+- // this constructor is used when we can't rely on the static-initializers
+- // having run. In this case we always allocate an empty string. It's less
+- // efficient than using getEmptyString(), but we assume it's uncommon.
+-
+ char* data = static_cast<char*>(
+ SharedBuffer::alloc(sizeof(char))->data());
+ data[0] = 0;
+@@ -324,16 +315,27 @@ status_t String8::appendFormat(const char* fmt, ...)
+ status_t String8::appendFormatV(const char* fmt, va_list args)
+ {
+ int result = NO_ERROR;
++#ifndef _MSC_VER
++ va_list o;
++ va_copy(o, args);
++#endif
+ int n = vsnprintf(NULL, 0, fmt, args);
+ if (n != 0) {
+ size_t oldLength = length();
+ char* buf = lockBuffer(oldLength + n);
+ if (buf) {
++#ifdef _MSC_VER
+ vsnprintf(buf + oldLength, n + 1, fmt, args);
++#else
++ vsnprintf(buf + oldLength, n + 1, fmt, o);
++#endif
+ } else {
+ result = NO_MEMORY;
+ }
+ }
++#ifndef _MSC_VER
++ va_end(o);
++#endif
+ return result;
+ }
+
+@@ -465,6 +467,8 @@ void String8::getUtf32(char32_t* dst) const
+ // ---------------------------------------------------------------------------
+ // Path functions
+
++#if 0
++
+ void String8::setPathName(const char* name)
+ {
+ setPathName(name, strlen(name));
+@@ -637,4 +641,6 @@ String8& String8::convertToResPath()
+ return *this;
+ }
+
+-}; // namespace android
++#endif
++
++}; // namespace stagefright
+diff --git a/libutils/Unicode.cpp b/libutils/Unicode.cpp
+index a66e3bb..b8aae5e 100644
+--- a/libutils/Unicode.cpp
++++ b/libutils/Unicode.cpp
+@@ -576,8 +576,8 @@ void utf8_to_utf16(const uint8_t* u8str, size_t u8len, char16_t* u16str) {
+ char16_t* utf8_to_utf16_n(const uint8_t* src, size_t srcLen, char16_t* dst, size_t dstLen) {
+ const uint8_t* const u8end = src + srcLen;
+ const uint8_t* u8cur = src;
+- const uint16_t* const u16end = dst + dstLen;
+- char16_t* u16cur = dst;
++ const uint16_t* const u16end = (const uint16_t* const) dst + dstLen;
++ uint16_t* u16cur = (uint16_t*) dst;
+
+ while (u8cur < u8end && u16cur < u16end) {
+ size_t u8len = utf8_codepoint_len(*u8cur);
+@@ -593,14 +593,14 @@ char16_t* utf8_to_utf16_n(const uint8_t* src, size_t srcLen, char16_t* dst, size
+ *u16cur++ = (char16_t) ((codepoint >> 10) + 0xD800);
+ if (u16cur >= u16end) {
+ // Ooops... not enough room for this surrogate pair.
+- return u16cur-1;
++ return (char16_t*) u16cur-1;
+ }
+ *u16cur++ = (char16_t) ((codepoint & 0x3FF) + 0xDC00);
+ }
+
+ u8cur += u8len;
+ }
+- return u16cur;
++ return (char16_t*) u16cur;
+ }
+
+ }
+diff --git a/libutils/VectorImpl.cpp b/libutils/VectorImpl.cpp
+index 5a79647..114fa90 100644
+--- a/libutils/VectorImpl.cpp
++++ b/libutils/VectorImpl.cpp
+@@ -29,7 +29,7 @@
+ /*****************************************************************************/
+
+
+-namespace android {
++namespace stagefright {
+
+ // ----------------------------------------------------------------------------
+
+@@ -621,5 +621,5 @@ ssize_t SortedVectorImpl::remove(const void* item)
+
+ /*****************************************************************************/
+
+-}; // namespace android
++}; // namespace stagefright
+
diff --git a/media/libstagefright/ports/bsd/include/byteswap.h b/media/libstagefright/ports/bsd/include/byteswap.h
new file mode 100644
index 000000000..5bd379a8c
--- /dev/null
+++ b/media/libstagefright/ports/bsd/include/byteswap.h
@@ -0,0 +1,15 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+#ifndef BYTESWAP_H_
+#define BYTESWAP_H_
+
+#include <sys/endian.h>
+
+#ifdef __OpenBSD__
+#define bswap_16(x) swap16(x)
+#else
+#define bswap_16(x) bswap16(x)
+#endif
+
+#endif
diff --git a/media/libstagefright/ports/darwin/include/byteswap.h b/media/libstagefright/ports/darwin/include/byteswap.h
new file mode 100644
index 000000000..903a9198c
--- /dev/null
+++ b/media/libstagefright/ports/darwin/include/byteswap.h
@@ -0,0 +1,10 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+#ifndef BYTESWAP_H_
+#define BYTESWAP_H_
+
+#include <libkern/OSByteOrder.h>
+#define bswap_16 OSSwapInt16
+
+#endif
diff --git a/media/libstagefright/ports/win32/include/arpa/inet.h b/media/libstagefright/ports/win32/include/arpa/inet.h
new file mode 100644
index 000000000..e693e856e
--- /dev/null
+++ b/media/libstagefright/ports/win32/include/arpa/inet.h
@@ -0,0 +1,9 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+#ifndef INET_H_
+#define INET_H_
+
+#include <netinet/in.h>
+
+#endif
diff --git a/media/libstagefright/ports/win32/include/byteswap.h b/media/libstagefright/ports/win32/include/byteswap.h
new file mode 100644
index 000000000..aabc69726
--- /dev/null
+++ b/media/libstagefright/ports/win32/include/byteswap.h
@@ -0,0 +1,10 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+#ifndef BYTESWAP_H_
+#define BYTESWAP_H_
+
+#include <stdlib.h>
+#define bswap_16 _byteswap_ushort
+
+#endif
diff --git a/media/libstagefright/ports/win32/include/netinet/in.h b/media/libstagefright/ports/win32/include/netinet/in.h
new file mode 100644
index 000000000..90feff10a
--- /dev/null
+++ b/media/libstagefright/ports/win32/include/netinet/in.h
@@ -0,0 +1,39 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+#ifndef IN_H_
+#define IN_H_
+
+#include <stdint.h>
+
+#if defined(_M_IX86) || defined(_M_AMD64)
+
+static uint32_t
+ntohl(uint32_t x)
+{
+ return x << 24 | (x << 8 & 0xff0000) | (x >> 8 & 0xff00) | x >> 24;
+}
+
+static uint16_t
+ntohs(uint16_t x)
+{
+ return x << 8 | x >> 8;
+}
+
+static uint32_t
+htonl(uint32_t x)
+{
+ return x << 24 | (x << 8 & 0xff0000) | (x >> 8 & 0xff00) | x >> 24;
+}
+
+static uint16_t
+htons(uint16_t x)
+{
+ return x << 8 | x >> 8;
+}
+
+#else
+#error Unsupported architecture
+#endif
+
+#endif
diff --git a/media/libstagefright/ports/win32/include/pthread.h b/media/libstagefright/ports/win32/include/pthread.h
new file mode 100644
index 000000000..1aa4e7032
--- /dev/null
+++ b/media/libstagefright/ports/win32/include/pthread.h
@@ -0,0 +1 @@
+// Intentionally left blank
diff --git a/media/libstagefright/ports/win32/include/sys/cdefs.h b/media/libstagefright/ports/win32/include/sys/cdefs.h
new file mode 100644
index 000000000..1aa4e7032
--- /dev/null
+++ b/media/libstagefright/ports/win32/include/sys/cdefs.h
@@ -0,0 +1 @@
+// Intentionally left blank
diff --git a/media/libstagefright/ports/win32/include/sys/time.h b/media/libstagefright/ports/win32/include/sys/time.h
new file mode 100644
index 000000000..b8dd50914
--- /dev/null
+++ b/media/libstagefright/ports/win32/include/sys/time.h
@@ -0,0 +1,9 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+#ifndef TIME_H_
+#define TIME_H_
+
+#include <time.h>
+
+#endif
diff --git a/media/libstagefright/ports/win32/include/unistd.h b/media/libstagefright/ports/win32/include/unistd.h
new file mode 100644
index 000000000..1aa4e7032
--- /dev/null
+++ b/media/libstagefright/ports/win32/include/unistd.h
@@ -0,0 +1 @@
+// Intentionally left blank
diff --git a/media/libstagefright/stubs/empty/ALooper.h b/media/libstagefright/stubs/empty/ALooper.h
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/media/libstagefright/stubs/empty/ALooper.h
diff --git a/media/libstagefright/stubs/empty/ALooperRoster.h b/media/libstagefright/stubs/empty/ALooperRoster.h
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/media/libstagefright/stubs/empty/ALooperRoster.h
diff --git a/media/libstagefright/stubs/empty/binder/Parcel.h b/media/libstagefright/stubs/empty/binder/Parcel.h
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/media/libstagefright/stubs/empty/binder/Parcel.h
diff --git a/media/libstagefright/stubs/empty/hardware/audio.h b/media/libstagefright/stubs/empty/hardware/audio.h
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/media/libstagefright/stubs/empty/hardware/audio.h
diff --git a/media/libstagefright/stubs/empty/media/AudioParameter.h b/media/libstagefright/stubs/empty/media/AudioParameter.h
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/media/libstagefright/stubs/empty/media/AudioParameter.h
diff --git a/media/libstagefright/stubs/empty/media/AudioSystem.h b/media/libstagefright/stubs/empty/media/AudioSystem.h
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/media/libstagefright/stubs/empty/media/AudioSystem.h
diff --git a/media/libstagefright/stubs/empty/media/MediaPlayerInterface.h b/media/libstagefright/stubs/empty/media/MediaPlayerInterface.h
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/media/libstagefright/stubs/empty/media/MediaPlayerInterface.h
diff --git a/media/libstagefright/stubs/empty/sys/system_properties.h b/media/libstagefright/stubs/empty/sys/system_properties.h
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/media/libstagefright/stubs/empty/sys/system_properties.h
diff --git a/media/libstagefright/stubs/empty/system/audio.h b/media/libstagefright/stubs/empty/system/audio.h
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/media/libstagefright/stubs/empty/system/audio.h
diff --git a/media/libstagefright/stubs/include/cutils/atomic.h b/media/libstagefright/stubs/include/cutils/atomic.h
new file mode 100644
index 000000000..e22550177
--- /dev/null
+++ b/media/libstagefright/stubs/include/cutils/atomic.h
@@ -0,0 +1,53 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+#ifndef ATOMIC_H_
+#define ATOMIC_H_
+
+#include <stdint.h>
+
+// This implements the atomic primatives without any atomicity guarantees. This
+// makes the totally unsafe. However we're only using the demuxer in a single
+// thread.
+
+namespace stagefright {
+static inline int32_t
+android_atomic_dec(volatile int32_t* aValue)
+{
+ return (*aValue)--;
+}
+
+static inline int32_t
+android_atomic_inc(volatile int32_t* aValue)
+{
+ return (*aValue)++;
+}
+
+static inline int32_t
+android_atomic_or(int32_t aModifier, volatile int32_t* aValue)
+{
+ int32_t ret = *aValue;
+ *aValue |= aModifier;
+ return ret;
+}
+
+static inline int32_t
+android_atomic_add(int32_t aModifier, volatile int32_t* aValue)
+{
+ int32_t ret = *aValue;
+ *aValue += aModifier;
+ return ret;
+}
+
+static inline int32_t
+android_atomic_cmpxchg(int32_t aOld, int32_t aNew, volatile int32_t* aValue)
+{
+ if (*aValue == aOld)
+ {
+ return *aValue = aNew;
+ }
+ return aOld;
+}
+}
+
+#endif
diff --git a/media/libstagefright/stubs/include/media/stagefright/foundation/AMessage.h b/media/libstagefright/stubs/include/media/stagefright/foundation/AMessage.h
new file mode 100644
index 000000000..f22439dd9
--- /dev/null
+++ b/media/libstagefright/stubs/include/media/stagefright/foundation/AMessage.h
@@ -0,0 +1,18 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+#ifndef A_MESSAGE_H_
+#define A_MESSAGE_H_
+
+#include <utils/RefBase.h>
+
+namespace stagefright {
+
+struct AMessage : public RefBase {
+public:
+ void post() {}
+};
+
+}
+
+#endif
diff --git a/media/libstagefright/stubs/include/sys/atomics.h b/media/libstagefright/stubs/include/sys/atomics.h
new file mode 100644
index 000000000..975f4f7d9
--- /dev/null
+++ b/media/libstagefright/stubs/include/sys/atomics.h
@@ -0,0 +1,10 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+#ifndef ATOMICS_H_
+#define ATOMICS_H_
+
+#define __atomic_dec android_atomic_dec
+#define __atomic_inc android_atomic_inc
+
+#endif
diff --git a/media/libstagefright/stubs/include/ui/GraphicBuffer.h b/media/libstagefright/stubs/include/ui/GraphicBuffer.h
new file mode 100644
index 000000000..47fa5dbad
--- /dev/null
+++ b/media/libstagefright/stubs/include/ui/GraphicBuffer.h
@@ -0,0 +1,16 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+#ifndef GRAPHIC_BUFFER_H_
+#define GRAPHIC_BUFFER_H_
+
+#include <utils/RefBase.h>
+
+namespace stagefright {
+
+class GraphicBuffer : public RefBase {
+};
+
+}
+
+#endif
diff --git a/media/libstagefright/stubs/include/utils/threads.h b/media/libstagefright/stubs/include/utils/threads.h
new file mode 100644
index 000000000..09b9bcb2e
--- /dev/null
+++ b/media/libstagefright/stubs/include/utils/threads.h
@@ -0,0 +1,5 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+#include <utils/Condition.h>
+#include <utils/Mutex.h>
diff --git a/media/libstagefright/system/core/debuggerd/backtrace.h b/media/libstagefright/system/core/debuggerd/backtrace.h
new file mode 100644
index 000000000..c5c786a01
--- /dev/null
+++ b/media/libstagefright/system/core/debuggerd/backtrace.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _DEBUGGERD_BACKTRACE_H
+#define _DEBUGGERD_BACKTRACE_H
+
+#include <stddef.h>
+#include <stdbool.h>
+#include <sys/types.h>
+
+#include <corkscrew/ptrace.h>
+
+/* Dumps a backtrace using a format similar to what Dalvik uses so that the result
+ * can be intermixed in a bug report. */
+void dump_backtrace(int fd, int amfd, pid_t pid, pid_t tid, bool* detach_failed,
+ int* total_sleep_time_usec);
+
+#endif // _DEBUGGERD_BACKTRACE_H
diff --git a/media/libstagefright/system/core/include/android/log.h b/media/libstagefright/system/core/include/android/log.h
new file mode 100644
index 000000000..0ea4c298b
--- /dev/null
+++ b/media/libstagefright/system/core/include/android/log.h
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _ANDROID_LOG_H
+#define _ANDROID_LOG_H
+
+/******************************************************************
+ *
+ * IMPORTANT NOTICE:
+ *
+ * This file is part of Android's set of stable system headers
+ * exposed by the Android NDK (Native Development Kit) since
+ * platform release 1.5
+ *
+ * Third-party source AND binary code relies on the definitions
+ * here to be FROZEN ON ALL UPCOMING PLATFORM RELEASES.
+ *
+ * - DO NOT MODIFY ENUMS (EXCEPT IF YOU ADD NEW 32-BIT VALUES)
+ * - DO NOT MODIFY CONSTANTS OR FUNCTIONAL MACROS
+ * - DO NOT CHANGE THE SIGNATURE OF FUNCTIONS IN ANY WAY
+ * - DO NOT CHANGE THE LAYOUT OR SIZE OF STRUCTURES
+ */
+
+/*
+ * Support routines to send messages to the Android in-kernel log buffer,
+ * which can later be accessed through the 'logcat' utility.
+ *
+ * Each log message must have
+ * - a priority
+ * - a log tag
+ * - some text
+ *
+ * The tag normally corresponds to the component that emits the log message,
+ * and should be reasonably small.
+ *
+ * Log message text may be truncated to less than an implementation-specific
+ * limit (e.g. 1023 characters max).
+ *
+ * Note that a newline character ("\n") will be appended automatically to your
+ * log message, if not already there. It is not possible to send several messages
+ * and have them appear on a single line in logcat.
+ *
+ * PLEASE USE LOGS WITH MODERATION:
+ *
+ * - Sending log messages eats CPU and slow down your application and the
+ * system.
+ *
+ * - The circular log buffer is pretty small (<64KB), sending many messages
+ * might push off other important log messages from the rest of the system.
+ *
+ * - In release builds, only send log messages to account for exceptional
+ * conditions.
+ *
+ * NOTE: These functions MUST be implemented by /system/lib/liblog.so
+ */
+
+#include <stdarg.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Android log priority values, in ascending priority order.
+ */
+typedef enum android_LogPriority {
+ ANDROID_LOG_UNKNOWN = 0,
+ ANDROID_LOG_DEFAULT, /* only for SetMinPriority() */
+ ANDROID_LOG_VERBOSE,
+ ANDROID_LOG_DEBUG,
+ ANDROID_LOG_INFO,
+ ANDROID_LOG_WARN,
+ ANDROID_LOG_ERROR,
+ ANDROID_LOG_FATAL,
+ ANDROID_LOG_SILENT, /* only for SetMinPriority(); must be last */
+} android_LogPriority;
+
+/*
+ * Send a simple string to the log.
+ */
+int __android_log_write(int prio, const char *tag, const char *text);
+
+/*
+ * Send a formatted string to the log, used like printf(fmt,...)
+ */
+int __android_log_print(int prio, const char *tag, const char *fmt, ...)
+#if defined(__GNUC__)
+ __attribute__ ((format(printf, 3, 4)))
+#endif
+ ;
+
+/*
+ * A variant of __android_log_print() that takes a va_list to list
+ * additional parameters.
+ */
+int __android_log_vprint(int prio, const char *tag,
+ const char *fmt, va_list ap);
+
+/*
+ * Log an assertion failure and SIGTRAP the process to have a chance
+ * to inspect it, if a debugger is attached. This uses the FATAL priority.
+ */
+void __android_log_assert(const char *cond, const char *tag,
+ const char *fmt, ...)
+#if defined(__GNUC__)
+ __attribute__ ((noreturn))
+ __attribute__ ((format(printf, 3, 4)))
+#endif
+ ;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _ANDROID_LOG_H */
diff --git a/media/libstagefright/system/core/include/corkscrew/backtrace.h b/media/libstagefright/system/core/include/corkscrew/backtrace.h
new file mode 100644
index 000000000..556ad04c0
--- /dev/null
+++ b/media/libstagefright/system/core/include/corkscrew/backtrace.h
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* A stack unwinder. */
+
+#ifndef _CORKSCREW_BACKTRACE_H
+#define _CORKSCREW_BACKTRACE_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <sys/types.h>
+#include <corkscrew/ptrace.h>
+#include <corkscrew/map_info.h>
+#include <corkscrew/symbol_table.h>
+
+/*
+ * Describes a single frame of a backtrace.
+ */
+typedef struct {
+ uintptr_t absolute_pc; /* absolute PC offset */
+ uintptr_t stack_top; /* top of stack for this frame */
+ size_t stack_size; /* size of this stack frame */
+} backtrace_frame_t;
+
+/*
+ * Describes the symbols associated with a backtrace frame.
+ */
+typedef struct {
+ uintptr_t relative_pc; /* relative frame PC offset from the start of the library,
+ or the absolute PC if the library is unknown */
+ uintptr_t relative_symbol_addr; /* relative offset of the symbol from the start of the
+ library or 0 if the library is unknown */
+ char* map_name; /* executable or library name, or NULL if unknown */
+ char* symbol_name; /* symbol name, or NULL if unknown */
+ char* demangled_name; /* demangled symbol name, or NULL if unknown */
+} backtrace_symbol_t;
+
+/*
+ * Unwinds the call stack for the current thread of execution.
+ * Populates the backtrace array with the program counters from the call stack.
+ * Returns the number of frames collected, or -1 if an error occurred.
+ */
+ssize_t unwind_backtrace(backtrace_frame_t* backtrace, size_t ignore_depth, size_t max_depth);
+
+/*
+ * Unwinds the call stack for a thread within this process.
+ * Populates the backtrace array with the program counters from the call stack.
+ * Returns the number of frames collected, or -1 if an error occurred.
+ *
+ * The task is briefly suspended while the backtrace is being collected.
+ */
+ssize_t unwind_backtrace_thread(pid_t tid, backtrace_frame_t* backtrace,
+ size_t ignore_depth, size_t max_depth);
+
+/*
+ * Unwinds the call stack of a task within a remote process using ptrace().
+ * Populates the backtrace array with the program counters from the call stack.
+ * Returns the number of frames collected, or -1 if an error occurred.
+ */
+ssize_t unwind_backtrace_ptrace(pid_t tid, const ptrace_context_t* context,
+ backtrace_frame_t* backtrace, size_t ignore_depth, size_t max_depth);
+
+/*
+ * Gets the symbols for each frame of a backtrace.
+ * The symbols array must be big enough to hold one symbol record per frame.
+ * The symbols must later be freed using free_backtrace_symbols.
+ */
+void get_backtrace_symbols(const backtrace_frame_t* backtrace, size_t frames,
+ backtrace_symbol_t* backtrace_symbols);
+
+/*
+ * Gets the symbols for each frame of a backtrace from a remote process.
+ * The symbols array must be big enough to hold one symbol record per frame.
+ * The symbols must later be freed using free_backtrace_symbols.
+ */
+void get_backtrace_symbols_ptrace(const ptrace_context_t* context,
+ const backtrace_frame_t* backtrace, size_t frames,
+ backtrace_symbol_t* backtrace_symbols);
+
+/*
+ * Frees the storage associated with backtrace symbols.
+ */
+void free_backtrace_symbols(backtrace_symbol_t* backtrace_symbols, size_t frames);
+
+enum {
+ // A hint for how big to make the line buffer for format_backtrace_line
+ MAX_BACKTRACE_LINE_LENGTH = 800,
+};
+
+/**
+ * Formats a line from a backtrace as a zero-terminated string into the specified buffer.
+ */
+void format_backtrace_line(unsigned frameNumber, const backtrace_frame_t* frame,
+ const backtrace_symbol_t* symbol, char* buffer, size_t bufferSize);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // _CORKSCREW_BACKTRACE_H
diff --git a/media/libstagefright/system/core/include/corkscrew/map_info.h b/media/libstagefright/system/core/include/corkscrew/map_info.h
new file mode 100644
index 000000000..14bfad67d
--- /dev/null
+++ b/media/libstagefright/system/core/include/corkscrew/map_info.h
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* Process memory map. */
+
+#ifndef _CORKSCREW_MAP_INFO_H
+#define _CORKSCREW_MAP_INFO_H
+
+#include <sys/types.h>
+#include <stdbool.h>
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct map_info {
+ struct map_info* next;
+ uintptr_t start;
+ uintptr_t end;
+ bool is_readable;
+ bool is_writable;
+ bool is_executable;
+ void* data; // arbitrary data associated with the map by the user, initially NULL
+ char name[];
+} map_info_t;
+
+/* Loads memory map from /proc/<tid>/maps. */
+map_info_t* load_map_info_list(pid_t tid);
+
+/* Frees memory map. */
+void free_map_info_list(map_info_t* milist);
+
+/* Finds the memory map that contains the specified address. */
+const map_info_t* find_map_info(const map_info_t* milist, uintptr_t addr);
+
+/* Returns true if the addr is in a readable map. */
+bool is_readable_map(const map_info_t* milist, uintptr_t addr);
+/* Returns true if the addr is in a writable map. */
+bool is_writable_map(const map_info_t* milist, uintptr_t addr);
+/* Returns true if the addr is in an executable map. */
+bool is_executable_map(const map_info_t* milist, uintptr_t addr);
+
+/* Acquires a reference to the memory map for this process.
+ * The result is cached and refreshed automatically.
+ * Make sure to release the map info when done. */
+map_info_t* acquire_my_map_info_list();
+
+/* Releases a reference to the map info for this process that was
+ * previous acquired using acquire_my_map_info_list(). */
+void release_my_map_info_list(map_info_t* milist);
+
+/* Flushes the cached memory map so the next call to
+ * acquire_my_map_info_list() gets fresh data. */
+void flush_my_map_info_list();
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // _CORKSCREW_MAP_INFO_H
diff --git a/media/libstagefright/system/core/include/corkscrew/ptrace.h b/media/libstagefright/system/core/include/corkscrew/ptrace.h
new file mode 100644
index 000000000..76276d89a
--- /dev/null
+++ b/media/libstagefright/system/core/include/corkscrew/ptrace.h
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* Useful ptrace() utility functions. */
+
+#ifndef _CORKSCREW_PTRACE_H
+#define _CORKSCREW_PTRACE_H
+
+#include <corkscrew/map_info.h>
+#include <corkscrew/symbol_table.h>
+
+#include <sys/types.h>
+#include <stdbool.h>
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Stores information about a process that is used for several different
+ * ptrace() based operations. */
+typedef struct {
+ map_info_t* map_info_list;
+} ptrace_context_t;
+
+/* Describes how to access memory from a process. */
+typedef struct {
+ pid_t tid;
+ const map_info_t* map_info_list;
+} memory_t;
+
+#if __i386__
+/* ptrace() register context. */
+typedef struct pt_regs_x86 {
+ uint32_t ebx;
+ uint32_t ecx;
+ uint32_t edx;
+ uint32_t esi;
+ uint32_t edi;
+ uint32_t ebp;
+ uint32_t eax;
+ uint32_t xds;
+ uint32_t xes;
+ uint32_t xfs;
+ uint32_t xgs;
+ uint32_t orig_eax;
+ uint32_t eip;
+ uint32_t xcs;
+ uint32_t eflags;
+ uint32_t esp;
+ uint32_t xss;
+} pt_regs_x86_t;
+#endif
+
+#if __mips__
+/* ptrace() GET_REGS context. */
+typedef struct pt_regs_mips {
+ uint64_t regs[32];
+ uint64_t lo;
+ uint64_t hi;
+ uint64_t cp0_epc;
+ uint64_t cp0_badvaddr;
+ uint64_t cp0_status;
+ uint64_t cp0_cause;
+} pt_regs_mips_t;
+#endif
+
+/*
+ * Initializes a memory structure for accessing memory from this process.
+ */
+void init_memory(memory_t* memory, const map_info_t* map_info_list);
+
+/*
+ * Initializes a memory structure for accessing memory from another process
+ * using ptrace().
+ */
+void init_memory_ptrace(memory_t* memory, pid_t tid);
+
+/*
+ * Reads a word of memory safely.
+ * If the memory is local, ensures that the address is readable before dereferencing it.
+ * Returns false and a value of 0xffffffff if the word could not be read.
+ */
+bool try_get_word(const memory_t* memory, uintptr_t ptr, uint32_t* out_value);
+
+/*
+ * Reads a word of memory safely using ptrace().
+ * Returns false and a value of 0xffffffff if the word could not be read.
+ */
+bool try_get_word_ptrace(pid_t tid, uintptr_t ptr, uint32_t* out_value);
+
+/*
+ * Loads information needed for examining a remote process using ptrace().
+ * The caller must already have successfully attached to the process
+ * using ptrace().
+ *
+ * The context can be used for any threads belonging to that process
+ * assuming ptrace() is attached to them before performing the actual
+ * unwinding. The context can continue to be used to decode backtraces
+ * even after ptrace() has been detached from the process.
+ */
+ptrace_context_t* load_ptrace_context(pid_t pid);
+
+/*
+ * Frees a ptrace context.
+ */
+void free_ptrace_context(ptrace_context_t* context);
+
+/*
+ * Finds a symbol using ptrace.
+ * Returns the containing map and information about the symbol, or
+ * NULL if one or the other is not available.
+ */
+void find_symbol_ptrace(const ptrace_context_t* context,
+ uintptr_t addr, const map_info_t** out_map_info, const symbol_t** out_symbol);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // _CORKSCREW_PTRACE_H
diff --git a/media/libstagefright/system/core/include/corkscrew/symbol_table.h b/media/libstagefright/system/core/include/corkscrew/symbol_table.h
new file mode 100644
index 000000000..4998750bf
--- /dev/null
+++ b/media/libstagefright/system/core/include/corkscrew/symbol_table.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _CORKSCREW_SYMBOL_TABLE_H
+#define _CORKSCREW_SYMBOL_TABLE_H
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct {
+ uintptr_t start;
+ uintptr_t end;
+ char* name;
+} symbol_t;
+
+typedef struct {
+ symbol_t* symbols;
+ size_t num_symbols;
+} symbol_table_t;
+
+/*
+ * Loads a symbol table from a given file.
+ * Returns NULL on error.
+ */
+symbol_table_t* load_symbol_table(const char* filename);
+
+/*
+ * Frees a symbol table.
+ */
+void free_symbol_table(symbol_table_t* table);
+
+/*
+ * Finds a symbol associated with an address in the symbol table.
+ * Returns NULL if not found.
+ */
+const symbol_t* find_symbol(const symbol_table_t* table, uintptr_t addr);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // _CORKSCREW_SYMBOL_TABLE_H
diff --git a/media/libstagefright/system/core/include/cutils/jstring.h b/media/libstagefright/system/core/include/cutils/jstring.h
new file mode 100644
index 000000000..ee0018fcc
--- /dev/null
+++ b/media/libstagefright/system/core/include/cutils/jstring.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __CUTILS_STRING16_H
+#define __CUTILS_STRING16_H
+
+#include <stdint.h>
+#include <stddef.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef uint16_t char16_t;
+
+extern char * strndup16to8 (const char16_t* s, size_t n);
+extern size_t strnlen16to8 (const char16_t* s, size_t n);
+extern char * strncpy16to8 (char *dest, const char16_t*s, size_t n);
+
+extern char16_t * strdup8to16 (const char* s, size_t *out_len);
+extern size_t strlen8to16 (const char* utf8Str);
+extern char16_t * strcpy8to16 (char16_t *dest, const char*s, size_t *out_len);
+extern char16_t * strcpylen8to16 (char16_t *dest, const char*s, int length,
+ size_t *out_len);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __CUTILS_STRING16_H */
diff --git a/media/libstagefright/system/core/include/cutils/log.h b/media/libstagefright/system/core/include/cutils/log.h
new file mode 100644
index 000000000..0e0248e50
--- /dev/null
+++ b/media/libstagefright/system/core/include/cutils/log.h
@@ -0,0 +1 @@
+#include <log/log.h>
diff --git a/media/libstagefright/system/core/include/cutils/properties.h b/media/libstagefright/system/core/include/cutils/properties.h
new file mode 100644
index 000000000..79029f3e6
--- /dev/null
+++ b/media/libstagefright/system/core/include/cutils/properties.h
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __CUTILS_PROPERTIES_H
+#define __CUTILS_PROPERTIES_H
+
+#include <sys/types.h>
+#include <stddef.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* System properties are *small* name value pairs managed by the
+** property service. If your data doesn't fit in the provided
+** space it is not appropriate for a system property.
+**
+** WARNING: system/bionic/include/sys/system_properties.h also defines
+** these, but with different names. (TODO: fix that)
+*/
+#define PROPERTY_KEY_MAX PROP_NAME_MAX
+#define PROPERTY_VALUE_MAX PROP_VALUE_MAX
+
+/* property_get: returns the length of the value which will never be
+** greater than PROPERTY_VALUE_MAX - 1 and will always be zero terminated.
+** (the length does not include the terminating zero).
+**
+** If the property read fails or returns an empty value, the default
+** value is used (if nonnull).
+*/
+int property_get(const char *key, char *value, const char *default_value);
+
+/* property_set: returns 0 on success, < 0 on failure
+*/
+int property_set(const char *key, const char *value);
+
+int property_list(void (*propfn)(const char *key, const char *value, void *cookie), void *cookie);
+
+#if defined(__BIONIC_FORTIFY)
+
+extern int __property_get_real(const char *, char *, const char *)
+ __asm__(__USER_LABEL_PREFIX__ "property_get");
+__errordecl(__property_get_too_small_error, "property_get() called with too small of a buffer");
+
+__BIONIC_FORTIFY_INLINE
+int property_get(const char *key, char *value, const char *default_value) {
+ size_t bos = __bos(value);
+ if (bos < PROPERTY_VALUE_MAX) {
+ __property_get_too_small_error();
+ }
+ return __property_get_real(key, value, default_value);
+}
+
+#endif
+
+#ifdef HAVE_SYSTEM_PROPERTY_SERVER
+/*
+ * We have an external property server instead of built-in libc support.
+ * Used by the simulator.
+ */
+#define SYSTEM_PROPERTY_PIPE_NAME "/tmp/android-sysprop"
+
+enum {
+ kSystemPropertyUnknown = 0,
+ kSystemPropertyGet,
+ kSystemPropertySet,
+ kSystemPropertyList
+};
+#endif /*HAVE_SYSTEM_PROPERTY_SERVER*/
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/media/libstagefright/system/core/include/cutils/sched_policy.h b/media/libstagefright/system/core/include/cutils/sched_policy.h
new file mode 100644
index 000000000..ba84ce32a
--- /dev/null
+++ b/media/libstagefright/system/core/include/cutils/sched_policy.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __CUTILS_SCHED_POLICY_H
+#define __CUTILS_SCHED_POLICY_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Keep in sync with THREAD_GROUP_* in frameworks/base/core/java/android/os/Process.java */
+typedef enum {
+ SP_DEFAULT = -1,
+ SP_BACKGROUND = 0,
+ SP_FOREGROUND = 1,
+ SP_SYSTEM = 2, // can't be used with set_sched_policy()
+ SP_AUDIO_APP = 3,
+ SP_AUDIO_SYS = 4,
+ SP_CNT,
+ SP_MAX = SP_CNT - 1,
+ SP_SYSTEM_DEFAULT = SP_FOREGROUND,
+} SchedPolicy;
+
+/* Assign thread tid to the cgroup associated with the specified policy.
+ * If the thread is a thread group leader, that is it's gettid() == getpid(),
+ * then the other threads in the same thread group are _not_ affected.
+ * On platforms which support gettid(), zero tid means current thread.
+ * Return value: 0 for success, or -errno for error.
+ */
+extern int set_sched_policy(int tid, SchedPolicy policy);
+
+/* Return the policy associated with the cgroup of thread tid via policy pointer.
+ * On platforms which support gettid(), zero tid means current thread.
+ * Return value: 0 for success, or -1 for error and set errno.
+ */
+extern int get_sched_policy(int tid, SchedPolicy *policy);
+
+/* Return a displayable string corresponding to policy.
+ * Return value: non-NULL NUL-terminated name of unspecified length;
+ * the caller is responsible for displaying the useful part of the string.
+ */
+extern const char *get_sched_policy_name(SchedPolicy policy);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __CUTILS_SCHED_POLICY_H */
diff --git a/media/libstagefright/system/core/include/log/event_tag_map.h b/media/libstagefright/system/core/include/log/event_tag_map.h
new file mode 100644
index 000000000..1653c61e9
--- /dev/null
+++ b/media/libstagefright/system/core/include/log/event_tag_map.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBS_CUTILS_EVENTTAGMAP_H
+#define _LIBS_CUTILS_EVENTTAGMAP_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define EVENT_TAG_MAP_FILE "/system/etc/event-log-tags"
+
+struct EventTagMap;
+typedef struct EventTagMap EventTagMap;
+
+/*
+ * Open the specified file as an event log tag map.
+ *
+ * Returns NULL on failure.
+ */
+EventTagMap* android_openEventTagMap(const char* fileName);
+
+/*
+ * Close the map.
+ */
+void android_closeEventTagMap(EventTagMap* map);
+
+/*
+ * Look up a tag by index. Returns the tag string, or NULL if not found.
+ */
+const char* android_lookupEventTag(const EventTagMap* map, int tag);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /*_LIBS_CUTILS_EVENTTAGMAP_H*/
diff --git a/media/libstagefright/system/core/include/log/log.h b/media/libstagefright/system/core/include/log/log.h
new file mode 100644
index 000000000..6131f0147
--- /dev/null
+++ b/media/libstagefright/system/core/include/log/log.h
@@ -0,0 +1,568 @@
+/*
+ * Copyright (C) 2005 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.
+ */
+
+//
+// C/C++ logging functions. See the logging documentation for API details.
+//
+// We'd like these to be available from C code (in case we import some from
+// somewhere), so this has a C interface.
+//
+// The output will be correct when the log file is shared between multiple
+// threads and/or multiple processes so long as the operating system
+// supports O_APPEND. These calls have mutex-protected data structures
+// and so are NOT reentrant. Do not use LOG in a signal handler.
+//
+
+/*
+ * This is the local tag used for the following simplified
+ * logging macros. You can change this preprocessor definition
+ * before using the other macros to change the tag.
+ */
+#ifndef LOG_TAG
+#define LOG_TAG NULL
+#endif
+
+#ifndef _LIBS_LOG_LOG_H
+#define _LIBS_LOG_LOG_H
+
+#include <stdio.h>
+#include <time.h>
+#include <sys/types.h>
+#include <unistd.h>
+#ifdef HAVE_PTHREADS
+#include <pthread.h>
+#endif
+#include <stdarg.h>
+
+#include <log/uio.h>
+#include <log/logd.h>
+
+#ifdef _MSC_VER
+#define __builtin_expect(X, Y) (X)
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// ---------------------------------------------------------------------
+
+/*
+ * Normally we strip ALOGV (VERBOSE messages) from release builds.
+ * You can modify this (for example with "#define LOG_NDEBUG 0"
+ * at the top of your source file) to change that behavior.
+ */
+#ifndef LOG_NDEBUG
+#ifdef NDEBUG
+#define LOG_NDEBUG 1
+#else
+#define LOG_NDEBUG 0
+#endif
+#endif
+
+// ---------------------------------------------------------------------
+
+/*
+ * Simplified macro to send a verbose log message using the current LOG_TAG.
+ */
+#ifndef ALOGV
+#if LOG_NDEBUG
+#define ALOGV(...) ((void)0)
+#else
+#define ALOGV(...) ((void)ALOG(LOG_VERBOSE, LOG_TAG, __VA_ARGS__))
+#endif
+#endif
+
+#define CONDITION(cond) (__builtin_expect((cond)!=0, 0))
+
+#ifndef ALOGV_IF
+#if LOG_NDEBUG
+#define ALOGV_IF(cond, ...) ((void)0)
+#else
+#define ALOGV_IF(cond, ...) \
+ ( (CONDITION(cond)) \
+ ? ((void)ALOG(LOG_VERBOSE, LOG_TAG, __VA_ARGS__)) \
+ : (void)0 )
+#endif
+#endif
+
+/*
+ * Simplified macro to send a debug log message using the current LOG_TAG.
+ */
+#ifndef ALOGD
+#define ALOGD(...) ((void)ALOG(LOG_DEBUG, LOG_TAG, __VA_ARGS__))
+#endif
+
+#ifndef ALOGD_IF
+#define ALOGD_IF(cond, ...) \
+ ( (CONDITION(cond)) \
+ ? ((void)ALOG(LOG_DEBUG, LOG_TAG, __VA_ARGS__)) \
+ : (void)0 )
+#endif
+
+/*
+ * Simplified macro to send an info log message using the current LOG_TAG.
+ */
+#ifndef ALOGI
+#define ALOGI(...) ((void)ALOG(LOG_INFO, LOG_TAG, __VA_ARGS__))
+#endif
+
+#ifndef ALOGI_IF
+#define ALOGI_IF(cond, ...) \
+ ( (CONDITION(cond)) \
+ ? ((void)ALOG(LOG_INFO, LOG_TAG, __VA_ARGS__)) \
+ : (void)0 )
+#endif
+
+/*
+ * Simplified macro to send a warning log message using the current LOG_TAG.
+ */
+#ifndef ALOGW
+#define ALOGW(...) ((void)ALOG(LOG_WARN, LOG_TAG, __VA_ARGS__))
+#endif
+
+#ifndef ALOGW_IF
+#define ALOGW_IF(cond, ...) \
+ ( (CONDITION(cond)) \
+ ? ((void)ALOG(LOG_WARN, LOG_TAG, __VA_ARGS__)) \
+ : (void)0 )
+#endif
+
+/*
+ * Simplified macro to send an error log message using the current LOG_TAG.
+ */
+#ifndef ALOGE
+#define ALOGE(...) ((void)ALOG(LOG_ERROR, LOG_TAG, __VA_ARGS__))
+#endif
+
+#ifndef ALOGE_IF
+#define ALOGE_IF(cond, ...) \
+ ( (CONDITION(cond)) \
+ ? ((void)ALOG(LOG_ERROR, LOG_TAG, __VA_ARGS__)) \
+ : (void)0 )
+#endif
+
+// ---------------------------------------------------------------------
+
+/*
+ * Conditional based on whether the current LOG_TAG is enabled at
+ * verbose priority.
+ */
+#ifndef IF_ALOGV
+#if LOG_NDEBUG
+#define IF_ALOGV() if (false)
+#else
+#define IF_ALOGV() IF_ALOG(LOG_VERBOSE, LOG_TAG)
+#endif
+#endif
+
+/*
+ * Conditional based on whether the current LOG_TAG is enabled at
+ * debug priority.
+ */
+#ifndef IF_ALOGD
+#define IF_ALOGD() IF_ALOG(LOG_DEBUG, LOG_TAG)
+#endif
+
+/*
+ * Conditional based on whether the current LOG_TAG is enabled at
+ * info priority.
+ */
+#ifndef IF_ALOGI
+#define IF_ALOGI() IF_ALOG(LOG_INFO, LOG_TAG)
+#endif
+
+/*
+ * Conditional based on whether the current LOG_TAG is enabled at
+ * warn priority.
+ */
+#ifndef IF_ALOGW
+#define IF_ALOGW() IF_ALOG(LOG_WARN, LOG_TAG)
+#endif
+
+/*
+ * Conditional based on whether the current LOG_TAG is enabled at
+ * error priority.
+ */
+#ifndef IF_ALOGE
+#define IF_ALOGE() IF_ALOG(LOG_ERROR, LOG_TAG)
+#endif
+
+
+// ---------------------------------------------------------------------
+
+/*
+ * Simplified macro to send a verbose system log message using the current LOG_TAG.
+ */
+#ifndef SLOGV
+#if LOG_NDEBUG
+#define SLOGV(...) ((void)0)
+#else
+#define SLOGV(...) ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__))
+#endif
+#endif
+
+#define CONDITION(cond) (__builtin_expect((cond)!=0, 0))
+
+#ifndef SLOGV_IF
+#if LOG_NDEBUG
+#define SLOGV_IF(cond, ...) ((void)0)
+#else
+#define SLOGV_IF(cond, ...) \
+ ( (CONDITION(cond)) \
+ ? ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__)) \
+ : (void)0 )
+#endif
+#endif
+
+/*
+ * Simplified macro to send a debug system log message using the current LOG_TAG.
+ */
+#ifndef SLOGD
+#define SLOGD(...) ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__))
+#endif
+
+#ifndef SLOGD_IF
+#define SLOGD_IF(cond, ...) \
+ ( (CONDITION(cond)) \
+ ? ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)) \
+ : (void)0 )
+#endif
+
+/*
+ * Simplified macro to send an info system log message using the current LOG_TAG.
+ */
+#ifndef SLOGI
+#define SLOGI(...) ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__))
+#endif
+
+#ifndef SLOGI_IF
+#define SLOGI_IF(cond, ...) \
+ ( (CONDITION(cond)) \
+ ? ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)) \
+ : (void)0 )
+#endif
+
+/*
+ * Simplified macro to send a warning system log message using the current LOG_TAG.
+ */
+#ifndef SLOGW
+#define SLOGW(...) ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__))
+#endif
+
+#ifndef SLOGW_IF
+#define SLOGW_IF(cond, ...) \
+ ( (CONDITION(cond)) \
+ ? ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__)) \
+ : (void)0 )
+#endif
+
+/*
+ * Simplified macro to send an error system log message using the current LOG_TAG.
+ */
+#ifndef SLOGE
+#define SLOGE(...) ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__))
+#endif
+
+#ifndef SLOGE_IF
+#define SLOGE_IF(cond, ...) \
+ ( (CONDITION(cond)) \
+ ? ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)) \
+ : (void)0 )
+#endif
+
+// ---------------------------------------------------------------------
+
+/*
+ * Simplified macro to send a verbose radio log message using the current LOG_TAG.
+ */
+#ifndef RLOGV
+#if LOG_NDEBUG
+#define RLOGV(...) ((void)0)
+#else
+#define RLOGV(...) ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__))
+#endif
+#endif
+
+#define CONDITION(cond) (__builtin_expect((cond)!=0, 0))
+
+#ifndef RLOGV_IF
+#if LOG_NDEBUG
+#define RLOGV_IF(cond, ...) ((void)0)
+#else
+#define RLOGV_IF(cond, ...) \
+ ( (CONDITION(cond)) \
+ ? ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__)) \
+ : (void)0 )
+#endif
+#endif
+
+/*
+ * Simplified macro to send a debug radio log message using the current LOG_TAG.
+ */
+#ifndef RLOGD
+#define RLOGD(...) ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__))
+#endif
+
+#ifndef RLOGD_IF
+#define RLOGD_IF(cond, ...) \
+ ( (CONDITION(cond)) \
+ ? ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)) \
+ : (void)0 )
+#endif
+
+/*
+ * Simplified macro to send an info radio log message using the current LOG_TAG.
+ */
+#ifndef RLOGI
+#define RLOGI(...) ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__))
+#endif
+
+#ifndef RLOGI_IF
+#define RLOGI_IF(cond, ...) \
+ ( (CONDITION(cond)) \
+ ? ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)) \
+ : (void)0 )
+#endif
+
+/*
+ * Simplified macro to send a warning radio log message using the current LOG_TAG.
+ */
+#ifndef RLOGW
+#define RLOGW(...) ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__))
+#endif
+
+#ifndef RLOGW_IF
+#define RLOGW_IF(cond, ...) \
+ ( (CONDITION(cond)) \
+ ? ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__)) \
+ : (void)0 )
+#endif
+
+/*
+ * Simplified macro to send an error radio log message using the current LOG_TAG.
+ */
+#ifndef RLOGE
+#define RLOGE(...) ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__))
+#endif
+
+#ifndef RLOGE_IF
+#define RLOGE_IF(cond, ...) \
+ ( (CONDITION(cond)) \
+ ? ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)) \
+ : (void)0 )
+#endif
+
+
+// ---------------------------------------------------------------------
+
+/*
+ * Log a fatal error. If the given condition fails, this stops program
+ * execution like a normal assertion, but also generating the given message.
+ * It is NOT stripped from release builds. Note that the condition test
+ * is -inverted- from the normal assert() semantics.
+ */
+#ifndef LOG_ALWAYS_FATAL_IF
+#define LOG_ALWAYS_FATAL_IF(cond, ...) \
+ ( (CONDITION(cond)) \
+ ? ((void)android_printAssert(#cond, LOG_TAG, ## __VA_ARGS__)) \
+ : (void)0 )
+#endif
+
+#ifndef LOG_ALWAYS_FATAL
+#define LOG_ALWAYS_FATAL(...) \
+ ( ((void)android_printAssert(NULL, LOG_TAG, ## __VA_ARGS__)) )
+#endif
+
+/*
+ * Versions of LOG_ALWAYS_FATAL_IF and LOG_ALWAYS_FATAL that
+ * are stripped out of release builds.
+ */
+#if LOG_NDEBUG
+
+#ifndef LOG_FATAL_IF
+#define LOG_FATAL_IF(cond, ...) ((void)0)
+#endif
+#ifndef LOG_FATAL
+#define LOG_FATAL(...) ((void)0)
+#endif
+
+#else
+
+#ifndef LOG_FATAL_IF
+#define LOG_FATAL_IF(cond, ...) LOG_ALWAYS_FATAL_IF(cond, ## __VA_ARGS__)
+#endif
+#ifndef LOG_FATAL
+#define LOG_FATAL(...) LOG_ALWAYS_FATAL(__VA_ARGS__)
+#endif
+
+#endif
+
+/*
+ * Assertion that generates a log message when the assertion fails.
+ * Stripped out of release builds. Uses the current LOG_TAG.
+ */
+#ifndef ALOG_ASSERT
+#define ALOG_ASSERT(cond, ...) LOG_FATAL_IF(!(cond), ## __VA_ARGS__)
+//#define ALOG_ASSERT(cond) LOG_FATAL_IF(!(cond), "Assertion failed: " #cond)
+#endif
+
+// ---------------------------------------------------------------------
+
+/*
+ * Basic log message macro.
+ *
+ * Example:
+ * ALOG(LOG_WARN, NULL, "Failed with error %d", errno);
+ *
+ * The second argument may be NULL or "" to indicate the "global" tag.
+ */
+#ifndef ALOG
+#define ALOG(priority, tag, ...) \
+ LOG_PRI(ANDROID_##priority, tag, __VA_ARGS__)
+#endif
+
+/*
+ * Log macro that allows you to specify a number for the priority.
+ */
+#ifndef LOG_PRI
+#define LOG_PRI(priority, tag, ...) \
+ android_printLog(priority, tag, __VA_ARGS__)
+#endif
+
+/*
+ * Log macro that allows you to pass in a varargs ("args" is a va_list).
+ */
+#ifndef LOG_PRI_VA
+#define LOG_PRI_VA(priority, tag, fmt, args) \
+ android_vprintLog(priority, NULL, tag, fmt, args)
+#endif
+
+/*
+ * Conditional given a desired logging priority and tag.
+ */
+#ifndef IF_ALOG
+#define IF_ALOG(priority, tag) \
+ if (android_testLog(ANDROID_##priority, tag))
+#endif
+
+// ---------------------------------------------------------------------
+
+/*
+ * Event logging.
+ */
+
+/*
+ * Event log entry types. These must match up with the declarations in
+ * java/android/android/util/EventLog.java.
+ */
+typedef enum {
+ EVENT_TYPE_INT = 0,
+ EVENT_TYPE_LONG = 1,
+ EVENT_TYPE_STRING = 2,
+ EVENT_TYPE_LIST = 3,
+} AndroidEventLogType;
+
+
+#ifndef LOG_EVENT_INT
+#define LOG_EVENT_INT(_tag, _value) { \
+ int intBuf = _value; \
+ (void) android_btWriteLog(_tag, EVENT_TYPE_INT, &intBuf, \
+ sizeof(intBuf)); \
+ }
+#endif
+#ifndef LOG_EVENT_LONG
+#define LOG_EVENT_LONG(_tag, _value) { \
+ long long longBuf = _value; \
+ (void) android_btWriteLog(_tag, EVENT_TYPE_LONG, &longBuf, \
+ sizeof(longBuf)); \
+ }
+#endif
+#ifndef LOG_EVENT_STRING
+#define LOG_EVENT_STRING(_tag, _value) \
+ ((void) 0) /* not implemented -- must combine len with string */
+#endif
+/* TODO: something for LIST */
+
+/*
+ * ===========================================================================
+ *
+ * The stuff in the rest of this file should not be used directly.
+ */
+
+#define android_printLog(prio, tag, ...) \
+ __android_log_print(prio, tag, __VA_ARGS__)
+
+#define android_vprintLog(prio, cond, tag, ...) \
+ __android_log_vprint(prio, tag, __VA_ARGS__)
+
+/* XXX Macros to work around syntax errors in places where format string
+ * arg is not passed to ALOG_ASSERT, LOG_ALWAYS_FATAL or LOG_ALWAYS_FATAL_IF
+ * (happens only in debug builds).
+ */
+
+/* Returns 2nd arg. Used to substitute default value if caller's vararg list
+ * is empty.
+ */
+#define __android_second(dummy, second, ...) second
+
+/* If passed multiple args, returns ',' followed by all but 1st arg, otherwise
+ * returns nothing.
+ */
+#define __android_rest(first, ...) , ## __VA_ARGS__
+
+#define android_printAssert(cond, tag, ...) \
+ __android_log_assert(cond, tag, \
+ __android_second(0, ## __VA_ARGS__, NULL) __android_rest(__VA_ARGS__))
+
+#define android_writeLog(prio, tag, text) \
+ __android_log_write(prio, tag, text)
+
+#define android_bWriteLog(tag, payload, len) \
+ __android_log_bwrite(tag, payload, len)
+#define android_btWriteLog(tag, type, payload, len) \
+ __android_log_btwrite(tag, type, payload, len)
+
+// TODO: remove these prototypes and their users
+#define android_testLog(prio, tag) (1)
+#define android_writevLog(vec,num) do{}while(0)
+#define android_write1Log(str,len) do{}while (0)
+#define android_setMinPriority(tag, prio) do{}while(0)
+//#define android_logToCallback(func) do{}while(0)
+#define android_logToFile(tag, file) (0)
+#define android_logToFd(tag, fd) (0)
+
+typedef enum {
+ LOG_ID_MAIN = 0,
+ LOG_ID_RADIO = 1,
+ LOG_ID_EVENTS = 2,
+ LOG_ID_SYSTEM = 3,
+
+ LOG_ID_MAX
+} log_id_t;
+
+/*
+ * Send a simple string to the log.
+ */
+int __android_log_buf_write(int bufID, int prio, const char *tag, const char *text);
+int __android_log_buf_print(int bufID, int prio, const char *tag, const char *fmt, ...);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // _LIBS_CUTILS_LOG_H
diff --git a/media/libstagefright/system/core/include/log/logd.h b/media/libstagefright/system/core/include/log/logd.h
new file mode 100644
index 000000000..379c37334
--- /dev/null
+++ b/media/libstagefright/system/core/include/log/logd.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _ANDROID_CUTILS_LOGD_H
+#define _ANDROID_CUTILS_LOGD_H
+
+/* the stable/frozen log-related definitions have been
+ * moved to this header, which is exposed by the NDK
+ */
+#include <android/log.h>
+
+/* the rest is only used internally by the system */
+#include <time.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdint.h>
+#include <sys/types.h>
+#ifdef HAVE_PTHREADS
+#include <pthread.h>
+#endif
+#include <log/uio.h>
+#include <stdarg.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int __android_log_bwrite(int32_t tag, const void *payload, size_t len);
+int __android_log_btwrite(int32_t tag, char type, const void *payload,
+ size_t len);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _LOGD_H */
diff --git a/media/libstagefright/system/core/include/log/logger.h b/media/libstagefright/system/core/include/log/logger.h
new file mode 100644
index 000000000..04f3fb05b
--- /dev/null
+++ b/media/libstagefright/system/core/include/log/logger.h
@@ -0,0 +1,81 @@
+/* utils/logger.h
+**
+** Copyright 2007, The Android Open Source Project
+**
+** This file is dual licensed. It may be redistributed and/or modified
+** under the terms of the Apache 2.0 License OR version 2 of the GNU
+** General Public License.
+*/
+
+#ifndef _UTILS_LOGGER_H
+#define _UTILS_LOGGER_H
+
+#include <stdint.h>
+
+/*
+ * The userspace structure for version 1 of the logger_entry ABI.
+ * This structure is returned to userspace by the kernel logger
+ * driver unless an upgrade to a newer ABI version is requested.
+ */
+struct logger_entry {
+ uint16_t len; /* length of the payload */
+ uint16_t __pad; /* no matter what, we get 2 bytes of padding */
+ int32_t pid; /* generating process's pid */
+ int32_t tid; /* generating process's tid */
+ int32_t sec; /* seconds since Epoch */
+ int32_t nsec; /* nanoseconds */
+ char msg[0]; /* the entry's payload */
+};
+
+/*
+ * The userspace structure for version 2 of the logger_entry ABI.
+ * This structure is returned to userspace if ioctl(LOGGER_SET_VERSION)
+ * is called with version==2
+ */
+struct logger_entry_v2 {
+ uint16_t len; /* length of the payload */
+ uint16_t hdr_size; /* sizeof(struct logger_entry_v2) */
+ int32_t pid; /* generating process's pid */
+ int32_t tid; /* generating process's tid */
+ int32_t sec; /* seconds since Epoch */
+ int32_t nsec; /* nanoseconds */
+ uint32_t euid; /* effective UID of logger */
+ char msg[0]; /* the entry's payload */
+};
+
+#define LOGGER_LOG_MAIN "log/main"
+#define LOGGER_LOG_RADIO "log/radio"
+#define LOGGER_LOG_EVENTS "log/events"
+#define LOGGER_LOG_SYSTEM "log/system"
+
+/*
+ * The maximum size of the log entry payload that can be
+ * written to the kernel logger driver. An attempt to write
+ * more than this amount to /dev/log/* will result in a
+ * truncated log entry.
+ */
+#define LOGGER_ENTRY_MAX_PAYLOAD 4076
+
+/*
+ * The maximum size of a log entry which can be read from the
+ * kernel logger driver. An attempt to read less than this amount
+ * may result in read() returning EINVAL.
+ */
+#define LOGGER_ENTRY_MAX_LEN (5*1024)
+
+#ifdef HAVE_IOCTL
+
+#include <sys/ioctl.h>
+
+#define __LOGGERIO 0xAE
+
+#define LOGGER_GET_LOG_BUF_SIZE _IO(__LOGGERIO, 1) /* size of log */
+#define LOGGER_GET_LOG_LEN _IO(__LOGGERIO, 2) /* used log len */
+#define LOGGER_GET_NEXT_ENTRY_LEN _IO(__LOGGERIO, 3) /* next entry len */
+#define LOGGER_FLUSH_LOG _IO(__LOGGERIO, 4) /* flush log */
+#define LOGGER_GET_VERSION _IO(__LOGGERIO, 5) /* abi version */
+#define LOGGER_SET_VERSION _IO(__LOGGERIO, 6) /* abi version */
+
+#endif // HAVE_IOCTL
+
+#endif /* _UTILS_LOGGER_H */
diff --git a/media/libstagefright/system/core/include/log/logprint.h b/media/libstagefright/system/core/include/log/logprint.h
new file mode 100644
index 000000000..9b57e0e78
--- /dev/null
+++ b/media/libstagefright/system/core/include/log/logprint.h
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LOGPRINT_H
+#define _LOGPRINT_H
+
+#include <log/log.h>
+#include <log/logger.h>
+#include <log/event_tag_map.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef enum {
+ FORMAT_OFF = 0,
+ FORMAT_BRIEF,
+ FORMAT_PROCESS,
+ FORMAT_TAG,
+ FORMAT_THREAD,
+ FORMAT_RAW,
+ FORMAT_TIME,
+ FORMAT_THREADTIME,
+ FORMAT_LONG,
+} AndroidLogPrintFormat;
+
+typedef struct AndroidLogFormat_t AndroidLogFormat;
+
+typedef struct AndroidLogEntry_t {
+ time_t tv_sec;
+ long tv_nsec;
+ android_LogPriority priority;
+ int32_t pid;
+ int32_t tid;
+ const char * tag;
+ size_t messageLen;
+ const char * message;
+} AndroidLogEntry;
+
+AndroidLogFormat *android_log_format_new();
+
+void android_log_format_free(AndroidLogFormat *p_format);
+
+void android_log_setPrintFormat(AndroidLogFormat *p_format,
+ AndroidLogPrintFormat format);
+
+/**
+ * Returns FORMAT_OFF on invalid string
+ */
+AndroidLogPrintFormat android_log_formatFromString(const char *s);
+
+/**
+ * filterExpression: a single filter expression
+ * eg "AT:d"
+ *
+ * returns 0 on success and -1 on invalid expression
+ *
+ * Assumes single threaded execution
+ *
+ */
+
+int android_log_addFilterRule(AndroidLogFormat *p_format,
+ const char *filterExpression);
+
+
+/**
+ * filterString: a whitespace-separated set of filter expressions
+ * eg "AT:d *:i"
+ *
+ * returns 0 on success and -1 on invalid expression
+ *
+ * Assumes single threaded execution
+ *
+ */
+
+int android_log_addFilterString(AndroidLogFormat *p_format,
+ const char *filterString);
+
+
+/**
+ * returns 1 if this log line should be printed based on its priority
+ * and tag, and 0 if it should not
+ */
+int android_log_shouldPrintLine (
+ AndroidLogFormat *p_format, const char *tag, android_LogPriority pri);
+
+
+/**
+ * Splits a wire-format buffer into an AndroidLogEntry
+ * entry allocated by caller. Pointers will point directly into buf
+ *
+ * Returns 0 on success and -1 on invalid wire format (entry will be
+ * in unspecified state)
+ */
+int android_log_processLogBuffer(struct logger_entry *buf,
+ AndroidLogEntry *entry);
+
+/**
+ * Like android_log_processLogBuffer, but for binary logs.
+ *
+ * If "map" is non-NULL, it will be used to convert the log tag number
+ * into a string.
+ */
+int android_log_processBinaryLogBuffer(struct logger_entry *buf,
+ AndroidLogEntry *entry, const EventTagMap* map, char* messageBuf,
+ int messageBufLen);
+
+
+/**
+ * Formats a log message into a buffer
+ *
+ * Uses defaultBuffer if it can, otherwise malloc()'s a new buffer
+ * If return value != defaultBuffer, caller must call free()
+ * Returns NULL on malloc error
+ */
+
+char *android_log_formatLogLine (
+ AndroidLogFormat *p_format,
+ char *defaultBuffer,
+ size_t defaultBufferSize,
+ const AndroidLogEntry *p_line,
+ size_t *p_outLength);
+
+
+/**
+ * Either print or do not print log line, based on filter
+ *
+ * Assumes single threaded execution
+ *
+ */
+int android_log_printLogLine(
+ AndroidLogFormat *p_format,
+ int fd,
+ const AndroidLogEntry *entry);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif /*_LOGPRINT_H*/
diff --git a/media/libstagefright/system/core/include/log/uio.h b/media/libstagefright/system/core/include/log/uio.h
new file mode 100644
index 000000000..01a74d26f
--- /dev/null
+++ b/media/libstagefright/system/core/include/log/uio.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2007 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.
+ */
+
+//
+// implementation of sys/uio.h for platforms that don't have it (Win32)
+//
+#ifndef _LIBS_CUTILS_UIO_H
+#define _LIBS_CUTILS_UIO_H
+
+#ifdef HAVE_SYS_UIO_H
+#include <sys/uio.h>
+#else
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stddef.h>
+
+struct iovec {
+ const void* iov_base;
+ size_t iov_len;
+};
+
+extern int readv( int fd, struct iovec* vecs, int count );
+extern int writev( int fd, const struct iovec* vecs, int count );
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* !HAVE_SYS_UIO_H */
+
+#endif /* _LIBS_UTILS_UIO_H */
+
diff --git a/media/libstagefright/system/core/include/system/graphics.h b/media/libstagefright/system/core/include/system/graphics.h
new file mode 100644
index 000000000..be86ae42b
--- /dev/null
+++ b/media/libstagefright/system/core/include/system/graphics.h
@@ -0,0 +1,304 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SYSTEM_CORE_INCLUDE_ANDROID_GRAPHICS_H
+#define SYSTEM_CORE_INCLUDE_ANDROID_GRAPHICS_H
+
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * If the HAL needs to create service threads to handle graphics related
+ * tasks, these threads need to run at HAL_PRIORITY_URGENT_DISPLAY priority
+ * if they can block the main rendering thread in any way.
+ *
+ * the priority of the current thread can be set with:
+ *
+ * #include <sys/resource.h>
+ * setpriority(PRIO_PROCESS, 0, HAL_PRIORITY_URGENT_DISPLAY);
+ *
+ */
+
+#define HAL_PRIORITY_URGENT_DISPLAY (-8)
+
+/**
+ * pixel format definitions
+ */
+
+enum {
+ /*
+ * "linear" color pixel formats:
+ *
+ * The pixel formats below contain sRGB data but are otherwise treated
+ * as linear formats, i.e.: no special operation is performed when
+ * reading or writing into a buffer in one of these formats
+ */
+ HAL_PIXEL_FORMAT_RGBA_8888 = 1,
+ HAL_PIXEL_FORMAT_RGBX_8888 = 2,
+ HAL_PIXEL_FORMAT_RGB_888 = 3,
+ HAL_PIXEL_FORMAT_RGB_565 = 4,
+ HAL_PIXEL_FORMAT_BGRA_8888 = 5,
+
+ /*
+ * sRGB color pixel formats:
+ *
+ * The red, green and blue components are stored in sRGB space, and converted
+ * to linear space when read, using the standard sRGB to linear equation:
+ *
+ * Clinear = Csrgb / 12.92 for Csrgb <= 0.04045
+ * = (Csrgb + 0.055 / 1.055)^2.4 for Csrgb > 0.04045
+ *
+ * When written the inverse transformation is performed:
+ *
+ * Csrgb = 12.92 * Clinear for Clinear <= 0.0031308
+ * = 1.055 * Clinear^(1/2.4) - 0.055 for Clinear > 0.0031308
+ *
+ *
+ * The alpha component, if present, is always stored in linear space and
+ * is left unmodified when read or written.
+ *
+ */
+ HAL_PIXEL_FORMAT_sRGB_A_8888 = 0xC,
+ HAL_PIXEL_FORMAT_sRGB_X_8888 = 0xD,
+
+ /*
+ * 0x100 - 0x1FF
+ *
+ * This range is reserved for pixel formats that are specific to the HAL
+ * implementation. Implementations can use any value in this range to
+ * communicate video pixel formats between their HAL modules. These formats
+ * must not have an alpha channel. Additionally, an EGLimage created from a
+ * gralloc buffer of one of these formats must be supported for use with the
+ * GL_OES_EGL_image_external OpenGL ES extension.
+ */
+
+ /*
+ * Android YUV format:
+ *
+ * This format is exposed outside of the HAL to software decoders and
+ * applications. EGLImageKHR must support it in conjunction with the
+ * OES_EGL_image_external extension.
+ *
+ * YV12 is a 4:2:0 YCrCb planar format comprised of a WxH Y plane followed
+ * by (W/2) x (H/2) Cr and Cb planes.
+ *
+ * This format assumes
+ * - an even width
+ * - an even height
+ * - a horizontal stride multiple of 16 pixels
+ * - a vertical stride equal to the height
+ *
+ * y_size = stride * height
+ * c_stride = ALIGN(stride/2, 16)
+ * c_size = c_stride * height/2
+ * size = y_size + c_size * 2
+ * cr_offset = y_size
+ * cb_offset = y_size + c_size
+ *
+ */
+ HAL_PIXEL_FORMAT_YV12 = 0x32315659, // YCrCb 4:2:0 Planar
+
+
+ /*
+ * Android Y8 format:
+ *
+ * This format is exposed outside of the HAL to the framework.
+ * The expected gralloc usage flags are SW_* and HW_CAMERA_*,
+ * and no other HW_ flags will be used.
+ *
+ * Y8 is a YUV planar format comprised of a WxH Y plane,
+ * with each pixel being represented by 8 bits.
+ *
+ * It is equivalent to just the Y plane from YV12.
+ *
+ * This format assumes
+ * - an even width
+ * - an even height
+ * - a horizontal stride multiple of 16 pixels
+ * - a vertical stride equal to the height
+ *
+ * size = stride * height
+ *
+ */
+ HAL_PIXEL_FORMAT_Y8 = 0x20203859,
+
+ /*
+ * Android Y16 format:
+ *
+ * This format is exposed outside of the HAL to the framework.
+ * The expected gralloc usage flags are SW_* and HW_CAMERA_*,
+ * and no other HW_ flags will be used.
+ *
+ * Y16 is a YUV planar format comprised of a WxH Y plane,
+ * with each pixel being represented by 16 bits.
+ *
+ * It is just like Y8, but has double the bits per pixel (little endian).
+ *
+ * This format assumes
+ * - an even width
+ * - an even height
+ * - a horizontal stride multiple of 16 pixels
+ * - a vertical stride equal to the height
+ * - strides are specified in pixels, not in bytes
+ *
+ * size = stride * height * 2
+ *
+ */
+ HAL_PIXEL_FORMAT_Y16 = 0x20363159,
+
+ /*
+ * Android RAW sensor format:
+ *
+ * This format is exposed outside of the HAL to applications.
+ *
+ * RAW_SENSOR is a single-channel 16-bit format, typically representing raw
+ * Bayer-pattern images from an image sensor, with minimal processing.
+ *
+ * The exact pixel layout of the data in the buffer is sensor-dependent, and
+ * needs to be queried from the camera device.
+ *
+ * Generally, not all 16 bits are used; more common values are 10 or 12
+ * bits. All parameters to interpret the raw data (black and white points,
+ * color space, etc) must be queried from the camera device.
+ *
+ * This format assumes
+ * - an even width
+ * - an even height
+ * - a horizontal stride multiple of 16 pixels (32 bytes).
+ */
+ HAL_PIXEL_FORMAT_RAW_SENSOR = 0x20,
+
+ /*
+ * Android binary blob graphics buffer format:
+ *
+ * This format is used to carry task-specific data which does not have a
+ * standard image structure. The details of the format are left to the two
+ * endpoints.
+ *
+ * A typical use case is for transporting JPEG-compressed images from the
+ * Camera HAL to the framework or to applications.
+ *
+ * Buffers of this format must have a height of 1, and width equal to their
+ * size in bytes.
+ */
+ HAL_PIXEL_FORMAT_BLOB = 0x21,
+
+ /*
+ * Android format indicating that the choice of format is entirely up to the
+ * device-specific Gralloc implementation.
+ *
+ * The Gralloc implementation should examine the usage bits passed in when
+ * allocating a buffer with this format, and it should derive the pixel
+ * format from those usage flags. This format will never be used with any
+ * of the GRALLOC_USAGE_SW_* usage flags.
+ *
+ * If a buffer of this format is to be used as an OpenGL ES texture, the
+ * framework will assume that sampling the texture will always return an
+ * alpha value of 1.0 (i.e. the buffer contains only opaque pixel values).
+ *
+ */
+ HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED = 0x22,
+
+ /*
+ * Android flexible YCbCr formats
+ *
+ * This format allows platforms to use an efficient YCbCr/YCrCb buffer
+ * layout, while still describing the buffer layout in a way accessible to
+ * the CPU in a device-independent manner. While called YCbCr, it can be
+ * used to describe formats with either chromatic ordering, as well as
+ * whole planar or semiplanar layouts.
+ *
+ * struct android_ycbcr (below) is the the struct used to describe it.
+ *
+ * This format must be accepted by the gralloc module when
+ * USAGE_HW_CAMERA_WRITE and USAGE_SW_READ_* are set.
+ *
+ * This format is locked for use by gralloc's (*lock_ycbcr) method, and
+ * locking with the (*lock) method will return an error.
+ */
+ HAL_PIXEL_FORMAT_YCbCr_420_888 = 0x23,
+
+ /* Legacy formats (deprecated), used by ImageFormat.java */
+ HAL_PIXEL_FORMAT_YCbCr_422_SP = 0x10, // NV16
+ HAL_PIXEL_FORMAT_YCrCb_420_SP = 0x11, // NV21
+ HAL_PIXEL_FORMAT_YCbCr_422_I = 0x14, // YUY2
+};
+
+/*
+ * Structure for describing YCbCr formats for consumption by applications.
+ * This is used with HAL_PIXEL_FORMAT_YCbCr_*_888.
+ *
+ * Buffer chroma subsampling is defined in the format.
+ * e.g. HAL_PIXEL_FORMAT_YCbCr_420_888 has subsampling 4:2:0.
+ *
+ * Buffers must have a 8 bit depth.
+ *
+ * @y, @cb, and @cr point to the first byte of their respective planes.
+ *
+ * Stride describes the distance in bytes from the first value of one row of
+ * the image to the first value of the next row. It includes the width of the
+ * image plus padding.
+ * @ystride is the stride of the luma plane.
+ * @cstride is the stride of the chroma planes.
+ *
+ * @chroma_step is the distance in bytes from one chroma pixel value to the
+ * next. This is 2 bytes for semiplanar (because chroma values are interleaved
+ * and each chroma value is one byte) and 1 for planar.
+ */
+
+struct android_ycbcr {
+ void *y;
+ void *cb;
+ void *cr;
+ size_t ystride;
+ size_t cstride;
+ size_t chroma_step;
+
+ /** reserved for future use, set to 0 by gralloc's (*lock_ycbcr)() */
+ uint32_t reserved[8];
+};
+
+/**
+ * Transformation definitions
+ *
+ * IMPORTANT NOTE:
+ * HAL_TRANSFORM_ROT_90 is applied CLOCKWISE and AFTER HAL_TRANSFORM_FLIP_{H|V}.
+ *
+ */
+
+enum {
+ /* flip source image horizontally (around the vertical axis) */
+ HAL_TRANSFORM_FLIP_H = 0x01,
+ /* flip source image vertically (around the horizontal axis)*/
+ HAL_TRANSFORM_FLIP_V = 0x02,
+ /* rotate source image 90 degrees clockwise */
+ HAL_TRANSFORM_ROT_90 = 0x04,
+ /* rotate source image 180 degrees */
+ HAL_TRANSFORM_ROT_180 = 0x03,
+ /* rotate source image 270 degrees clockwise */
+ HAL_TRANSFORM_ROT_270 = 0x07,
+ /* don't use. see system/window.h */
+ HAL_TRANSFORM_RESERVED = 0x08,
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* SYSTEM_CORE_INCLUDE_ANDROID_GRAPHICS_H */
diff --git a/media/libstagefright/system/core/include/sysutils/List.h b/media/libstagefright/system/core/include/sysutils/List.h
new file mode 100644
index 000000000..72a9c4460
--- /dev/null
+++ b/media/libstagefright/system/core/include/sysutils/List.h
@@ -0,0 +1,334 @@
+/*
+ * Copyright (C) 2005 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.
+ */
+
+//
+// Templated list class. Normally we'd use STL, but we don't have that.
+// This class mimics STL's interfaces.
+//
+// Objects are copied into the list with the '=' operator or with copy-
+// construction, so if the compiler's auto-generated versions won't work for
+// you, define your own.
+//
+// The only class you want to use from here is "List".
+//
+#ifndef _SYSUTILS_LIST_H
+#define _SYSUTILS_LIST_H
+
+#include <stddef.h>
+#include <stdint.h>
+
+namespace stagefright {
+namespace sysutils {
+
+/*
+ * Doubly-linked list. Instantiate with "List<MyClass> myList".
+ *
+ * Objects added to the list are copied using the assignment operator,
+ * so this must be defined.
+ */
+template<typename T>
+class List
+{
+protected:
+ /*
+ * One element in the list.
+ */
+ class _Node {
+ public:
+ explicit _Node(const T& val) : mVal(val) {}
+ ~_Node() {}
+ inline T& getRef() { return mVal; }
+ inline const T& getRef() const { return mVal; }
+ inline _Node* getPrev() const { return mpPrev; }
+ inline _Node* getNext() const { return mpNext; }
+ inline void setVal(const T& val) { mVal = val; }
+ inline void setPrev(_Node* ptr) { mpPrev = ptr; }
+ inline void setNext(_Node* ptr) { mpNext = ptr; }
+ private:
+ friend class List;
+ friend class _ListIterator;
+ T mVal;
+ _Node* mpPrev;
+ _Node* mpNext;
+ };
+
+ /*
+ * Iterator for walking through the list.
+ */
+
+ template <typename TYPE>
+ struct CONST_ITERATOR {
+ typedef _Node const * NodePtr;
+ typedef const TYPE Type;
+ };
+
+ template <typename TYPE>
+ struct NON_CONST_ITERATOR {
+ typedef _Node* NodePtr;
+ typedef TYPE Type;
+ };
+
+ template<
+ typename U,
+ template <class> class Constness
+ >
+ class _ListIterator {
+ typedef _ListIterator<U, Constness> _Iter;
+ typedef typename Constness<U>::NodePtr _NodePtr;
+ typedef typename Constness<U>::Type _Type;
+
+ explicit _ListIterator(_NodePtr ptr) : mpNode(ptr) {}
+
+ public:
+ _ListIterator() {}
+ _ListIterator(const _Iter& rhs) : mpNode(rhs.mpNode) {}
+ ~_ListIterator() {}
+
+ // this will handle conversions from iterator to const_iterator
+ // (and also all convertible iterators)
+ // Here, in this implementation, the iterators can be converted
+ // if the nodes can be converted
+ template<typename V> explicit
+ _ListIterator(const V& rhs) : mpNode(rhs.mpNode) {}
+
+
+ /*
+ * Dereference operator. Used to get at the juicy insides.
+ */
+ _Type& operator*() const { return mpNode->getRef(); }
+ _Type* operator->() const { return &(mpNode->getRef()); }
+
+ /*
+ * Iterator comparison.
+ */
+ inline bool operator==(const _Iter& right) const {
+ return mpNode == right.mpNode; }
+
+ inline bool operator!=(const _Iter& right) const {
+ return mpNode != right.mpNode; }
+
+ /*
+ * handle comparisons between iterator and const_iterator
+ */
+ template<typename OTHER>
+ inline bool operator==(const OTHER& right) const {
+ return mpNode == right.mpNode; }
+
+ template<typename OTHER>
+ inline bool operator!=(const OTHER& right) const {
+ return mpNode != right.mpNode; }
+
+ /*
+ * Incr/decr, used to move through the list.
+ */
+ inline _Iter& operator++() { // pre-increment
+ mpNode = mpNode->getNext();
+ return *this;
+ }
+ const _Iter operator++(int) { // post-increment
+ _Iter tmp(*this);
+ mpNode = mpNode->getNext();
+ return tmp;
+ }
+ inline _Iter& operator--() { // pre-increment
+ mpNode = mpNode->getPrev();
+ return *this;
+ }
+ const _Iter operator--(int) { // post-increment
+ _Iter tmp(*this);
+ mpNode = mpNode->getPrev();
+ return tmp;
+ }
+
+ inline _NodePtr getNode() const { return mpNode; }
+
+ _NodePtr mpNode; /* should be private, but older gcc fails */
+ private:
+ friend class List;
+ };
+
+public:
+ List() {
+ prep();
+ }
+ List(const List<T>& src) { // copy-constructor
+ prep();
+ insert(begin(), src.begin(), src.end());
+ }
+ virtual ~List() {
+ clear();
+ delete[] (unsigned char*) mpMiddle;
+ }
+
+ typedef _ListIterator<T, NON_CONST_ITERATOR> iterator;
+ typedef _ListIterator<T, CONST_ITERATOR> const_iterator;
+
+ List<T>& operator=(const List<T>& right);
+
+ /* returns true if the list is empty */
+ inline bool empty() const { return mpMiddle->getNext() == mpMiddle; }
+
+ /* return #of elements in list */
+ size_t size() const {
+ return size_t(distance(begin(), end()));
+ }
+
+ /*
+ * Return the first element or one past the last element. The
+ * _Node* we're returning is converted to an "iterator" by a
+ * constructor in _ListIterator.
+ */
+ inline iterator begin() {
+ return iterator(mpMiddle->getNext());
+ }
+ inline const_iterator begin() const {
+ return const_iterator(const_cast<_Node const*>(mpMiddle->getNext()));
+ }
+ inline iterator end() {
+ return iterator(mpMiddle);
+ }
+ inline const_iterator end() const {
+ return const_iterator(const_cast<_Node const*>(mpMiddle));
+ }
+
+ /* add the object to the head or tail of the list */
+ void push_front(const T& val) { insert(begin(), val); }
+ void push_back(const T& val) { insert(end(), val); }
+
+ /* insert before the current node; returns iterator at new node */
+ iterator insert(iterator posn, const T& val)
+ {
+ _Node* newNode = new _Node(val); // alloc & copy-construct
+ newNode->setNext(posn.getNode());
+ newNode->setPrev(posn.getNode()->getPrev());
+ posn.getNode()->getPrev()->setNext(newNode);
+ posn.getNode()->setPrev(newNode);
+ return iterator(newNode);
+ }
+
+ /* insert a range of elements before the current node */
+ void insert(iterator posn, const_iterator first, const_iterator last) {
+ for ( ; first != last; ++first)
+ insert(posn, *first);
+ }
+
+ /* remove one entry; returns iterator at next node */
+ iterator erase(iterator posn) {
+ _Node* pNext = posn.getNode()->getNext();
+ _Node* pPrev = posn.getNode()->getPrev();
+ pPrev->setNext(pNext);
+ pNext->setPrev(pPrev);
+ delete posn.getNode();
+ return iterator(pNext);
+ }
+
+ /* remove a range of elements */
+ iterator erase(iterator first, iterator last) {
+ while (first != last)
+ erase(first++); // don't erase than incr later!
+ return iterator(last);
+ }
+
+ /* remove all contents of the list */
+ void clear() {
+ _Node* pCurrent = mpMiddle->getNext();
+ _Node* pNext;
+
+ while (pCurrent != mpMiddle) {
+ pNext = pCurrent->getNext();
+ delete pCurrent;
+ pCurrent = pNext;
+ }
+ mpMiddle->setPrev(mpMiddle);
+ mpMiddle->setNext(mpMiddle);
+ }
+
+ /*
+ * Measure the distance between two iterators. On exist, "first"
+ * will be equal to "last". The iterators must refer to the same
+ * list.
+ *
+ * FIXME: This is actually a generic iterator function. It should be a
+ * template function at the top-level with specializations for things like
+ * vector<>, which can just do pointer math). Here we limit it to
+ * _ListIterator of the same type but different constness.
+ */
+ template<
+ typename U,
+ template <class> class CL,
+ template <class> class CR
+ >
+ ptrdiff_t distance(
+ _ListIterator<U, CL> first, _ListIterator<U, CR> last) const
+ {
+ ptrdiff_t count = 0;
+ while (first != last) {
+ ++first;
+ ++count;
+ }
+ return count;
+ }
+
+private:
+ /*
+ * I want a _Node but don't need it to hold valid data. More
+ * to the point, I don't want T's constructor to fire, since it
+ * might have side-effects or require arguments. So, we do this
+ * slightly uncouth storage alloc.
+ */
+ void prep() {
+ mpMiddle = (_Node*) new unsigned char[sizeof(_Node)];
+ mpMiddle->setPrev(mpMiddle);
+ mpMiddle->setNext(mpMiddle);
+ }
+
+ /*
+ * This node plays the role of "pointer to head" and "pointer to tail".
+ * It sits in the middle of a circular list of nodes. The iterator
+ * runs around the circle until it encounters this one.
+ */
+ _Node* mpMiddle;
+};
+
+/*
+ * Assignment operator.
+ *
+ * The simplest way to do this would be to clear out the target list and
+ * fill it with the source. However, we can speed things along by
+ * re-using existing elements.
+ */
+template<class T>
+List<T>& List<T>::operator=(const List<T>& right)
+{
+ if (this == &right)
+ return *this; // self-assignment
+ iterator firstDst = begin();
+ iterator lastDst = end();
+ const_iterator firstSrc = right.begin();
+ const_iterator lastSrc = right.end();
+ while (firstSrc != lastSrc && firstDst != lastDst)
+ *firstDst++ = *firstSrc++;
+ if (firstSrc == lastSrc) // ran out of elements in source?
+ erase(firstDst, lastDst); // yes, erase any extras
+ else
+ insert(lastDst, firstSrc, lastSrc); // copy remaining over
+ return *this;
+}
+
+}; // namespace sysutils
+}; // namespace stagefright
+
+#endif // _SYSUTILS_LIST_H
diff --git a/media/libstagefright/system/core/include/utils/Atomic.h b/media/libstagefright/system/core/include/utils/Atomic.h
new file mode 100644
index 000000000..7eb476c94
--- /dev/null
+++ b/media/libstagefright/system/core/include/utils/Atomic.h
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2005 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_UTILS_ATOMIC_H
+#define ANDROID_UTILS_ATOMIC_H
+
+#include <cutils/atomic.h>
+
+#endif // ANDROID_UTILS_ATOMIC_H
diff --git a/media/libstagefright/system/core/include/utils/CallStack.h b/media/libstagefright/system/core/include/utils/CallStack.h
new file mode 100644
index 000000000..eb52c8b36
--- /dev/null
+++ b/media/libstagefright/system/core/include/utils/CallStack.h
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_CALLSTACK_H
+#define ANDROID_CALLSTACK_H
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <utils/String8.h>
+#include <corkscrew/backtrace.h>
+
+// ---------------------------------------------------------------------------
+
+namespace stagefright {
+
+class CallStack
+{
+public:
+ enum {
+ MAX_DEPTH = 31
+ };
+
+ CallStack();
+ CallStack(const char* logtag, int32_t ignoreDepth=1,
+ int32_t maxDepth=MAX_DEPTH);
+ CallStack(const CallStack& rhs);
+ ~CallStack();
+
+ CallStack& operator = (const CallStack& rhs);
+
+ bool operator == (const CallStack& rhs) const;
+ bool operator != (const CallStack& rhs) const;
+ bool operator < (const CallStack& rhs) const;
+ bool operator >= (const CallStack& rhs) const;
+ bool operator > (const CallStack& rhs) const;
+ bool operator <= (const CallStack& rhs) const;
+
+ const void* operator [] (int index) const;
+
+ void clear();
+
+ void update(int32_t ignoreDepth=1, int32_t maxDepth=MAX_DEPTH);
+
+ // Dump a stack trace to the log using the supplied logtag
+ void dump(const char* logtag, const char* prefix = 0) const;
+
+ // Return a string (possibly very long) containing the complete stack trace
+ String8 toString(const char* prefix = 0) const;
+
+ size_t size() const { return mCount; }
+
+private:
+ size_t mCount;
+ backtrace_frame_t mStack[MAX_DEPTH];
+};
+
+}; // namespace stagefright
+
+
+// ---------------------------------------------------------------------------
+
+#endif // ANDROID_CALLSTACK_H
diff --git a/media/libstagefright/system/core/include/utils/Condition.h b/media/libstagefright/system/core/include/utils/Condition.h
new file mode 100644
index 000000000..e8e7ae976
--- /dev/null
+++ b/media/libstagefright/system/core/include/utils/Condition.h
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBS_UTILS_CONDITION_H
+#define _LIBS_UTILS_CONDITION_H
+
+#include <stdint.h>
+#include <sys/types.h>
+#include <time.h>
+
+#if defined(HAVE_PTHREADS)
+# include <pthread.h>
+#endif
+
+#include <utils/Errors.h>
+#include <utils/Mutex.h>
+#include <utils/Timers.h>
+
+// ---------------------------------------------------------------------------
+namespace stagefright {
+// ---------------------------------------------------------------------------
+
+/*
+ * Condition variable class. The implementation is system-dependent.
+ *
+ * Condition variables are paired up with mutexes. Lock the mutex,
+ * call wait(), then either re-wait() if things aren't quite what you want,
+ * or unlock the mutex and continue. All threads calling wait() must
+ * use the same mutex for a given Condition.
+ */
+class Condition {
+public:
+ enum {
+ PRIVATE = 0,
+ SHARED = 1
+ };
+
+ enum WakeUpType {
+ WAKE_UP_ONE = 0,
+ WAKE_UP_ALL = 1
+ };
+
+ Condition();
+ Condition(int type);
+ ~Condition();
+ // Wait on the condition variable. Lock the mutex before calling.
+ status_t wait(Mutex& mutex);
+ // same with relative timeout
+ status_t waitRelative(Mutex& mutex, nsecs_t reltime);
+ // Signal the condition variable, allowing one thread to continue.
+ void signal();
+ // Signal the condition variable, allowing one or all threads to continue.
+ void signal(WakeUpType type) {
+ if (type == WAKE_UP_ONE) {
+ signal();
+ } else {
+ broadcast();
+ }
+ }
+ // Signal the condition variable, allowing all threads to continue.
+ void broadcast();
+
+private:
+#if defined(HAVE_PTHREADS)
+ pthread_cond_t mCond;
+#else
+ void* mState;
+#endif
+};
+
+// ---------------------------------------------------------------------------
+
+#if defined(HAVE_PTHREADS)
+
+inline Condition::Condition() {
+ pthread_cond_init(&mCond, NULL);
+}
+inline Condition::Condition(int type) {
+ if (type == SHARED) {
+ pthread_condattr_t attr;
+ pthread_condattr_init(&attr);
+ pthread_condattr_setpshared(&attr, PTHREAD_PROCESS_SHARED);
+ pthread_cond_init(&mCond, &attr);
+ pthread_condattr_destroy(&attr);
+ } else {
+ pthread_cond_init(&mCond, NULL);
+ }
+}
+inline Condition::~Condition() {
+ pthread_cond_destroy(&mCond);
+}
+inline status_t Condition::wait(Mutex& mutex) {
+ return -pthread_cond_wait(&mCond, &mutex.mMutex);
+}
+inline status_t Condition::waitRelative(Mutex& mutex, nsecs_t reltime) {
+#if defined(HAVE_PTHREAD_COND_TIMEDWAIT_RELATIVE)
+ struct timespec ts;
+ ts.tv_sec = reltime/1000000000;
+ ts.tv_nsec = reltime%1000000000;
+ return -pthread_cond_timedwait_relative_np(&mCond, &mutex.mMutex, &ts);
+#else // HAVE_PTHREAD_COND_TIMEDWAIT_RELATIVE
+ struct timespec ts;
+#if defined(HAVE_POSIX_CLOCKS)
+ clock_gettime(CLOCK_REALTIME, &ts);
+#else // HAVE_POSIX_CLOCKS
+ // we don't support the clocks here.
+ struct timeval t;
+ gettimeofday(&t, NULL);
+ ts.tv_sec = t.tv_sec;
+ ts.tv_nsec= t.tv_usec*1000;
+#endif // HAVE_POSIX_CLOCKS
+ ts.tv_sec += reltime/1000000000;
+ ts.tv_nsec+= reltime%1000000000;
+ if (ts.tv_nsec >= 1000000000) {
+ ts.tv_nsec -= 1000000000;
+ ts.tv_sec += 1;
+ }
+ return -pthread_cond_timedwait(&mCond, &mutex.mMutex, &ts);
+#endif // HAVE_PTHREAD_COND_TIMEDWAIT_RELATIVE
+}
+inline void Condition::signal() {
+ pthread_cond_signal(&mCond);
+}
+inline void Condition::broadcast() {
+ pthread_cond_broadcast(&mCond);
+}
+
+#else
+
+inline Condition::Condition() {}
+inline Condition::Condition(int type) {}
+inline Condition::~Condition() {}
+inline status_t Condition::wait(Mutex& mutex) { return OK; }
+inline status_t Condition::waitRelative(Mutex& mutex, nsecs_t reltime) {
+ return OK;
+}
+inline void Condition::signal() {}
+inline void Condition::broadcast() {}
+
+#endif // HAVE_PTHREADS
+
+// ---------------------------------------------------------------------------
+}; // namespace stagefright
+// ---------------------------------------------------------------------------
+
+#endif // _LIBS_UTILS_CONDITON_H
diff --git a/media/libstagefright/system/core/include/utils/Debug.h b/media/libstagefright/system/core/include/utils/Debug.h
new file mode 100644
index 000000000..41f09ebb7
--- /dev/null
+++ b/media/libstagefright/system/core/include/utils/Debug.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2005 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_UTILS_DEBUG_H
+#define ANDROID_UTILS_DEBUG_H
+
+#include <stdint.h>
+#include <sys/types.h>
+
+namespace stagefright {
+// ---------------------------------------------------------------------------
+
+#ifdef __cplusplus
+template<bool> struct CompileTimeAssert;
+template<> struct CompileTimeAssert<true> {};
+#define COMPILE_TIME_ASSERT(_exp) \
+ template class CompileTimeAssert< (_exp) >;
+#endif
+#define COMPILE_TIME_ASSERT_FUNCTION_SCOPE(_exp) \
+ CompileTimeAssert<( _exp )>();
+
+// ---------------------------------------------------------------------------
+
+#ifdef __cplusplus
+template<bool C, typename LSH, typename RHS> struct CompileTimeIfElse;
+template<typename LHS, typename RHS>
+struct CompileTimeIfElse<true, LHS, RHS> { typedef LHS TYPE; };
+template<typename LHS, typename RHS>
+struct CompileTimeIfElse<false, LHS, RHS> { typedef RHS TYPE; };
+#endif
+
+// ---------------------------------------------------------------------------
+}; // namespace stagefright
+
+#endif // ANDROID_UTILS_DEBUG_H
diff --git a/media/libstagefright/system/core/include/utils/Errors.h b/media/libstagefright/system/core/include/utils/Errors.h
new file mode 100644
index 000000000..0ceda595a
--- /dev/null
+++ b/media/libstagefright/system/core/include/utils/Errors.h
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_ERRORS_H
+#define ANDROID_ERRORS_H
+
+#include <sys/types.h>
+#include <errno.h>
+
+namespace stagefright {
+
+// use this type to return error codes
+#ifdef HAVE_MS_C_RUNTIME
+typedef int status_t;
+#else
+typedef int32_t status_t;
+#endif
+
+/* the MS C runtime lacks a few error codes */
+
+/*
+ * Error codes.
+ * All error codes are negative values.
+ */
+
+// Win32 #defines NO_ERROR as well. It has the same value, so there's no
+// real conflict, though it's a bit awkward.
+#ifdef _WIN32
+# undef NO_ERROR
+#endif
+
+enum {
+ OK = 0, // Everything's swell.
+ NO_ERROR = 0, // No errors.
+
+ UNKNOWN_ERROR = 0x80000000,
+
+ NO_MEMORY = -ENOMEM,
+ INVALID_OPERATION = -ENOSYS,
+ BAD_VALUE = -EINVAL,
+ BAD_TYPE = 0x80000001,
+ NAME_NOT_FOUND = -ENOENT,
+ PERMISSION_DENIED = -EPERM,
+ NO_INIT = -ENODEV,
+ ALREADY_EXISTS = -EEXIST,
+ DEAD_OBJECT = -EPIPE,
+ FAILED_TRANSACTION = 0x80000002,
+ JPARKS_BROKE_IT = -EPIPE,
+#if !defined(HAVE_MS_C_RUNTIME)
+ BAD_INDEX = -EOVERFLOW,
+ NOT_ENOUGH_DATA = -ENODATA,
+ WOULD_BLOCK = -EWOULDBLOCK,
+ TIMED_OUT = -ETIMEDOUT,
+ UNKNOWN_TRANSACTION = -EBADMSG,
+#else
+ BAD_INDEX = -E2BIG,
+ NOT_ENOUGH_DATA = 0x80000003,
+ WOULD_BLOCK = 0x80000004,
+ TIMED_OUT = 0x80000005,
+ UNKNOWN_TRANSACTION = 0x80000006,
+#endif
+ FDS_NOT_ALLOWED = 0x80000007,
+};
+
+// Restore define; enumeration is in "android" namespace, so the value defined
+// there won't work for Win32 code in a different namespace.
+#ifdef _WIN32
+# define NO_ERROR 0L
+#endif
+
+}; // namespace stagefright
+
+// ---------------------------------------------------------------------------
+
+#endif // ANDROID_ERRORS_H
diff --git a/media/libstagefright/system/core/include/utils/KeyedVector.h b/media/libstagefright/system/core/include/utils/KeyedVector.h
new file mode 100644
index 000000000..ca4bfd68a
--- /dev/null
+++ b/media/libstagefright/system/core/include/utils/KeyedVector.h
@@ -0,0 +1,224 @@
+/*
+ * Copyright (C) 2005 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_KEYED_VECTOR_H
+#define ANDROID_KEYED_VECTOR_H
+
+#include <assert.h>
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <cutils/log.h>
+
+#include <utils/SortedVector.h>
+#include <utils/TypeHelpers.h>
+#include <utils/Errors.h>
+
+// ---------------------------------------------------------------------------
+
+namespace stagefright {
+
+template <typename KEY, typename VALUE>
+class KeyedVector
+{
+public:
+ typedef KEY key_type;
+ typedef VALUE value_type;
+
+ inline KeyedVector();
+
+ /*
+ * empty the vector
+ */
+
+ inline void clear() { mVector.clear(); }
+
+ /*!
+ * vector stats
+ */
+
+ //! returns number of items in the vector
+ inline size_t size() const { return mVector.size(); }
+ //! returns whether or not the vector is empty
+ inline bool isEmpty() const { return mVector.isEmpty(); }
+ //! returns how many items can be stored without reallocating the backing store
+ inline size_t capacity() const { return mVector.capacity(); }
+ //! sets the capacity. capacity can never be reduced less than size()
+ inline ssize_t setCapacity(size_t size) { return mVector.setCapacity(size); }
+
+ // returns true if the arguments is known to be identical to this vector
+ inline bool isIdenticalTo(const KeyedVector& rhs) const;
+
+ /*!
+ * accessors
+ */
+ const VALUE& valueFor(const KEY& key) const;
+ const VALUE& valueAt(size_t index) const;
+ const KEY& keyAt(size_t index) const;
+ ssize_t indexOfKey(const KEY& key) const;
+ const VALUE& operator[] (size_t index) const;
+
+ /*!
+ * modifying the array
+ */
+
+ VALUE& editValueFor(const KEY& key);
+ VALUE& editValueAt(size_t index);
+
+ /*!
+ * add/insert/replace items
+ */
+
+ ssize_t add(const KEY& key, const VALUE& item);
+ ssize_t replaceValueFor(const KEY& key, const VALUE& item);
+ ssize_t replaceValueAt(size_t index, const VALUE& item);
+
+ /*!
+ * remove items
+ */
+
+ ssize_t removeItem(const KEY& key);
+ ssize_t removeItemsAt(size_t index, size_t count = 1);
+
+private:
+ SortedVector< key_value_pair_t<KEY, VALUE> > mVector;
+};
+
+// KeyedVector<KEY, VALUE> can be trivially moved using memcpy() because its
+// underlying SortedVector can be trivially moved.
+template<typename KEY, typename VALUE> struct trait_trivial_move<KeyedVector<KEY, VALUE> > {
+ enum { value = trait_trivial_move<SortedVector< key_value_pair_t<KEY, VALUE> > >::value };
+};
+
+
+// ---------------------------------------------------------------------------
+
+/**
+ * Variation of KeyedVector that holds a default value to return when
+ * valueFor() is called with a key that doesn't exist.
+ */
+template <typename KEY, typename VALUE>
+class DefaultKeyedVector : public KeyedVector<KEY, VALUE>
+{
+public:
+ inline DefaultKeyedVector(const VALUE& defValue = VALUE());
+ const VALUE& valueFor(const KEY& key) const;
+
+private:
+ VALUE mDefault;
+};
+
+// ---------------------------------------------------------------------------
+
+template<typename KEY, typename VALUE> inline
+KeyedVector<KEY,VALUE>::KeyedVector()
+{
+}
+
+template<typename KEY, typename VALUE> inline
+bool KeyedVector<KEY,VALUE>::isIdenticalTo(const KeyedVector<KEY,VALUE>& rhs) const {
+ return mVector.array() == rhs.mVector.array();
+}
+
+template<typename KEY, typename VALUE> inline
+ssize_t KeyedVector<KEY,VALUE>::indexOfKey(const KEY& key) const {
+ return mVector.indexOf( key_value_pair_t<KEY,VALUE>(key) );
+}
+
+template<typename KEY, typename VALUE> inline
+const VALUE& KeyedVector<KEY,VALUE>::valueFor(const KEY& key) const {
+ ssize_t i = this->indexOfKey(key);
+ LOG_ALWAYS_FATAL_IF(i<0, "%s: key not found", __PRETTY_FUNCTION__);
+ return mVector.itemAt(i).value;
+}
+
+template<typename KEY, typename VALUE> inline
+const VALUE& KeyedVector<KEY,VALUE>::valueAt(size_t index) const {
+ return mVector.itemAt(index).value;
+}
+
+template<typename KEY, typename VALUE> inline
+const VALUE& KeyedVector<KEY,VALUE>::operator[] (size_t index) const {
+ return valueAt(index);
+}
+
+template<typename KEY, typename VALUE> inline
+const KEY& KeyedVector<KEY,VALUE>::keyAt(size_t index) const {
+ return mVector.itemAt(index).key;
+}
+
+template<typename KEY, typename VALUE> inline
+VALUE& KeyedVector<KEY,VALUE>::editValueFor(const KEY& key) {
+ ssize_t i = this->indexOfKey(key);
+ LOG_ALWAYS_FATAL_IF(i<0, "%s: key not found", __PRETTY_FUNCTION__);
+ return mVector.editItemAt(i).value;
+}
+
+template<typename KEY, typename VALUE> inline
+VALUE& KeyedVector<KEY,VALUE>::editValueAt(size_t index) {
+ return mVector.editItemAt(index).value;
+}
+
+template<typename KEY, typename VALUE> inline
+ssize_t KeyedVector<KEY,VALUE>::add(const KEY& key, const VALUE& value) {
+ return mVector.add( key_value_pair_t<KEY,VALUE>(key, value) );
+}
+
+template<typename KEY, typename VALUE> inline
+ssize_t KeyedVector<KEY,VALUE>::replaceValueFor(const KEY& key, const VALUE& value) {
+ key_value_pair_t<KEY,VALUE> pair(key, value);
+ mVector.remove(pair);
+ return mVector.add(pair);
+}
+
+template<typename KEY, typename VALUE> inline
+ssize_t KeyedVector<KEY,VALUE>::replaceValueAt(size_t index, const VALUE& item) {
+ if (index<size()) {
+ mVector.editItemAt(index).value = item;
+ return index;
+ }
+ return BAD_INDEX;
+}
+
+template<typename KEY, typename VALUE> inline
+ssize_t KeyedVector<KEY,VALUE>::removeItem(const KEY& key) {
+ return mVector.remove(key_value_pair_t<KEY,VALUE>(key));
+}
+
+template<typename KEY, typename VALUE> inline
+ssize_t KeyedVector<KEY, VALUE>::removeItemsAt(size_t index, size_t count) {
+ return mVector.removeItemsAt(index, count);
+}
+
+// ---------------------------------------------------------------------------
+
+template<typename KEY, typename VALUE> inline
+DefaultKeyedVector<KEY,VALUE>::DefaultKeyedVector(const VALUE& defValue)
+ : mDefault(defValue)
+{
+}
+
+template<typename KEY, typename VALUE> inline
+const VALUE& DefaultKeyedVector<KEY,VALUE>::valueFor(const KEY& key) const {
+ ssize_t i = this->indexOfKey(key);
+ return i >= 0 ? KeyedVector<KEY,VALUE>::valueAt(i) : mDefault;
+}
+
+}; // namespace stagefright
+
+// ---------------------------------------------------------------------------
+
+#endif // ANDROID_KEYED_VECTOR_H
diff --git a/media/libstagefright/system/core/include/utils/List.h b/media/libstagefright/system/core/include/utils/List.h
new file mode 100644
index 000000000..f5c110e0f
--- /dev/null
+++ b/media/libstagefright/system/core/include/utils/List.h
@@ -0,0 +1,334 @@
+/*
+ * Copyright (C) 2005 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.
+ */
+
+//
+// Templated list class. Normally we'd use STL, but we don't have that.
+// This class mimics STL's interfaces.
+//
+// Objects are copied into the list with the '=' operator or with copy-
+// construction, so if the compiler's auto-generated versions won't work for
+// you, define your own.
+//
+// The only class you want to use from here is "List".
+//
+#ifndef _LIBS_UTILS_LIST_H
+#define _LIBS_UTILS_LIST_H
+
+#include <stddef.h>
+#include <stdint.h>
+
+namespace stagefright {
+
+/*
+ * Doubly-linked list. Instantiate with "List<MyClass> myList".
+ *
+ * Objects added to the list are copied using the assignment operator,
+ * so this must be defined.
+ */
+template<typename T>
+class List
+{
+protected:
+ /*
+ * One element in the list.
+ */
+ class _Node {
+ public:
+ explicit _Node(const T& val) : mVal(val) {}
+ ~_Node() {}
+ inline T& getRef() { return mVal; }
+ inline const T& getRef() const { return mVal; }
+ inline _Node* getPrev() const { return mpPrev; }
+ inline _Node* getNext() const { return mpNext; }
+ inline void setVal(const T& val) { mVal = val; }
+ inline void setPrev(_Node* ptr) { mpPrev = ptr; }
+ inline void setNext(_Node* ptr) { mpNext = ptr; }
+#ifndef _MSC_VER
+ private:
+ friend class List;
+ friend class _ListIterator;
+#endif
+ T mVal;
+ _Node* mpPrev;
+ _Node* mpNext;
+ };
+
+ /*
+ * Iterator for walking through the list.
+ */
+
+ template <typename TYPE>
+ struct CONST_ITERATOR {
+ typedef _Node const * NodePtr;
+ typedef const TYPE Type;
+ };
+
+ template <typename TYPE>
+ struct NON_CONST_ITERATOR {
+ typedef _Node* NodePtr;
+ typedef TYPE Type;
+ };
+
+ template<
+ typename U,
+ template <class> class Constness
+ >
+ class _ListIterator {
+ typedef _ListIterator<U, Constness> _Iter;
+ typedef typename Constness<U>::NodePtr _NodePtr;
+ typedef typename Constness<U>::Type _Type;
+
+ explicit _ListIterator(_NodePtr ptr) : mpNode(ptr) {}
+
+ public:
+ _ListIterator() {}
+ _ListIterator(const _Iter& rhs) : mpNode(rhs.mpNode) {}
+ ~_ListIterator() {}
+
+ // this will handle conversions from iterator to const_iterator
+ // (and also all convertible iterators)
+ // Here, in this implementation, the iterators can be converted
+ // if the nodes can be converted
+ template<typename V> explicit
+ _ListIterator(const V& rhs) : mpNode(rhs.mpNode) {}
+
+
+ /*
+ * Dereference operator. Used to get at the juicy insides.
+ */
+ _Type& operator*() const { return mpNode->getRef(); }
+ _Type* operator->() const { return &(mpNode->getRef()); }
+
+ /*
+ * Iterator comparison.
+ */
+ inline bool operator==(const _Iter& right) const {
+ return mpNode == right.mpNode; }
+
+ inline bool operator!=(const _Iter& right) const {
+ return mpNode != right.mpNode; }
+
+ /*
+ * handle comparisons between iterator and const_iterator
+ */
+ template<typename OTHER>
+ inline bool operator==(const OTHER& right) const {
+ return mpNode == right.mpNode; }
+
+ template<typename OTHER>
+ inline bool operator!=(const OTHER& right) const {
+ return mpNode != right.mpNode; }
+
+ /*
+ * Incr/decr, used to move through the list.
+ */
+ inline _Iter& operator++() { // pre-increment
+ mpNode = mpNode->getNext();
+ return *this;
+ }
+ const _Iter operator++(int) { // post-increment
+ _Iter tmp(*this);
+ mpNode = mpNode->getNext();
+ return tmp;
+ }
+ inline _Iter& operator--() { // pre-increment
+ mpNode = mpNode->getPrev();
+ return *this;
+ }
+ const _Iter operator--(int) { // post-increment
+ _Iter tmp(*this);
+ mpNode = mpNode->getPrev();
+ return tmp;
+ }
+
+ inline _NodePtr getNode() const { return mpNode; }
+
+ _NodePtr mpNode; /* should be private, but older gcc fails */
+ private:
+ friend class List;
+ };
+
+public:
+ List() {
+ prep();
+ }
+ List(const List<T>& src) { // copy-constructor
+ prep();
+ insert(begin(), src.begin(), src.end());
+ }
+ virtual ~List() {
+ clear();
+ delete[] (unsigned char*) mpMiddle;
+ }
+
+ typedef _ListIterator<T, NON_CONST_ITERATOR> iterator;
+ typedef _ListIterator<T, CONST_ITERATOR> const_iterator;
+
+ List<T>& operator=(const List<T>& right);
+
+ /* returns true if the list is empty */
+ inline bool empty() const { return mpMiddle->getNext() == mpMiddle; }
+
+ /* return #of elements in list */
+ size_t size() const {
+ return size_t(distance(begin(), end()));
+ }
+
+ /*
+ * Return the first element or one past the last element. The
+ * _Node* we're returning is converted to an "iterator" by a
+ * constructor in _ListIterator.
+ */
+ inline iterator begin() {
+ return iterator(mpMiddle->getNext());
+ }
+ inline const_iterator begin() const {
+ return const_iterator(const_cast<_Node const*>(mpMiddle->getNext()));
+ }
+ inline iterator end() {
+ return iterator(mpMiddle);
+ }
+ inline const_iterator end() const {
+ return const_iterator(const_cast<_Node const*>(mpMiddle));
+ }
+
+ /* add the object to the head or tail of the list */
+ void push_front(const T& val) { insert(begin(), val); }
+ void push_back(const T& val) { insert(end(), val); }
+
+ /* insert before the current node; returns iterator at new node */
+ iterator insert(iterator posn, const T& val)
+ {
+ _Node* newNode = new _Node(val); // alloc & copy-construct
+ newNode->setNext(posn.getNode());
+ newNode->setPrev(posn.getNode()->getPrev());
+ posn.getNode()->getPrev()->setNext(newNode);
+ posn.getNode()->setPrev(newNode);
+ return iterator(newNode);
+ }
+
+ /* insert a range of elements before the current node */
+ void insert(iterator posn, const_iterator first, const_iterator last) {
+ for ( ; first != last; ++first)
+ insert(posn, *first);
+ }
+
+ /* remove one entry; returns iterator at next node */
+ iterator erase(iterator posn) {
+ _Node* pNext = posn.getNode()->getNext();
+ _Node* pPrev = posn.getNode()->getPrev();
+ pPrev->setNext(pNext);
+ pNext->setPrev(pPrev);
+ delete posn.getNode();
+ return iterator(pNext);
+ }
+
+ /* remove a range of elements */
+ iterator erase(iterator first, iterator last) {
+ while (first != last)
+ erase(first++); // don't erase than incr later!
+ return iterator(last);
+ }
+
+ /* remove all contents of the list */
+ void clear() {
+ _Node* pCurrent = mpMiddle->getNext();
+ _Node* pNext;
+
+ while (pCurrent != mpMiddle) {
+ pNext = pCurrent->getNext();
+ delete pCurrent;
+ pCurrent = pNext;
+ }
+ mpMiddle->setPrev(mpMiddle);
+ mpMiddle->setNext(mpMiddle);
+ }
+
+ /*
+ * Measure the distance between two iterators. On exist, "first"
+ * will be equal to "last". The iterators must refer to the same
+ * list.
+ *
+ * FIXME: This is actually a generic iterator function. It should be a
+ * template function at the top-level with specializations for things like
+ * vector<>, which can just do pointer math). Here we limit it to
+ * _ListIterator of the same type but different constness.
+ */
+ template<
+ typename U,
+ template <class> class CL,
+ template <class> class CR
+ >
+ ptrdiff_t distance(
+ _ListIterator<U, CL> first, _ListIterator<U, CR> last) const
+ {
+ ptrdiff_t count = 0;
+ while (first != last) {
+ ++first;
+ ++count;
+ }
+ return count;
+ }
+
+private:
+ /*
+ * I want a _Node but don't need it to hold valid data. More
+ * to the point, I don't want T's constructor to fire, since it
+ * might have side-effects or require arguments. So, we do this
+ * slightly uncouth storage alloc.
+ */
+ void prep() {
+ mpMiddle = (_Node*) new unsigned char[sizeof(_Node)];
+ mpMiddle->setPrev(mpMiddle);
+ mpMiddle->setNext(mpMiddle);
+ }
+
+ /*
+ * This node plays the role of "pointer to head" and "pointer to tail".
+ * It sits in the middle of a circular list of nodes. The iterator
+ * runs around the circle until it encounters this one.
+ */
+ _Node* mpMiddle;
+};
+
+/*
+ * Assignment operator.
+ *
+ * The simplest way to do this would be to clear out the target list and
+ * fill it with the source. However, we can speed things along by
+ * re-using existing elements.
+ */
+template<class T>
+List<T>& List<T>::operator=(const List<T>& right)
+{
+ if (this == &right)
+ return *this; // self-assignment
+ iterator firstDst = begin();
+ iterator lastDst = end();
+ const_iterator firstSrc = right.begin();
+ const_iterator lastSrc = right.end();
+ while (firstSrc != lastSrc && firstDst != lastDst)
+ *firstDst++ = *firstSrc++;
+ if (firstSrc == lastSrc) // ran out of elements in source?
+ erase(firstDst, lastDst); // yes, erase any extras
+ else
+ insert(lastDst, firstSrc, lastSrc); // copy remaining over
+ return *this;
+}
+
+}; // namespace stagefright
+
+#endif // _LIBS_UTILS_LIST_H
diff --git a/media/libstagefright/system/core/include/utils/Log.h b/media/libstagefright/system/core/include/utils/Log.h
new file mode 100644
index 000000000..97cc4f3cf
--- /dev/null
+++ b/media/libstagefright/system/core/include/utils/Log.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2005 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.
+ */
+
+//
+// C/C++ logging functions. See the logging documentation for API details.
+//
+// We'd like these to be available from C code (in case we import some from
+// somewhere), so this has a C interface.
+//
+// The output will be correct when the log file is shared between multiple
+// threads and/or multiple processes so long as the operating system
+// supports O_APPEND. These calls have mutex-protected data structures
+// and so are NOT reentrant. Do not use LOG in a signal handler.
+//
+#ifndef _LIBS_UTILS_LOG_H
+#define _LIBS_UTILS_LOG_H
+
+#include <cutils/log.h>
+#include <sys/types.h>
+
+#ifdef __cplusplus
+
+namespace stagefright {
+
+/*
+ * A very simple utility that yells in the log when an operation takes too long.
+ */
+class LogIfSlow {
+public:
+ LogIfSlow(const char* tag, android_LogPriority priority,
+ int timeoutMillis, const char* message);
+ ~LogIfSlow();
+
+private:
+ const char* const mTag;
+ const android_LogPriority mPriority;
+ const int mTimeoutMillis;
+ const char* const mMessage;
+ const int64_t mStart;
+};
+
+/*
+ * Writes the specified debug log message if this block takes longer than the
+ * specified number of milliseconds to run. Includes the time actually taken.
+ *
+ * {
+ * ALOGD_IF_SLOW(50, "Excessive delay doing something.");
+ * doSomething();
+ * }
+ */
+#define ALOGD_IF_SLOW(timeoutMillis, message) \
+ stagefright::LogIfSlow _logIfSlow(LOG_TAG, ANDROID_LOG_DEBUG, timeoutMillis, message);
+
+} // namespace stagefright
+
+#endif // __cplusplus
+
+#endif // _LIBS_UTILS_LOG_H
diff --git a/media/libstagefright/system/core/include/utils/Mutex.h b/media/libstagefright/system/core/include/utils/Mutex.h
new file mode 100644
index 000000000..b33efef76
--- /dev/null
+++ b/media/libstagefright/system/core/include/utils/Mutex.h
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBS_UTILS_MUTEX_H
+#define _LIBS_UTILS_MUTEX_H
+
+#include <stdint.h>
+#include <sys/types.h>
+#include <time.h>
+
+#if defined(HAVE_PTHREADS)
+# include <pthread.h>
+#endif
+
+#include <utils/Errors.h>
+
+// ---------------------------------------------------------------------------
+namespace stagefright {
+// ---------------------------------------------------------------------------
+
+class Condition;
+
+/*
+ * Simple mutex class. The implementation is system-dependent.
+ *
+ * The mutex must be unlocked by the thread that locked it. They are not
+ * recursive, i.e. the same thread can't lock it multiple times.
+ */
+class Mutex {
+public:
+ enum {
+ PRIVATE = 0,
+ SHARED = 1
+ };
+
+ Mutex();
+ Mutex(const char* name);
+ Mutex(int type, const char* name = NULL);
+ ~Mutex();
+
+ // lock or unlock the mutex
+ status_t lock();
+ void unlock();
+
+ // lock if possible; returns 0 on success, error otherwise
+ status_t tryLock();
+
+ // Manages the mutex automatically. It'll be locked when Autolock is
+ // constructed and released when Autolock goes out of scope.
+ class Autolock {
+ public:
+ inline Autolock(Mutex& mutex) : mLock(mutex) { mLock.lock(); }
+ inline Autolock(Mutex* mutex) : mLock(*mutex) { mLock.lock(); }
+ inline ~Autolock() { mLock.unlock(); }
+ private:
+ Mutex& mLock;
+ };
+
+private:
+ friend class Condition;
+
+ // A mutex cannot be copied
+ Mutex(const Mutex&);
+ Mutex& operator = (const Mutex&);
+
+#if defined(HAVE_PTHREADS)
+ pthread_mutex_t mMutex;
+#else
+ void _init();
+ void* mState;
+#endif
+};
+
+// ---------------------------------------------------------------------------
+
+#if defined(HAVE_PTHREADS)
+
+inline Mutex::Mutex() {
+ pthread_mutex_init(&mMutex, NULL);
+}
+inline Mutex::Mutex(__attribute__((unused)) const char* name) {
+ pthread_mutex_init(&mMutex, NULL);
+}
+inline Mutex::Mutex(int type, __attribute__((unused)) const char* name) {
+ if (type == SHARED) {
+ pthread_mutexattr_t attr;
+ pthread_mutexattr_init(&attr);
+ pthread_mutexattr_setpshared(&attr, PTHREAD_PROCESS_SHARED);
+ pthread_mutex_init(&mMutex, &attr);
+ pthread_mutexattr_destroy(&attr);
+ } else {
+ pthread_mutex_init(&mMutex, NULL);
+ }
+}
+inline Mutex::~Mutex() {
+ pthread_mutex_destroy(&mMutex);
+}
+inline status_t Mutex::lock() {
+ return -pthread_mutex_lock(&mMutex);
+}
+inline void Mutex::unlock() {
+ pthread_mutex_unlock(&mMutex);
+}
+inline status_t Mutex::tryLock() {
+ return -pthread_mutex_trylock(&mMutex);
+}
+
+#else
+
+inline Mutex::Mutex() {}
+inline Mutex::Mutex(const char* name) {}
+inline Mutex::Mutex(int type, const char* name) {}
+inline Mutex::~Mutex() {}
+inline status_t Mutex::lock() { return OK; }
+inline void Mutex::unlock() {}
+inline status_t Mutex::tryLock() { return OK; }
+inline void Mutex::_init() {}
+
+#endif // HAVE_PTHREADS
+
+// ---------------------------------------------------------------------------
+
+/*
+ * Automatic mutex. Declare one of these at the top of a function.
+ * When the function returns, it will go out of scope, and release the
+ * mutex.
+ */
+
+typedef Mutex::Autolock AutoMutex;
+
+// ---------------------------------------------------------------------------
+}; // namespace stagefright
+// ---------------------------------------------------------------------------
+
+#endif // _LIBS_UTILS_MUTEX_H
diff --git a/media/libstagefright/system/core/include/utils/RWLock.h b/media/libstagefright/system/core/include/utils/RWLock.h
new file mode 100644
index 000000000..4c438272f
--- /dev/null
+++ b/media/libstagefright/system/core/include/utils/RWLock.h
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBS_UTILS_RWLOCK_H
+#define _LIBS_UTILS_RWLOCK_H
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#if defined(HAVE_PTHREADS)
+# include <pthread.h>
+#endif
+
+#include <utils/Errors.h>
+#include <utils/ThreadDefs.h>
+
+// ---------------------------------------------------------------------------
+namespace stagefright {
+// ---------------------------------------------------------------------------
+
+#if defined(HAVE_PTHREADS)
+
+/*
+ * Simple mutex class. The implementation is system-dependent.
+ *
+ * The mutex must be unlocked by the thread that locked it. They are not
+ * recursive, i.e. the same thread can't lock it multiple times.
+ */
+class RWLock {
+public:
+ enum {
+ PRIVATE = 0,
+ SHARED = 1
+ };
+
+ RWLock();
+ RWLock(const char* name);
+ RWLock(int type, const char* name = NULL);
+ ~RWLock();
+
+ status_t readLock();
+ status_t tryReadLock();
+ status_t writeLock();
+ status_t tryWriteLock();
+ void unlock();
+
+ class AutoRLock {
+ public:
+ inline AutoRLock(RWLock& rwlock) : mLock(rwlock) { mLock.readLock(); }
+ inline ~AutoRLock() { mLock.unlock(); }
+ private:
+ RWLock& mLock;
+ };
+
+ class AutoWLock {
+ public:
+ inline AutoWLock(RWLock& rwlock) : mLock(rwlock) { mLock.writeLock(); }
+ inline ~AutoWLock() { mLock.unlock(); }
+ private:
+ RWLock& mLock;
+ };
+
+private:
+ // A RWLock cannot be copied
+ RWLock(const RWLock&);
+ RWLock& operator = (const RWLock&);
+
+ pthread_rwlock_t mRWLock;
+};
+
+inline RWLock::RWLock() {
+ pthread_rwlock_init(&mRWLock, NULL);
+}
+inline RWLock::RWLock(__attribute__((unused)) const char* name) {
+ pthread_rwlock_init(&mRWLock, NULL);
+}
+inline RWLock::RWLock(int type, __attribute__((unused)) const char* name) {
+ if (type == SHARED) {
+ pthread_rwlockattr_t attr;
+ pthread_rwlockattr_init(&attr);
+ pthread_rwlockattr_setpshared(&attr, PTHREAD_PROCESS_SHARED);
+ pthread_rwlock_init(&mRWLock, &attr);
+ pthread_rwlockattr_destroy(&attr);
+ } else {
+ pthread_rwlock_init(&mRWLock, NULL);
+ }
+}
+inline RWLock::~RWLock() {
+ pthread_rwlock_destroy(&mRWLock);
+}
+inline status_t RWLock::readLock() {
+ return -pthread_rwlock_rdlock(&mRWLock);
+}
+inline status_t RWLock::tryReadLock() {
+ return -pthread_rwlock_tryrdlock(&mRWLock);
+}
+inline status_t RWLock::writeLock() {
+ return -pthread_rwlock_wrlock(&mRWLock);
+}
+inline status_t RWLock::tryWriteLock() {
+ return -pthread_rwlock_trywrlock(&mRWLock);
+}
+inline void RWLock::unlock() {
+ pthread_rwlock_unlock(&mRWLock);
+}
+
+#endif // HAVE_PTHREADS
+
+// ---------------------------------------------------------------------------
+}; // namespace stagefright
+// ---------------------------------------------------------------------------
+
+#endif // _LIBS_UTILS_RWLOCK_H
diff --git a/media/libstagefright/system/core/include/utils/RefBase.h b/media/libstagefright/system/core/include/utils/RefBase.h
new file mode 100644
index 000000000..e1f97c9e9
--- /dev/null
+++ b/media/libstagefright/system/core/include/utils/RefBase.h
@@ -0,0 +1,554 @@
+/*
+ * Copyright (C) 2005 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_REF_BASE_H
+#define ANDROID_REF_BASE_H
+
+#include <cutils/atomic.h>
+
+#include <stdint.h>
+#include <sys/types.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <utils/StrongPointer.h>
+#include <utils/TypeHelpers.h>
+
+#ifdef _MSC_VER
+#define __attribute__(X)
+#endif
+
+// ---------------------------------------------------------------------------
+namespace stagefright {
+
+class TextOutput;
+TextOutput& printWeakPointer(TextOutput& to, const void* val);
+
+// ---------------------------------------------------------------------------
+
+#define COMPARE_WEAK(_op_) \
+inline bool operator _op_ (const sp<T>& o) const { \
+ return m_ptr _op_ o.m_ptr; \
+} \
+inline bool operator _op_ (const T* o) const { \
+ return m_ptr _op_ o; \
+} \
+template<typename U> \
+inline bool operator _op_ (const sp<U>& o) const { \
+ return m_ptr _op_ o.m_ptr; \
+} \
+template<typename U> \
+inline bool operator _op_ (const U* o) const { \
+ return m_ptr _op_ o; \
+}
+
+// ---------------------------------------------------------------------------
+
+class ReferenceRenamer {
+protected:
+ // destructor is purposedly not virtual so we avoid code overhead from
+ // subclasses; we have to make it protected to guarantee that it
+ // cannot be called from this base class (and to make strict compilers
+ // happy).
+ ~ReferenceRenamer() { }
+public:
+ virtual void operator()(size_t i) const = 0;
+};
+
+// ---------------------------------------------------------------------------
+
+class RefBase
+{
+public:
+ void incStrong(const void* id) const;
+ void decStrong(const void* id) const;
+
+ void forceIncStrong(const void* id) const;
+
+ //! DEBUGGING ONLY: Get current strong ref count.
+ int32_t getStrongCount() const;
+
+ class weakref_type
+ {
+ public:
+ RefBase* refBase() const;
+
+ void incWeak(const void* id);
+ void decWeak(const void* id);
+
+ // acquires a strong reference if there is already one.
+ bool attemptIncStrong(const void* id);
+
+ // acquires a weak reference if there is already one.
+ // This is not always safe. see ProcessState.cpp and BpBinder.cpp
+ // for proper use.
+ bool attemptIncWeak(const void* id);
+
+ //! DEBUGGING ONLY: Get current weak ref count.
+ int32_t getWeakCount() const;
+
+ //! DEBUGGING ONLY: Print references held on object.
+ void printRefs() const;
+
+ //! DEBUGGING ONLY: Enable tracking for this object.
+ // enable -- enable/disable tracking
+ // retain -- when tracking is enable, if true, then we save a stack trace
+ // for each reference and dereference; when retain == false, we
+ // match up references and dereferences and keep only the
+ // outstanding ones.
+
+ void trackMe(bool enable, bool retain);
+ };
+
+ weakref_type* createWeak(const void* id) const;
+
+ weakref_type* getWeakRefs() const;
+
+ //! DEBUGGING ONLY: Print references held on object.
+ inline void printRefs() const { getWeakRefs()->printRefs(); }
+
+ //! DEBUGGING ONLY: Enable tracking of object.
+ inline void trackMe(bool enable, bool retain)
+ {
+ getWeakRefs()->trackMe(enable, retain);
+ }
+
+ typedef RefBase basetype;
+
+protected:
+ RefBase();
+ virtual ~RefBase();
+
+ //! Flags for extendObjectLifetime()
+ enum {
+ OBJECT_LIFETIME_STRONG = 0x0000,
+ OBJECT_LIFETIME_WEAK = 0x0001,
+ OBJECT_LIFETIME_MASK = 0x0001
+ };
+
+ void extendObjectLifetime(int32_t mode);
+
+ //! Flags for onIncStrongAttempted()
+ enum {
+ FIRST_INC_STRONG = 0x0001
+ };
+
+ virtual void onFirstRef();
+ virtual void onLastStrongRef(const void* id);
+ virtual bool onIncStrongAttempted(uint32_t flags, const void* id);
+ virtual void onLastWeakRef(const void* id);
+
+private:
+ friend class weakref_type;
+ class weakref_impl;
+
+ RefBase(const RefBase& o);
+ RefBase& operator=(const RefBase& o);
+
+private:
+ friend class ReferenceMover;
+
+ static void renameRefs(size_t n, const ReferenceRenamer& renamer);
+
+ static void renameRefId(weakref_type* ref,
+ const void* old_id, const void* new_id);
+
+ static void renameRefId(RefBase* ref,
+ const void* old_id, const void* new_id);
+
+ weakref_impl* const mRefs;
+};
+
+// ---------------------------------------------------------------------------
+
+template <class T>
+class LightRefBase
+{
+public:
+ inline LightRefBase() : mCount(0) { }
+ inline void incStrong(__attribute__((unused)) const void* id) const {
+ android_atomic_inc(&mCount);
+ }
+ inline void decStrong(__attribute__((unused)) const void* id) const {
+ if (android_atomic_dec(&mCount) == 1) {
+ delete static_cast<const T*>(this);
+ }
+ }
+ //! DEBUGGING ONLY: Get current strong ref count.
+ inline int32_t getStrongCount() const {
+ return mCount;
+ }
+
+ typedef LightRefBase<T> basetype;
+
+protected:
+ inline ~LightRefBase() { }
+
+private:
+ friend class ReferenceMover;
+ inline static void renameRefs(size_t n, const ReferenceRenamer& renamer) { }
+ inline static void renameRefId(T* ref,
+ const void* old_id, const void* new_id) { }
+
+private:
+ mutable volatile int32_t mCount;
+};
+
+// ---------------------------------------------------------------------------
+
+template <typename T>
+class wp
+{
+public:
+ typedef typename RefBase::weakref_type weakref_type;
+
+ inline wp() : m_ptr(0) { }
+
+ wp(T* other);
+ wp(const wp<T>& other);
+ wp(const sp<T>& other);
+ template<typename U> wp(U* other);
+ template<typename U> wp(const sp<U>& other);
+ template<typename U> wp(const wp<U>& other);
+
+ ~wp();
+
+ // Assignment
+
+ wp& operator = (T* other);
+ wp& operator = (const wp<T>& other);
+ wp& operator = (const sp<T>& other);
+
+ template<typename U> wp& operator = (U* other);
+ template<typename U> wp& operator = (const wp<U>& other);
+ template<typename U> wp& operator = (const sp<U>& other);
+
+ void set_object_and_refs(T* other, weakref_type* refs);
+
+ // promotion to sp
+
+ sp<T> promote() const;
+
+ // Reset
+
+ void clear();
+
+ // Accessors
+
+ inline weakref_type* get_refs() const { return m_refs; }
+
+ inline T* unsafe_get() const { return m_ptr; }
+
+ // Operators
+
+ COMPARE_WEAK(==)
+ COMPARE_WEAK(!=)
+ COMPARE_WEAK(>)
+ COMPARE_WEAK(<)
+ COMPARE_WEAK(<=)
+ COMPARE_WEAK(>=)
+
+ inline bool operator == (const wp<T>& o) const {
+ return (m_ptr == o.m_ptr) && (m_refs == o.m_refs);
+ }
+ template<typename U>
+ inline bool operator == (const wp<U>& o) const {
+ return m_ptr == o.m_ptr;
+ }
+
+ inline bool operator > (const wp<T>& o) const {
+ return (m_ptr == o.m_ptr) ? (m_refs > o.m_refs) : (m_ptr > o.m_ptr);
+ }
+ template<typename U>
+ inline bool operator > (const wp<U>& o) const {
+ return (m_ptr == o.m_ptr) ? (m_refs > o.m_refs) : (m_ptr > o.m_ptr);
+ }
+
+ inline bool operator < (const wp<T>& o) const {
+ return (m_ptr == o.m_ptr) ? (m_refs < o.m_refs) : (m_ptr < o.m_ptr);
+ }
+ template<typename U>
+ inline bool operator < (const wp<U>& o) const {
+ return (m_ptr == o.m_ptr) ? (m_refs < o.m_refs) : (m_ptr < o.m_ptr);
+ }
+ inline bool operator != (const wp<T>& o) const { return m_refs != o.m_refs; }
+ template<typename U> inline bool operator != (const wp<U>& o) const { return !operator == (o); }
+ inline bool operator <= (const wp<T>& o) const { return !operator > (o); }
+ template<typename U> inline bool operator <= (const wp<U>& o) const { return !operator > (o); }
+ inline bool operator >= (const wp<T>& o) const { return !operator < (o); }
+ template<typename U> inline bool operator >= (const wp<U>& o) const { return !operator < (o); }
+
+private:
+ template<typename Y> friend class sp;
+ template<typename Y> friend class wp;
+
+ T* m_ptr;
+ weakref_type* m_refs;
+};
+
+template <typename T>
+TextOutput& operator<<(TextOutput& to, const wp<T>& val);
+
+#undef COMPARE_WEAK
+
+// ---------------------------------------------------------------------------
+// No user serviceable parts below here.
+
+template<typename T>
+wp<T>::wp(T* other)
+ : m_ptr(other)
+{
+ if (other) m_refs = other->createWeak(this);
+}
+
+template<typename T>
+wp<T>::wp(const wp<T>& other)
+ : m_ptr(other.m_ptr), m_refs(other.m_refs)
+{
+ if (m_ptr) m_refs->incWeak(this);
+}
+
+template<typename T>
+wp<T>::wp(const sp<T>& other)
+ : m_ptr(other.m_ptr)
+{
+ if (m_ptr) {
+ m_refs = m_ptr->createWeak(this);
+ }
+}
+
+template<typename T> template<typename U>
+wp<T>::wp(U* other)
+ : m_ptr(other)
+{
+ if (other) m_refs = other->createWeak(this);
+}
+
+template<typename T> template<typename U>
+wp<T>::wp(const wp<U>& other)
+ : m_ptr(other.m_ptr)
+{
+ if (m_ptr) {
+ m_refs = other.m_refs;
+ m_refs->incWeak(this);
+ }
+}
+
+template<typename T> template<typename U>
+wp<T>::wp(const sp<U>& other)
+ : m_ptr(other.m_ptr)
+{
+ if (m_ptr) {
+ m_refs = m_ptr->createWeak(this);
+ }
+}
+
+template<typename T>
+wp<T>::~wp()
+{
+ if (m_ptr) m_refs->decWeak(this);
+}
+
+template<typename T>
+wp<T>& wp<T>::operator = (T* other)
+{
+ weakref_type* newRefs =
+ other ? other->createWeak(this) : 0;
+ if (m_ptr) m_refs->decWeak(this);
+ m_ptr = other;
+ m_refs = newRefs;
+ return *this;
+}
+
+template<typename T>
+wp<T>& wp<T>::operator = (const wp<T>& other)
+{
+ weakref_type* otherRefs(other.m_refs);
+ T* otherPtr(other.m_ptr);
+ if (otherPtr) otherRefs->incWeak(this);
+ if (m_ptr) m_refs->decWeak(this);
+ m_ptr = otherPtr;
+ m_refs = otherRefs;
+ return *this;
+}
+
+template<typename T>
+wp<T>& wp<T>::operator = (const sp<T>& other)
+{
+ weakref_type* newRefs =
+ other != NULL ? other->createWeak(this) : 0;
+ T* otherPtr(other.m_ptr);
+ if (m_ptr) m_refs->decWeak(this);
+ m_ptr = otherPtr;
+ m_refs = newRefs;
+ return *this;
+}
+
+template<typename T> template<typename U>
+wp<T>& wp<T>::operator = (U* other)
+{
+ weakref_type* newRefs =
+ other ? other->createWeak(this) : 0;
+ if (m_ptr) m_refs->decWeak(this);
+ m_ptr = other;
+ m_refs = newRefs;
+ return *this;
+}
+
+template<typename T> template<typename U>
+wp<T>& wp<T>::operator = (const wp<U>& other)
+{
+ weakref_type* otherRefs(other.m_refs);
+ U* otherPtr(other.m_ptr);
+ if (otherPtr) otherRefs->incWeak(this);
+ if (m_ptr) m_refs->decWeak(this);
+ m_ptr = otherPtr;
+ m_refs = otherRefs;
+ return *this;
+}
+
+template<typename T> template<typename U>
+wp<T>& wp<T>::operator = (const sp<U>& other)
+{
+ weakref_type* newRefs =
+ other != NULL ? other->createWeak(this) : 0;
+ U* otherPtr(other.m_ptr);
+ if (m_ptr) m_refs->decWeak(this);
+ m_ptr = otherPtr;
+ m_refs = newRefs;
+ return *this;
+}
+
+template<typename T>
+void wp<T>::set_object_and_refs(T* other, weakref_type* refs)
+{
+ if (other) refs->incWeak(this);
+ if (m_ptr) m_refs->decWeak(this);
+ m_ptr = other;
+ m_refs = refs;
+}
+
+template<typename T>
+sp<T> wp<T>::promote() const
+{
+ sp<T> result;
+ if (m_ptr && m_refs->attemptIncStrong(&result)) {
+ result.set_pointer(m_ptr);
+ }
+ return result;
+}
+
+template<typename T>
+void wp<T>::clear()
+{
+ if (m_ptr) {
+ m_refs->decWeak(this);
+ m_ptr = 0;
+ }
+}
+
+template <typename T>
+inline TextOutput& operator<<(TextOutput& to, const wp<T>& val)
+{
+ return printWeakPointer(to, val.unsafe_get());
+}
+
+// ---------------------------------------------------------------------------
+
+// this class just serves as a namespace so TYPE::moveReferences can stay
+// private.
+class ReferenceMover {
+public:
+ // it would be nice if we could make sure no extra code is generated
+ // for sp<TYPE> or wp<TYPE> when TYPE is a descendant of RefBase:
+ // Using a sp<RefBase> override doesn't work; it's a bit like we wanted
+ // a template<typename TYPE inherits RefBase> template...
+
+ template<typename TYPE> static inline
+ void move_references(sp<TYPE>* d, sp<TYPE> const* s, size_t n) {
+
+ class Renamer : public ReferenceRenamer {
+ sp<TYPE>* d;
+ sp<TYPE> const* s;
+ virtual void operator()(size_t i) const {
+ // The id are known to be the sp<>'s this pointer
+ TYPE::renameRefId(d[i].get(), &s[i], &d[i]);
+ }
+ public:
+ Renamer(sp<TYPE>* d, sp<TYPE> const* s) : s(s), d(d) { }
+ };
+
+ memmove(d, s, n*sizeof(sp<TYPE>));
+ TYPE::renameRefs(n, Renamer(d, s));
+ }
+
+
+ template<typename TYPE> static inline
+ void move_references(wp<TYPE>* d, wp<TYPE> const* s, size_t n) {
+
+ class Renamer : public ReferenceRenamer {
+ wp<TYPE>* d;
+ wp<TYPE> const* s;
+ virtual void operator()(size_t i) const {
+ // The id are known to be the wp<>'s this pointer
+ TYPE::renameRefId(d[i].get_refs(), &s[i], &d[i]);
+ }
+ public:
+ Renamer(wp<TYPE>* d, wp<TYPE> const* s) : s(s), d(d) { }
+ };
+
+ memmove(d, s, n*sizeof(wp<TYPE>));
+ TYPE::renameRefs(n, Renamer(d, s));
+ }
+};
+
+// specialization for moving sp<> and wp<> types.
+// these are used by the [Sorted|Keyed]Vector<> implementations
+// sp<> and wp<> need to be handled specially, because they do not
+// have trivial copy operation in the general case (see RefBase.cpp
+// when DEBUG ops are enabled), but can be implemented very
+// efficiently in most cases.
+
+template<typename TYPE> inline
+void move_forward_type(sp<TYPE>* d, sp<TYPE> const* s, size_t n) {
+ ReferenceMover::move_references(d, s, n);
+}
+
+template<typename TYPE> inline
+void move_backward_type(sp<TYPE>* d, sp<TYPE> const* s, size_t n) {
+ ReferenceMover::move_references(d, s, n);
+}
+
+template<typename TYPE> inline
+void move_forward_type(wp<TYPE>* d, wp<TYPE> const* s, size_t n) {
+ ReferenceMover::move_references(d, s, n);
+}
+
+template<typename TYPE> inline
+void move_backward_type(wp<TYPE>* d, wp<TYPE> const* s, size_t n) {
+ ReferenceMover::move_references(d, s, n);
+}
+
+
+}; // namespace stagefright
+
+#ifdef _MSC_VER
+#undef __attribute__
+#endif
+
+// ---------------------------------------------------------------------------
+
+#endif // ANDROID_REF_BASE_H
diff --git a/media/libstagefright/system/core/include/utils/SharedBuffer.h b/media/libstagefright/system/core/include/utils/SharedBuffer.h
new file mode 100644
index 000000000..62d3ca415
--- /dev/null
+++ b/media/libstagefright/system/core/include/utils/SharedBuffer.h
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2005 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_SHARED_BUFFER_H
+#define ANDROID_SHARED_BUFFER_H
+
+#include <stdint.h>
+#include <sys/types.h>
+
+// ---------------------------------------------------------------------------
+
+namespace stagefright {
+
+class SharedBuffer
+{
+public:
+
+ /* flags to use with release() */
+ enum {
+ eKeepStorage = 0x00000001
+ };
+
+ /*! allocate a buffer of size 'size' and acquire() it.
+ * call release() to free it.
+ */
+ static SharedBuffer* alloc(size_t size);
+
+ /*! free the memory associated with the SharedBuffer.
+ * Fails if there are any users associated with this SharedBuffer.
+ * In other words, the buffer must have been release by all its
+ * users.
+ */
+ static ssize_t dealloc(const SharedBuffer* released);
+
+ //! access the data for read
+ inline const void* data() const;
+
+ //! access the data for read/write
+ inline void* data();
+
+ //! get size of the buffer
+ inline size_t size() const;
+
+ //! get back a SharedBuffer object from its data
+ static inline SharedBuffer* bufferFromData(void* data);
+
+ //! get back a SharedBuffer object from its data
+ static inline const SharedBuffer* bufferFromData(const void* data);
+
+ //! get the size of a SharedBuffer object from its data
+ static inline size_t sizeFromData(const void* data);
+
+ //! edit the buffer (get a writtable, or non-const, version of it)
+ SharedBuffer* edit() const;
+
+ //! edit the buffer, resizing if needed
+ SharedBuffer* editResize(size_t size) const;
+
+ //! like edit() but fails if a copy is required
+ SharedBuffer* attemptEdit() const;
+
+ //! resize and edit the buffer, loose it's content.
+ SharedBuffer* reset(size_t size) const;
+
+ //! acquire/release a reference on this buffer
+ void acquire() const;
+
+ /*! release a reference on this buffer, with the option of not
+ * freeing the memory associated with it if it was the last reference
+ * returns the previous reference count
+ */
+ int32_t release(uint32_t flags = 0) const;
+
+ //! returns wether or not we're the only owner
+ inline bool onlyOwner() const;
+
+
+private:
+ inline SharedBuffer() { }
+ inline ~SharedBuffer() { }
+ SharedBuffer(const SharedBuffer&);
+ SharedBuffer& operator = (const SharedBuffer&);
+
+ // 16 bytes. must be sized to preserve correct alignment.
+ mutable int32_t mRefs;
+ size_t mSize;
+ uint32_t mReserved[2];
+};
+
+// ---------------------------------------------------------------------------
+
+const void* SharedBuffer::data() const {
+ return this + 1;
+}
+
+void* SharedBuffer::data() {
+ return this + 1;
+}
+
+size_t SharedBuffer::size() const {
+ return mSize;
+}
+
+SharedBuffer* SharedBuffer::bufferFromData(void* data) {
+ return data ? static_cast<SharedBuffer *>(data)-1 : 0;
+}
+
+const SharedBuffer* SharedBuffer::bufferFromData(const void* data) {
+ return data ? static_cast<const SharedBuffer *>(data)-1 : 0;
+}
+
+size_t SharedBuffer::sizeFromData(const void* data) {
+ return data ? bufferFromData(data)->mSize : 0;
+}
+
+bool SharedBuffer::onlyOwner() const {
+ return (mRefs == 1);
+}
+
+}; // namespace stagefright
+
+// ---------------------------------------------------------------------------
+
+#endif // ANDROID_VECTOR_H
diff --git a/media/libstagefright/system/core/include/utils/SortedVector.h b/media/libstagefright/system/core/include/utils/SortedVector.h
new file mode 100644
index 000000000..67bfea8cd
--- /dev/null
+++ b/media/libstagefright/system/core/include/utils/SortedVector.h
@@ -0,0 +1,275 @@
+/*
+ * Copyright (C) 2005 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_SORTED_VECTOR_H
+#define ANDROID_SORTED_VECTOR_H
+
+#include <assert.h>
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <cutils/log.h>
+
+#include <utils/Vector.h>
+#include <utils/VectorImpl.h>
+#include <utils/TypeHelpers.h>
+
+// ---------------------------------------------------------------------------
+
+namespace stagefright {
+
+template <class TYPE>
+class SortedVector : private SortedVectorImpl
+{
+ friend class Vector<TYPE>;
+
+public:
+ typedef TYPE value_type;
+
+ /*!
+ * Constructors and destructors
+ */
+
+ SortedVector();
+ SortedVector(const SortedVector<TYPE>& rhs);
+ virtual ~SortedVector();
+
+ /*! copy operator */
+ SortedVector<TYPE>& operator = (const SortedVector<TYPE>& rhs);
+
+ /*
+ * empty the vector
+ */
+
+ inline void clear() { VectorImpl::clear(); }
+
+ /*!
+ * vector stats
+ */
+
+ //! returns number of items in the vector
+ inline size_t size() const { return VectorImpl::size(); }
+ //! returns whether or not the vector is empty
+ inline bool isEmpty() const { return VectorImpl::isEmpty(); }
+ //! returns how many items can be stored without reallocating the backing store
+ inline size_t capacity() const { return VectorImpl::capacity(); }
+ //! sets the capacity. capacity can never be reduced less than size()
+ inline ssize_t setCapacity(size_t size) { return VectorImpl::setCapacity(size); }
+
+ /*!
+ * C-style array access
+ */
+
+ //! read-only C-style access
+ inline const TYPE* array() const;
+
+ //! read-write C-style access. BE VERY CAREFUL when modifying the array
+ //! you must keep it sorted! You usually don't use this function.
+ TYPE* editArray();
+
+ //! finds the index of an item
+ ssize_t indexOf(const TYPE& item) const;
+
+ //! finds where this item should be inserted
+ size_t orderOf(const TYPE& item) const;
+
+
+ /*!
+ * accessors
+ */
+
+ //! read-only access to an item at a given index
+ inline const TYPE& operator [] (size_t index) const;
+ //! alternate name for operator []
+ inline const TYPE& itemAt(size_t index) const;
+ //! stack-usage of the vector. returns the top of the stack (last element)
+ const TYPE& top() const;
+
+ /*!
+ * modifying the array
+ */
+
+ //! add an item in the right place (and replace the one that is there)
+ ssize_t add(const TYPE& item);
+
+ //! editItemAt() MUST NOT change the order of this item
+ TYPE& editItemAt(size_t index) {
+ return *( static_cast<TYPE *>(VectorImpl::editItemLocation(index)) );
+ }
+
+ //! merges a vector into this one
+ ssize_t merge(const Vector<TYPE>& vector);
+ ssize_t merge(const SortedVector<TYPE>& vector);
+
+ //! removes an item
+ ssize_t remove(const TYPE&);
+
+ //! remove several items
+ inline ssize_t removeItemsAt(size_t index, size_t count = 1);
+ //! remove one item
+ inline ssize_t removeAt(size_t index) { return removeItemsAt(index); }
+
+protected:
+ virtual void do_construct(void* storage, size_t num) const;
+ virtual void do_destroy(void* storage, size_t num) const;
+ virtual void do_copy(void* dest, const void* from, size_t num) const;
+ virtual void do_splat(void* dest, const void* item, size_t num) const;
+ virtual void do_move_forward(void* dest, const void* from, size_t num) const;
+ virtual void do_move_backward(void* dest, const void* from, size_t num) const;
+ virtual int do_compare(const void* lhs, const void* rhs) const;
+};
+
+// SortedVector<T> can be trivially moved using memcpy() because moving does not
+// require any change to the underlying SharedBuffer contents or reference count.
+template<typename T> struct trait_trivial_move<SortedVector<T> > { enum { value = true }; };
+
+// ---------------------------------------------------------------------------
+// No user serviceable parts from here...
+// ---------------------------------------------------------------------------
+
+template<class TYPE> inline
+SortedVector<TYPE>::SortedVector()
+ : SortedVectorImpl(sizeof(TYPE),
+ ((traits<TYPE>::has_trivial_ctor ? HAS_TRIVIAL_CTOR : 0)
+ |(traits<TYPE>::has_trivial_dtor ? HAS_TRIVIAL_DTOR : 0)
+ |(traits<TYPE>::has_trivial_copy ? HAS_TRIVIAL_COPY : 0))
+ )
+{
+}
+
+template<class TYPE> inline
+SortedVector<TYPE>::SortedVector(const SortedVector<TYPE>& rhs)
+ : SortedVectorImpl(rhs) {
+}
+
+template<class TYPE> inline
+SortedVector<TYPE>::~SortedVector() {
+ finish_vector();
+}
+
+template<class TYPE> inline
+SortedVector<TYPE>& SortedVector<TYPE>::operator = (const SortedVector<TYPE>& rhs) {
+ SortedVectorImpl::operator = (rhs);
+ return *this;
+}
+
+template<class TYPE> inline
+const TYPE* SortedVector<TYPE>::array() const {
+ return static_cast<const TYPE *>(arrayImpl());
+}
+
+template<class TYPE> inline
+TYPE* SortedVector<TYPE>::editArray() {
+ return static_cast<TYPE *>(editArrayImpl());
+}
+
+
+template<class TYPE> inline
+const TYPE& SortedVector<TYPE>::operator[](size_t index) const {
+ LOG_FATAL_IF(index>=size(),
+ "%s: index=%u out of range (%u)", __PRETTY_FUNCTION__,
+ int(index), int(size()));
+ return *(array() + index);
+}
+
+template<class TYPE> inline
+const TYPE& SortedVector<TYPE>::itemAt(size_t index) const {
+ return operator[](index);
+}
+
+template<class TYPE> inline
+const TYPE& SortedVector<TYPE>::top() const {
+ return *(array() + size() - 1);
+}
+
+template<class TYPE> inline
+ssize_t SortedVector<TYPE>::add(const TYPE& item) {
+ return SortedVectorImpl::add(&item);
+}
+
+template<class TYPE> inline
+ssize_t SortedVector<TYPE>::indexOf(const TYPE& item) const {
+ return SortedVectorImpl::indexOf(&item);
+}
+
+template<class TYPE> inline
+size_t SortedVector<TYPE>::orderOf(const TYPE& item) const {
+ return SortedVectorImpl::orderOf(&item);
+}
+
+template<class TYPE> inline
+ssize_t SortedVector<TYPE>::merge(const Vector<TYPE>& vector) {
+ return SortedVectorImpl::merge(reinterpret_cast<const VectorImpl&>(vector));
+}
+
+template<class TYPE> inline
+ssize_t SortedVector<TYPE>::merge(const SortedVector<TYPE>& vector) {
+ return SortedVectorImpl::merge(reinterpret_cast<const SortedVectorImpl&>(vector));
+}
+
+template<class TYPE> inline
+ssize_t SortedVector<TYPE>::remove(const TYPE& item) {
+ return SortedVectorImpl::remove(&item);
+}
+
+template<class TYPE> inline
+ssize_t SortedVector<TYPE>::removeItemsAt(size_t index, size_t count) {
+ return VectorImpl::removeItemsAt(index, count);
+}
+
+// ---------------------------------------------------------------------------
+
+template<class TYPE>
+void SortedVector<TYPE>::do_construct(void* storage, size_t num) const {
+ construct_type( reinterpret_cast<TYPE*>(storage), num );
+}
+
+template<class TYPE>
+void SortedVector<TYPE>::do_destroy(void* storage, size_t num) const {
+ destroy_type( reinterpret_cast<TYPE*>(storage), num );
+}
+
+template<class TYPE>
+void SortedVector<TYPE>::do_copy(void* dest, const void* from, size_t num) const {
+ copy_type( reinterpret_cast<TYPE*>(dest), reinterpret_cast<const TYPE*>(from), num );
+}
+
+template<class TYPE>
+void SortedVector<TYPE>::do_splat(void* dest, const void* item, size_t num) const {
+ splat_type( reinterpret_cast<TYPE*>(dest), reinterpret_cast<const TYPE*>(item), num );
+}
+
+template<class TYPE>
+void SortedVector<TYPE>::do_move_forward(void* dest, const void* from, size_t num) const {
+ move_forward_type( reinterpret_cast<TYPE*>(dest), reinterpret_cast<const TYPE*>(from), num );
+}
+
+template<class TYPE>
+void SortedVector<TYPE>::do_move_backward(void* dest, const void* from, size_t num) const {
+ move_backward_type( reinterpret_cast<TYPE*>(dest), reinterpret_cast<const TYPE*>(from), num );
+}
+
+template<class TYPE>
+int SortedVector<TYPE>::do_compare(const void* lhs, const void* rhs) const {
+ return compare_type( *reinterpret_cast<const TYPE*>(lhs), *reinterpret_cast<const TYPE*>(rhs) );
+}
+
+}; // namespace stagefright
+
+
+// ---------------------------------------------------------------------------
+
+#endif // ANDROID_SORTED_VECTOR_H
diff --git a/media/libstagefright/system/core/include/utils/String16.h b/media/libstagefright/system/core/include/utils/String16.h
new file mode 100644
index 000000000..40632d7ef
--- /dev/null
+++ b/media/libstagefright/system/core/include/utils/String16.h
@@ -0,0 +1,250 @@
+/*
+ * Copyright (C) 2005 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_STRING16_H
+#define ANDROID_STRING16_H
+
+#include <utils/Errors.h>
+#include <utils/SharedBuffer.h>
+#include <utils/Unicode.h>
+#include <utils/TypeHelpers.h>
+
+// ---------------------------------------------------------------------------
+
+extern "C" {
+
+}
+
+// ---------------------------------------------------------------------------
+
+namespace stagefright {
+
+// ---------------------------------------------------------------------------
+
+class String8;
+class TextOutput;
+
+//! This is a string holding UTF-16 characters.
+class String16
+{
+public:
+ /* use String16(StaticLinkage) if you're statically linking against
+ * libutils and declaring an empty static String16, e.g.:
+ *
+ * static String16 sAStaticEmptyString(String16::kEmptyString);
+ * static String16 sAnotherStaticEmptyString(sAStaticEmptyString);
+ */
+ enum StaticLinkage { kEmptyString };
+
+ String16();
+ explicit String16(StaticLinkage);
+ String16(const String16& o);
+ String16(const String16& o,
+ size_t len,
+ size_t begin=0);
+ explicit String16(const char16_t* o);
+ explicit String16(const char16_t* o, size_t len);
+ explicit String16(const String8& o);
+ explicit String16(const char* o);
+ explicit String16(const char* o, size_t len);
+
+ ~String16();
+
+ inline const char16_t* string() const;
+ inline size_t size() const;
+
+ inline const SharedBuffer* sharedBuffer() const;
+
+ void setTo(const String16& other);
+ status_t setTo(const char16_t* other);
+ status_t setTo(const char16_t* other, size_t len);
+ status_t setTo(const String16& other,
+ size_t len,
+ size_t begin=0);
+
+ status_t append(const String16& other);
+ status_t append(const char16_t* other, size_t len);
+
+ inline String16& operator=(const String16& other);
+
+ inline String16& operator+=(const String16& other);
+ inline String16 operator+(const String16& other) const;
+
+ status_t insert(size_t pos, const char16_t* chrs);
+ status_t insert(size_t pos,
+ const char16_t* chrs, size_t len);
+
+ ssize_t findFirst(char16_t c) const;
+ ssize_t findLast(char16_t c) const;
+
+ bool startsWith(const String16& prefix) const;
+ bool startsWith(const char16_t* prefix) const;
+
+ status_t makeLower();
+
+ status_t replaceAll(char16_t replaceThis,
+ char16_t withThis);
+
+ status_t remove(size_t len, size_t begin=0);
+
+ inline int compare(const String16& other) const;
+
+ inline bool operator<(const String16& other) const;
+ inline bool operator<=(const String16& other) const;
+ inline bool operator==(const String16& other) const;
+ inline bool operator!=(const String16& other) const;
+ inline bool operator>=(const String16& other) const;
+ inline bool operator>(const String16& other) const;
+
+ inline bool operator<(const char16_t* other) const;
+ inline bool operator<=(const char16_t* other) const;
+ inline bool operator==(const char16_t* other) const;
+ inline bool operator!=(const char16_t* other) const;
+ inline bool operator>=(const char16_t* other) const;
+ inline bool operator>(const char16_t* other) const;
+
+ inline operator const char16_t*() const;
+
+private:
+ const char16_t* mString;
+};
+
+// String16 can be trivially moved using memcpy() because moving does not
+// require any change to the underlying SharedBuffer contents or reference count.
+ANDROID_TRIVIAL_MOVE_TRAIT(String16)
+
+// ---------------------------------------------------------------------------
+// No user servicable parts below.
+
+inline int compare_type(const String16& lhs, const String16& rhs)
+{
+ return lhs.compare(rhs);
+}
+
+inline int strictly_order_type(const String16& lhs, const String16& rhs)
+{
+ return compare_type(lhs, rhs) < 0;
+}
+
+inline const char16_t* String16::string() const
+{
+ return mString;
+}
+
+inline size_t String16::size() const
+{
+ return SharedBuffer::sizeFromData(mString)/sizeof(char16_t)-1;
+}
+
+inline const SharedBuffer* String16::sharedBuffer() const
+{
+ return SharedBuffer::bufferFromData(mString);
+}
+
+inline String16& String16::operator=(const String16& other)
+{
+ setTo(other);
+ return *this;
+}
+
+inline String16& String16::operator+=(const String16& other)
+{
+ append(other);
+ return *this;
+}
+
+inline String16 String16::operator+(const String16& other) const
+{
+ String16 tmp(*this);
+ tmp += other;
+ return tmp;
+}
+
+inline int String16::compare(const String16& other) const
+{
+ return strzcmp16(mString, size(), other.mString, other.size());
+}
+
+inline bool String16::operator<(const String16& other) const
+{
+ return strzcmp16(mString, size(), other.mString, other.size()) < 0;
+}
+
+inline bool String16::operator<=(const String16& other) const
+{
+ return strzcmp16(mString, size(), other.mString, other.size()) <= 0;
+}
+
+inline bool String16::operator==(const String16& other) const
+{
+ return strzcmp16(mString, size(), other.mString, other.size()) == 0;
+}
+
+inline bool String16::operator!=(const String16& other) const
+{
+ return strzcmp16(mString, size(), other.mString, other.size()) != 0;
+}
+
+inline bool String16::operator>=(const String16& other) const
+{
+ return strzcmp16(mString, size(), other.mString, other.size()) >= 0;
+}
+
+inline bool String16::operator>(const String16& other) const
+{
+ return strzcmp16(mString, size(), other.mString, other.size()) > 0;
+}
+
+inline bool String16::operator<(const char16_t* other) const
+{
+ return strcmp16(mString, other) < 0;
+}
+
+inline bool String16::operator<=(const char16_t* other) const
+{
+ return strcmp16(mString, other) <= 0;
+}
+
+inline bool String16::operator==(const char16_t* other) const
+{
+ return strcmp16(mString, other) == 0;
+}
+
+inline bool String16::operator!=(const char16_t* other) const
+{
+ return strcmp16(mString, other) != 0;
+}
+
+inline bool String16::operator>=(const char16_t* other) const
+{
+ return strcmp16(mString, other) >= 0;
+}
+
+inline bool String16::operator>(const char16_t* other) const
+{
+ return strcmp16(mString, other) > 0;
+}
+
+inline String16::operator const char16_t*() const
+{
+ return mString;
+}
+
+}; // namespace stagefright
+
+// ---------------------------------------------------------------------------
+
+#endif // ANDROID_STRING16_H
diff --git a/media/libstagefright/system/core/include/utils/String8.h b/media/libstagefright/system/core/include/utils/String8.h
new file mode 100644
index 000000000..3007f21af
--- /dev/null
+++ b/media/libstagefright/system/core/include/utils/String8.h
@@ -0,0 +1,403 @@
+/*
+ * Copyright (C) 2005 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_STRING8_H
+#define ANDROID_STRING8_H
+
+#include <utils/Errors.h>
+#include <utils/SharedBuffer.h>
+#include <utils/Unicode.h>
+#include <utils/TypeHelpers.h>
+
+#include <string.h> // for strcmp
+#include <stdarg.h>
+
+// ---------------------------------------------------------------------------
+
+#ifdef _MSC_VER
+#define __attribute__(X)
+#endif
+
+namespace stagefright {
+
+class String16;
+class TextOutput;
+
+//! This is a string holding UTF-8 characters. Does not allow the value more
+// than 0x10FFFF, which is not valid unicode codepoint.
+class String8
+{
+public:
+ /* use String8(StaticLinkage) if you're statically linking against
+ * libutils and declaring an empty static String8, e.g.:
+ *
+ * static String8 sAStaticEmptyString(String8::kEmptyString);
+ * static String8 sAnotherStaticEmptyString(sAStaticEmptyString);
+ */
+ enum StaticLinkage { kEmptyString };
+
+ String8();
+ explicit String8(StaticLinkage);
+ String8(const String8& o);
+ explicit String8(const char* o);
+ explicit String8(const char* o, size_t numChars);
+
+ explicit String8(const String16& o);
+ explicit String8(const char16_t* o);
+ explicit String8(const char16_t* o, size_t numChars);
+ explicit String8(const char32_t* o);
+ explicit String8(const char32_t* o, size_t numChars);
+ ~String8();
+
+ static inline const String8 empty();
+
+ static String8 format(const char* fmt, ...) __attribute__((format (printf, 1, 2)));
+ static String8 formatV(const char* fmt, va_list args);
+
+ inline const char* string() const;
+ inline size_t size() const;
+ inline size_t length() const;
+ inline size_t bytes() const;
+ inline bool isEmpty() const;
+
+ inline const SharedBuffer* sharedBuffer() const;
+
+ void clear();
+
+ void setTo(const String8& other);
+ status_t setTo(const char* other);
+ status_t setTo(const char* other, size_t numChars);
+ status_t setTo(const char16_t* other, size_t numChars);
+ status_t setTo(const char32_t* other,
+ size_t length);
+
+ status_t append(const String8& other);
+ status_t append(const char* other);
+ status_t append(const char* other, size_t numChars);
+
+ status_t appendFormat(const char* fmt, ...)
+ __attribute__((format (printf, 2, 3)));
+ status_t appendFormatV(const char* fmt, va_list args);
+
+ // Note that this function takes O(N) time to calculate the value.
+ // No cache value is stored.
+ size_t getUtf32Length() const;
+ int32_t getUtf32At(size_t index,
+ size_t *next_index) const;
+ void getUtf32(char32_t* dst) const;
+
+ inline String8& operator=(const String8& other);
+ inline String8& operator=(const char* other);
+
+ inline String8& operator+=(const String8& other);
+ inline String8 operator+(const String8& other) const;
+
+ inline String8& operator+=(const char* other);
+ inline String8 operator+(const char* other) const;
+
+ inline int compare(const String8& other) const;
+
+ inline bool operator<(const String8& other) const;
+ inline bool operator<=(const String8& other) const;
+ inline bool operator==(const String8& other) const;
+ inline bool operator!=(const String8& other) const;
+ inline bool operator>=(const String8& other) const;
+ inline bool operator>(const String8& other) const;
+
+ inline bool operator<(const char* other) const;
+ inline bool operator<=(const char* other) const;
+ inline bool operator==(const char* other) const;
+ inline bool operator!=(const char* other) const;
+ inline bool operator>=(const char* other) const;
+ inline bool operator>(const char* other) const;
+
+ inline operator const char*() const;
+
+ char* lockBuffer(size_t size);
+ void unlockBuffer();
+ status_t unlockBuffer(size_t size);
+
+ // return the index of the first byte of other in this at or after
+ // start, or -1 if not found
+ ssize_t find(const char* other, size_t start = 0) const;
+
+ void toLower();
+ void toLower(size_t start, size_t numChars);
+ void toUpper();
+ void toUpper(size_t start, size_t numChars);
+
+ /*
+ * These methods operate on the string as if it were a path name.
+ */
+
+ /*
+ * Set the filename field to a specific value.
+ *
+ * Normalizes the filename, removing a trailing '/' if present.
+ */
+ void setPathName(const char* name);
+ void setPathName(const char* name, size_t numChars);
+
+ /*
+ * Get just the filename component.
+ *
+ * "/tmp/foo/bar.c" --> "bar.c"
+ */
+ String8 getPathLeaf(void) const;
+
+ /*
+ * Remove the last (file name) component, leaving just the directory
+ * name.
+ *
+ * "/tmp/foo/bar.c" --> "/tmp/foo"
+ * "/tmp" --> "" // ????? shouldn't this be "/" ???? XXX
+ * "bar.c" --> ""
+ */
+ String8 getPathDir(void) const;
+
+ /*
+ * Retrieve the front (root dir) component. Optionally also return the
+ * remaining components.
+ *
+ * "/tmp/foo/bar.c" --> "tmp" (remain = "foo/bar.c")
+ * "/tmp" --> "tmp" (remain = "")
+ * "bar.c" --> "bar.c" (remain = "")
+ */
+ String8 walkPath(String8* outRemains = NULL) const;
+
+ /*
+ * Return the filename extension. This is the last '.' and any number
+ * of characters that follow it. The '.' is included in case we
+ * decide to expand our definition of what constitutes an extension.
+ *
+ * "/tmp/foo/bar.c" --> ".c"
+ * "/tmp" --> ""
+ * "/tmp/foo.bar/baz" --> ""
+ * "foo.jpeg" --> ".jpeg"
+ * "foo." --> ""
+ */
+ String8 getPathExtension(void) const;
+
+ /*
+ * Return the path without the extension. Rules for what constitutes
+ * an extension are described in the comment for getPathExtension().
+ *
+ * "/tmp/foo/bar.c" --> "/tmp/foo/bar"
+ */
+ String8 getBasePath(void) const;
+
+ /*
+ * Add a component to the pathname. We guarantee that there is
+ * exactly one path separator between the old path and the new.
+ * If there is no existing name, we just copy the new name in.
+ *
+ * If leaf is a fully qualified path (i.e. starts with '/', it
+ * replaces whatever was there before.
+ */
+ String8& appendPath(const char* leaf);
+ String8& appendPath(const String8& leaf) { return appendPath(leaf.string()); }
+
+ /*
+ * Like appendPath(), but does not affect this string. Returns a new one instead.
+ */
+ String8 appendPathCopy(const char* leaf) const
+ { String8 p(*this); p.appendPath(leaf); return p; }
+ String8 appendPathCopy(const String8& leaf) const { return appendPathCopy(leaf.string()); }
+
+ /*
+ * Converts all separators in this string to /, the default path separator.
+ *
+ * If the default OS separator is backslash, this converts all
+ * backslashes to slashes, in-place. Otherwise it does nothing.
+ * Returns self.
+ */
+ String8& convertToResPath();
+
+private:
+ status_t real_append(const char* other, size_t numChars);
+ char* find_extension(void) const;
+
+ const char* mString;
+};
+
+// String8 can be trivially moved using memcpy() because moving does not
+// require any change to the underlying SharedBuffer contents or reference count.
+ANDROID_TRIVIAL_MOVE_TRAIT(String8)
+
+// ---------------------------------------------------------------------------
+// No user servicable parts below.
+
+inline int compare_type(const String8& lhs, const String8& rhs)
+{
+ return lhs.compare(rhs);
+}
+
+inline int strictly_order_type(const String8& lhs, const String8& rhs)
+{
+ return compare_type(lhs, rhs) < 0;
+}
+
+inline const String8 String8::empty() {
+ return String8();
+}
+
+inline const char* String8::string() const
+{
+ return mString;
+}
+
+inline size_t String8::length() const
+{
+ return SharedBuffer::sizeFromData(mString)-1;
+}
+
+inline size_t String8::size() const
+{
+ return length();
+}
+
+inline bool String8::isEmpty() const
+{
+ return length() == 0;
+}
+
+inline size_t String8::bytes() const
+{
+ return SharedBuffer::sizeFromData(mString)-1;
+}
+
+inline const SharedBuffer* String8::sharedBuffer() const
+{
+ return SharedBuffer::bufferFromData(mString);
+}
+
+inline String8& String8::operator=(const String8& other)
+{
+ setTo(other);
+ return *this;
+}
+
+inline String8& String8::operator=(const char* other)
+{
+ setTo(other);
+ return *this;
+}
+
+inline String8& String8::operator+=(const String8& other)
+{
+ append(other);
+ return *this;
+}
+
+inline String8 String8::operator+(const String8& other) const
+{
+ String8 tmp(*this);
+ tmp += other;
+ return tmp;
+}
+
+inline String8& String8::operator+=(const char* other)
+{
+ append(other);
+ return *this;
+}
+
+inline String8 String8::operator+(const char* other) const
+{
+ String8 tmp(*this);
+ tmp += other;
+ return tmp;
+}
+
+inline int String8::compare(const String8& other) const
+{
+ return strcmp(mString, other.mString);
+}
+
+inline bool String8::operator<(const String8& other) const
+{
+ return strcmp(mString, other.mString) < 0;
+}
+
+inline bool String8::operator<=(const String8& other) const
+{
+ return strcmp(mString, other.mString) <= 0;
+}
+
+inline bool String8::operator==(const String8& other) const
+{
+ return strcmp(mString, other.mString) == 0;
+}
+
+inline bool String8::operator!=(const String8& other) const
+{
+ return strcmp(mString, other.mString) != 0;
+}
+
+inline bool String8::operator>=(const String8& other) const
+{
+ return strcmp(mString, other.mString) >= 0;
+}
+
+inline bool String8::operator>(const String8& other) const
+{
+ return strcmp(mString, other.mString) > 0;
+}
+
+inline bool String8::operator<(const char* other) const
+{
+ return strcmp(mString, other) < 0;
+}
+
+inline bool String8::operator<=(const char* other) const
+{
+ return strcmp(mString, other) <= 0;
+}
+
+inline bool String8::operator==(const char* other) const
+{
+ return strcmp(mString, other) == 0;
+}
+
+inline bool String8::operator!=(const char* other) const
+{
+ return strcmp(mString, other) != 0;
+}
+
+inline bool String8::operator>=(const char* other) const
+{
+ return strcmp(mString, other) >= 0;
+}
+
+inline bool String8::operator>(const char* other) const
+{
+ return strcmp(mString, other) > 0;
+}
+
+inline String8::operator const char*() const
+{
+ return mString;
+}
+
+} // namespace stagefright
+
+#ifdef _MSC_VER
+#undef __attribute__
+#endif
+
+// ---------------------------------------------------------------------------
+
+#endif // ANDROID_STRING8_H
diff --git a/media/libstagefright/system/core/include/utils/StrongPointer.h b/media/libstagefright/system/core/include/utils/StrongPointer.h
new file mode 100644
index 000000000..db22900f0
--- /dev/null
+++ b/media/libstagefright/system/core/include/utils/StrongPointer.h
@@ -0,0 +1,211 @@
+/*
+ * Copyright (C) 2005 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_STRONG_POINTER_H
+#define ANDROID_STRONG_POINTER_H
+
+#include <cutils/atomic.h>
+
+#include <stdint.h>
+#include <sys/types.h>
+#include <stdlib.h>
+
+// ---------------------------------------------------------------------------
+namespace stagefright {
+
+template<typename T> class wp;
+
+// ---------------------------------------------------------------------------
+
+#define COMPARE(_op_) \
+inline bool operator _op_ (const sp<T>& o) const { \
+ return m_ptr _op_ o.m_ptr; \
+} \
+inline bool operator _op_ (const T* o) const { \
+ return m_ptr _op_ o; \
+} \
+template<typename U> \
+inline bool operator _op_ (const sp<U>& o) const { \
+ return m_ptr _op_ o.m_ptr; \
+} \
+template<typename U> \
+inline bool operator _op_ (const U* o) const { \
+ return m_ptr _op_ o; \
+} \
+inline bool operator _op_ (const wp<T>& o) const { \
+ return m_ptr _op_ o.m_ptr; \
+} \
+template<typename U> \
+inline bool operator _op_ (const wp<U>& o) const { \
+ return m_ptr _op_ o.m_ptr; \
+}
+
+// ---------------------------------------------------------------------------
+
+template<typename T>
+class sp {
+public:
+ inline sp() : m_ptr(0) { }
+
+ sp(T* other);
+ sp(const sp<T>& other);
+ template<typename U> sp(U* other);
+ template<typename U> sp(const sp<U>& other);
+
+ ~sp();
+
+ // Assignment
+
+ sp& operator = (T* other);
+ sp& operator = (const sp<T>& other);
+
+ template<typename U> sp& operator = (const sp<U>& other);
+ template<typename U> sp& operator = (U* other);
+
+ //! Special optimization for use by ProcessState (and nobody else).
+ void force_set(T* other);
+
+ // Reset
+
+ void clear();
+
+ // Accessors
+
+ inline T& operator* () const { return *m_ptr; }
+ inline T* operator-> () const { return m_ptr; }
+ inline T* get() const { return m_ptr; }
+
+ // Operators
+
+ COMPARE(==)
+ COMPARE(!=)
+ COMPARE(>)
+ COMPARE(<)
+ COMPARE(<=)
+ COMPARE(>=)
+
+private:
+ template<typename Y> friend class sp;
+ template<typename Y> friend class wp;
+ void set_pointer(T* ptr);
+ T* m_ptr;
+};
+
+#undef COMPARE
+
+// ---------------------------------------------------------------------------
+// No user serviceable parts below here.
+
+template<typename T>
+sp<T>::sp(T* other)
+ : m_ptr(other) {
+ if (other)
+ other->incStrong(this);
+}
+
+template<typename T>
+sp<T>::sp(const sp<T>& other)
+ : m_ptr(other.m_ptr) {
+ if (m_ptr)
+ m_ptr->incStrong(this);
+}
+
+template<typename T> template<typename U>
+sp<T>::sp(U* other)
+ : m_ptr(other) {
+ if (other)
+ ((T*) other)->incStrong(this);
+}
+
+template<typename T> template<typename U>
+sp<T>::sp(const sp<U>& other)
+ : m_ptr(other.m_ptr) {
+ if (m_ptr)
+ m_ptr->incStrong(this);
+}
+
+template<typename T>
+sp<T>::~sp() {
+ if (m_ptr)
+ m_ptr->decStrong(this);
+}
+
+template<typename T>
+sp<T>& sp<T>::operator =(const sp<T>& other) {
+ T* otherPtr(other.m_ptr);
+ if (otherPtr)
+ otherPtr->incStrong(this);
+ if (m_ptr)
+ m_ptr->decStrong(this);
+ m_ptr = otherPtr;
+ return *this;
+}
+
+template<typename T>
+sp<T>& sp<T>::operator =(T* other) {
+ if (other)
+ other->incStrong(this);
+ if (m_ptr)
+ m_ptr->decStrong(this);
+ m_ptr = other;
+ return *this;
+}
+
+template<typename T> template<typename U>
+sp<T>& sp<T>::operator =(const sp<U>& other) {
+ T* otherPtr(other.m_ptr);
+ if (otherPtr)
+ otherPtr->incStrong(this);
+ if (m_ptr)
+ m_ptr->decStrong(this);
+ m_ptr = otherPtr;
+ return *this;
+}
+
+template<typename T> template<typename U>
+sp<T>& sp<T>::operator =(U* other) {
+ if (other)
+ ((T*) other)->incStrong(this);
+ if (m_ptr)
+ m_ptr->decStrong(this);
+ m_ptr = other;
+ return *this;
+}
+
+template<typename T>
+void sp<T>::force_set(T* other) {
+ other->forceIncStrong(this);
+ m_ptr = other;
+}
+
+template<typename T>
+void sp<T>::clear() {
+ if (m_ptr) {
+ m_ptr->decStrong(this);
+ m_ptr = 0;
+ }
+}
+
+template<typename T>
+void sp<T>::set_pointer(T* ptr) {
+ m_ptr = ptr;
+}
+
+}; // namespace stagefright
+
+// ---------------------------------------------------------------------------
+
+#endif // ANDROID_STRONG_POINTER_H
diff --git a/media/libstagefright/system/core/include/utils/Timers.h b/media/libstagefright/system/core/include/utils/Timers.h
new file mode 100644
index 000000000..d01542169
--- /dev/null
+++ b/media/libstagefright/system/core/include/utils/Timers.h
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2005 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.
+ */
+
+//
+// Timer functions.
+//
+#ifndef _LIBS_UTILS_TIMERS_H
+#define _LIBS_UTILS_TIMERS_H
+
+#include <stdint.h>
+#include <sys/types.h>
+#include <sys/time.h>
+
+// ------------------------------------------------------------------
+// C API
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef int64_t nsecs_t; // nano-seconds
+
+static inline nsecs_t seconds_to_nanoseconds(nsecs_t secs)
+{
+ return secs*1000000000;
+}
+
+static inline nsecs_t milliseconds_to_nanoseconds(nsecs_t secs)
+{
+ return secs*1000000;
+}
+
+static inline nsecs_t microseconds_to_nanoseconds(nsecs_t secs)
+{
+ return secs*1000;
+}
+
+static inline nsecs_t nanoseconds_to_seconds(nsecs_t secs)
+{
+ return secs/1000000000;
+}
+
+static inline nsecs_t nanoseconds_to_milliseconds(nsecs_t secs)
+{
+ return secs/1000000;
+}
+
+static inline nsecs_t nanoseconds_to_microseconds(nsecs_t secs)
+{
+ return secs/1000;
+}
+
+static inline nsecs_t s2ns(nsecs_t v) {return seconds_to_nanoseconds(v);}
+static inline nsecs_t ms2ns(nsecs_t v) {return milliseconds_to_nanoseconds(v);}
+static inline nsecs_t us2ns(nsecs_t v) {return microseconds_to_nanoseconds(v);}
+static inline nsecs_t ns2s(nsecs_t v) {return nanoseconds_to_seconds(v);}
+static inline nsecs_t ns2ms(nsecs_t v) {return nanoseconds_to_milliseconds(v);}
+static inline nsecs_t ns2us(nsecs_t v) {return nanoseconds_to_microseconds(v);}
+
+static inline nsecs_t seconds(nsecs_t v) { return s2ns(v); }
+static inline nsecs_t milliseconds(nsecs_t v) { return ms2ns(v); }
+static inline nsecs_t microseconds(nsecs_t v) { return us2ns(v); }
+
+enum {
+ SYSTEM_TIME_REALTIME = 0, // system-wide realtime clock
+ SYSTEM_TIME_MONOTONIC = 1, // monotonic time since unspecified starting point
+ SYSTEM_TIME_PROCESS = 2, // high-resolution per-process clock
+ SYSTEM_TIME_THREAD = 3, // high-resolution per-thread clock
+ SYSTEM_TIME_BOOTTIME = 4 // same as SYSTEM_TIME_MONOTONIC, but including CPU suspend time
+};
+
+// return the system-time according to the specified clock
+#ifdef __cplusplus
+nsecs_t systemTime(int clock = SYSTEM_TIME_MONOTONIC);
+#else
+nsecs_t systemTime(int clock);
+#endif // def __cplusplus
+
+/**
+ * Returns the number of milliseconds to wait between the reference time and the timeout time.
+ * If the timeout is in the past relative to the reference time, returns 0.
+ * If the timeout is more than INT_MAX milliseconds in the future relative to the reference time,
+ * such as when timeoutTime == LLONG_MAX, returns -1 to indicate an infinite timeout delay.
+ * Otherwise, returns the difference between the reference time and timeout time
+ * rounded up to the next millisecond.
+ */
+int toMillisecondTimeoutDelay(nsecs_t referenceTime, nsecs_t timeoutTime);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // _LIBS_UTILS_TIMERS_H
diff --git a/media/libstagefright/system/core/include/utils/TypeHelpers.h b/media/libstagefright/system/core/include/utils/TypeHelpers.h
new file mode 100644
index 000000000..7a1924416
--- /dev/null
+++ b/media/libstagefright/system/core/include/utils/TypeHelpers.h
@@ -0,0 +1,302 @@
+/*
+ * Copyright (C) 2005 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_TYPE_HELPERS_H
+#define ANDROID_TYPE_HELPERS_H
+
+#include <new>
+#include <stdint.h>
+#include <string.h>
+#include <sys/types.h>
+
+// ---------------------------------------------------------------------------
+
+namespace stagefright {
+
+/*
+ * Types traits
+ */
+
+template <typename T> struct trait_trivial_ctor { enum { value = false }; };
+template <typename T> struct trait_trivial_dtor { enum { value = false }; };
+template <typename T> struct trait_trivial_copy { enum { value = false }; };
+template <typename T> struct trait_trivial_move { enum { value = false }; };
+template <typename T> struct trait_pointer { enum { value = false }; };
+template <typename T> struct trait_pointer<T*> { enum { value = true }; };
+
+template <typename TYPE>
+struct traits {
+ enum {
+ // whether this type is a pointer
+ is_pointer = trait_pointer<TYPE>::value,
+ // whether this type's constructor is a no-op
+ has_trivial_ctor = is_pointer || trait_trivial_ctor<TYPE>::value,
+ // whether this type's destructor is a no-op
+ has_trivial_dtor = is_pointer || trait_trivial_dtor<TYPE>::value,
+ // whether this type type can be copy-constructed with memcpy
+ has_trivial_copy = is_pointer || trait_trivial_copy<TYPE>::value,
+ // whether this type can be moved with memmove
+ has_trivial_move = is_pointer || trait_trivial_move<TYPE>::value
+ };
+};
+
+template <typename T, typename U>
+struct aggregate_traits {
+ enum {
+ is_pointer = false,
+ has_trivial_ctor =
+ traits<T>::has_trivial_ctor && traits<U>::has_trivial_ctor,
+ has_trivial_dtor =
+ traits<T>::has_trivial_dtor && traits<U>::has_trivial_dtor,
+ has_trivial_copy =
+ traits<T>::has_trivial_copy && traits<U>::has_trivial_copy,
+ has_trivial_move =
+ traits<T>::has_trivial_move && traits<U>::has_trivial_move
+ };
+};
+
+#define ANDROID_TRIVIAL_CTOR_TRAIT( T ) \
+ template<> struct trait_trivial_ctor< T > { enum { value = true }; };
+
+#define ANDROID_TRIVIAL_DTOR_TRAIT( T ) \
+ template<> struct trait_trivial_dtor< T > { enum { value = true }; };
+
+#define ANDROID_TRIVIAL_COPY_TRAIT( T ) \
+ template<> struct trait_trivial_copy< T > { enum { value = true }; };
+
+#define ANDROID_TRIVIAL_MOVE_TRAIT( T ) \
+ template<> struct trait_trivial_move< T > { enum { value = true }; };
+
+#define ANDROID_BASIC_TYPES_TRAITS( T ) \
+ ANDROID_TRIVIAL_CTOR_TRAIT( T ) \
+ ANDROID_TRIVIAL_DTOR_TRAIT( T ) \
+ ANDROID_TRIVIAL_COPY_TRAIT( T ) \
+ ANDROID_TRIVIAL_MOVE_TRAIT( T )
+
+// ---------------------------------------------------------------------------
+
+/*
+ * basic types traits
+ */
+
+ANDROID_BASIC_TYPES_TRAITS( void )
+ANDROID_BASIC_TYPES_TRAITS( bool )
+ANDROID_BASIC_TYPES_TRAITS( char )
+ANDROID_BASIC_TYPES_TRAITS( unsigned char )
+ANDROID_BASIC_TYPES_TRAITS( short )
+ANDROID_BASIC_TYPES_TRAITS( unsigned short )
+ANDROID_BASIC_TYPES_TRAITS( int )
+ANDROID_BASIC_TYPES_TRAITS( unsigned int )
+ANDROID_BASIC_TYPES_TRAITS( long )
+ANDROID_BASIC_TYPES_TRAITS( unsigned long )
+ANDROID_BASIC_TYPES_TRAITS( long long )
+ANDROID_BASIC_TYPES_TRAITS( unsigned long long )
+ANDROID_BASIC_TYPES_TRAITS( float )
+ANDROID_BASIC_TYPES_TRAITS( double )
+
+// ---------------------------------------------------------------------------
+
+
+/*
+ * compare and order types
+ */
+
+template<typename TYPE> inline
+int strictly_order_type(const TYPE& lhs, const TYPE& rhs) {
+ return (lhs < rhs) ? 1 : 0;
+}
+
+template<typename TYPE> inline
+int compare_type(const TYPE& lhs, const TYPE& rhs) {
+ return strictly_order_type(rhs, lhs) - strictly_order_type(lhs, rhs);
+}
+
+/*
+ * create, destroy, copy and move types...
+ */
+
+template<typename TYPE> inline
+void construct_type(TYPE* p, size_t n) {
+ if (!traits<TYPE>::has_trivial_ctor) {
+ while (n--) {
+ new(p++) TYPE;
+ }
+ }
+}
+
+template<typename TYPE> inline
+void destroy_type(TYPE* p, size_t n) {
+ if (!traits<TYPE>::has_trivial_dtor) {
+ while (n--) {
+ p->~TYPE();
+ p++;
+ }
+ }
+}
+
+template<typename TYPE> inline
+void copy_type(TYPE* d, const TYPE* s, size_t n) {
+ if (!traits<TYPE>::has_trivial_copy) {
+ while (n--) {
+ new(d) TYPE(*s);
+ d++, s++;
+ }
+ } else {
+ memcpy(d,s,n*sizeof(TYPE));
+ }
+}
+
+template<typename TYPE> inline
+void splat_type(TYPE* where, const TYPE* what, size_t n) {
+ if (!traits<TYPE>::has_trivial_copy) {
+ while (n--) {
+ new(where) TYPE(*what);
+ where++;
+ }
+ } else {
+ while (n--) {
+ *where++ = *what;
+ }
+ }
+}
+
+template<typename TYPE> inline
+void move_forward_type(TYPE* d, const TYPE* s, size_t n = 1) {
+ if ((traits<TYPE>::has_trivial_dtor && traits<TYPE>::has_trivial_copy)
+ || traits<TYPE>::has_trivial_move)
+ {
+ memmove(d,s,n*sizeof(TYPE));
+ } else {
+ d += n;
+ s += n;
+ while (n--) {
+ --d, --s;
+ if (!traits<TYPE>::has_trivial_copy) {
+ new(d) TYPE(*s);
+ } else {
+ *d = *s;
+ }
+ if (!traits<TYPE>::has_trivial_dtor) {
+ s->~TYPE();
+ }
+ }
+ }
+}
+
+template<typename TYPE> inline
+void move_backward_type(TYPE* d, const TYPE* s, size_t n = 1) {
+ if ((traits<TYPE>::has_trivial_dtor && traits<TYPE>::has_trivial_copy)
+ || traits<TYPE>::has_trivial_move)
+ {
+ memmove((void*)d,(void*)s,n*sizeof(TYPE));
+ } else {
+ while (n--) {
+ if (!traits<TYPE>::has_trivial_copy) {
+ new(d) TYPE(*s);
+ } else {
+ *d = *s;
+ }
+ if (!traits<TYPE>::has_trivial_dtor) {
+ s->~TYPE();
+ }
+ d++, s++;
+ }
+ }
+}
+
+// ---------------------------------------------------------------------------
+
+/*
+ * a key/value pair
+ */
+
+template <typename KEY, typename VALUE>
+struct key_value_pair_t {
+ typedef KEY key_t;
+ typedef VALUE value_t;
+
+ KEY key;
+ VALUE value;
+ key_value_pair_t() { }
+ key_value_pair_t(const key_value_pair_t& o) : key(o.key), value(o.value) { }
+ key_value_pair_t(const KEY& k, const VALUE& v) : key(k), value(v) { }
+ key_value_pair_t(const KEY& k) : key(k) { }
+ inline bool operator < (const key_value_pair_t& o) const {
+ return strictly_order_type(key, o.key);
+ }
+ inline const KEY& getKey() const {
+ return key;
+ }
+ inline const VALUE& getValue() const {
+ return value;
+ }
+};
+
+template <typename K, typename V>
+struct trait_trivial_ctor< key_value_pair_t<K, V> >
+{ enum { value = aggregate_traits<K,V>::has_trivial_ctor }; };
+template <typename K, typename V>
+struct trait_trivial_dtor< key_value_pair_t<K, V> >
+{ enum { value = aggregate_traits<K,V>::has_trivial_dtor }; };
+template <typename K, typename V>
+struct trait_trivial_copy< key_value_pair_t<K, V> >
+{ enum { value = aggregate_traits<K,V>::has_trivial_copy }; };
+template <typename K, typename V>
+struct trait_trivial_move< key_value_pair_t<K, V> >
+{ enum { value = aggregate_traits<K,V>::has_trivial_move }; };
+
+// ---------------------------------------------------------------------------
+
+/*
+ * Hash codes.
+ */
+typedef uint32_t hash_t;
+
+template <typename TKey>
+hash_t hash_type(const TKey& key);
+
+/* Built-in hash code specializations.
+ * Assumes pointers are 32bit. */
+#define ANDROID_INT32_HASH(T) \
+ template <> inline hash_t hash_type(const T& value) { return hash_t(value); }
+#define ANDROID_INT64_HASH(T) \
+ template <> inline hash_t hash_type(const T& value) { \
+ return hash_t((value >> 32) ^ value); }
+#define ANDROID_REINTERPRET_HASH(T, R) \
+ template <> inline hash_t hash_type(const T& value) { \
+ return hash_type(*reinterpret_cast<const R*>(&value)); }
+
+ANDROID_INT32_HASH(bool)
+ANDROID_INT32_HASH(int8_t)
+ANDROID_INT32_HASH(uint8_t)
+ANDROID_INT32_HASH(int16_t)
+ANDROID_INT32_HASH(uint16_t)
+ANDROID_INT32_HASH(int32_t)
+ANDROID_INT32_HASH(uint32_t)
+ANDROID_INT64_HASH(int64_t)
+ANDROID_INT64_HASH(uint64_t)
+ANDROID_REINTERPRET_HASH(float, uint32_t)
+ANDROID_REINTERPRET_HASH(double, uint64_t)
+
+template <typename T> inline hash_t hash_type(T* const & value) {
+ return hash_type(uintptr_t(value));
+}
+
+}; // namespace stagefright
+
+// ---------------------------------------------------------------------------
+
+#endif // ANDROID_TYPE_HELPERS_H
diff --git a/media/libstagefright/system/core/include/utils/Unicode.h b/media/libstagefright/system/core/include/utils/Unicode.h
new file mode 100644
index 000000000..b76a5e268
--- /dev/null
+++ b/media/libstagefright/system/core/include/utils/Unicode.h
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) 2005 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_UNICODE_H
+#define ANDROID_UNICODE_H
+
+#include <sys/types.h>
+#include <stdint.h>
+
+extern "C" {
+
+// Standard string functions on char16_t strings.
+int strcmp16(const char16_t *, const char16_t *);
+int strncmp16(const char16_t *s1, const char16_t *s2, size_t n);
+size_t strlen16(const char16_t *);
+size_t strnlen16(const char16_t *, size_t);
+char16_t *strcpy16(char16_t *, const char16_t *);
+char16_t *strncpy16(char16_t *, const char16_t *, size_t);
+
+// Version of comparison that supports embedded nulls.
+// This is different than strncmp() because we don't stop
+// at a nul character and consider the strings to be different
+// if the lengths are different (thus we need to supply the
+// lengths of both strings). This can also be used when
+// your string is not nul-terminated as it will have the
+// equivalent result as strcmp16 (unlike strncmp16).
+int strzcmp16(const char16_t *s1, size_t n1, const char16_t *s2, size_t n2);
+
+// Version of strzcmp16 for comparing strings in different endianness.
+int strzcmp16_h_n(const char16_t *s1H, size_t n1, const char16_t *s2N, size_t n2);
+
+// Standard string functions on char32_t strings.
+size_t strlen32(const char32_t *);
+size_t strnlen32(const char32_t *, size_t);
+
+/**
+ * Measure the length of a UTF-32 string in UTF-8. If the string is invalid
+ * such as containing a surrogate character, -1 will be returned.
+ */
+ssize_t utf32_to_utf8_length(const char32_t *src, size_t src_len);
+
+/**
+ * Stores a UTF-8 string converted from "src" in "dst", if "dst_length" is not
+ * large enough to store the string, the part of the "src" string is stored
+ * into "dst" as much as possible. See the examples for more detail.
+ * Returns the size actually used for storing the string.
+ * dst" is not null-terminated when dst_len is fully used (like strncpy).
+ *
+ * Example 1
+ * "src" == \u3042\u3044 (\xE3\x81\x82\xE3\x81\x84)
+ * "src_len" == 2
+ * "dst_len" >= 7
+ * ->
+ * Returned value == 6
+ * "dst" becomes \xE3\x81\x82\xE3\x81\x84\0
+ * (note that "dst" is null-terminated)
+ *
+ * Example 2
+ * "src" == \u3042\u3044 (\xE3\x81\x82\xE3\x81\x84)
+ * "src_len" == 2
+ * "dst_len" == 5
+ * ->
+ * Returned value == 3
+ * "dst" becomes \xE3\x81\x82\0
+ * (note that "dst" is null-terminated, but \u3044 is not stored in "dst"
+ * since "dst" does not have enough size to store the character)
+ *
+ * Example 3
+ * "src" == \u3042\u3044 (\xE3\x81\x82\xE3\x81\x84)
+ * "src_len" == 2
+ * "dst_len" == 6
+ * ->
+ * Returned value == 6
+ * "dst" becomes \xE3\x81\x82\xE3\x81\x84
+ * (note that "dst" is NOT null-terminated, like strncpy)
+ */
+void utf32_to_utf8(const char32_t* src, size_t src_len, char* dst);
+
+/**
+ * Returns the unicode value at "index".
+ * Returns -1 when the index is invalid (equals to or more than "src_len").
+ * If returned value is positive, it is able to be converted to char32_t, which
+ * is unsigned. Then, if "next_index" is not NULL, the next index to be used is
+ * stored in "next_index". "next_index" can be NULL.
+ */
+int32_t utf32_from_utf8_at(const char *src, size_t src_len, size_t index, size_t *next_index);
+
+
+/**
+ * Returns the UTF-8 length of UTF-16 string "src".
+ */
+ssize_t utf16_to_utf8_length(const char16_t *src, size_t src_len);
+
+/**
+ * Converts a UTF-16 string to UTF-8. The destination buffer must be large
+ * enough to fit the UTF-16 as measured by utf16_to_utf8_length with an added
+ * NULL terminator.
+ */
+void utf16_to_utf8(const char16_t* src, size_t src_len, char* dst);
+
+/**
+ * Returns the length of "src" when "src" is valid UTF-8 string.
+ * Returns 0 if src is NULL or 0-length string. Returns -1 when the source
+ * is an invalid string.
+ *
+ * This function should be used to determine whether "src" is valid UTF-8
+ * characters with valid unicode codepoints. "src" must be null-terminated.
+ *
+ * If you are going to use other utf8_to_... functions defined in this header
+ * with string which may not be valid UTF-8 with valid codepoint (form 0 to
+ * 0x10FFFF), you should use this function before calling others, since the
+ * other functions do not check whether the string is valid UTF-8 or not.
+ *
+ * If you do not care whether "src" is valid UTF-8 or not, you should use
+ * strlen() as usual, which should be much faster.
+ */
+ssize_t utf8_length(const char *src);
+
+/**
+ * Measure the length of a UTF-32 string.
+ */
+size_t utf8_to_utf32_length(const char *src, size_t src_len);
+
+/**
+ * Stores a UTF-32 string converted from "src" in "dst". "dst" must be large
+ * enough to store the entire converted string as measured by
+ * utf8_to_utf32_length plus space for a NULL terminator.
+ */
+void utf8_to_utf32(const char* src, size_t src_len, char32_t* dst);
+
+/**
+ * Returns the UTF-16 length of UTF-8 string "src".
+ */
+ssize_t utf8_to_utf16_length(const uint8_t* src, size_t srcLen);
+
+/**
+ * Convert UTF-8 to UTF-16 including surrogate pairs.
+ * Returns a pointer to the end of the string (where a null terminator might go
+ * if you wanted to add one).
+ */
+char16_t* utf8_to_utf16_no_null_terminator(const uint8_t* src, size_t srcLen, char16_t* dst);
+
+/**
+ * Convert UTF-8 to UTF-16 including surrogate pairs. The destination buffer
+ * must be large enough to hold the result as measured by utf8_to_utf16_length
+ * plus an added NULL terminator.
+ */
+void utf8_to_utf16(const uint8_t* src, size_t srcLen, char16_t* dst);
+
+/**
+ * Like utf8_to_utf16_no_null_terminator, but you can supply a maximum length of the
+ * decoded string. The decoded string will fill up to that length; if it is longer
+ * the returned pointer will be to the character after dstLen.
+ */
+char16_t* utf8_to_utf16_n(const uint8_t* src, size_t srcLen, char16_t* dst, size_t dstLen);
+
+}
+
+#endif
diff --git a/media/libstagefright/system/core/include/utils/Vector.h b/media/libstagefright/system/core/include/utils/Vector.h
new file mode 100644
index 000000000..c08577a33
--- /dev/null
+++ b/media/libstagefright/system/core/include/utils/Vector.h
@@ -0,0 +1,404 @@
+/*
+ * Copyright (C) 2005 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_VECTOR_H
+#define ANDROID_VECTOR_H
+
+#include <new>
+#include <assert.h>
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <cutils/log.h>
+
+#include <utils/VectorImpl.h>
+#include <utils/TypeHelpers.h>
+
+// ---------------------------------------------------------------------------
+
+namespace stagefright {
+
+template <typename TYPE>
+class SortedVector;
+
+/*!
+ * The main templated vector class ensuring type safety
+ * while making use of VectorImpl.
+ * This is the class users want to use.
+ */
+
+template <class TYPE>
+class Vector : private VectorImpl
+{
+public:
+ typedef TYPE value_type;
+
+ /*!
+ * Constructors and destructors
+ */
+
+ Vector();
+ Vector(const Vector<TYPE>& rhs);
+ explicit Vector(const SortedVector<TYPE>& rhs);
+ virtual ~Vector();
+
+ /*! copy operator */
+ Vector<TYPE>& operator = (const Vector<TYPE>& rhs);
+
+ Vector<TYPE>& operator = (const SortedVector<TYPE>& rhs);
+
+ /*
+ * empty the vector
+ */
+
+ inline void clear() { VectorImpl::clear(); }
+
+ /*!
+ * vector stats
+ */
+
+ //! returns number of items in the vector
+ inline size_t size() const { return VectorImpl::size(); }
+ //! returns whether or not the vector is empty
+ inline bool isEmpty() const { return VectorImpl::isEmpty(); }
+ //! returns how many items can be stored without reallocating the backing store
+ inline size_t capacity() const { return VectorImpl::capacity(); }
+ //! sets the capacity. capacity can never be reduced less than size()
+ inline ssize_t setCapacity(size_t size) { return VectorImpl::setCapacity(size); }
+
+ /*!
+ * set the size of the vector. items are appended with the default
+ * constructor, or removed from the end as needed.
+ */
+ inline ssize_t resize(size_t size) { return VectorImpl::resize(size); }
+
+ /*!
+ * C-style array access
+ */
+
+ //! read-only C-style access
+ inline const TYPE* array() const;
+ //! read-write C-style access
+ TYPE* editArray();
+
+ /*!
+ * accessors
+ */
+
+ //! read-only access to an item at a given index
+ inline const TYPE& operator [] (size_t index) const;
+ //! alternate name for operator []
+ inline const TYPE& itemAt(size_t index) const;
+ //! stack-usage of the vector. returns the top of the stack (last element)
+ const TYPE& top() const;
+
+ /*!
+ * modifying the array
+ */
+
+ //! copy-on write support, grants write access to an item
+ TYPE& editItemAt(size_t index);
+ //! grants right access to the top of the stack (last element)
+ TYPE& editTop();
+
+ /*!
+ * append/insert another vector
+ */
+
+ //! insert another vector at a given index
+ ssize_t insertVectorAt(const Vector<TYPE>& vector, size_t index);
+
+ //! append another vector at the end of this one
+ ssize_t appendVector(const Vector<TYPE>& vector);
+
+
+ //! insert an array at a given index
+ ssize_t insertArrayAt(const TYPE* array, size_t index, size_t length);
+
+ //! append an array at the end of this vector
+ ssize_t appendArray(const TYPE* array, size_t length);
+
+ /*!
+ * add/insert/replace items
+ */
+
+ //! insert one or several items initialized with their default constructor
+ inline ssize_t insertAt(size_t index, size_t numItems = 1);
+ //! insert one or several items initialized from a prototype item
+ ssize_t insertAt(const TYPE& prototype_item, size_t index, size_t numItems = 1);
+ //! pop the top of the stack (removes the last element). No-op if the stack's empty
+ inline void pop();
+ //! pushes an item initialized with its default constructor
+ inline void push();
+ //! pushes an item on the top of the stack
+ void push(const TYPE& item);
+ //! same as push() but returns the index the item was added at (or an error)
+ inline ssize_t add();
+ //! same as push() but returns the index the item was added at (or an error)
+ ssize_t add(const TYPE& item);
+ //! replace an item with a new one initialized with its default constructor
+ inline ssize_t replaceAt(size_t index);
+ //! replace an item with a new one
+ ssize_t replaceAt(const TYPE& item, size_t index);
+
+ /*!
+ * remove items
+ */
+
+ //! remove several items
+ inline ssize_t removeItemsAt(size_t index, size_t count = 1);
+ //! remove one item
+ inline ssize_t removeAt(size_t index) { return removeItemsAt(index); }
+
+ /*!
+ * sort (stable) the array
+ */
+
+ typedef int (*compar_t)(const TYPE* lhs, const TYPE* rhs);
+ typedef int (*compar_r_t)(const TYPE* lhs, const TYPE* rhs, void* state);
+
+ inline status_t sort(compar_t cmp) {
+ return VectorImpl::sort((VectorImpl::compar_t)cmp);
+ }
+ inline status_t sort(compar_r_t cmp, void* state) {
+ return VectorImpl::sort((VectorImpl::compar_r_t)cmp, state);
+ }
+
+ // for debugging only
+ inline size_t getItemSize() const { return itemSize(); }
+
+
+ /*
+ * these inlines add some level of compatibility with STL. eventually
+ * we should probably turn things around.
+ */
+ typedef TYPE* iterator;
+ typedef TYPE const* const_iterator;
+
+ inline iterator begin() { return editArray(); }
+ inline iterator end() { return editArray() + size(); }
+ inline const_iterator begin() const { return array(); }
+ inline const_iterator end() const { return array() + size(); }
+ inline void reserve(size_t n) { assert(setCapacity(n) >= 0); }
+ inline bool empty() const{ return isEmpty(); }
+ inline void push_back(const TYPE& item) { insertAt(item, size(), 1); }
+ inline void push_front(const TYPE& item) { insertAt(item, 0, 1); }
+ inline iterator erase(iterator pos) {
+ ssize_t index = removeItemsAt(pos-array());
+ return begin() + index;
+ }
+
+protected:
+ virtual void do_construct(void* storage, size_t num) const;
+ virtual void do_destroy(void* storage, size_t num) const;
+ virtual void do_copy(void* dest, const void* from, size_t num) const;
+ virtual void do_splat(void* dest, const void* item, size_t num) const;
+ virtual void do_move_forward(void* dest, const void* from, size_t num) const;
+ virtual void do_move_backward(void* dest, const void* from, size_t num) const;
+};
+
+// Vector<T> can be trivially moved using memcpy() because moving does not
+// require any change to the underlying SharedBuffer contents or reference count.
+template<typename T> struct trait_trivial_move<Vector<T> > { enum { value = true }; };
+
+// ---------------------------------------------------------------------------
+// No user serviceable parts from here...
+// ---------------------------------------------------------------------------
+
+template<class TYPE> inline
+Vector<TYPE>::Vector()
+ : VectorImpl(sizeof(TYPE),
+ ((traits<TYPE>::has_trivial_ctor ? HAS_TRIVIAL_CTOR : 0)
+ |(traits<TYPE>::has_trivial_dtor ? HAS_TRIVIAL_DTOR : 0)
+ |(traits<TYPE>::has_trivial_copy ? HAS_TRIVIAL_COPY : 0))
+ )
+{
+}
+
+template<class TYPE> inline
+Vector<TYPE>::Vector(const Vector<TYPE>& rhs)
+ : VectorImpl(rhs) {
+}
+
+template<class TYPE> inline
+Vector<TYPE>::Vector(const SortedVector<TYPE>& rhs)
+ : VectorImpl(static_cast<const VectorImpl&>(rhs)) {
+}
+
+template<class TYPE> inline
+Vector<TYPE>::~Vector() {
+ finish_vector();
+}
+
+template<class TYPE> inline
+Vector<TYPE>& Vector<TYPE>::operator = (const Vector<TYPE>& rhs) {
+ VectorImpl::operator = (rhs);
+ return *this;
+}
+
+template<class TYPE> inline
+Vector<TYPE>& Vector<TYPE>::operator = (const SortedVector<TYPE>& rhs) {
+ VectorImpl::operator = (static_cast<const VectorImpl&>(rhs));
+ return *this;
+}
+
+template<class TYPE> inline
+const TYPE* Vector<TYPE>::array() const {
+ return static_cast<const TYPE *>(arrayImpl());
+}
+
+template<class TYPE> inline
+TYPE* Vector<TYPE>::editArray() {
+ return static_cast<TYPE *>(editArrayImpl());
+}
+
+
+template<class TYPE> inline
+const TYPE& Vector<TYPE>::operator[](size_t index) const {
+ LOG_FATAL_IF(index>=size(),
+ "%s: index=%u out of range (%u)", __PRETTY_FUNCTION__,
+ int(index), int(size()));
+ return *(array() + index);
+}
+
+template<class TYPE> inline
+const TYPE& Vector<TYPE>::itemAt(size_t index) const {
+ return operator[](index);
+}
+
+template<class TYPE> inline
+const TYPE& Vector<TYPE>::top() const {
+ return *(array() + size() - 1);
+}
+
+template<class TYPE> inline
+TYPE& Vector<TYPE>::editItemAt(size_t index) {
+ return *( static_cast<TYPE *>(editItemLocation(index)) );
+}
+
+template<class TYPE> inline
+TYPE& Vector<TYPE>::editTop() {
+ return *( static_cast<TYPE *>(editItemLocation(size()-1)) );
+}
+
+template<class TYPE> inline
+ssize_t Vector<TYPE>::insertVectorAt(const Vector<TYPE>& vector, size_t index) {
+ return VectorImpl::insertVectorAt(reinterpret_cast<const VectorImpl&>(vector), index);
+}
+
+template<class TYPE> inline
+ssize_t Vector<TYPE>::appendVector(const Vector<TYPE>& vector) {
+ return VectorImpl::appendVector(reinterpret_cast<const VectorImpl&>(vector));
+}
+
+template<class TYPE> inline
+ssize_t Vector<TYPE>::insertArrayAt(const TYPE* array, size_t index, size_t length) {
+ return VectorImpl::insertArrayAt(array, index, length);
+}
+
+template<class TYPE> inline
+ssize_t Vector<TYPE>::appendArray(const TYPE* array, size_t length) {
+ return VectorImpl::appendArray(array, length);
+}
+
+template<class TYPE> inline
+ssize_t Vector<TYPE>::insertAt(const TYPE& item, size_t index, size_t numItems) {
+ return VectorImpl::insertAt(&item, index, numItems);
+}
+
+template<class TYPE> inline
+void Vector<TYPE>::push(const TYPE& item) {
+ return VectorImpl::push(&item);
+}
+
+template<class TYPE> inline
+ssize_t Vector<TYPE>::add(const TYPE& item) {
+ return VectorImpl::add(&item);
+}
+
+template<class TYPE> inline
+ssize_t Vector<TYPE>::replaceAt(const TYPE& item, size_t index) {
+ return VectorImpl::replaceAt(&item, index);
+}
+
+template<class TYPE> inline
+ssize_t Vector<TYPE>::insertAt(size_t index, size_t numItems) {
+ return VectorImpl::insertAt(index, numItems);
+}
+
+template<class TYPE> inline
+void Vector<TYPE>::pop() {
+ VectorImpl::pop();
+}
+
+template<class TYPE> inline
+void Vector<TYPE>::push() {
+ VectorImpl::push();
+}
+
+template<class TYPE> inline
+ssize_t Vector<TYPE>::add() {
+ return VectorImpl::add();
+}
+
+template<class TYPE> inline
+ssize_t Vector<TYPE>::replaceAt(size_t index) {
+ return VectorImpl::replaceAt(index);
+}
+
+template<class TYPE> inline
+ssize_t Vector<TYPE>::removeItemsAt(size_t index, size_t count) {
+ return VectorImpl::removeItemsAt(index, count);
+}
+
+// ---------------------------------------------------------------------------
+
+template<class TYPE>
+void Vector<TYPE>::do_construct(void* storage, size_t num) const {
+ construct_type( reinterpret_cast<TYPE*>(storage), num );
+}
+
+template<class TYPE>
+void Vector<TYPE>::do_destroy(void* storage, size_t num) const {
+ destroy_type( reinterpret_cast<TYPE*>(storage), num );
+}
+
+template<class TYPE>
+void Vector<TYPE>::do_copy(void* dest, const void* from, size_t num) const {
+ copy_type( reinterpret_cast<TYPE*>(dest), reinterpret_cast<const TYPE*>(from), num );
+}
+
+template<class TYPE>
+void Vector<TYPE>::do_splat(void* dest, const void* item, size_t num) const {
+ splat_type( reinterpret_cast<TYPE*>(dest), reinterpret_cast<const TYPE*>(item), num );
+}
+
+template<class TYPE>
+void Vector<TYPE>::do_move_forward(void* dest, const void* from, size_t num) const {
+ move_forward_type( reinterpret_cast<TYPE*>(dest), reinterpret_cast<const TYPE*>(from), num );
+}
+
+template<class TYPE>
+void Vector<TYPE>::do_move_backward(void* dest, const void* from, size_t num) const {
+ move_backward_type( reinterpret_cast<TYPE*>(dest), reinterpret_cast<const TYPE*>(from), num );
+}
+
+}; // namespace stagefright
+
+
+// ---------------------------------------------------------------------------
+
+#endif // ANDROID_VECTOR_H
diff --git a/media/libstagefright/system/core/include/utils/VectorImpl.h b/media/libstagefright/system/core/include/utils/VectorImpl.h
new file mode 100644
index 000000000..b83c9462c
--- /dev/null
+++ b/media/libstagefright/system/core/include/utils/VectorImpl.h
@@ -0,0 +1,183 @@
+/*
+ * Copyright (C) 2005 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_VECTOR_IMPL_H
+#define ANDROID_VECTOR_IMPL_H
+
+#include <assert.h>
+#include <stdint.h>
+#include <sys/types.h>
+#include <utils/Errors.h>
+
+// ---------------------------------------------------------------------------
+// No user serviceable parts in here...
+// ---------------------------------------------------------------------------
+
+namespace stagefright {
+
+/*!
+ * Implementation of the guts of the vector<> class
+ * this ensures backward binary compatibility and
+ * reduces code size.
+ * For performance reasons, we expose mStorage and mCount
+ * so these fields are set in stone.
+ *
+ */
+
+class VectorImpl
+{
+public:
+ enum { // flags passed to the ctor
+ HAS_TRIVIAL_CTOR = 0x00000001,
+ HAS_TRIVIAL_DTOR = 0x00000002,
+ HAS_TRIVIAL_COPY = 0x00000004,
+ };
+
+ VectorImpl(size_t itemSize, uint32_t flags);
+ VectorImpl(const VectorImpl& rhs);
+ virtual ~VectorImpl();
+
+ /*! must be called from subclasses destructor */
+ void finish_vector();
+
+ VectorImpl& operator = (const VectorImpl& rhs);
+
+ /*! C-style array access */
+ inline const void* arrayImpl() const { return mStorage; }
+ void* editArrayImpl();
+
+ /*! vector stats */
+ inline size_t size() const { return mCount; }
+ inline bool isEmpty() const { return mCount == 0; }
+ size_t capacity() const;
+ ssize_t setCapacity(size_t size);
+ ssize_t resize(size_t size);
+
+ /*! append/insert another vector or array */
+ ssize_t insertVectorAt(const VectorImpl& vector, size_t index);
+ ssize_t appendVector(const VectorImpl& vector);
+ ssize_t insertArrayAt(const void* array, size_t index, size_t length);
+ ssize_t appendArray(const void* array, size_t length);
+
+ /*! add/insert/replace items */
+ ssize_t insertAt(size_t where, size_t numItems = 1);
+ ssize_t insertAt(const void* item, size_t where, size_t numItems = 1);
+ void pop();
+ void push();
+ void push(const void* item);
+ ssize_t add();
+ ssize_t add(const void* item);
+ ssize_t replaceAt(size_t index);
+ ssize_t replaceAt(const void* item, size_t index);
+
+ /*! remove items */
+ ssize_t removeItemsAt(size_t index, size_t count = 1);
+ void clear();
+
+ const void* itemLocation(size_t index) const;
+ void* editItemLocation(size_t index);
+
+ typedef int (*compar_t)(const void* lhs, const void* rhs);
+ typedef int (*compar_r_t)(const void* lhs, const void* rhs, void* state);
+ status_t sort(compar_t cmp);
+ status_t sort(compar_r_t cmp, void* state);
+
+protected:
+ size_t itemSize() const;
+ void release_storage();
+
+ virtual void do_construct(void* storage, size_t num) const = 0;
+ virtual void do_destroy(void* storage, size_t num) const = 0;
+ virtual void do_copy(void* dest, const void* from, size_t num) const = 0;
+ virtual void do_splat(void* dest, const void* item, size_t num) const = 0;
+ virtual void do_move_forward(void* dest, const void* from, size_t num) const = 0;
+ virtual void do_move_backward(void* dest, const void* from, size_t num) const = 0;
+
+private:
+ void* _grow(size_t where, size_t amount);
+ void _shrink(size_t where, size_t amount);
+
+ inline void _do_construct(void* storage, size_t num) const;
+ inline void _do_destroy(void* storage, size_t num) const;
+ inline void _do_copy(void* dest, const void* from, size_t num) const;
+ inline void _do_splat(void* dest, const void* item, size_t num) const;
+ inline void _do_move_forward(void* dest, const void* from, size_t num) const;
+ inline void _do_move_backward(void* dest, const void* from, size_t num) const;
+
+ // These 2 fields are exposed in the inlines below,
+ // so they're set in stone.
+ void * mStorage; // base address of the vector
+ size_t mCount; // number of items
+
+ const uint32_t mFlags;
+ const size_t mItemSize;
+};
+
+
+
+class SortedVectorImpl : public VectorImpl
+{
+public:
+ SortedVectorImpl(size_t itemSize, uint32_t flags);
+ SortedVectorImpl(const VectorImpl& rhs);
+ virtual ~SortedVectorImpl();
+
+ SortedVectorImpl& operator = (const SortedVectorImpl& rhs);
+
+ //! finds the index of an item
+ ssize_t indexOf(const void* item) const;
+
+ //! finds where this item should be inserted
+ size_t orderOf(const void* item) const;
+
+ //! add an item in the right place (or replaces it if there is one)
+ ssize_t add(const void* item);
+
+ //! merges a vector into this one
+ ssize_t merge(const VectorImpl& vector);
+ ssize_t merge(const SortedVectorImpl& vector);
+
+ //! removes an item
+ ssize_t remove(const void* item);
+
+protected:
+ virtual int do_compare(const void* lhs, const void* rhs) const = 0;
+
+private:
+ ssize_t _indexOrderOf(const void* item, size_t* order = 0) const;
+
+ // these are made private, because they can't be used on a SortedVector
+ // (they don't have an implementation either)
+ ssize_t add();
+ void pop();
+ void push();
+ void push(const void* item);
+ ssize_t insertVectorAt(const VectorImpl& vector, size_t index);
+ ssize_t appendVector(const VectorImpl& vector);
+ ssize_t insertArrayAt(const void* array, size_t index, size_t length);
+ ssize_t appendArray(const void* array, size_t length);
+ ssize_t insertAt(size_t where, size_t numItems = 1);
+ ssize_t insertAt(const void* item, size_t where, size_t numItems = 1);
+ ssize_t replaceAt(size_t index);
+ ssize_t replaceAt(const void* item, size_t index);
+};
+
+}; // namespace stagefright
+
+
+// ---------------------------------------------------------------------------
+
+#endif // ANDROID_VECTOR_IMPL_H
diff --git a/media/libstagefright/system/core/libcutils/strdup16to8.c b/media/libstagefright/system/core/libcutils/strdup16to8.c
new file mode 100644
index 000000000..1a8ba8674
--- /dev/null
+++ b/media/libstagefright/system/core/libcutils/strdup16to8.c
@@ -0,0 +1,168 @@
+/* libs/cutils/strdup16to8.c
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include <limits.h> /* for SIZE_MAX */
+
+#include <cutils/jstring.h>
+#include <assert.h>
+#include <stdlib.h>
+
+
+/**
+ * Given a UTF-16 string, compute the length of the corresponding UTF-8
+ * string in bytes.
+ */
+extern size_t strnlen16to8(const char16_t* utf16Str, size_t len)
+{
+ size_t utf8Len = 0;
+
+ /* A small note on integer overflow. The result can
+ * potentially be as big as 3*len, which will overflow
+ * for len > SIZE_MAX/3.
+ *
+ * Moreover, the result of a strnlen16to8 is typically used
+ * to allocate a destination buffer to strncpy16to8 which
+ * requires one more byte to terminate the UTF-8 copy, and
+ * this is generally done by careless users by incrementing
+ * the result without checking for integer overflows, e.g.:
+ *
+ * dst = malloc(strnlen16to8(utf16,len)+1)
+ *
+ * Due to this, the following code will try to detect
+ * overflows, and never return more than (SIZE_MAX-1)
+ * when it detects one. A careless user will try to malloc
+ * SIZE_MAX bytes, which will return NULL which can at least
+ * be detected appropriately.
+ *
+ * As far as I know, this function is only used by strndup16(),
+ * but better be safe than sorry.
+ */
+
+ /* Fast path for the usual case where 3*len is < SIZE_MAX-1.
+ */
+ if (len < (SIZE_MAX-1)/3) {
+ while (len--) {
+ unsigned int uic = *utf16Str++;
+
+ if (uic > 0x07ff)
+ utf8Len += 3;
+ else if (uic > 0x7f || uic == 0)
+ utf8Len += 2;
+ else
+ utf8Len++;
+ }
+ return utf8Len;
+ }
+
+ /* The slower but paranoid version */
+ while (len--) {
+ unsigned int uic = *utf16Str++;
+ size_t utf8Cur = utf8Len;
+
+ if (uic > 0x07ff)
+ utf8Len += 3;
+ else if (uic > 0x7f || uic == 0)
+ utf8Len += 2;
+ else
+ utf8Len++;
+
+ if (utf8Len < utf8Cur) /* overflow detected */
+ return SIZE_MAX-1;
+ }
+
+ /* don't return SIZE_MAX to avoid common user bug */
+ if (utf8Len == SIZE_MAX)
+ utf8Len = SIZE_MAX-1;
+
+ return utf8Len;
+}
+
+
+/**
+ * Convert a Java-Style UTF-16 string + length to a JNI-Style UTF-8 string.
+ *
+ * This basically means: embedded \0's in the UTF-16 string are encoded
+ * as "0xc0 0x80"
+ *
+ * Make sure you allocate "utf8Str" with the result of strlen16to8() + 1,
+ * not just "len".
+ *
+ * Please note, a terminated \0 is always added, so your result will always
+ * be "strlen16to8() + 1" bytes long.
+ */
+extern char* strncpy16to8(char* utf8Str, const char16_t* utf16Str, size_t len)
+{
+ char* utf8cur = utf8Str;
+
+ /* Note on overflows: We assume the user did check the result of
+ * strnlen16to8() properly or at a minimum checked the result of
+ * its malloc(SIZE_MAX) in case of overflow.
+ */
+ while (len--) {
+ unsigned int uic = *utf16Str++;
+
+ if (uic > 0x07ff) {
+ *utf8cur++ = (uic >> 12) | 0xe0;
+ *utf8cur++ = ((uic >> 6) & 0x3f) | 0x80;
+ *utf8cur++ = (uic & 0x3f) | 0x80;
+ } else if (uic > 0x7f || uic == 0) {
+ *utf8cur++ = (uic >> 6) | 0xc0;
+ *utf8cur++ = (uic & 0x3f) | 0x80;
+ } else {
+ *utf8cur++ = uic;
+
+ if (uic == 0) {
+ break;
+ }
+ }
+ }
+
+ *utf8cur = '\0';
+
+ return utf8Str;
+}
+
+/**
+ * Convert a UTF-16 string to UTF-8.
+ *
+ */
+char * strndup16to8 (const char16_t* s, size_t n)
+{
+ char* ret;
+ size_t len;
+
+ if (s == NULL) {
+ return NULL;
+ }
+
+ len = strnlen16to8(s, n);
+
+ /* We are paranoid, and we check for SIZE_MAX-1
+ * too since it is an overflow value for our
+ * strnlen16to8 implementation.
+ */
+ if (len >= SIZE_MAX-1)
+ return NULL;
+
+ ret = malloc(len + 1);
+ if (ret == NULL)
+ return NULL;
+
+ strncpy16to8 (ret, s, n);
+
+ return ret;
+}
diff --git a/media/libstagefright/system/core/liblog/fake_log_device.c b/media/libstagefright/system/core/liblog/fake_log_device.c
new file mode 100644
index 000000000..9a4f07f15
--- /dev/null
+++ b/media/libstagefright/system/core/liblog/fake_log_device.c
@@ -0,0 +1,737 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+/*
+ * Intercepts log messages intended for the Android log device.
+ * When running in the context of the simulator, the messages are
+ * passed on to the underlying (fake) log device. When not in the
+ * simulator, messages are printed to stderr.
+ */
+#include <log/logd.h>
+
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+
+#ifdef HAVE_PTHREADS
+#include <pthread.h>
+#endif
+
+#ifdef _MSC_VER
+#include <io.h>
+#include <process.h>
+#if _MSC_VER < 1900
+#include <nspr/prprf.h>
+#define snprintf PR_snprintf
+#endif
+
+/* We don't want to indent large blocks because it causes unnecessary merge
+ * conflicts */
+#define UNINDENTED_BLOCK_START {
+#define UNINDENTED_BLOCK_END }
+#else
+#define UNINDENTED_BLOCK_START
+#define UNINDENTED_BLOCK_END
+#endif
+
+#ifdef _MSC_VER
+#include <io.h>
+#include <process.h>
+
+/* We don't want to indent large blocks because it causes unnecessary merge
+ * conflicts */
+#define UNINDENTED_BLOCK_START {
+#define UNINDENTED_BLOCK_END }
+#else
+#define UNINDENTED_BLOCK_START
+#define UNINDENTED_BLOCK_END
+#endif
+
+#define kMaxTagLen 16 /* from the long-dead utils/Log.cpp */
+
+#define kTagSetSize 16 /* arbitrary */
+
+#if 0
+#define TRACE(...) printf("fake_log_device: " __VA_ARGS__)
+#else
+#define TRACE(...) ((void)0)
+#endif
+
+/* from the long-dead utils/Log.cpp */
+typedef enum {
+ FORMAT_OFF = 0,
+ FORMAT_BRIEF,
+ FORMAT_PROCESS,
+ FORMAT_TAG,
+ FORMAT_THREAD,
+ FORMAT_RAW,
+ FORMAT_TIME,
+ FORMAT_THREADTIME,
+ FORMAT_LONG
+} LogFormat;
+
+
+/*
+ * Log driver state.
+ */
+typedef struct LogState {
+ /* the fake fd that's seen by the user */
+ int fakeFd;
+
+ /* a printable name for this fake device */
+ char *debugName;
+
+ /* nonzero if this is a binary log */
+ int isBinary;
+
+ /* global minimum priority */
+ int globalMinPriority;
+
+ /* output format */
+ LogFormat outputFormat;
+
+ /* tags and priorities */
+ struct {
+ char tag[kMaxTagLen];
+ int minPriority;
+ } tagSet[kTagSetSize];
+} LogState;
+
+
+#ifdef HAVE_PTHREADS
+/*
+ * Locking. Since we're emulating a device, we need to be prepared
+ * to have multiple callers at the same time. This lock is used
+ * to both protect the fd list and to prevent LogStates from being
+ * freed out from under a user.
+ */
+static pthread_mutex_t fakeLogDeviceLock = PTHREAD_MUTEX_INITIALIZER;
+
+static void lock()
+{
+ pthread_mutex_lock(&fakeLogDeviceLock);
+}
+
+static void unlock()
+{
+ pthread_mutex_unlock(&fakeLogDeviceLock);
+}
+#else // !HAVE_PTHREADS
+#define lock() ((void)0)
+#define unlock() ((void)0)
+#endif // !HAVE_PTHREADS
+
+
+/*
+ * File descriptor management.
+ */
+#define FAKE_FD_BASE 10000
+#define MAX_OPEN_LOGS 16
+static LogState *openLogTable[MAX_OPEN_LOGS];
+
+/*
+ * Allocate an fd and associate a new LogState with it.
+ * The fd is available via the fakeFd field of the return value.
+ */
+static LogState *createLogState()
+{
+ size_t i;
+
+ for (i = 0; i < sizeof(openLogTable); i++) {
+ if (openLogTable[i] == NULL) {
+ openLogTable[i] = calloc(1, sizeof(LogState));
+ openLogTable[i]->fakeFd = FAKE_FD_BASE + i;
+ return openLogTable[i];
+ }
+ }
+ return NULL;
+}
+
+/*
+ * Translate an fd to a LogState.
+ */
+static LogState *fdToLogState(int fd)
+{
+ if (fd >= FAKE_FD_BASE && fd < FAKE_FD_BASE + MAX_OPEN_LOGS) {
+ return openLogTable[fd - FAKE_FD_BASE];
+ }
+ return NULL;
+}
+
+/*
+ * Unregister the fake fd and free the memory it pointed to.
+ */
+static void deleteFakeFd(int fd)
+{
+ LogState *ls;
+
+ lock();
+
+ ls = fdToLogState(fd);
+ if (ls != NULL) {
+ openLogTable[fd - FAKE_FD_BASE] = NULL;
+ free(ls->debugName);
+ free(ls);
+ }
+
+ unlock();
+}
+
+/*
+ * Configure logging based on ANDROID_LOG_TAGS environment variable. We
+ * need to parse a string that looks like
+ *
+ * *:v jdwp:d dalvikvm:d dalvikvm-gc:i dalvikvmi:i
+ *
+ * The tag (or '*' for the global level) comes first, followed by a colon
+ * and a letter indicating the minimum priority level we're expected to log.
+ * This can be used to reveal or conceal logs with specific tags.
+ *
+ * We also want to check ANDROID_PRINTF_LOG to determine how the output
+ * will look.
+ */
+static void configureInitialState(const char* pathName, LogState* logState)
+{
+ static const int kDevLogLen = sizeof("/dev/log/") - 1;
+
+ logState->debugName = strdup(pathName);
+
+ /* identify binary logs */
+ if (strcmp(pathName + kDevLogLen, "events") == 0) {
+ logState->isBinary = 1;
+ }
+
+ /* global min priority defaults to "info" level */
+ logState->globalMinPriority = ANDROID_LOG_INFO;
+
+ /*
+ * This is based on the the long-dead utils/Log.cpp code.
+ */
+ UNINDENTED_BLOCK_START
+ const char* tags = getenv("ANDROID_LOG_TAGS");
+ TRACE("Found ANDROID_LOG_TAGS='%s'\n", tags);
+ if (tags != NULL) {
+ int entry = 0;
+
+ while (*tags != '\0') {
+ char tagName[kMaxTagLen];
+ int i, minPrio;
+
+ while (isspace(*tags))
+ tags++;
+
+ i = 0;
+ while (*tags != '\0' && !isspace(*tags) && *tags != ':' &&
+ i < kMaxTagLen)
+ {
+ tagName[i++] = *tags++;
+ }
+ if (i == kMaxTagLen) {
+ TRACE("ERROR: env tag too long (%d chars max)\n", kMaxTagLen-1);
+ return;
+ }
+ tagName[i] = '\0';
+
+ /* default priority, if there's no ":" part; also zero out '*' */
+ minPrio = ANDROID_LOG_VERBOSE;
+ if (tagName[0] == '*' && tagName[1] == '\0') {
+ minPrio = ANDROID_LOG_DEBUG;
+ tagName[0] = '\0';
+ }
+
+ if (*tags == ':') {
+ tags++;
+ if (*tags >= '0' && *tags <= '9') {
+ if (*tags >= ('0' + ANDROID_LOG_SILENT))
+ minPrio = ANDROID_LOG_VERBOSE;
+ else
+ minPrio = *tags - '\0';
+ } else {
+ switch (*tags) {
+ case 'v': minPrio = ANDROID_LOG_VERBOSE; break;
+ case 'd': minPrio = ANDROID_LOG_DEBUG; break;
+ case 'i': minPrio = ANDROID_LOG_INFO; break;
+ case 'w': minPrio = ANDROID_LOG_WARN; break;
+ case 'e': minPrio = ANDROID_LOG_ERROR; break;
+ case 'f': minPrio = ANDROID_LOG_FATAL; break;
+ case 's': minPrio = ANDROID_LOG_SILENT; break;
+ default: minPrio = ANDROID_LOG_DEFAULT; break;
+ }
+ }
+
+ tags++;
+ if (*tags != '\0' && !isspace(*tags)) {
+ TRACE("ERROR: garbage in tag env; expected whitespace\n");
+ TRACE(" env='%s'\n", tags);
+ return;
+ }
+ }
+
+ if (tagName[0] == 0) {
+ logState->globalMinPriority = minPrio;
+ TRACE("+++ global min prio %d\n", logState->globalMinPriority);
+ } else {
+ logState->tagSet[entry].minPriority = minPrio;
+ strcpy(logState->tagSet[entry].tag, tagName);
+ TRACE("+++ entry %d: %s:%d\n",
+ entry,
+ logState->tagSet[entry].tag,
+ logState->tagSet[entry].minPriority);
+ entry++;
+ }
+ }
+ }
+ UNINDENTED_BLOCK_END
+
+ /*
+ * Taken from the long-dead utils/Log.cpp
+ */
+ UNINDENTED_BLOCK_START
+ const char* fstr = getenv("ANDROID_PRINTF_LOG");
+ LogFormat format;
+ if (fstr == NULL) {
+ format = FORMAT_BRIEF;
+ } else {
+ if (strcmp(fstr, "brief") == 0)
+ format = FORMAT_BRIEF;
+ else if (strcmp(fstr, "process") == 0)
+ format = FORMAT_PROCESS;
+ else if (strcmp(fstr, "tag") == 0)
+ format = FORMAT_PROCESS;
+ else if (strcmp(fstr, "thread") == 0)
+ format = FORMAT_PROCESS;
+ else if (strcmp(fstr, "raw") == 0)
+ format = FORMAT_PROCESS;
+ else if (strcmp(fstr, "time") == 0)
+ format = FORMAT_PROCESS;
+ else if (strcmp(fstr, "long") == 0)
+ format = FORMAT_PROCESS;
+ else
+ format = (LogFormat) atoi(fstr); // really?!
+ }
+
+ logState->outputFormat = format;
+ UNINDENTED_BLOCK_END
+}
+
+/*
+ * Return a human-readable string for the priority level. Always returns
+ * a valid string.
+ */
+static const char* getPriorityString(int priority)
+{
+ /* the first character of each string should be unique */
+ static const char* priorityStrings[] = {
+ "Verbose", "Debug", "Info", "Warn", "Error", "Assert"
+ };
+ int idx;
+
+ idx = (int) priority - (int) ANDROID_LOG_VERBOSE;
+ if (idx < 0 ||
+ idx >= (int) (sizeof(priorityStrings) / sizeof(priorityStrings[0])))
+ return "?unknown?";
+ return priorityStrings[idx];
+}
+
+#ifndef HAVE_WRITEV
+/*
+ * Some platforms like WIN32 do not have writev().
+ * Make up something to replace it.
+ */
+static ssize_t fake_writev(int fd, const struct iovec *iov, int iovcnt) {
+ int result = 0;
+ const struct iovec* end = iov + iovcnt;
+ for (; iov < end; iov++) {
+ int w = write(fd, iov->iov_base, iov->iov_len);
+ if (w != iov->iov_len) {
+ if (w < 0)
+ return w;
+ return result + w;
+ }
+ result += w;
+ }
+ return result;
+}
+
+#define writev fake_writev
+#endif
+
+
+/*
+ * Write a filtered log message to stderr.
+ *
+ * Log format parsing taken from the long-dead utils/Log.cpp.
+ */
+static void showLog(LogState *state,
+ int logPrio, const char* tag, const char* msg)
+{
+#if defined(HAVE_LOCALTIME_R)
+ struct tm tmBuf;
+#endif
+ struct tm* ptm;
+ char timeBuf[32];
+ char prefixBuf[128], suffixBuf[128];
+ char priChar;
+ time_t when;
+#ifdef _MSC_VER
+ int pid, tid;
+#else
+ pid_t pid, tid;
+#endif
+
+ TRACE("LOG %d: %s %s", logPrio, tag, msg);
+
+ priChar = getPriorityString(logPrio)[0];
+ when = time(NULL);
+ pid = tid = getpid(); // find gettid()?
+
+ /*
+ * Get the current date/time in pretty form
+ *
+ * It's often useful when examining a log with "less" to jump to
+ * a specific point in the file by searching for the date/time stamp.
+ * For this reason it's very annoying to have regexp meta characters
+ * in the time stamp. Don't use forward slashes, parenthesis,
+ * brackets, asterisks, or other special chars here.
+ */
+#if defined(HAVE_LOCALTIME_R)
+ ptm = localtime_r(&when, &tmBuf);
+#else
+ ptm = localtime(&when);
+#endif
+ //strftime(timeBuf, sizeof(timeBuf), "%Y-%m-%d %H:%M:%S", ptm);
+ strftime(timeBuf, sizeof(timeBuf), "%m-%d %H:%M:%S", ptm);
+
+ /*
+ * Construct a buffer containing the log header and log message.
+ */
+ UNINDENTED_BLOCK_START
+ size_t prefixLen, suffixLen;
+
+ switch (state->outputFormat) {
+ case FORMAT_TAG:
+ prefixLen = snprintf(prefixBuf, sizeof(prefixBuf),
+ "%c/%-8s: ", priChar, tag);
+ strcpy(suffixBuf, "\n"); suffixLen = 1;
+ break;
+ case FORMAT_PROCESS:
+ prefixLen = snprintf(prefixBuf, sizeof(prefixBuf),
+ "%c(%5d) ", priChar, pid);
+ suffixLen = snprintf(suffixBuf, sizeof(suffixBuf),
+ " (%s)\n", tag);
+ break;
+ case FORMAT_THREAD:
+ prefixLen = snprintf(prefixBuf, sizeof(prefixBuf),
+ "%c(%5d:%5d) ", priChar, pid, tid);
+ strcpy(suffixBuf, "\n"); suffixLen = 1;
+ break;
+ case FORMAT_RAW:
+ prefixBuf[0] = 0; prefixLen = 0;
+ strcpy(suffixBuf, "\n"); suffixLen = 1;
+ break;
+ case FORMAT_TIME:
+ prefixLen = snprintf(prefixBuf, sizeof(prefixBuf),
+ "%s %-8s\n\t", timeBuf, tag);
+ strcpy(suffixBuf, "\n"); suffixLen = 1;
+ break;
+ case FORMAT_THREADTIME:
+ prefixLen = snprintf(prefixBuf, sizeof(prefixBuf),
+ "%s %5d %5d %c %-8s \n\t", timeBuf, pid, tid, priChar, tag);
+ strcpy(suffixBuf, "\n"); suffixLen = 1;
+ break;
+ case FORMAT_LONG:
+ prefixLen = snprintf(prefixBuf, sizeof(prefixBuf),
+ "[ %s %5d:%5d %c/%-8s ]\n",
+ timeBuf, pid, tid, priChar, tag);
+ strcpy(suffixBuf, "\n\n"); suffixLen = 2;
+ break;
+ default:
+ prefixLen = snprintf(prefixBuf, sizeof(prefixBuf),
+ "%c/%-8s(%5d): ", priChar, tag, pid);
+ strcpy(suffixBuf, "\n"); suffixLen = 1;
+ break;
+ }
+
+ /*
+ * Figure out how many lines there will be.
+ */
+ UNINDENTED_BLOCK_START
+ const char* end = msg + strlen(msg);
+ size_t numLines = 0;
+ const char* p = msg;
+ while (p < end) {
+ if (*p++ == '\n') numLines++;
+ }
+ if (p > msg && *(p-1) != '\n') numLines++;
+
+ /*
+ * Create an array of iovecs large enough to write all of
+ * the lines with a prefix and a suffix.
+ */
+ UNINDENTED_BLOCK_START
+ #define INLINE_VECS 6
+ const size_t MAX_LINES = ((size_t)~0)/(3*sizeof(struct iovec*));
+ struct iovec stackVec[INLINE_VECS];
+ struct iovec* vec = stackVec;
+ size_t numVecs;
+
+ if (numLines > MAX_LINES)
+ numLines = MAX_LINES;
+
+ numVecs = numLines*3; // 3 iovecs per line.
+ if (numVecs > INLINE_VECS) {
+ vec = (struct iovec*)malloc(sizeof(struct iovec)*numVecs);
+ if (vec == NULL) {
+ msg = "LOG: write failed, no memory";
+ numVecs = 3;
+ numLines = 1;
+ vec = stackVec;
+ }
+ }
+
+ /*
+ * Fill in the iovec pointers.
+ */
+ p = msg;
+ UNINDENTED_BLOCK_START
+ struct iovec* v = vec;
+ int totalLen = 0;
+ while (numLines > 0 && p < end) {
+ if (prefixLen > 0) {
+ v->iov_base = prefixBuf;
+ v->iov_len = prefixLen;
+ totalLen += prefixLen;
+ v++;
+ }
+ UNINDENTED_BLOCK_START
+ const char* start = p;
+ while (p < end && *p != '\n') p++;
+ if ((p-start) > 0) {
+ v->iov_base = (void*)start;
+ v->iov_len = p-start;
+ totalLen += p-start;
+ v++;
+ }
+ if (*p == '\n') p++;
+ if (suffixLen > 0) {
+ v->iov_base = suffixBuf;
+ v->iov_len = suffixLen;
+ totalLen += suffixLen;
+ v++;
+ }
+ numLines -= 1;
+ UNINDENTED_BLOCK_END
+ }
+
+ /*
+ * Write the entire message to the log file with a single writev() call.
+ * We need to use this rather than a collection of printf()s on a FILE*
+ * because of multi-threading and multi-process issues.
+ *
+ * If the file was not opened with O_APPEND, this will produce interleaved
+ * output when called on the same file from multiple processes.
+ *
+ * If the file descriptor is actually a network socket, the writev()
+ * call may return with a partial write. Putting the writev() call in
+ * a loop can result in interleaved data. This can be alleviated
+ * somewhat by wrapping the writev call in the Mutex.
+ */
+
+ for(;;) {
+ int cc = writev(fileno(stderr), vec, v-vec);
+
+ if (cc == totalLen) break;
+
+ if (cc < 0) {
+ if(errno == EINTR) continue;
+
+ /* can't really log the failure; for now, throw out a stderr */
+ fprintf(stderr, "+++ LOG: write failed (errno=%d)\n", errno);
+ break;
+ } else {
+ /* shouldn't happen when writing to file or tty */
+ fprintf(stderr, "+++ LOG: write partial (%d of %d)\n", cc, totalLen);
+ break;
+ }
+ }
+
+ /* if we allocated storage for the iovecs, free it */
+ if (vec != stackVec)
+ free(vec);
+ UNINDENTED_BLOCK_END
+ UNINDENTED_BLOCK_END
+ UNINDENTED_BLOCK_END
+ UNINDENTED_BLOCK_END
+}
+
+
+/*
+ * Receive a log message. We happen to know that "vector" has three parts:
+ *
+ * priority (1 byte)
+ * tag (N bytes -- null-terminated ASCII string)
+ * message (N bytes -- null-terminated ASCII string)
+ */
+static ssize_t logWritev(int fd, const struct iovec* vector, int count)
+{
+ LogState* state;
+
+ /* Make sure that no-one frees the LogState while we're using it.
+ * Also guarantees that only one thread is in showLog() at a given
+ * time (if it matters).
+ */
+ lock();
+
+ state = fdToLogState(fd);
+ if (state == NULL) {
+ errno = EBADF;
+ goto error;
+ }
+
+ if (state->isBinary) {
+ TRACE("%s: ignoring binary log\n", state->debugName);
+ goto bail;
+ }
+
+ if (count != 3) {
+ TRACE("%s: writevLog with count=%d not expected\n",
+ state->debugName, count);
+ goto error;
+ }
+
+ /* pull out the three fields */
+ UNINDENTED_BLOCK_START
+ int logPrio = *(const char*)vector[0].iov_base;
+ const char* tag = (const char*) vector[1].iov_base;
+ const char* msg = (const char*) vector[2].iov_base;
+
+ /* see if this log tag is configured */
+ int i;
+ int minPrio = state->globalMinPriority;
+ for (i = 0; i < kTagSetSize; i++) {
+ if (state->tagSet[i].minPriority == ANDROID_LOG_UNKNOWN)
+ break; /* reached end of configured values */
+
+ if (strcmp(state->tagSet[i].tag, tag) == 0) {
+ //TRACE("MATCH tag '%s'\n", tag);
+ minPrio = state->tagSet[i].minPriority;
+ break;
+ }
+ }
+
+ if (logPrio >= minPrio) {
+ showLog(state, logPrio, tag, msg);
+ } else {
+ //TRACE("+++ NOLOG(%d): %s %s", logPrio, tag, msg);
+ }
+ UNINDENTED_BLOCK_END
+
+bail:
+ unlock();
+ return vector[0].iov_len + vector[1].iov_len + vector[2].iov_len;
+error:
+ unlock();
+ return -1;
+}
+
+/*
+ * Free up our state and close the fake descriptor.
+ */
+static int logClose(int fd)
+{
+ deleteFakeFd(fd);
+ return 0;
+}
+
+/*
+ * Open a log output device and return a fake fd.
+ */
+static int logOpen(const char* pathName, int flags)
+{
+ LogState *logState;
+ int fd = -1;
+
+ lock();
+
+ logState = createLogState();
+ if (logState != NULL) {
+ configureInitialState(pathName, logState);
+ fd = logState->fakeFd;
+ } else {
+ errno = ENFILE;
+ }
+
+ unlock();
+
+ return fd;
+}
+
+
+/*
+ * Runtime redirection. If this binary is running in the simulator,
+ * just pass log messages to the emulated device. If it's running
+ * outside of the simulator, write the log messages to stderr.
+ */
+
+static int (*redirectOpen)(const char *pathName, int flags) = NULL;
+static int (*redirectClose)(int fd) = NULL;
+static ssize_t (*redirectWritev)(int fd, const struct iovec* vector, int count)
+ = NULL;
+
+static void setRedirects()
+{
+ const char *ws;
+
+ /* Wrapsim sets this environment variable on children that it's
+ * created using its LD_PRELOAD wrapper.
+ */
+ ws = getenv("ANDROID_WRAPSIM");
+ if (ws != NULL && strcmp(ws, "1") == 0) {
+ /* We're running inside wrapsim, so we can just write to the device. */
+ redirectOpen = (int (*)(const char *pathName, int flags))open;
+ redirectClose = close;
+ redirectWritev = writev;
+ } else {
+ /* There's no device to delegate to; handle the logging ourselves. */
+ redirectOpen = logOpen;
+ redirectClose = logClose;
+ redirectWritev = logWritev;
+ }
+}
+
+int fakeLogOpen(const char *pathName, int flags)
+{
+ if (redirectOpen == NULL) {
+ setRedirects();
+ }
+ return redirectOpen(pathName, flags);
+}
+
+int fakeLogClose(int fd)
+{
+ /* Assume that open() was called first. */
+ return redirectClose(fd);
+}
+
+ssize_t fakeLogWritev(int fd, const struct iovec* vector, int count)
+{
+ /* Assume that open() was called first. */
+ return redirectWritev(fd, vector, count);
+}
+
+#undef UNINDENTED_BLOCK_START
+#undef UNINDENTED_BLOCK_END
diff --git a/media/libstagefright/system/core/liblog/logd_write.c b/media/libstagefright/system/core/liblog/logd_write.c
new file mode 100644
index 000000000..7bdf61e87
--- /dev/null
+++ b/media/libstagefright/system/core/liblog/logd_write.c
@@ -0,0 +1,312 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <time.h>
+#include <stdio.h>
+#ifdef HAVE_PTHREADS
+#include <pthread.h>
+#endif
+#include <unistd.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <log/logger.h>
+#include <log/logd.h>
+#include <log/log.h>
+
+#define LOG_BUF_SIZE 1024
+
+#ifdef _MSC_VER
+#if _MSC_VER < 1900
+#include <nspr/prprf.h>
+#define snprintf PR_snprintf
+#endif
+#define __builtin_trap abort
+static int W_OK = 0;
+static int access(char* c, int i) { return -1; }
+#endif
+
+#if FAKE_LOG_DEVICE
+int fakeLogOpen(const char *pathName, int flags);
+ssize_t fakeLogWritev(int fd, const struct iovec* vector, int count);
+int fakeLogClose(int fd);
+
+// This will be defined when building for the host.
+#define log_open(pathname, flags) fakeLogOpen(pathname, flags)
+#define log_writev(filedes, vector, count) fakeLogWritev(filedes, vector, count)
+#define log_close(filedes) fakeLogClose(filedes)
+#else
+#define log_open(pathname, flags) open(pathname, (flags) | O_CLOEXEC)
+#define log_writev(filedes, vector, count) writev(filedes, vector, count)
+#define log_close(filedes) close(filedes)
+#endif
+
+static int __write_to_log_init(log_id_t, struct iovec *vec, size_t nr);
+static int (*write_to_log)(log_id_t, struct iovec *vec, size_t nr) = __write_to_log_init;
+#ifdef HAVE_PTHREADS
+static pthread_mutex_t log_init_lock = PTHREAD_MUTEX_INITIALIZER;
+#endif
+
+static int log_fds[(int)LOG_ID_MAX] = { -1, -1, -1, -1 };
+
+/*
+ * This is used by the C++ code to decide if it should write logs through
+ * the C code. Basically, if /dev/log/... is available, we're running in
+ * the simulator rather than a desktop tool and want to use the device.
+ */
+static enum {
+ kLogUninitialized, kLogNotAvailable, kLogAvailable
+} g_log_status = kLogUninitialized;
+int __android_log_dev_available(void)
+{
+ if (g_log_status == kLogUninitialized) {
+ if (access("/dev/"LOGGER_LOG_MAIN, W_OK) == 0)
+ g_log_status = kLogAvailable;
+ else
+ g_log_status = kLogNotAvailable;
+ }
+
+ return (g_log_status == kLogAvailable);
+}
+
+static int __write_to_log_null(log_id_t log_fd, struct iovec *vec, size_t nr)
+{
+ return -1;
+}
+
+static int __write_to_log_kernel(log_id_t log_id, struct iovec *vec, size_t nr)
+{
+ ssize_t ret;
+ int log_fd;
+
+ if (/*(int)log_id >= 0 &&*/ (int)log_id < (int)LOG_ID_MAX) {
+ log_fd = log_fds[(int)log_id];
+ } else {
+ return EBADF;
+ }
+
+ do {
+ ret = log_writev(log_fd, vec, nr);
+ } while (ret < 0 && errno == EINTR);
+
+ return ret;
+}
+
+static int __write_to_log_init(log_id_t log_id, struct iovec *vec, size_t nr)
+{
+#ifdef HAVE_PTHREADS
+ pthread_mutex_lock(&log_init_lock);
+#endif
+
+ if (write_to_log == __write_to_log_init) {
+ log_fds[LOG_ID_MAIN] = log_open("/dev/"LOGGER_LOG_MAIN, O_WRONLY);
+ log_fds[LOG_ID_RADIO] = log_open("/dev/"LOGGER_LOG_RADIO, O_WRONLY);
+ log_fds[LOG_ID_EVENTS] = log_open("/dev/"LOGGER_LOG_EVENTS, O_WRONLY);
+ log_fds[LOG_ID_SYSTEM] = log_open("/dev/"LOGGER_LOG_SYSTEM, O_WRONLY);
+
+ write_to_log = __write_to_log_kernel;
+
+ if (log_fds[LOG_ID_MAIN] < 0 || log_fds[LOG_ID_RADIO] < 0 ||
+ log_fds[LOG_ID_EVENTS] < 0) {
+ log_close(log_fds[LOG_ID_MAIN]);
+ log_close(log_fds[LOG_ID_RADIO]);
+ log_close(log_fds[LOG_ID_EVENTS]);
+ log_fds[LOG_ID_MAIN] = -1;
+ log_fds[LOG_ID_RADIO] = -1;
+ log_fds[LOG_ID_EVENTS] = -1;
+ write_to_log = __write_to_log_null;
+ }
+
+ if (log_fds[LOG_ID_SYSTEM] < 0) {
+ log_fds[LOG_ID_SYSTEM] = log_fds[LOG_ID_MAIN];
+ }
+ }
+
+#ifdef HAVE_PTHREADS
+ pthread_mutex_unlock(&log_init_lock);
+#endif
+
+ return write_to_log(log_id, vec, nr);
+}
+
+int __android_log_write(int prio, const char *tag, const char *msg)
+{
+ struct iovec vec[3];
+ log_id_t log_id = LOG_ID_MAIN;
+ char tmp_tag[32];
+
+ if (!tag)
+ tag = "";
+
+ /* XXX: This needs to go! */
+ if (!strcmp(tag, "HTC_RIL") ||
+ !strncmp(tag, "RIL", 3) || /* Any log tag with "RIL" as the prefix */
+ !strncmp(tag, "IMS", 3) || /* Any log tag with "IMS" as the prefix */
+ !strcmp(tag, "AT") ||
+ !strcmp(tag, "GSM") ||
+ !strcmp(tag, "STK") ||
+ !strcmp(tag, "CDMA") ||
+ !strcmp(tag, "PHONE") ||
+ !strcmp(tag, "SMS")) {
+ log_id = LOG_ID_RADIO;
+ // Inform third party apps/ril/radio.. to use Rlog or RLOG
+ snprintf(tmp_tag, sizeof(tmp_tag), "use-Rlog/RLOG-%s", tag);
+ tag = tmp_tag;
+ }
+
+ vec[0].iov_base = (unsigned char *) &prio;
+ vec[0].iov_len = 1;
+ vec[1].iov_base = (void *) tag;
+ vec[1].iov_len = strlen(tag) + 1;
+ vec[2].iov_base = (void *) msg;
+ vec[2].iov_len = strlen(msg) + 1;
+
+ return write_to_log(log_id, vec, 3);
+}
+
+int __android_log_buf_write(int bufID, int prio, const char *tag, const char *msg)
+{
+ struct iovec vec[3];
+ char tmp_tag[32];
+
+ if (!tag)
+ tag = "";
+
+ /* XXX: This needs to go! */
+ if ((bufID != LOG_ID_RADIO) &&
+ (!strcmp(tag, "HTC_RIL") ||
+ !strncmp(tag, "RIL", 3) || /* Any log tag with "RIL" as the prefix */
+ !strncmp(tag, "IMS", 3) || /* Any log tag with "IMS" as the prefix */
+ !strcmp(tag, "AT") ||
+ !strcmp(tag, "GSM") ||
+ !strcmp(tag, "STK") ||
+ !strcmp(tag, "CDMA") ||
+ !strcmp(tag, "PHONE") ||
+ !strcmp(tag, "SMS"))) {
+ bufID = LOG_ID_RADIO;
+ // Inform third party apps/ril/radio.. to use Rlog or RLOG
+ snprintf(tmp_tag, sizeof(tmp_tag), "use-Rlog/RLOG-%s", tag);
+ tag = tmp_tag;
+ }
+
+ vec[0].iov_base = (unsigned char *) &prio;
+ vec[0].iov_len = 1;
+ vec[1].iov_base = (void *) tag;
+ vec[1].iov_len = strlen(tag) + 1;
+ vec[2].iov_base = (void *) msg;
+ vec[2].iov_len = strlen(msg) + 1;
+
+ return write_to_log(bufID, vec, 3);
+}
+
+int __android_log_vprint(int prio, const char *tag, const char *fmt, va_list ap)
+{
+ char buf[LOG_BUF_SIZE];
+
+ vsnprintf(buf, LOG_BUF_SIZE, fmt, ap);
+
+ return __android_log_write(prio, tag, buf);
+}
+
+int __android_log_print(int prio, const char *tag, const char *fmt, ...)
+{
+ va_list ap;
+ char buf[LOG_BUF_SIZE];
+
+ va_start(ap, fmt);
+ vsnprintf(buf, LOG_BUF_SIZE, fmt, ap);
+ va_end(ap);
+
+ return __android_log_write(prio, tag, buf);
+}
+
+int __android_log_buf_print(int bufID, int prio, const char *tag, const char *fmt, ...)
+{
+ va_list ap;
+ char buf[LOG_BUF_SIZE];
+
+ va_start(ap, fmt);
+ vsnprintf(buf, LOG_BUF_SIZE, fmt, ap);
+ va_end(ap);
+
+ return __android_log_buf_write(bufID, prio, tag, buf);
+}
+
+void __android_log_assert(const char *cond, const char *tag,
+ const char *fmt, ...)
+{
+ char buf[LOG_BUF_SIZE];
+
+ if (fmt) {
+ va_list ap;
+ va_start(ap, fmt);
+ vsnprintf(buf, LOG_BUF_SIZE, fmt, ap);
+ va_end(ap);
+ } else {
+ /* Msg not provided, log condition. N.B. Do not use cond directly as
+ * format string as it could contain spurious '%' syntax (e.g.
+ * "%d" in "blocks%devs == 0").
+ */
+ if (cond)
+ snprintf(buf, LOG_BUF_SIZE, "Assertion failed: %s", cond);
+ else
+ strcpy(buf, "Unspecified assertion failed");
+ }
+
+ __android_log_write(ANDROID_LOG_FATAL, tag, buf);
+
+#ifdef _MSC_VER
+ abort();
+#else
+ __builtin_trap(); /* trap so we have a chance to debug the situation */
+#endif
+}
+
+int __android_log_bwrite(int32_t tag, const void *payload, size_t len)
+{
+ struct iovec vec[2];
+
+ vec[0].iov_base = &tag;
+ vec[0].iov_len = sizeof(tag);
+ vec[1].iov_base = (void*)payload;
+ vec[1].iov_len = len;
+
+ return write_to_log(LOG_ID_EVENTS, vec, 2);
+}
+
+/*
+ * Like __android_log_bwrite, but takes the type as well. Doesn't work
+ * for the general case where we're generating lists of stuff, but very
+ * handy if we just want to dump an integer into the log.
+ */
+int __android_log_btwrite(int32_t tag, char type, const void *payload,
+ size_t len)
+{
+ struct iovec vec[3];
+
+ vec[0].iov_base = &tag;
+ vec[0].iov_len = sizeof(tag);
+ vec[1].iov_base = &type;
+ vec[1].iov_len = sizeof(type);
+ vec[2].iov_base = (void*)payload;
+ vec[2].iov_len = len;
+
+ return write_to_log(LOG_ID_EVENTS, vec, 3);
+}
diff --git a/media/libstagefright/system/core/liblog/logprint.c b/media/libstagefright/system/core/liblog/logprint.c
new file mode 100644
index 000000000..6bdd8e0b3
--- /dev/null
+++ b/media/libstagefright/system/core/liblog/logprint.c
@@ -0,0 +1,1057 @@
+/* //device/libs/cutils/logprint.c
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#define _GNU_SOURCE /* for asprintf */
+
+#include <ctype.h>
+#include <stdio.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <assert.h>
+#include <arpa/inet.h>
+
+#include <log/logd.h>
+#include <log/logprint.h>
+
+#ifdef _MSC_VER
+#if _MSC_VER < 1900
+#include <nspr/prprf.h>
+#define snprintf PR_snprintf
+#endif
+#define inline
+/* We don't want to indent large blocks because it causes unnecessary merge
+ * conflicts */
+#define UNINDENTED_BLOCK_START {
+#define UNINDENTED_BLOCK_END }
+#else
+#define UNINDENTED_BLOCK_START
+#define UNINDENTED_BLOCK_END
+#endif
+
+#ifdef WIN32
+static char *
+strsep(char **stringp, const char *delim)
+{
+ char* res = *stringp;
+ while (**stringp) {
+ const char *c;
+ for (c = delim; *c; c++) {
+ if (**stringp == *c) {
+ **stringp++ = 0;
+ return res;
+ }
+ }
+ }
+ return res;
+}
+#endif
+
+typedef struct FilterInfo_t {
+ char *mTag;
+ android_LogPriority mPri;
+ struct FilterInfo_t *p_next;
+} FilterInfo;
+
+struct AndroidLogFormat_t {
+ android_LogPriority global_pri;
+ FilterInfo *filters;
+ AndroidLogPrintFormat format;
+};
+
+static FilterInfo * filterinfo_new(const char * tag, android_LogPriority pri)
+{
+ FilterInfo *p_ret;
+
+ p_ret = (FilterInfo *)calloc(1, sizeof(FilterInfo));
+ p_ret->mTag = strdup(tag);
+ p_ret->mPri = pri;
+
+ return p_ret;
+}
+
+static void filterinfo_free(FilterInfo *p_info)
+{
+ if (p_info == NULL) {
+ return;
+ }
+
+ free(p_info->mTag);
+ p_info->mTag = NULL;
+}
+
+/*
+ * Note: also accepts 0-9 priorities
+ * returns ANDROID_LOG_UNKNOWN if the character is unrecognized
+ */
+static android_LogPriority filterCharToPri (char c)
+{
+ android_LogPriority pri;
+
+ c = tolower(c);
+
+ if (c >= '0' && c <= '9') {
+ if (c >= ('0'+ANDROID_LOG_SILENT)) {
+ pri = ANDROID_LOG_VERBOSE;
+ } else {
+ pri = (android_LogPriority)(c - '0');
+ }
+ } else if (c == 'v') {
+ pri = ANDROID_LOG_VERBOSE;
+ } else if (c == 'd') {
+ pri = ANDROID_LOG_DEBUG;
+ } else if (c == 'i') {
+ pri = ANDROID_LOG_INFO;
+ } else if (c == 'w') {
+ pri = ANDROID_LOG_WARN;
+ } else if (c == 'e') {
+ pri = ANDROID_LOG_ERROR;
+ } else if (c == 'f') {
+ pri = ANDROID_LOG_FATAL;
+ } else if (c == 's') {
+ pri = ANDROID_LOG_SILENT;
+ } else if (c == '*') {
+ pri = ANDROID_LOG_DEFAULT;
+ } else {
+ pri = ANDROID_LOG_UNKNOWN;
+ }
+
+ return pri;
+}
+
+static char filterPriToChar (android_LogPriority pri)
+{
+ switch (pri) {
+ case ANDROID_LOG_VERBOSE: return 'V';
+ case ANDROID_LOG_DEBUG: return 'D';
+ case ANDROID_LOG_INFO: return 'I';
+ case ANDROID_LOG_WARN: return 'W';
+ case ANDROID_LOG_ERROR: return 'E';
+ case ANDROID_LOG_FATAL: return 'F';
+ case ANDROID_LOG_SILENT: return 'S';
+
+ case ANDROID_LOG_DEFAULT:
+ case ANDROID_LOG_UNKNOWN:
+ default: return '?';
+ }
+}
+
+static android_LogPriority filterPriForTag(
+ AndroidLogFormat *p_format, const char *tag)
+{
+ FilterInfo *p_curFilter;
+
+ for (p_curFilter = p_format->filters
+ ; p_curFilter != NULL
+ ; p_curFilter = p_curFilter->p_next
+ ) {
+ if (0 == strcmp(tag, p_curFilter->mTag)) {
+ if (p_curFilter->mPri == ANDROID_LOG_DEFAULT) {
+ return p_format->global_pri;
+ } else {
+ return p_curFilter->mPri;
+ }
+ }
+ }
+
+ return p_format->global_pri;
+}
+
+/** for debugging */
+static void dumpFilters(AndroidLogFormat *p_format)
+{
+ FilterInfo *p_fi;
+
+ for (p_fi = p_format->filters ; p_fi != NULL ; p_fi = p_fi->p_next) {
+ char cPri = filterPriToChar(p_fi->mPri);
+ if (p_fi->mPri == ANDROID_LOG_DEFAULT) {
+ cPri = filterPriToChar(p_format->global_pri);
+ }
+ fprintf(stderr,"%s:%c\n", p_fi->mTag, cPri);
+ }
+
+ fprintf(stderr,"*:%c\n", filterPriToChar(p_format->global_pri));
+
+}
+
+/**
+ * returns 1 if this log line should be printed based on its priority
+ * and tag, and 0 if it should not
+ */
+int android_log_shouldPrintLine (
+ AndroidLogFormat *p_format, const char *tag, android_LogPriority pri)
+{
+ return pri >= filterPriForTag(p_format, tag);
+}
+
+AndroidLogFormat *android_log_format_new()
+{
+ AndroidLogFormat *p_ret;
+
+ p_ret = calloc(1, sizeof(AndroidLogFormat));
+
+ p_ret->global_pri = ANDROID_LOG_VERBOSE;
+ p_ret->format = FORMAT_BRIEF;
+
+ return p_ret;
+}
+
+void android_log_format_free(AndroidLogFormat *p_format)
+{
+ FilterInfo *p_info, *p_info_old;
+
+ p_info = p_format->filters;
+
+ while (p_info != NULL) {
+ p_info_old = p_info;
+ p_info = p_info->p_next;
+
+ free(p_info_old);
+ }
+
+ free(p_format);
+}
+
+
+
+void android_log_setPrintFormat(AndroidLogFormat *p_format,
+ AndroidLogPrintFormat format)
+{
+ p_format->format=format;
+}
+
+/**
+ * Returns FORMAT_OFF on invalid string
+ */
+AndroidLogPrintFormat android_log_formatFromString(const char * formatString)
+{
+ static AndroidLogPrintFormat format;
+
+ if (strcmp(formatString, "brief") == 0) format = FORMAT_BRIEF;
+ else if (strcmp(formatString, "process") == 0) format = FORMAT_PROCESS;
+ else if (strcmp(formatString, "tag") == 0) format = FORMAT_TAG;
+ else if (strcmp(formatString, "thread") == 0) format = FORMAT_THREAD;
+ else if (strcmp(formatString, "raw") == 0) format = FORMAT_RAW;
+ else if (strcmp(formatString, "time") == 0) format = FORMAT_TIME;
+ else if (strcmp(formatString, "threadtime") == 0) format = FORMAT_THREADTIME;
+ else if (strcmp(formatString, "long") == 0) format = FORMAT_LONG;
+ else format = FORMAT_OFF;
+
+ return format;
+}
+
+/**
+ * filterExpression: a single filter expression
+ * eg "AT:d"
+ *
+ * returns 0 on success and -1 on invalid expression
+ *
+ * Assumes single threaded execution
+ */
+
+int android_log_addFilterRule(AndroidLogFormat *p_format,
+ const char *filterExpression)
+{
+ size_t tagNameLength;
+ android_LogPriority pri = ANDROID_LOG_DEFAULT;
+
+ tagNameLength = strcspn(filterExpression, ":");
+
+ if (tagNameLength == 0) {
+ goto error;
+ }
+
+ if(filterExpression[tagNameLength] == ':') {
+ pri = filterCharToPri(filterExpression[tagNameLength+1]);
+
+ if (pri == ANDROID_LOG_UNKNOWN) {
+ goto error;
+ }
+ }
+
+ if(0 == strncmp("*", filterExpression, tagNameLength)) {
+ // This filter expression refers to the global filter
+ // The default level for this is DEBUG if the priority
+ // is unspecified
+ if (pri == ANDROID_LOG_DEFAULT) {
+ pri = ANDROID_LOG_DEBUG;
+ }
+
+ p_format->global_pri = pri;
+ } else {
+ // for filter expressions that don't refer to the global
+ // filter, the default is verbose if the priority is unspecified
+ if (pri == ANDROID_LOG_DEFAULT) {
+ pri = ANDROID_LOG_VERBOSE;
+ }
+
+ UNINDENTED_BLOCK_START
+ char *tagName;
+
+// Presently HAVE_STRNDUP is never defined, so the second case is always taken
+// Darwin doesn't have strnup, everything else does
+#ifdef HAVE_STRNDUP
+ tagName = strndup(filterExpression, tagNameLength);
+#else
+ //a few extra bytes copied...
+ tagName = strdup(filterExpression);
+ tagName[tagNameLength] = '\0';
+#endif /*HAVE_STRNDUP*/
+
+ UNINDENTED_BLOCK_START
+ FilterInfo *p_fi = filterinfo_new(tagName, pri);
+ free(tagName);
+
+ p_fi->p_next = p_format->filters;
+ p_format->filters = p_fi;
+ UNINDENTED_BLOCK_END
+ UNINDENTED_BLOCK_END
+ }
+
+ return 0;
+error:
+ return -1;
+}
+
+
+/**
+ * filterString: a comma/whitespace-separated set of filter expressions
+ *
+ * eg "AT:d *:i"
+ *
+ * returns 0 on success and -1 on invalid expression
+ *
+ * Assumes single threaded execution
+ *
+ */
+
+int android_log_addFilterString(AndroidLogFormat *p_format,
+ const char *filterString)
+{
+ char *filterStringCopy = strdup (filterString);
+ char *p_cur = filterStringCopy;
+ char *p_ret;
+ int err;
+
+ // Yes, I'm using strsep
+ while (NULL != (p_ret = strsep(&p_cur, " \t,"))) {
+ // ignore whitespace-only entries
+ if(p_ret[0] != '\0') {
+ err = android_log_addFilterRule(p_format, p_ret);
+
+ if (err < 0) {
+ goto error;
+ }
+ }
+ }
+
+ free (filterStringCopy);
+ return 0;
+error:
+ free (filterStringCopy);
+ return -1;
+}
+
+static inline char * strip_end(char *str)
+{
+ char *end = str + strlen(str) - 1;
+
+ while (end >= str && isspace(*end))
+ *end-- = '\0';
+ return str;
+}
+
+/**
+ * Splits a wire-format buffer into an AndroidLogEntry
+ * entry allocated by caller. Pointers will point directly into buf
+ *
+ * Returns 0 on success and -1 on invalid wire format (entry will be
+ * in unspecified state)
+ */
+int android_log_processLogBuffer(struct logger_entry *buf,
+ AndroidLogEntry *entry)
+{
+ entry->tv_sec = buf->sec;
+ entry->tv_nsec = buf->nsec;
+ entry->pid = buf->pid;
+ entry->tid = buf->tid;
+
+ /*
+ * format: <priority:1><tag:N>\0<message:N>\0
+ *
+ * tag str
+ * starts at buf->msg+1
+ * msg
+ * starts at buf->msg+1+len(tag)+1
+ *
+ * The message may have been truncated by the kernel log driver.
+ * When that happens, we must null-terminate the message ourselves.
+ */
+ if (buf->len < 3) {
+ // An well-formed entry must consist of at least a priority
+ // and two null characters
+ fprintf(stderr, "+++ LOG: entry too small\n");
+ return -1;
+ }
+
+ UNINDENTED_BLOCK_START
+ int msgStart = -1;
+ int msgEnd = -1;
+
+ int i;
+ for (i = 1; i < buf->len; i++) {
+ if (buf->msg[i] == '\0') {
+ if (msgStart == -1) {
+ msgStart = i + 1;
+ } else {
+ msgEnd = i;
+ break;
+ }
+ }
+ }
+
+ if (msgStart == -1) {
+ fprintf(stderr, "+++ LOG: malformed log message\n");
+ return -1;
+ }
+ if (msgEnd == -1) {
+ // incoming message not null-terminated; force it
+ msgEnd = buf->len - 1;
+ buf->msg[msgEnd] = '\0';
+ }
+
+ entry->priority = buf->msg[0];
+ entry->tag = buf->msg + 1;
+ entry->message = buf->msg + msgStart;
+ entry->messageLen = msgEnd - msgStart;
+
+ return 0;
+ UNINDENTED_BLOCK_END
+}
+
+/*
+ * Extract a 4-byte value from a byte stream.
+ */
+static inline uint32_t get4LE(const uint8_t* src)
+{
+ return src[0] | (src[1] << 8) | (src[2] << 16) | (src[3] << 24);
+}
+
+/*
+ * Extract an 8-byte value from a byte stream.
+ */
+static inline uint64_t get8LE(const uint8_t* src)
+{
+ uint32_t low, high;
+
+ low = src[0] | (src[1] << 8) | (src[2] << 16) | (src[3] << 24);
+ high = src[4] | (src[5] << 8) | (src[6] << 16) | (src[7] << 24);
+ return ((long long) high << 32) | (long long) low;
+}
+
+
+/*
+ * Recursively convert binary log data to printable form.
+ *
+ * This needs to be recursive because you can have lists of lists.
+ *
+ * If we run out of room, we stop processing immediately. It's important
+ * for us to check for space on every output element to avoid producing
+ * garbled output.
+ *
+ * Returns 0 on success, 1 on buffer full, -1 on failure.
+ */
+static int android_log_printBinaryEvent(const unsigned char** pEventData,
+ size_t* pEventDataLen, char** pOutBuf, size_t* pOutBufLen)
+{
+ const unsigned char* eventData = *pEventData;
+ size_t eventDataLen = *pEventDataLen;
+ char* outBuf = *pOutBuf;
+ size_t outBufLen = *pOutBufLen;
+ unsigned char type;
+ size_t outCount;
+ int result = 0;
+
+ if (eventDataLen < 1)
+ return -1;
+ type = *eventData++;
+ eventDataLen--;
+
+ //fprintf(stderr, "--- type=%d (rem len=%d)\n", type, eventDataLen);
+
+ switch (type) {
+ case EVENT_TYPE_INT:
+ /* 32-bit signed int */
+ {
+ int ival;
+
+ if (eventDataLen < 4)
+ return -1;
+ ival = get4LE(eventData);
+ eventData += 4;
+ eventDataLen -= 4;
+
+ outCount = snprintf(outBuf, outBufLen, "%d", ival);
+ if (outCount < outBufLen) {
+ outBuf += outCount;
+ outBufLen -= outCount;
+ } else {
+ /* halt output */
+ goto no_room;
+ }
+ }
+ break;
+ case EVENT_TYPE_LONG:
+ /* 64-bit signed long */
+ {
+ long long lval;
+
+ if (eventDataLen < 8)
+ return -1;
+ lval = get8LE(eventData);
+ eventData += 8;
+ eventDataLen -= 8;
+
+ outCount = snprintf(outBuf, outBufLen, "%lld", lval);
+ if (outCount < outBufLen) {
+ outBuf += outCount;
+ outBufLen -= outCount;
+ } else {
+ /* halt output */
+ goto no_room;
+ }
+ }
+ break;
+ case EVENT_TYPE_STRING:
+ /* UTF-8 chars, not NULL-terminated */
+ {
+ unsigned int strLen;
+
+ if (eventDataLen < 4)
+ return -1;
+ strLen = get4LE(eventData);
+ eventData += 4;
+ eventDataLen -= 4;
+
+ if (eventDataLen < strLen)
+ return -1;
+
+ if (strLen < outBufLen) {
+ memcpy(outBuf, eventData, strLen);
+ outBuf += strLen;
+ outBufLen -= strLen;
+ } else if (outBufLen > 0) {
+ /* copy what we can */
+ memcpy(outBuf, eventData, outBufLen);
+ outBuf += outBufLen;
+ outBufLen -= outBufLen;
+ goto no_room;
+ }
+ eventData += strLen;
+ eventDataLen -= strLen;
+ break;
+ }
+ case EVENT_TYPE_LIST:
+ /* N items, all different types */
+ {
+ unsigned char count;
+ int i;
+
+ if (eventDataLen < 1)
+ return -1;
+
+ count = *eventData++;
+ eventDataLen--;
+
+ if (outBufLen > 0) {
+ *outBuf++ = '[';
+ outBufLen--;
+ } else {
+ goto no_room;
+ }
+
+ for (i = 0; i < count; i++) {
+ result = android_log_printBinaryEvent(&eventData, &eventDataLen,
+ &outBuf, &outBufLen);
+ if (result != 0)
+ goto bail;
+
+ if (i < count-1) {
+ if (outBufLen > 0) {
+ *outBuf++ = ',';
+ outBufLen--;
+ } else {
+ goto no_room;
+ }
+ }
+ }
+
+ if (outBufLen > 0) {
+ *outBuf++ = ']';
+ outBufLen--;
+ } else {
+ goto no_room;
+ }
+ }
+ break;
+ default:
+ fprintf(stderr, "Unknown binary event type %d\n", type);
+ return -1;
+ }
+
+bail:
+ *pEventData = eventData;
+ *pEventDataLen = eventDataLen;
+ *pOutBuf = outBuf;
+ *pOutBufLen = outBufLen;
+ return result;
+
+no_room:
+ result = 1;
+ goto bail;
+}
+
+/**
+ * Convert a binary log entry to ASCII form.
+ *
+ * For convenience we mimic the processLogBuffer API. There is no
+ * pre-defined output length for the binary data, since we're free to format
+ * it however we choose, which means we can't really use a fixed-size buffer
+ * here.
+ */
+int android_log_processBinaryLogBuffer(struct logger_entry *buf,
+ AndroidLogEntry *entry, const EventTagMap* map, char* messageBuf,
+ int messageBufLen)
+{
+ size_t inCount;
+ unsigned int tagIndex;
+ const unsigned char* eventData;
+
+ entry->tv_sec = buf->sec;
+ entry->tv_nsec = buf->nsec;
+ entry->priority = ANDROID_LOG_INFO;
+ entry->pid = buf->pid;
+ entry->tid = buf->tid;
+
+ /*
+ * Pull the tag out.
+ */
+ eventData = (const unsigned char*) buf->msg;
+ inCount = buf->len;
+ if (inCount < 4)
+ return -1;
+ tagIndex = get4LE(eventData);
+ eventData += 4;
+ inCount -= 4;
+
+ entry->tag = NULL;
+
+ /*
+ * If we don't have a map, or didn't find the tag number in the map,
+ * stuff a generated tag value into the start of the output buffer and
+ * shift the buffer pointers down.
+ */
+ if (entry->tag == NULL) {
+ int tagLen;
+
+ tagLen = snprintf(messageBuf, messageBufLen, "[%d]", tagIndex);
+ entry->tag = messageBuf;
+ messageBuf += tagLen+1;
+ messageBufLen -= tagLen+1;
+ }
+
+ /*
+ * Format the event log data into the buffer.
+ */
+ UNINDENTED_BLOCK_START
+ char* outBuf = messageBuf;
+ size_t outRemaining = messageBufLen-1; /* leave one for nul byte */
+ int result;
+ result = android_log_printBinaryEvent(&eventData, &inCount, &outBuf,
+ &outRemaining);
+ if (result < 0) {
+ fprintf(stderr, "Binary log entry conversion failed\n");
+ return -1;
+ } else if (result == 1) {
+ if (outBuf > messageBuf) {
+ /* leave an indicator */
+ *(outBuf-1) = '!';
+ } else {
+ /* no room to output anything at all */
+ *outBuf++ = '!';
+ outRemaining--;
+ }
+ /* pretend we ate all the data */
+ inCount = 0;
+ }
+
+ /* eat the silly terminating '\n' */
+ if (inCount == 1 && *eventData == '\n') {
+ eventData++;
+ inCount--;
+ }
+
+ if (inCount != 0) {
+ fprintf(stderr,
+ "Warning: leftover binary log data (%zu bytes)\n", inCount);
+ }
+
+ /*
+ * Terminate the buffer. The NUL byte does not count as part of
+ * entry->messageLen.
+ */
+ *outBuf = '\0';
+ entry->messageLen = outBuf - messageBuf;
+ assert(entry->messageLen == (messageBufLen-1) - outRemaining);
+
+ entry->message = messageBuf;
+
+ return 0;
+ UNINDENTED_BLOCK_END
+}
+
+/**
+ * Formats a log message into a buffer
+ *
+ * Uses defaultBuffer if it can, otherwise malloc()'s a new buffer
+ * If return value != defaultBuffer, caller must call free()
+ * Returns NULL on malloc error
+ */
+
+char *android_log_formatLogLine (
+ AndroidLogFormat *p_format,
+ char *defaultBuffer,
+ size_t defaultBufferSize,
+ const AndroidLogEntry *entry,
+ size_t *p_outLength)
+{
+#if defined(HAVE_LOCALTIME_R)
+ struct tm tmBuf;
+#endif
+ struct tm* ptm;
+ char timeBuf[32];
+ char prefixBuf[128], suffixBuf[128];
+ char priChar;
+ int prefixSuffixIsHeaderFooter = 0;
+ char * ret = NULL;
+
+ priChar = filterPriToChar(entry->priority);
+
+ /*
+ * Get the current date/time in pretty form
+ *
+ * It's often useful when examining a log with "less" to jump to
+ * a specific point in the file by searching for the date/time stamp.
+ * For this reason it's very annoying to have regexp meta characters
+ * in the time stamp. Don't use forward slashes, parenthesis,
+ * brackets, asterisks, or other special chars here.
+ */
+#if defined(HAVE_LOCALTIME_R)
+ ptm = localtime_r(&(entry->tv_sec), &tmBuf);
+#else
+ ptm = localtime(&(entry->tv_sec));
+#endif
+ //strftime(timeBuf, sizeof(timeBuf), "%Y-%m-%d %H:%M:%S", ptm);
+ strftime(timeBuf, sizeof(timeBuf), "%m-%d %H:%M:%S", ptm);
+
+ /*
+ * Construct a buffer containing the log header and log message.
+ */
+ UNINDENTED_BLOCK_START
+ size_t prefixLen, suffixLen;
+
+ switch (p_format->format) {
+ case FORMAT_TAG:
+ prefixLen = snprintf(prefixBuf, sizeof(prefixBuf),
+ "%c/%-8s: ", priChar, entry->tag);
+ strcpy(suffixBuf, "\n"); suffixLen = 1;
+ break;
+ case FORMAT_PROCESS:
+ prefixLen = snprintf(prefixBuf, sizeof(prefixBuf),
+ "%c(%5d) ", priChar, entry->pid);
+ suffixLen = snprintf(suffixBuf, sizeof(suffixBuf),
+ " (%s)\n", entry->tag);
+ break;
+ case FORMAT_THREAD:
+ prefixLen = snprintf(prefixBuf, sizeof(prefixBuf),
+ "%c(%5d:%5d) ", priChar, entry->pid, entry->tid);
+ strcpy(suffixBuf, "\n");
+ suffixLen = 1;
+ break;
+ case FORMAT_RAW:
+ prefixBuf[0] = 0;
+ prefixLen = 0;
+ strcpy(suffixBuf, "\n");
+ suffixLen = 1;
+ break;
+ case FORMAT_TIME:
+ prefixLen = snprintf(prefixBuf, sizeof(prefixBuf),
+ "%s.%03ld %c/%-8s(%5d): ", timeBuf, entry->tv_nsec / 1000000,
+ priChar, entry->tag, entry->pid);
+ strcpy(suffixBuf, "\n");
+ suffixLen = 1;
+ break;
+ case FORMAT_THREADTIME:
+ prefixLen = snprintf(prefixBuf, sizeof(prefixBuf),
+ "%s.%03ld %5d %5d %c %-8s: ", timeBuf, entry->tv_nsec / 1000000,
+ entry->pid, entry->tid, priChar, entry->tag);
+ strcpy(suffixBuf, "\n");
+ suffixLen = 1;
+ break;
+ case FORMAT_LONG:
+ prefixLen = snprintf(prefixBuf, sizeof(prefixBuf),
+ "[ %s.%03ld %5d:%5d %c/%-8s ]\n",
+ timeBuf, entry->tv_nsec / 1000000, entry->pid,
+ entry->tid, priChar, entry->tag);
+ strcpy(suffixBuf, "\n\n");
+ suffixLen = 2;
+ prefixSuffixIsHeaderFooter = 1;
+ break;
+ case FORMAT_BRIEF:
+ default:
+ prefixLen = snprintf(prefixBuf, sizeof(prefixBuf),
+ "%c/%-8s(%5d): ", priChar, entry->tag, entry->pid);
+ strcpy(suffixBuf, "\n");
+ suffixLen = 1;
+ break;
+ }
+ /* snprintf has a weird return value. It returns what would have been
+ * written given a large enough buffer. In the case that the prefix is
+ * longer then our buffer(128), it messes up the calculations below
+ * possibly causing heap corruption. To avoid this we double check and
+ * set the length at the maximum (size minus null byte)
+ */
+ if(prefixLen >= sizeof(prefixBuf))
+ prefixLen = sizeof(prefixBuf) - 1;
+ if(suffixLen >= sizeof(suffixBuf))
+ suffixLen = sizeof(suffixBuf) - 1;
+
+ /* the following code is tragically unreadable */
+
+ UNINDENTED_BLOCK_START
+ size_t numLines;
+ char *p;
+ size_t bufferSize;
+ const char *pm;
+
+ if (prefixSuffixIsHeaderFooter) {
+ // we're just wrapping message with a header/footer
+ numLines = 1;
+ } else {
+ pm = entry->message;
+ numLines = 0;
+
+ // The line-end finding here must match the line-end finding
+ // in for ( ... numLines...) loop below
+ while (pm < (entry->message + entry->messageLen)) {
+ if (*pm++ == '\n') numLines++;
+ }
+ // plus one line for anything not newline-terminated at the end
+ if (pm > entry->message && *(pm-1) != '\n') numLines++;
+ }
+
+ // this is an upper bound--newlines in message may be counted
+ // extraneously
+ bufferSize = (numLines * (prefixLen + suffixLen)) + entry->messageLen + 1;
+
+ if (defaultBufferSize >= bufferSize) {
+ ret = defaultBuffer;
+ } else {
+ ret = (char *)malloc(bufferSize);
+
+ if (ret == NULL) {
+ return ret;
+ }
+ }
+
+ ret[0] = '\0'; /* to start strcat off */
+
+ p = ret;
+ pm = entry->message;
+
+ if (prefixSuffixIsHeaderFooter) {
+ strcat(p, prefixBuf);
+ p += prefixLen;
+ strncat(p, entry->message, entry->messageLen);
+ p += entry->messageLen;
+ strcat(p, suffixBuf);
+ p += suffixLen;
+ } else {
+ while(pm < (entry->message + entry->messageLen)) {
+ const char *lineStart;
+ size_t lineLen;
+ lineStart = pm;
+
+ // Find the next end-of-line in message
+ while (pm < (entry->message + entry->messageLen)
+ && *pm != '\n') pm++;
+ lineLen = pm - lineStart;
+
+ strcat(p, prefixBuf);
+ p += prefixLen;
+ strncat(p, lineStart, lineLen);
+ p += lineLen;
+ strcat(p, suffixBuf);
+ p += suffixLen;
+
+ if (*pm == '\n') pm++;
+ }
+ }
+
+ if (p_outLength != NULL) {
+ *p_outLength = p - ret;
+ }
+
+ return ret;
+ UNINDENTED_BLOCK_END
+ UNINDENTED_BLOCK_END
+}
+
+/**
+ * Either print or do not print log line, based on filter
+ *
+ * Returns count bytes written
+ */
+
+int android_log_printLogLine(
+ AndroidLogFormat *p_format,
+ int fd,
+ const AndroidLogEntry *entry)
+{
+ int ret;
+ char defaultBuffer[512];
+ char *outBuffer = NULL;
+ size_t totalLen;
+
+ outBuffer = android_log_formatLogLine(p_format, defaultBuffer,
+ sizeof(defaultBuffer), entry, &totalLen);
+
+ if (!outBuffer)
+ return -1;
+
+ do {
+ ret = write(fd, outBuffer, totalLen);
+ } while (ret < 0 && errno == EINTR);
+
+ if (ret < 0) {
+ fprintf(stderr, "+++ LOG: write failed (errno=%d)\n", errno);
+ ret = 0;
+ goto done;
+ }
+
+ if (((size_t)ret) < totalLen) {
+ fprintf(stderr, "+++ LOG: write partial (%d of %d)\n", ret,
+ (int)totalLen);
+ goto done;
+ }
+
+done:
+ if (outBuffer != defaultBuffer) {
+ free(outBuffer);
+ }
+
+ return ret;
+}
+
+
+
+void logprint_run_tests()
+{
+#if 0
+
+ fprintf(stderr, "tests disabled\n");
+
+#else
+
+ int err;
+ const char *tag;
+ AndroidLogFormat *p_format;
+
+ p_format = android_log_format_new();
+
+ fprintf(stderr, "running tests\n");
+
+ tag = "random";
+
+ android_log_addFilterRule(p_format,"*:i");
+
+ assert (ANDROID_LOG_INFO == filterPriForTag(p_format, "random"));
+ assert(android_log_shouldPrintLine(p_format, tag, ANDROID_LOG_DEBUG) == 0);
+ android_log_addFilterRule(p_format, "*");
+ assert (ANDROID_LOG_DEBUG == filterPriForTag(p_format, "random"));
+ assert(android_log_shouldPrintLine(p_format, tag, ANDROID_LOG_DEBUG) > 0);
+ android_log_addFilterRule(p_format, "*:v");
+ assert (ANDROID_LOG_VERBOSE == filterPriForTag(p_format, "random"));
+ assert(android_log_shouldPrintLine(p_format, tag, ANDROID_LOG_DEBUG) > 0);
+ android_log_addFilterRule(p_format, "*:i");
+ assert (ANDROID_LOG_INFO == filterPriForTag(p_format, "random"));
+ assert(android_log_shouldPrintLine(p_format, tag, ANDROID_LOG_DEBUG) == 0);
+
+ android_log_addFilterRule(p_format, "random");
+ assert (ANDROID_LOG_VERBOSE == filterPriForTag(p_format, "random"));
+ assert(android_log_shouldPrintLine(p_format, tag, ANDROID_LOG_DEBUG) > 0);
+ android_log_addFilterRule(p_format, "random:v");
+ assert (ANDROID_LOG_VERBOSE == filterPriForTag(p_format, "random"));
+ assert(android_log_shouldPrintLine(p_format, tag, ANDROID_LOG_DEBUG) > 0);
+ android_log_addFilterRule(p_format, "random:d");
+ assert (ANDROID_LOG_DEBUG == filterPriForTag(p_format, "random"));
+ assert(android_log_shouldPrintLine(p_format, tag, ANDROID_LOG_DEBUG) > 0);
+ android_log_addFilterRule(p_format, "random:w");
+ assert (ANDROID_LOG_WARN == filterPriForTag(p_format, "random"));
+ assert(android_log_shouldPrintLine(p_format, tag, ANDROID_LOG_DEBUG) == 0);
+
+ android_log_addFilterRule(p_format, "crap:*");
+ assert (ANDROID_LOG_VERBOSE== filterPriForTag(p_format, "crap"));
+ assert(android_log_shouldPrintLine(p_format, "crap", ANDROID_LOG_VERBOSE) > 0);
+
+ // invalid expression
+ err = android_log_addFilterRule(p_format, "random:z");
+ assert (err < 0);
+ assert (ANDROID_LOG_WARN == filterPriForTag(p_format, "random"));
+ assert(android_log_shouldPrintLine(p_format, tag, ANDROID_LOG_DEBUG) == 0);
+
+ // Issue #550946
+ err = android_log_addFilterString(p_format, " ");
+ assert(err == 0);
+ assert(ANDROID_LOG_WARN == filterPriForTag(p_format, "random"));
+
+ // note trailing space
+ err = android_log_addFilterString(p_format, "*:s random:d ");
+ assert(err == 0);
+ assert(ANDROID_LOG_DEBUG == filterPriForTag(p_format, "random"));
+
+ err = android_log_addFilterString(p_format, "*:s random:z");
+ assert(err < 0);
+
+
+#if 0
+ char *ret;
+ char defaultBuffer[512];
+
+ ret = android_log_formatLogLine(p_format,
+ defaultBuffer, sizeof(defaultBuffer), 0, ANDROID_LOG_ERROR, 123,
+ 123, 123, "random", "nofile", strlen("Hello"), "Hello", NULL);
+#endif
+
+
+ fprintf(stderr, "tests complete\n");
+#endif
+}
+
+#undef UNINDENTED_BLOCK_START
+#undef UNINDENTED_BLOCK_END
diff --git a/media/libstagefright/system/core/libpixelflinger/codeflinger/tinyutils/Errors.h b/media/libstagefright/system/core/libpixelflinger/codeflinger/tinyutils/Errors.h
new file mode 100644
index 000000000..98f2190ac
--- /dev/null
+++ b/media/libstagefright/system/core/libpixelflinger/codeflinger/tinyutils/Errors.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_PIXELFLINGER_ERRORS_H
+#define ANDROID_PIXELFLINGER_ERRORS_H
+
+#include <sys/types.h>
+#include <errno.h>
+
+namespace stagefright {
+namespace tinyutils {
+
+// use this type to return error codes
+typedef int32_t status_t;
+
+/*
+ * Error codes.
+ * All error codes are negative values.
+ */
+
+enum {
+ NO_ERROR = 0, // No errors.
+ NO_MEMORY = -ENOMEM,
+ BAD_VALUE = -EINVAL,
+ BAD_INDEX = -EOVERFLOW,
+ NAME_NOT_FOUND = -ENOENT,
+};
+
+
+} // namespace tinyutils
+} // namespace stagefright
+
+// ---------------------------------------------------------------------------
+
+#endif // ANDROID_PIXELFLINGER_ERRORS_H
diff --git a/media/libstagefright/system/core/libpixelflinger/codeflinger/tinyutils/KeyedVector.h b/media/libstagefright/system/core/libpixelflinger/codeflinger/tinyutils/KeyedVector.h
new file mode 100644
index 000000000..62fc7604b
--- /dev/null
+++ b/media/libstagefright/system/core/libpixelflinger/codeflinger/tinyutils/KeyedVector.h
@@ -0,0 +1,203 @@
+/*
+ * Copyright 2005 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_PIXELFLINGER_KEYED_VECTOR_H
+#define ANDROID_PIXELFLINGER_KEYED_VECTOR_H
+
+#include <assert.h>
+#include <stdint.h>
+#include <sys/types.h>
+
+#include "Errors.h"
+#include "SortedVector.h"
+#include "TypeHelpers.h"
+
+// ---------------------------------------------------------------------------
+
+namespace stagefright {
+namespace tinyutils {
+
+template <typename KEY, typename VALUE>
+class KeyedVector
+{
+public:
+ typedef KEY key_type;
+ typedef VALUE value_type;
+
+ inline KeyedVector();
+
+ /*
+ * empty the vector
+ */
+
+ inline void clear() { mVector.clear(); }
+
+ /*!
+ * vector stats
+ */
+
+ //! returns number of items in the vector
+ inline size_t size() const { return mVector.size(); }
+ //! returns wether or not the vector is empty
+ inline bool isEmpty() const { return mVector.isEmpty(); }
+ //! returns how many items can be stored without reallocating the backing store
+ inline size_t capacity() const { return mVector.capacity(); }
+ //! setst the capacity. capacity can never be reduced less than size()
+ inline ssize_t setCapacity(size_t size) { return mVector.setCapacity(size); }
+
+ /*!
+ * accessors
+ */
+ const VALUE& valueFor(const KEY& key) const;
+ const VALUE& valueAt(size_t index) const;
+ const KEY& keyAt(size_t index) const;
+ ssize_t indexOfKey(const KEY& key) const;
+
+ /*!
+ * modifing the array
+ */
+
+ VALUE& editValueFor(const KEY& key);
+ VALUE& editValueAt(size_t index);
+
+ /*!
+ * add/insert/replace items
+ */
+
+ ssize_t add(const KEY& key, const VALUE& item);
+ ssize_t replaceValueFor(const KEY& key, const VALUE& item);
+ ssize_t replaceValueAt(size_t index, const VALUE& item);
+
+ /*!
+ * remove items
+ */
+
+ ssize_t removeItem(const KEY& key);
+ ssize_t removeItemsAt(size_t index, size_t count = 1);
+
+private:
+ SortedVector< key_value_pair_t<KEY, VALUE> > mVector;
+};
+
+// ---------------------------------------------------------------------------
+
+/**
+ * Variation of KeyedVector that holds a default value to return when
+ * valueFor() is called with a key that doesn't exist.
+ */
+template <typename KEY, typename VALUE>
+class DefaultKeyedVector : public KeyedVector<KEY, VALUE>
+{
+public:
+ inline DefaultKeyedVector(const VALUE& defValue = VALUE());
+ const VALUE& valueFor(const KEY& key) const;
+
+private:
+ VALUE mDefault;
+};
+
+// ---------------------------------------------------------------------------
+
+template<typename KEY, typename VALUE> inline
+KeyedVector<KEY,VALUE>::KeyedVector()
+{
+}
+
+template<typename KEY, typename VALUE> inline
+ssize_t KeyedVector<KEY,VALUE>::indexOfKey(const KEY& key) const {
+ return mVector.indexOf( key_value_pair_t<KEY,VALUE>(key) );
+}
+
+template<typename KEY, typename VALUE> inline
+const VALUE& KeyedVector<KEY,VALUE>::valueFor(const KEY& key) const {
+ ssize_t i = indexOfKey(key);
+ assert(i>=0);
+ return mVector.itemAt(i).value;
+}
+
+template<typename KEY, typename VALUE> inline
+const VALUE& KeyedVector<KEY,VALUE>::valueAt(size_t index) const {
+ return mVector.itemAt(index).value;
+}
+
+template<typename KEY, typename VALUE> inline
+const KEY& KeyedVector<KEY,VALUE>::keyAt(size_t index) const {
+ return mVector.itemAt(index).key;
+}
+
+template<typename KEY, typename VALUE> inline
+VALUE& KeyedVector<KEY,VALUE>::editValueFor(const KEY& key) {
+ ssize_t i = indexOfKey(key);
+ assert(i>=0);
+ return mVector.editItemAt(i).value;
+}
+
+template<typename KEY, typename VALUE> inline
+VALUE& KeyedVector<KEY,VALUE>::editValueAt(size_t index) {
+ return mVector.editItemAt(index).value;
+}
+
+template<typename KEY, typename VALUE> inline
+ssize_t KeyedVector<KEY,VALUE>::add(const KEY& key, const VALUE& value) {
+ return mVector.add( key_value_pair_t<KEY,VALUE>(key, value) );
+}
+
+template<typename KEY, typename VALUE> inline
+ssize_t KeyedVector<KEY,VALUE>::replaceValueFor(const KEY& key, const VALUE& value) {
+ key_value_pair_t<KEY,VALUE> pair(key, value);
+ mVector.remove(pair);
+ return mVector.add(pair);
+}
+
+template<typename KEY, typename VALUE> inline
+ssize_t KeyedVector<KEY,VALUE>::replaceValueAt(size_t index, const VALUE& item) {
+ if (index<size()) {
+ mVector.editValueAt(index).value = item;
+ return index;
+ }
+ return BAD_INDEX;
+}
+
+template<typename KEY, typename VALUE> inline
+ssize_t KeyedVector<KEY,VALUE>::removeItem(const KEY& key) {
+ return mVector.remove(key_value_pair_t<KEY,VALUE>(key));
+}
+
+template<typename KEY, typename VALUE> inline
+ssize_t KeyedVector<KEY, VALUE>::removeItemsAt(size_t index, size_t count) {
+ return mVector.removeItemsAt(index, count);
+}
+
+// ---------------------------------------------------------------------------
+
+template<typename KEY, typename VALUE> inline
+DefaultKeyedVector<KEY,VALUE>::DefaultKeyedVector(const VALUE& defValue)
+ : mDefault(defValue)
+{
+}
+
+template<typename KEY, typename VALUE> inline
+const VALUE& DefaultKeyedVector<KEY,VALUE>::valueFor(const KEY& key) const {
+ ssize_t i = indexOfKey(key);
+ return i >= 0 ? KeyedVector<KEY,VALUE>::valueAt(i) : mDefault;
+}
+
+} // namespace tinyutils
+} // namespace stagefright
+
+// ---------------------------------------------------------------------------
+
+#endif // ANDROID_PIXELFLINGER_KEYED_VECTOR_H
diff --git a/media/libstagefright/system/core/libpixelflinger/codeflinger/tinyutils/SortedVector.h b/media/libstagefright/system/core/libpixelflinger/codeflinger/tinyutils/SortedVector.h
new file mode 100644
index 000000000..71026c8df
--- /dev/null
+++ b/media/libstagefright/system/core/libpixelflinger/codeflinger/tinyutils/SortedVector.h
@@ -0,0 +1,284 @@
+/*
+ * Copyright 2005 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_PIXELFLINGER_SORTED_VECTOR_H
+#define ANDROID_PIXELFLINGER_SORTED_VECTOR_H
+
+#include <assert.h>
+#include <stdint.h>
+#include <sys/types.h>
+
+#include "Vector.h"
+#include "VectorImpl.h"
+#include "TypeHelpers.h"
+
+// ---------------------------------------------------------------------------
+
+namespace stagefright {
+namespace tinyutils {
+
+template <class TYPE>
+class SortedVector : private SortedVectorImpl
+{
+public:
+ typedef TYPE value_type;
+
+ /*!
+ * Constructors and destructors
+ */
+
+ SortedVector();
+ SortedVector(const SortedVector<TYPE>& rhs);
+ virtual ~SortedVector();
+
+ /*! copy operator */
+ const SortedVector<TYPE>& operator = (const SortedVector<TYPE>& rhs) const;
+ SortedVector<TYPE>& operator = (const SortedVector<TYPE>& rhs);
+
+ /*
+ * empty the vector
+ */
+
+ inline void clear() { VectorImpl::clear(); }
+
+ /*!
+ * vector stats
+ */
+
+ //! returns number of items in the vector
+ inline size_t size() const { return VectorImpl::size(); }
+ //! returns wether or not the vector is empty
+ inline bool isEmpty() const { return VectorImpl::isEmpty(); }
+ //! returns how many items can be stored without reallocating the backing store
+ inline size_t capacity() const { return VectorImpl::capacity(); }
+ //! setst the capacity. capacity can never be reduced less than size()
+ inline ssize_t setCapacity(size_t size) { return VectorImpl::setCapacity(size); }
+
+ /*!
+ * C-style array access
+ */
+
+ //! read-only C-style access
+ inline const TYPE* array() const;
+
+ //! read-write C-style access. BE VERY CAREFUL when modifying the array
+ //! you ust keep it sorted! You usually don't use this function.
+ TYPE* editArray();
+
+ //! finds the index of an item
+ ssize_t indexOf(const TYPE& item) const;
+
+ //! finds where this item should be inserted
+ size_t orderOf(const TYPE& item) const;
+
+
+ /*!
+ * accessors
+ */
+
+ //! read-only access to an item at a given index
+ inline const TYPE& operator [] (size_t index) const;
+ //! alternate name for operator []
+ inline const TYPE& itemAt(size_t index) const;
+ //! stack-usage of the vector. returns the top of the stack (last element)
+ const TYPE& top() const;
+ //! same as operator [], but allows to access the vector backward (from the end) with a negative index
+ const TYPE& mirrorItemAt(ssize_t index) const;
+
+ /*!
+ * modifing the array
+ */
+
+ //! add an item in the right place (and replace the one that is there)
+ ssize_t add(const TYPE& item);
+
+ //! editItemAt() MUST NOT change the order of this item
+ TYPE& editItemAt(size_t index) {
+ return *( static_cast<TYPE *>(VectorImpl::editItemLocation(index)) );
+ }
+
+ //! merges a vector into this one
+ ssize_t merge(const Vector<TYPE>& vector);
+ ssize_t merge(const SortedVector<TYPE>& vector);
+
+ //! removes an item
+ ssize_t remove(const TYPE&);
+
+ //! remove several items
+ inline ssize_t removeItemsAt(size_t index, size_t count = 1);
+ //! remove one item
+ inline ssize_t removeAt(size_t index) { return removeItemsAt(index); }
+
+protected:
+ virtual void do_construct(void* storage, size_t num) const;
+ virtual void do_destroy(void* storage, size_t num) const;
+ virtual void do_copy(void* dest, const void* from, size_t num) const;
+ virtual void do_splat(void* dest, const void* item, size_t num) const;
+ virtual void do_move_forward(void* dest, const void* from, size_t num) const;
+ virtual void do_move_backward(void* dest, const void* from, size_t num) const;
+ virtual int do_compare(const void* lhs, const void* rhs) const;
+};
+
+
+// ---------------------------------------------------------------------------
+// No user serviceable parts from here...
+// ---------------------------------------------------------------------------
+
+template<class TYPE> inline
+SortedVector<TYPE>::SortedVector()
+ : SortedVectorImpl(sizeof(TYPE),
+ ((traits<TYPE>::has_trivial_ctor ? HAS_TRIVIAL_CTOR : 0)
+ |(traits<TYPE>::has_trivial_dtor ? HAS_TRIVIAL_DTOR : 0)
+ |(traits<TYPE>::has_trivial_copy ? HAS_TRIVIAL_COPY : 0)
+ |(traits<TYPE>::has_trivial_assign ? HAS_TRIVIAL_ASSIGN : 0))
+ )
+{
+}
+
+template<class TYPE> inline
+SortedVector<TYPE>::SortedVector(const SortedVector<TYPE>& rhs)
+ : SortedVectorImpl(rhs) {
+}
+
+template<class TYPE> inline
+SortedVector<TYPE>::~SortedVector() {
+ finish_vector();
+}
+
+template<class TYPE> inline
+SortedVector<TYPE>& SortedVector<TYPE>::operator = (const SortedVector<TYPE>& rhs) {
+ SortedVectorImpl::operator = (rhs);
+ return *this;
+}
+
+template<class TYPE> inline
+const SortedVector<TYPE>& SortedVector<TYPE>::operator = (const SortedVector<TYPE>& rhs) const {
+ SortedVectorImpl::operator = (rhs);
+ return *this;
+}
+
+template<class TYPE> inline
+const TYPE* SortedVector<TYPE>::array() const {
+ return static_cast<const TYPE *>(arrayImpl());
+}
+
+template<class TYPE> inline
+TYPE* SortedVector<TYPE>::editArray() {
+ return static_cast<TYPE *>(editArrayImpl());
+}
+
+
+template<class TYPE> inline
+const TYPE& SortedVector<TYPE>::operator[](size_t index) const {
+ assert( index<size() );
+ return *(array() + index);
+}
+
+template<class TYPE> inline
+const TYPE& SortedVector<TYPE>::itemAt(size_t index) const {
+ return operator[](index);
+}
+
+template<class TYPE> inline
+const TYPE& SortedVector<TYPE>::mirrorItemAt(ssize_t index) const {
+ assert( (index>0 ? index : -index)<size() );
+ return *(array() + ((index<0) ? (size()-index) : index));
+}
+
+template<class TYPE> inline
+const TYPE& SortedVector<TYPE>::top() const {
+ return *(array() + size() - 1);
+}
+
+template<class TYPE> inline
+ssize_t SortedVector<TYPE>::add(const TYPE& item) {
+ return SortedVectorImpl::add(&item);
+}
+
+template<class TYPE> inline
+ssize_t SortedVector<TYPE>::indexOf(const TYPE& item) const {
+ return SortedVectorImpl::indexOf(&item);
+}
+
+template<class TYPE> inline
+size_t SortedVector<TYPE>::orderOf(const TYPE& item) const {
+ return SortedVectorImpl::orderOf(&item);
+}
+
+template<class TYPE> inline
+ssize_t SortedVector<TYPE>::merge(const Vector<TYPE>& vector) {
+ return SortedVectorImpl::merge(reinterpret_cast<const VectorImpl&>(vector));
+}
+
+template<class TYPE> inline
+ssize_t SortedVector<TYPE>::merge(const SortedVector<TYPE>& vector) {
+ return SortedVectorImpl::merge(reinterpret_cast<const SortedVectorImpl&>(vector));
+}
+
+template<class TYPE> inline
+ssize_t SortedVector<TYPE>::remove(const TYPE& item) {
+ return SortedVectorImpl::remove(&item);
+}
+
+template<class TYPE> inline
+ssize_t SortedVector<TYPE>::removeItemsAt(size_t index, size_t count) {
+ return VectorImpl::removeItemsAt(index, count);
+}
+
+// ---------------------------------------------------------------------------
+
+template<class TYPE>
+void SortedVector<TYPE>::do_construct(void* storage, size_t num) const {
+ construct_type( reinterpret_cast<TYPE*>(storage), num );
+}
+
+template<class TYPE>
+void SortedVector<TYPE>::do_destroy(void* storage, size_t num) const {
+ destroy_type( reinterpret_cast<TYPE*>(storage), num );
+}
+
+template<class TYPE>
+void SortedVector<TYPE>::do_copy(void* dest, const void* from, size_t num) const {
+ copy_type( reinterpret_cast<TYPE*>(dest), reinterpret_cast<const TYPE*>(from), num );
+}
+
+template<class TYPE>
+void SortedVector<TYPE>::do_splat(void* dest, const void* item, size_t num) const {
+ splat_type( reinterpret_cast<TYPE*>(dest), reinterpret_cast<const TYPE*>(item), num );
+}
+
+template<class TYPE>
+void SortedVector<TYPE>::do_move_forward(void* dest, const void* from, size_t num) const {
+ move_forward_type( reinterpret_cast<TYPE*>(dest), reinterpret_cast<const TYPE*>(from), num );
+}
+
+template<class TYPE>
+void SortedVector<TYPE>::do_move_backward(void* dest, const void* from, size_t num) const {
+ move_backward_type( reinterpret_cast<TYPE*>(dest), reinterpret_cast<const TYPE*>(from), num );
+}
+
+template<class TYPE>
+int SortedVector<TYPE>::do_compare(const void* lhs, const void* rhs) const {
+ return compare_type( *reinterpret_cast<const TYPE*>(lhs), *reinterpret_cast<const TYPE*>(rhs) );
+}
+
+} // namespace tinyutils
+} // namespace stagefright
+
+
+// ---------------------------------------------------------------------------
+
+#endif // ANDROID_PIXELFLINGER_SORTED_VECTOR_H
diff --git a/media/libstagefright/system/core/libpixelflinger/codeflinger/tinyutils/Vector.h b/media/libstagefright/system/core/libpixelflinger/codeflinger/tinyutils/Vector.h
new file mode 100644
index 000000000..3fe87a248
--- /dev/null
+++ b/media/libstagefright/system/core/libpixelflinger/codeflinger/tinyutils/Vector.h
@@ -0,0 +1,353 @@
+/*
+ * Copyright 2005 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_PIXELFLINGER_VECTOR_H
+#define ANDROID_PIXELFLINGER_VECTOR_H
+
+#include <new>
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <cutils/log.h>
+
+#include "Errors.h"
+#include "VectorImpl.h"
+#include "TypeHelpers.h"
+
+// ---------------------------------------------------------------------------
+
+namespace stagefright {
+namespace tinyutils {
+
+/*!
+ * The main templated vector class ensuring type safety
+ * while making use of VectorImpl.
+ * This is the class users want to use.
+ */
+
+template <class TYPE>
+class Vector : private VectorImpl
+{
+public:
+ typedef TYPE value_type;
+
+ /*!
+ * Constructors and destructors
+ */
+
+ Vector();
+ Vector(const Vector<TYPE>& rhs);
+ virtual ~Vector();
+
+ /*! copy operator */
+ const Vector<TYPE>& operator = (const Vector<TYPE>& rhs) const;
+ Vector<TYPE>& operator = (const Vector<TYPE>& rhs);
+
+ /*
+ * empty the vector
+ */
+
+ inline void clear() { VectorImpl::clear(); }
+
+ /*!
+ * vector stats
+ */
+
+ //! returns number of items in the vector
+ inline size_t size() const { return VectorImpl::size(); }
+ //! returns wether or not the vector is empty
+ inline bool isEmpty() const { return VectorImpl::isEmpty(); }
+ //! returns how many items can be stored without reallocating the backing store
+ inline size_t capacity() const { return VectorImpl::capacity(); }
+ //! setst the capacity. capacity can never be reduced less than size()
+ inline ssize_t setCapacity(size_t size) { return VectorImpl::setCapacity(size); }
+
+ /*!
+ * C-style array access
+ */
+
+ //! read-only C-style access
+ inline const TYPE* array() const;
+ //! read-write C-style access
+ TYPE* editArray();
+
+ /*!
+ * accessors
+ */
+
+ //! read-only access to an item at a given index
+ inline const TYPE& operator [] (size_t index) const;
+ //! alternate name for operator []
+ inline const TYPE& itemAt(size_t index) const;
+ //! stack-usage of the vector. returns the top of the stack (last element)
+ const TYPE& top() const;
+ //! same as operator [], but allows to access the vector backward (from the end) with a negative index
+ const TYPE& mirrorItemAt(ssize_t index) const;
+
+ /*!
+ * modifing the array
+ */
+
+ //! copy-on write support, grants write access to an item
+ TYPE& editItemAt(size_t index);
+ //! grants right acces to the top of the stack (last element)
+ TYPE& editTop();
+
+ /*!
+ * append/insert another vector
+ */
+
+ //! insert another vector at a given index
+ ssize_t insertVectorAt(const Vector<TYPE>& vector, size_t index);
+
+ //! append another vector at the end of this one
+ ssize_t appendVector(const Vector<TYPE>& vector);
+
+
+ /*!
+ * add/insert/replace items
+ */
+
+ //! insert one or several items initialized with their default constructor
+ inline ssize_t insertAt(size_t index, size_t numItems = 1);
+ //! insert on onr several items initialized from a prototype item
+ ssize_t insertAt(const TYPE& prototype_item, size_t index, size_t numItems = 1);
+ //! pop the top of the stack (removes the last element). No-op if the stack's empty
+ inline void pop();
+ //! pushes an item initialized with its default constructor
+ inline void push();
+ //! pushes an item on the top of the stack
+ void push(const TYPE& item);
+ //! same as push() but returns the index the item was added at (or an error)
+ inline ssize_t add();
+ //! same as push() but returns the index the item was added at (or an error)
+ ssize_t add(const TYPE& item);
+ //! replace an item with a new one initialized with its default constructor
+ inline ssize_t replaceAt(size_t index);
+ //! replace an item with a new one
+ ssize_t replaceAt(const TYPE& item, size_t index);
+
+ /*!
+ * remove items
+ */
+
+ //! remove several items
+ inline ssize_t removeItemsAt(size_t index, size_t count = 1);
+ //! remove one item
+ inline ssize_t removeAt(size_t index) { return removeItemsAt(index); }
+
+ /*!
+ * sort (stable) the array
+ */
+
+ typedef int (*compar_t)(const TYPE* lhs, const TYPE* rhs);
+ typedef int (*compar_r_t)(const TYPE* lhs, const TYPE* rhs, void* state);
+
+ inline status_t sort(compar_t cmp);
+ inline status_t sort(compar_r_t cmp, void* state);
+
+protected:
+ virtual void do_construct(void* storage, size_t num) const;
+ virtual void do_destroy(void* storage, size_t num) const;
+ virtual void do_copy(void* dest, const void* from, size_t num) const;
+ virtual void do_splat(void* dest, const void* item, size_t num) const;
+ virtual void do_move_forward(void* dest, const void* from, size_t num) const;
+ virtual void do_move_backward(void* dest, const void* from, size_t num) const;
+};
+
+
+// ---------------------------------------------------------------------------
+// No user serviceable parts from here...
+// ---------------------------------------------------------------------------
+
+template<class TYPE> inline
+Vector<TYPE>::Vector()
+ : VectorImpl(sizeof(TYPE),
+ ((traits<TYPE>::has_trivial_ctor ? HAS_TRIVIAL_CTOR : 0)
+ |(traits<TYPE>::has_trivial_dtor ? HAS_TRIVIAL_DTOR : 0)
+ |(traits<TYPE>::has_trivial_copy ? HAS_TRIVIAL_COPY : 0)
+ |(traits<TYPE>::has_trivial_assign ? HAS_TRIVIAL_ASSIGN : 0))
+ )
+{
+}
+
+template<class TYPE> inline
+Vector<TYPE>::Vector(const Vector<TYPE>& rhs)
+ : VectorImpl(rhs) {
+}
+
+template<class TYPE> inline
+Vector<TYPE>::~Vector() {
+ finish_vector();
+}
+
+template<class TYPE> inline
+Vector<TYPE>& Vector<TYPE>::operator = (const Vector<TYPE>& rhs) {
+ VectorImpl::operator = (rhs);
+ return *this;
+}
+
+template<class TYPE> inline
+const Vector<TYPE>& Vector<TYPE>::operator = (const Vector<TYPE>& rhs) const {
+ VectorImpl::operator = (rhs);
+ return *this;
+}
+
+template<class TYPE> inline
+const TYPE* Vector<TYPE>::array() const {
+ return static_cast<const TYPE *>(arrayImpl());
+}
+
+template<class TYPE> inline
+TYPE* Vector<TYPE>::editArray() {
+ return static_cast<TYPE *>(editArrayImpl());
+}
+
+
+template<class TYPE> inline
+const TYPE& Vector<TYPE>::operator[](size_t index) const {
+ LOG_FATAL_IF( index>=size(),
+ "itemAt: index %d is past size %d", (int)index, (int)size() );
+ return *(array() + index);
+}
+
+template<class TYPE> inline
+const TYPE& Vector<TYPE>::itemAt(size_t index) const {
+ return operator[](index);
+}
+
+template<class TYPE> inline
+const TYPE& Vector<TYPE>::mirrorItemAt(ssize_t index) const {
+ LOG_FATAL_IF( (index>0 ? index : -index)>=size(),
+ "mirrorItemAt: index %d is past size %d",
+ (int)index, (int)size() );
+ return *(array() + ((index<0) ? (size()-index) : index));
+}
+
+template<class TYPE> inline
+const TYPE& Vector<TYPE>::top() const {
+ return *(array() + size() - 1);
+}
+
+template<class TYPE> inline
+TYPE& Vector<TYPE>::editItemAt(size_t index) {
+ return *( static_cast<TYPE *>(editItemLocation(index)) );
+}
+
+template<class TYPE> inline
+TYPE& Vector<TYPE>::editTop() {
+ return *( static_cast<TYPE *>(editItemLocation(size()-1)) );
+}
+
+template<class TYPE> inline
+ssize_t Vector<TYPE>::insertVectorAt(const Vector<TYPE>& vector, size_t index) {
+ return VectorImpl::insertVectorAt(reinterpret_cast<const VectorImpl&>(vector), index);
+}
+
+template<class TYPE> inline
+ssize_t Vector<TYPE>::appendVector(const Vector<TYPE>& vector) {
+ return VectorImpl::appendVector(reinterpret_cast<const VectorImpl&>(vector));
+}
+
+template<class TYPE> inline
+ssize_t Vector<TYPE>::insertAt(const TYPE& item, size_t index, size_t numItems) {
+ return VectorImpl::insertAt(&item, index, numItems);
+}
+
+template<class TYPE> inline
+void Vector<TYPE>::push(const TYPE& item) {
+ return VectorImpl::push(&item);
+}
+
+template<class TYPE> inline
+ssize_t Vector<TYPE>::add(const TYPE& item) {
+ return VectorImpl::add(&item);
+}
+
+template<class TYPE> inline
+ssize_t Vector<TYPE>::replaceAt(const TYPE& item, size_t index) {
+ return VectorImpl::replaceAt(&item, index);
+}
+
+template<class TYPE> inline
+ssize_t Vector<TYPE>::insertAt(size_t index, size_t numItems) {
+ return VectorImpl::insertAt(index, numItems);
+}
+
+template<class TYPE> inline
+void Vector<TYPE>::pop() {
+ VectorImpl::pop();
+}
+
+template<class TYPE> inline
+void Vector<TYPE>::push() {
+ VectorImpl::push();
+}
+
+template<class TYPE> inline
+ssize_t Vector<TYPE>::add() {
+ return VectorImpl::add();
+}
+
+template<class TYPE> inline
+ssize_t Vector<TYPE>::replaceAt(size_t index) {
+ return VectorImpl::replaceAt(index);
+}
+
+template<class TYPE> inline
+ssize_t Vector<TYPE>::removeItemsAt(size_t index, size_t count) {
+ return VectorImpl::removeItemsAt(index, count);
+}
+
+// ---------------------------------------------------------------------------
+
+template<class TYPE>
+void Vector<TYPE>::do_construct(void* storage, size_t num) const {
+ construct_type( reinterpret_cast<TYPE*>(storage), num );
+}
+
+template<class TYPE>
+void Vector<TYPE>::do_destroy(void* storage, size_t num) const {
+ destroy_type( reinterpret_cast<TYPE*>(storage), num );
+}
+
+template<class TYPE>
+void Vector<TYPE>::do_copy(void* dest, const void* from, size_t num) const {
+ copy_type( reinterpret_cast<TYPE*>(dest), reinterpret_cast<const TYPE*>(from), num );
+}
+
+template<class TYPE>
+void Vector<TYPE>::do_splat(void* dest, const void* item, size_t num) const {
+ splat_type( reinterpret_cast<TYPE*>(dest), reinterpret_cast<const TYPE*>(item), num );
+}
+
+template<class TYPE>
+void Vector<TYPE>::do_move_forward(void* dest, const void* from, size_t num) const {
+ move_forward_type( reinterpret_cast<TYPE*>(dest), reinterpret_cast<const TYPE*>(from), num );
+}
+
+template<class TYPE>
+void Vector<TYPE>::do_move_backward(void* dest, const void* from, size_t num) const {
+ move_backward_type( reinterpret_cast<TYPE*>(dest), reinterpret_cast<const TYPE*>(from), num );
+}
+
+} // namespace tinyutils
+} // namespace stagefright
+
+
+// ---------------------------------------------------------------------------
+
+#endif // ANDROID_PIXELFLINGER_VECTOR_H
diff --git a/media/libstagefright/system/core/libpixelflinger/codeflinger/tinyutils/VectorImpl.h b/media/libstagefright/system/core/libpixelflinger/codeflinger/tinyutils/VectorImpl.h
new file mode 100644
index 000000000..6cc55c49a
--- /dev/null
+++ b/media/libstagefright/system/core/libpixelflinger/codeflinger/tinyutils/VectorImpl.h
@@ -0,0 +1,195 @@
+/*
+ * Copyright 2005 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_PIXELFLINGER_VECTOR_IMPL_H
+#define ANDROID_PIXELFLINGER_VECTOR_IMPL_H
+
+#include <assert.h>
+#include <stdint.h>
+#include <sys/types.h>
+
+// ---------------------------------------------------------------------------
+// No user serviceable parts in here...
+// ---------------------------------------------------------------------------
+
+namespace stagefright {
+namespace tinyutils {
+
+/*!
+ * Implementation of the guts of the vector<> class
+ * this ensures backward binary compatibility and
+ * reduces code size.
+ * For performance reasons, we expose mStorage and mCount
+ * so these fields are set in stone.
+ *
+ */
+
+class VectorImpl
+{
+public:
+ enum { // flags passed to the ctor
+ HAS_TRIVIAL_CTOR = 0x00000001,
+ HAS_TRIVIAL_DTOR = 0x00000002,
+ HAS_TRIVIAL_COPY = 0x00000004,
+ HAS_TRIVIAL_ASSIGN = 0x00000008
+ };
+
+ VectorImpl(size_t itemSize, uint32_t flags);
+ VectorImpl(const VectorImpl& rhs);
+ virtual ~VectorImpl();
+
+ /*! must be called from subclasses destructor */
+ void finish_vector();
+
+ VectorImpl& operator = (const VectorImpl& rhs);
+
+ /*! C-style array access */
+ inline const void* arrayImpl() const { return mStorage; }
+ void* editArrayImpl();
+
+ /*! vector stats */
+ inline size_t size() const { return mCount; }
+ inline bool isEmpty() const { return mCount == 0; }
+ size_t capacity() const;
+ ssize_t setCapacity(size_t size);
+
+ /*! append/insert another vector */
+ ssize_t insertVectorAt(const VectorImpl& vector, size_t index);
+ ssize_t appendVector(const VectorImpl& vector);
+
+ /*! add/insert/replace items */
+ ssize_t insertAt(size_t where, size_t numItems = 1);
+ ssize_t insertAt(const void* item, size_t where, size_t numItems = 1);
+ void pop();
+ void push();
+ void push(const void* item);
+ ssize_t add();
+ ssize_t add(const void* item);
+ ssize_t replaceAt(size_t index);
+ ssize_t replaceAt(const void* item, size_t index);
+
+ /*! remove items */
+ ssize_t removeItemsAt(size_t index, size_t count = 1);
+ void clear();
+
+ const void* itemLocation(size_t index) const;
+ void* editItemLocation(size_t index);
+
+protected:
+ size_t itemSize() const;
+ void release_storage();
+
+ virtual void do_construct(void* storage, size_t num) const = 0;
+ virtual void do_destroy(void* storage, size_t num) const = 0;
+ virtual void do_copy(void* dest, const void* from, size_t num) const = 0;
+ virtual void do_splat(void* dest, const void* item, size_t num) const = 0;
+ virtual void do_move_forward(void* dest, const void* from, size_t num) const = 0;
+ virtual void do_move_backward(void* dest, const void* from, size_t num) const = 0;
+
+ // take care of FBC...
+ virtual void reservedVectorImpl1();
+ virtual void reservedVectorImpl2();
+ virtual void reservedVectorImpl3();
+ virtual void reservedVectorImpl4();
+ virtual void reservedVectorImpl5();
+ virtual void reservedVectorImpl6();
+ virtual void reservedVectorImpl7();
+ virtual void reservedVectorImpl8();
+
+private:
+ void* _grow(size_t where, size_t amount);
+ void _shrink(size_t where, size_t amount);
+
+ inline void _do_construct(void* storage, size_t num) const;
+ inline void _do_destroy(void* storage, size_t num) const;
+ inline void _do_copy(void* dest, const void* from, size_t num) const;
+ inline void _do_splat(void* dest, const void* item, size_t num) const;
+ inline void _do_move_forward(void* dest, const void* from, size_t num) const;
+ inline void _do_move_backward(void* dest, const void* from, size_t num) const;
+
+ // These 2 fields are exposed in the inlines below,
+ // so they're set in stone.
+ void * mStorage; // base address of the vector
+ size_t mCount; // number of items
+
+ const uint32_t mFlags;
+ const size_t mItemSize;
+};
+
+
+
+class SortedVectorImpl : public VectorImpl
+{
+public:
+ SortedVectorImpl(size_t itemSize, uint32_t flags);
+ SortedVectorImpl(const VectorImpl& rhs);
+ virtual ~SortedVectorImpl();
+
+ SortedVectorImpl& operator = (const SortedVectorImpl& rhs);
+
+ //! finds the index of an item
+ ssize_t indexOf(const void* item) const;
+
+ //! finds where this item should be inserted
+ size_t orderOf(const void* item) const;
+
+ //! add an item in the right place (or replaces it if there is one)
+ ssize_t add(const void* item);
+
+ //! merges a vector into this one
+ ssize_t merge(const VectorImpl& vector);
+ ssize_t merge(const SortedVectorImpl& vector);
+
+ //! removes an item
+ ssize_t remove(const void* item);
+
+protected:
+ virtual int do_compare(const void* lhs, const void* rhs) const = 0;
+
+ // take care of FBC...
+ virtual void reservedSortedVectorImpl1();
+ virtual void reservedSortedVectorImpl2();
+ virtual void reservedSortedVectorImpl3();
+ virtual void reservedSortedVectorImpl4();
+ virtual void reservedSortedVectorImpl5();
+ virtual void reservedSortedVectorImpl6();
+ virtual void reservedSortedVectorImpl7();
+ virtual void reservedSortedVectorImpl8();
+
+private:
+ ssize_t _indexOrderOf(const void* item, size_t* order = 0) const;
+
+ // these are made private, because they can't be used on a SortedVector
+ // (they don't have an implementation either)
+ ssize_t add();
+ void pop();
+ void push();
+ void push(const void* item);
+ ssize_t insertVectorAt(const VectorImpl& vector, size_t index);
+ ssize_t appendVector(const VectorImpl& vector);
+ ssize_t insertAt(size_t where, size_t numItems = 1);
+ ssize_t insertAt(const void* item, size_t where, size_t numItems = 1);
+ ssize_t replaceAt(size_t index);
+ ssize_t replaceAt(const void* item, size_t index);
+};
+
+} // namespace tinyutils
+} // namespace stagefright
+
+
+// ---------------------------------------------------------------------------
+
+#endif // ANDROID_PIXELFLINGER_VECTOR_IMPL_H
diff --git a/media/libstagefright/system/core/libutils/RefBase.cpp b/media/libstagefright/system/core/libutils/RefBase.cpp
new file mode 100644
index 000000000..259b0a4e6
--- /dev/null
+++ b/media/libstagefright/system/core/libutils/RefBase.cpp
@@ -0,0 +1,657 @@
+/*
+ * Copyright (C) 2005 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "RefBase"
+// #define LOG_NDEBUG 0
+
+#include <utils/RefBase.h>
+
+#include <utils/Atomic.h>
+#ifdef _MSC_VER
+class CallStack {
+public:
+ CallStack(int x) {}
+};
+#else
+#include <utils/CallStack.h>
+#endif
+#include <utils/Log.h>
+#include <utils/threads.h>
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <typeinfo>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+// compile with refcounting debugging enabled
+#define DEBUG_REFS 0
+
+// whether ref-tracking is enabled by default, if not, trackMe(true, false)
+// needs to be called explicitly
+#define DEBUG_REFS_ENABLED_BY_DEFAULT 0
+
+// whether callstack are collected (significantly slows things down)
+#define DEBUG_REFS_CALLSTACK_ENABLED 0
+
+// folder where stack traces are saved when DEBUG_REFS is enabled
+// this folder needs to exist and be writable
+#define DEBUG_REFS_CALLSTACK_PATH "/data/debug"
+
+// log all reference counting operations
+#define PRINT_REFS 0
+
+// ---------------------------------------------------------------------------
+
+namespace stagefright {
+
+#define INITIAL_STRONG_VALUE (1<<28)
+
+// ---------------------------------------------------------------------------
+
+class RefBase::weakref_impl : public RefBase::weakref_type
+{
+public:
+ volatile int32_t mStrong;
+ volatile int32_t mWeak;
+ RefBase* const mBase;
+ volatile int32_t mFlags;
+
+#if !DEBUG_REFS
+
+ weakref_impl(RefBase* base)
+ : mStrong(INITIAL_STRONG_VALUE)
+ , mWeak(0)
+ , mBase(base)
+ , mFlags(0)
+ {
+ }
+
+ void addStrongRef(const void* /*id*/) { }
+ void removeStrongRef(const void* /*id*/) { }
+ void renameStrongRefId(const void* /*old_id*/, const void* /*new_id*/) { }
+ void addWeakRef(const void* /*id*/) { }
+ void removeWeakRef(const void* /*id*/) { }
+ void renameWeakRefId(const void* /*old_id*/, const void* /*new_id*/) { }
+ void printRefs() const { }
+ void trackMe(bool, bool) { }
+
+#else
+
+ weakref_impl(RefBase* base)
+ : mStrong(INITIAL_STRONG_VALUE)
+ , mWeak(0)
+ , mBase(base)
+ , mFlags(0)
+ , mStrongRefs(NULL)
+ , mWeakRefs(NULL)
+ , mTrackEnabled(!!DEBUG_REFS_ENABLED_BY_DEFAULT)
+ , mRetain(false)
+ {
+ }
+
+ ~weakref_impl()
+ {
+ bool dumpStack = false;
+ if (!mRetain && mStrongRefs != NULL) {
+ dumpStack = true;
+ ALOGE("Strong references remain:");
+ ref_entry* refs = mStrongRefs;
+ while (refs) {
+ char inc = refs->ref >= 0 ? '+' : '-';
+ ALOGD("\t%c ID %p (ref %d):", inc, refs->id, refs->ref);
+#if DEBUG_REFS_CALLSTACK_ENABLED
+ refs->stack.dump(LOG_TAG);
+#endif
+ refs = refs->next;
+ }
+ }
+
+ if (!mRetain && mWeakRefs != NULL) {
+ dumpStack = true;
+ ALOGE("Weak references remain!");
+ ref_entry* refs = mWeakRefs;
+ while (refs) {
+ char inc = refs->ref >= 0 ? '+' : '-';
+ ALOGD("\t%c ID %p (ref %d):", inc, refs->id, refs->ref);
+#if DEBUG_REFS_CALLSTACK_ENABLED
+ refs->stack.dump(LOG_TAG);
+#endif
+ refs = refs->next;
+ }
+ }
+ if (dumpStack) {
+ ALOGE("above errors at:");
+ CallStack stack(LOG_TAG);
+ }
+ }
+
+ void addStrongRef(const void* id) {
+ //ALOGD_IF(mTrackEnabled,
+ // "addStrongRef: RefBase=%p, id=%p", mBase, id);
+ addRef(&mStrongRefs, id, mStrong);
+ }
+
+ void removeStrongRef(const void* id) {
+ //ALOGD_IF(mTrackEnabled,
+ // "removeStrongRef: RefBase=%p, id=%p", mBase, id);
+ if (!mRetain) {
+ removeRef(&mStrongRefs, id);
+ } else {
+ addRef(&mStrongRefs, id, -mStrong);
+ }
+ }
+
+ void renameStrongRefId(const void* old_id, const void* new_id) {
+ //ALOGD_IF(mTrackEnabled,
+ // "renameStrongRefId: RefBase=%p, oid=%p, nid=%p",
+ // mBase, old_id, new_id);
+ renameRefsId(mStrongRefs, old_id, new_id);
+ }
+
+ void addWeakRef(const void* id) {
+ addRef(&mWeakRefs, id, mWeak);
+ }
+
+ void removeWeakRef(const void* id) {
+ if (!mRetain) {
+ removeRef(&mWeakRefs, id);
+ } else {
+ addRef(&mWeakRefs, id, -mWeak);
+ }
+ }
+
+ void renameWeakRefId(const void* old_id, const void* new_id) {
+ renameRefsId(mWeakRefs, old_id, new_id);
+ }
+
+ void trackMe(bool track, bool retain)
+ {
+ mTrackEnabled = track;
+ mRetain = retain;
+ }
+
+ void printRefs() const
+ {
+ String8 text;
+
+ {
+ Mutex::Autolock _l(mMutex);
+ char buf[128];
+ sprintf(buf, "Strong references on RefBase %p (weakref_type %p):\n", mBase, this);
+ text.append(buf);
+ printRefsLocked(&text, mStrongRefs);
+ sprintf(buf, "Weak references on RefBase %p (weakref_type %p):\n", mBase, this);
+ text.append(buf);
+ printRefsLocked(&text, mWeakRefs);
+ }
+
+ {
+ char name[100];
+ snprintf(name, 100, DEBUG_REFS_CALLSTACK_PATH "/%p.stack", this);
+ int rc = open(name, O_RDWR | O_CREAT | O_APPEND, 644);
+ if (rc >= 0) {
+ write(rc, text.string(), text.length());
+ close(rc);
+ ALOGD("STACK TRACE for %p saved in %s", this, name);
+ }
+ else ALOGE("FAILED TO PRINT STACK TRACE for %p in %s: %s", this,
+ name, strerror(errno));
+ }
+ }
+
+private:
+ struct ref_entry
+ {
+ ref_entry* next;
+ const void* id;
+#if DEBUG_REFS_CALLSTACK_ENABLED
+ CallStack stack;
+#endif
+ int32_t ref;
+ };
+
+ void addRef(ref_entry** refs, const void* id, int32_t mRef)
+ {
+ if (mTrackEnabled) {
+ AutoMutex _l(mMutex);
+
+ ref_entry* ref = new ref_entry;
+ // Reference count at the time of the snapshot, but before the
+ // update. Positive value means we increment, negative--we
+ // decrement the reference count.
+ ref->ref = mRef;
+ ref->id = id;
+#if DEBUG_REFS_CALLSTACK_ENABLED
+ ref->stack.update(2);
+#endif
+ ref->next = *refs;
+ *refs = ref;
+ }
+ }
+
+ void removeRef(ref_entry** refs, const void* id)
+ {
+ if (mTrackEnabled) {
+ AutoMutex _l(mMutex);
+
+ ref_entry* const head = *refs;
+ ref_entry* ref = head;
+ while (ref != NULL) {
+ if (ref->id == id) {
+ *refs = ref->next;
+ delete ref;
+ return;
+ }
+ refs = &ref->next;
+ ref = *refs;
+ }
+
+ ALOGE("RefBase: removing id %p on RefBase %p"
+ "(weakref_type %p) that doesn't exist!",
+ id, mBase, this);
+
+ ref = head;
+ while (ref) {
+ char inc = ref->ref >= 0 ? '+' : '-';
+ ALOGD("\t%c ID %p (ref %d):", inc, ref->id, ref->ref);
+ ref = ref->next;
+ }
+
+ CallStack stack(LOG_TAG);
+ }
+ }
+
+ void renameRefsId(ref_entry* r, const void* old_id, const void* new_id)
+ {
+ if (mTrackEnabled) {
+ AutoMutex _l(mMutex);
+ ref_entry* ref = r;
+ while (ref != NULL) {
+ if (ref->id == old_id) {
+ ref->id = new_id;
+ }
+ ref = ref->next;
+ }
+ }
+ }
+
+ void printRefsLocked(String8* out, const ref_entry* refs) const
+ {
+ char buf[128];
+ while (refs) {
+ char inc = refs->ref >= 0 ? '+' : '-';
+ sprintf(buf, "\t%c ID %p (ref %d):\n",
+ inc, refs->id, refs->ref);
+ out->append(buf);
+#if DEBUG_REFS_CALLSTACK_ENABLED
+ out->append(refs->stack.toString("\t\t"));
+#else
+ out->append("\t\t(call stacks disabled)");
+#endif
+ refs = refs->next;
+ }
+ }
+
+ mutable Mutex mMutex;
+ ref_entry* mStrongRefs;
+ ref_entry* mWeakRefs;
+
+ bool mTrackEnabled;
+ // Collect stack traces on addref and removeref, instead of deleting the stack references
+ // on removeref that match the address ones.
+ bool mRetain;
+
+#endif
+};
+
+// ---------------------------------------------------------------------------
+
+void RefBase::incStrong(const void* id) const
+{
+ weakref_impl* const refs = mRefs;
+ refs->incWeak(id);
+
+ refs->addStrongRef(id);
+ const int32_t c = android_atomic_inc(&refs->mStrong);
+ ALOG_ASSERT(c > 0, "incStrong() called on %p after last strong ref", refs);
+#if PRINT_REFS
+ ALOGD("incStrong of %p from %p: cnt=%d\n", this, id, c);
+#endif
+ if (c != INITIAL_STRONG_VALUE) {
+ return;
+ }
+
+ android_atomic_add(-INITIAL_STRONG_VALUE, &refs->mStrong);
+ refs->mBase->onFirstRef();
+}
+
+void RefBase::decStrong(const void* id) const
+{
+ weakref_impl* const refs = mRefs;
+ refs->removeStrongRef(id);
+ const int32_t c = android_atomic_dec(&refs->mStrong);
+#if PRINT_REFS
+ ALOGD("decStrong of %p from %p: cnt=%d\n", this, id, c);
+#endif
+ ALOG_ASSERT(c >= 1, "decStrong() called on %p too many times", refs);
+ if (c == 1) {
+ refs->mBase->onLastStrongRef(id);
+ if ((refs->mFlags&OBJECT_LIFETIME_MASK) == OBJECT_LIFETIME_STRONG) {
+ delete this;
+ }
+ }
+ refs->decWeak(id);
+}
+
+void RefBase::forceIncStrong(const void* id) const
+{
+ weakref_impl* const refs = mRefs;
+ refs->incWeak(id);
+
+ refs->addStrongRef(id);
+ const int32_t c = android_atomic_inc(&refs->mStrong);
+ ALOG_ASSERT(c >= 0, "forceIncStrong called on %p after ref count underflow",
+ refs);
+#if PRINT_REFS
+ ALOGD("forceIncStrong of %p from %p: cnt=%d\n", this, id, c);
+#endif
+
+ switch (c) {
+ case INITIAL_STRONG_VALUE:
+ android_atomic_add(-INITIAL_STRONG_VALUE, &refs->mStrong);
+ // fall through...
+ case 0:
+ refs->mBase->onFirstRef();
+ }
+}
+
+int32_t RefBase::getStrongCount() const
+{
+ return mRefs->mStrong;
+}
+
+RefBase* RefBase::weakref_type::refBase() const
+{
+ return static_cast<const weakref_impl*>(this)->mBase;
+}
+
+void RefBase::weakref_type::incWeak(const void* id)
+{
+ weakref_impl* const impl = static_cast<weakref_impl*>(this);
+ impl->addWeakRef(id);
+ const int32_t c = android_atomic_inc(&impl->mWeak);
+ ALOG_ASSERT(c >= 0, "incWeak called on %p after last weak ref", this);
+}
+
+
+void RefBase::weakref_type::decWeak(const void* id)
+{
+ weakref_impl* const impl = static_cast<weakref_impl*>(this);
+ impl->removeWeakRef(id);
+ const int32_t c = android_atomic_dec(&impl->mWeak);
+ ALOG_ASSERT(c >= 1, "decWeak called on %p too many times", this);
+ if (c != 1) return;
+
+ if ((impl->mFlags&OBJECT_LIFETIME_WEAK) == OBJECT_LIFETIME_STRONG) {
+ // This is the regular lifetime case. The object is destroyed
+ // when the last strong reference goes away. Since weakref_impl
+ // outlive the object, it is not destroyed in the dtor, and
+ // we'll have to do it here.
+ if (impl->mStrong == INITIAL_STRONG_VALUE) {
+ // Special case: we never had a strong reference, so we need to
+ // destroy the object now.
+ delete impl->mBase;
+ } else {
+ // ALOGV("Freeing refs %p of old RefBase %p\n", this, impl->mBase);
+ delete impl;
+ }
+ } else {
+ // less common case: lifetime is OBJECT_LIFETIME_{WEAK|FOREVER}
+ impl->mBase->onLastWeakRef(id);
+ if ((impl->mFlags&OBJECT_LIFETIME_MASK) == OBJECT_LIFETIME_WEAK) {
+ // this is the OBJECT_LIFETIME_WEAK case. The last weak-reference
+ // is gone, we can destroy the object.
+ delete impl->mBase;
+ }
+ }
+}
+
+bool RefBase::weakref_type::attemptIncStrong(const void* id)
+{
+ incWeak(id);
+
+ weakref_impl* const impl = static_cast<weakref_impl*>(this);
+ int32_t curCount = impl->mStrong;
+
+ ALOG_ASSERT(curCount >= 0,
+ "attemptIncStrong called on %p after underflow", this);
+
+ while (curCount > 0 && curCount != INITIAL_STRONG_VALUE) {
+ // we're in the easy/common case of promoting a weak-reference
+ // from an existing strong reference.
+ if (android_atomic_cmpxchg(curCount, curCount+1, &impl->mStrong) == 0) {
+ break;
+ }
+ // the strong count has changed on us, we need to re-assert our
+ // situation.
+ curCount = impl->mStrong;
+ }
+
+ if (curCount <= 0 || curCount == INITIAL_STRONG_VALUE) {
+ // we're now in the harder case of either:
+ // - there never was a strong reference on us
+ // - or, all strong references have been released
+ if ((impl->mFlags&OBJECT_LIFETIME_WEAK) == OBJECT_LIFETIME_STRONG) {
+ // this object has a "normal" life-time, i.e.: it gets destroyed
+ // when the last strong reference goes away
+ if (curCount <= 0) {
+ // the last strong-reference got released, the object cannot
+ // be revived.
+ decWeak(id);
+ return false;
+ }
+
+ // here, curCount == INITIAL_STRONG_VALUE, which means
+ // there never was a strong-reference, so we can try to
+ // promote this object; we need to do that atomically.
+ while (curCount > 0) {
+ if (android_atomic_cmpxchg(curCount, curCount + 1,
+ &impl->mStrong) == 0) {
+ break;
+ }
+ // the strong count has changed on us, we need to re-assert our
+ // situation (e.g.: another thread has inc/decStrong'ed us)
+ curCount = impl->mStrong;
+ }
+
+ if (curCount <= 0) {
+ // promote() failed, some other thread destroyed us in the
+ // meantime (i.e.: strong count reached zero).
+ decWeak(id);
+ return false;
+ }
+ } else {
+ // this object has an "extended" life-time, i.e.: it can be
+ // revived from a weak-reference only.
+ // Ask the object's implementation if it agrees to be revived
+ if (!impl->mBase->onIncStrongAttempted(FIRST_INC_STRONG, id)) {
+ // it didn't so give-up.
+ decWeak(id);
+ return false;
+ }
+ // grab a strong-reference, which is always safe due to the
+ // extended life-time.
+ curCount = android_atomic_inc(&impl->mStrong);
+ }
+
+ // If the strong reference count has already been incremented by
+ // someone else, the implementor of onIncStrongAttempted() is holding
+ // an unneeded reference. So call onLastStrongRef() here to remove it.
+ // (No, this is not pretty.) Note that we MUST NOT do this if we
+ // are in fact acquiring the first reference.
+ if (curCount > 0 && curCount < INITIAL_STRONG_VALUE) {
+ impl->mBase->onLastStrongRef(id);
+ }
+ }
+
+ impl->addStrongRef(id);
+
+#if PRINT_REFS
+ ALOGD("attemptIncStrong of %p from %p: cnt=%d\n", this, id, curCount);
+#endif
+
+ // now we need to fix-up the count if it was INITIAL_STRONG_VALUE
+ // this must be done safely, i.e.: handle the case where several threads
+ // were here in attemptIncStrong().
+ curCount = impl->mStrong;
+ while (curCount >= INITIAL_STRONG_VALUE) {
+ ALOG_ASSERT(curCount > INITIAL_STRONG_VALUE,
+ "attemptIncStrong in %p underflowed to INITIAL_STRONG_VALUE",
+ this);
+ if (android_atomic_cmpxchg(curCount, curCount-INITIAL_STRONG_VALUE,
+ &impl->mStrong) == 0) {
+ break;
+ }
+ // the strong-count changed on us, we need to re-assert the situation,
+ // for e.g.: it's possible the fix-up happened in another thread.
+ curCount = impl->mStrong;
+ }
+
+ return true;
+}
+
+bool RefBase::weakref_type::attemptIncWeak(const void* id)
+{
+ weakref_impl* const impl = static_cast<weakref_impl*>(this);
+
+ int32_t curCount = impl->mWeak;
+ ALOG_ASSERT(curCount >= 0, "attemptIncWeak called on %p after underflow",
+ this);
+ while (curCount > 0) {
+ if (android_atomic_cmpxchg(curCount, curCount+1, &impl->mWeak) == 0) {
+ break;
+ }
+ curCount = impl->mWeak;
+ }
+
+ if (curCount > 0) {
+ impl->addWeakRef(id);
+ }
+
+ return curCount > 0;
+}
+
+int32_t RefBase::weakref_type::getWeakCount() const
+{
+ return static_cast<const weakref_impl*>(this)->mWeak;
+}
+
+void RefBase::weakref_type::printRefs() const
+{
+ static_cast<const weakref_impl*>(this)->printRefs();
+}
+
+void RefBase::weakref_type::trackMe(bool enable, bool retain)
+{
+ static_cast<weakref_impl*>(this)->trackMe(enable, retain);
+}
+
+RefBase::weakref_type* RefBase::createWeak(const void* id) const
+{
+ mRefs->incWeak(id);
+ return mRefs;
+}
+
+RefBase::weakref_type* RefBase::getWeakRefs() const
+{
+ return mRefs;
+}
+
+RefBase::RefBase()
+ : mRefs(new weakref_impl(this))
+{
+}
+
+RefBase::~RefBase()
+{
+ if (mRefs->mStrong == INITIAL_STRONG_VALUE) {
+ // we never acquired a strong (and/or weak) reference on this object.
+ delete mRefs;
+ } else {
+ // life-time of this object is extended to WEAK or FOREVER, in
+ // which case weakref_impl doesn't out-live the object and we
+ // can free it now.
+ if ((mRefs->mFlags & OBJECT_LIFETIME_MASK) != OBJECT_LIFETIME_STRONG) {
+ // It's possible that the weak count is not 0 if the object
+ // re-acquired a weak reference in its destructor
+ if (mRefs->mWeak == 0) {
+ delete mRefs;
+ }
+ }
+ }
+ // for debugging purposes, clear this.
+ const_cast<weakref_impl*&>(mRefs) = NULL;
+}
+
+void RefBase::extendObjectLifetime(int32_t mode)
+{
+ android_atomic_or(mode, &mRefs->mFlags);
+}
+
+void RefBase::onFirstRef()
+{
+}
+
+void RefBase::onLastStrongRef(const void* /*id*/)
+{
+}
+
+bool RefBase::onIncStrongAttempted(uint32_t flags, const void* id)
+{
+ return (flags&FIRST_INC_STRONG) ? true : false;
+}
+
+void RefBase::onLastWeakRef(const void* /*id*/)
+{
+}
+
+// ---------------------------------------------------------------------------
+
+void RefBase::renameRefs(size_t n, const ReferenceRenamer& renamer) {
+#if DEBUG_REFS
+ for (size_t i=0 ; i<n ; i++) {
+ renamer(i);
+ }
+#endif
+}
+
+void RefBase::renameRefId(weakref_type* ref,
+ const void* old_id, const void* new_id) {
+ weakref_impl* const impl = static_cast<weakref_impl*>(ref);
+ impl->renameStrongRefId(old_id, new_id);
+ impl->renameWeakRefId(old_id, new_id);
+}
+
+void RefBase::renameRefId(RefBase* ref,
+ const void* old_id, const void* new_id) {
+ ref->mRefs->renameStrongRefId(old_id, new_id);
+ ref->mRefs->renameWeakRefId(old_id, new_id);
+}
+
+}; // namespace stagefright
diff --git a/media/libstagefright/system/core/libutils/SharedBuffer.cpp b/media/libstagefright/system/core/libutils/SharedBuffer.cpp
new file mode 100644
index 000000000..7aefe80bd
--- /dev/null
+++ b/media/libstagefright/system/core/libutils/SharedBuffer.cpp
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2005 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <utils/SharedBuffer.h>
+#include <utils/Atomic.h>
+
+// ---------------------------------------------------------------------------
+
+namespace stagefright {
+
+SharedBuffer* SharedBuffer::alloc(size_t size)
+{
+ SharedBuffer* sb = static_cast<SharedBuffer *>(malloc(sizeof(SharedBuffer) + size));
+ if (sb) {
+ sb->mRefs = 1;
+ sb->mSize = size;
+ }
+ return sb;
+}
+
+
+ssize_t SharedBuffer::dealloc(const SharedBuffer* released)
+{
+ if (released->mRefs != 0) return -1; // XXX: invalid operation
+ free(const_cast<SharedBuffer*>(released));
+ return 0;
+}
+
+SharedBuffer* SharedBuffer::edit() const
+{
+ if (onlyOwner()) {
+ return const_cast<SharedBuffer*>(this);
+ }
+ SharedBuffer* sb = alloc(mSize);
+ if (sb) {
+ memcpy(sb->data(), data(), size());
+ release();
+ }
+ return sb;
+}
+
+SharedBuffer* SharedBuffer::editResize(size_t newSize) const
+{
+ if (onlyOwner()) {
+ SharedBuffer* buf = const_cast<SharedBuffer*>(this);
+ if (buf->mSize == newSize) return buf;
+ buf = (SharedBuffer*)realloc(buf, sizeof(SharedBuffer) + newSize);
+ if (buf != NULL) {
+ buf->mSize = newSize;
+ return buf;
+ }
+ }
+ SharedBuffer* sb = alloc(newSize);
+ if (sb) {
+ const size_t mySize = mSize;
+ memcpy(sb->data(), data(), newSize < mySize ? newSize : mySize);
+ release();
+ }
+ return sb;
+}
+
+SharedBuffer* SharedBuffer::attemptEdit() const
+{
+ if (onlyOwner()) {
+ return const_cast<SharedBuffer*>(this);
+ }
+ return 0;
+}
+
+SharedBuffer* SharedBuffer::reset(size_t new_size) const
+{
+ // cheap-o-reset.
+ SharedBuffer* sb = alloc(new_size);
+ if (sb) {
+ release();
+ }
+ return sb;
+}
+
+void SharedBuffer::acquire() const {
+ android_atomic_inc(&mRefs);
+}
+
+int32_t SharedBuffer::release(uint32_t flags) const
+{
+ int32_t prev = 1;
+ if (onlyOwner() || ((prev = android_atomic_dec(&mRefs)) == 1)) {
+ mRefs = 0;
+ if ((flags & eKeepStorage) == 0) {
+ free(const_cast<SharedBuffer*>(this));
+ }
+ }
+ return prev;
+}
+
+
+}; // namespace stagefright
diff --git a/media/libstagefright/system/core/libutils/Static.cpp b/media/libstagefright/system/core/libutils/Static.cpp
new file mode 100644
index 000000000..476240f4f
--- /dev/null
+++ b/media/libstagefright/system/core/libutils/Static.cpp
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+
+// All static variables go here, to control initialization and
+// destruction order in the library.
+
+namespace stagefright {
+
+// For String8.cpp
+extern void initialize_string8();
+extern void terminate_string8();
+
+// For String16.cpp
+extern void initialize_string16();
+extern void terminate_string16();
+
+class LibUtilsFirstStatics
+{
+public:
+ LibUtilsFirstStatics()
+ {
+ initialize_string8();
+ initialize_string16();
+ }
+
+ ~LibUtilsFirstStatics()
+ {
+ terminate_string16();
+ terminate_string8();
+ }
+};
+
+static LibUtilsFirstStatics gFirstStatics;
+int gDarwinCantLoadAllObjects = 1;
+
+} // namespace stagefright
diff --git a/media/libstagefright/system/core/libutils/String16.cpp b/media/libstagefright/system/core/libutils/String16.cpp
new file mode 100644
index 000000000..998cb9482
--- /dev/null
+++ b/media/libstagefright/system/core/libutils/String16.cpp
@@ -0,0 +1,422 @@
+/*
+ * Copyright (C) 2005 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <utils/String16.h>
+
+#include <utils/Debug.h>
+#include <utils/Log.h>
+#include <utils/Unicode.h>
+#include <utils/String8.h>
+#include <utils/threads.h>
+
+#include <memory.h>
+#include <stdio.h>
+#include <ctype.h>
+
+
+namespace stagefright {
+
+static SharedBuffer* gEmptyStringBuf = NULL;
+static char16_t* gEmptyString = NULL;
+
+static inline char16_t* getEmptyString()
+{
+ gEmptyStringBuf->acquire();
+ return gEmptyString;
+}
+
+void initialize_string16()
+{
+ SharedBuffer* buf = SharedBuffer::alloc(sizeof(char16_t));
+ char16_t* str = (char16_t*)buf->data();
+ *str = 0;
+ gEmptyStringBuf = buf;
+ gEmptyString = str;
+}
+
+void terminate_string16()
+{
+ SharedBuffer::bufferFromData(gEmptyString)->release();
+ gEmptyStringBuf = NULL;
+ gEmptyString = NULL;
+}
+
+// ---------------------------------------------------------------------------
+
+static char16_t* allocFromUTF8(const char* u8str, size_t u8len)
+{
+ if (u8len == 0) return getEmptyString();
+
+ const uint8_t* u8cur = (const uint8_t*) u8str;
+
+ const ssize_t u16len = utf8_to_utf16_length(u8cur, u8len);
+ if (u16len < 0) {
+ return getEmptyString();
+ }
+
+ const uint8_t* const u8end = u8cur + u8len;
+
+ SharedBuffer* buf = SharedBuffer::alloc(sizeof(char16_t)*(u16len+1));
+ if (buf) {
+ u8cur = (const uint8_t*) u8str;
+ char16_t* u16str = (char16_t*)buf->data();
+
+ utf8_to_utf16(u8cur, u8len, u16str);
+
+ //printf("Created UTF-16 string from UTF-8 \"%s\":", in);
+ //printHexData(1, str, buf->size(), 16, 1);
+ //printf("\n");
+
+ return u16str;
+ }
+
+ return getEmptyString();
+}
+
+// ---------------------------------------------------------------------------
+
+String16::String16()
+ : mString(getEmptyString())
+{
+}
+
+String16::String16(StaticLinkage)
+ : mString(0)
+{
+ // this constructor is used when we can't rely on the static-initializers
+ // having run. In this case we always allocate an empty string. It's less
+ // efficient than using getEmptyString(), but we assume it's uncommon.
+
+ char16_t* data = static_cast<char16_t*>(
+ SharedBuffer::alloc(sizeof(char16_t))->data());
+ data[0] = 0;
+ mString = data;
+}
+
+String16::String16(const String16& o)
+ : mString(o.mString)
+{
+ SharedBuffer::bufferFromData(mString)->acquire();
+}
+
+String16::String16(const String16& o, size_t len, size_t begin)
+ : mString(getEmptyString())
+{
+ setTo(o, len, begin);
+}
+
+String16::String16(const char16_t* o)
+{
+ size_t len = strlen16(o);
+ SharedBuffer* buf = SharedBuffer::alloc((len+1)*sizeof(char16_t));
+ ALOG_ASSERT(buf, "Unable to allocate shared buffer");
+ if (buf) {
+ char16_t* str = (char16_t*)buf->data();
+ strcpy16(str, o);
+ mString = str;
+ return;
+ }
+
+ mString = getEmptyString();
+}
+
+String16::String16(const char16_t* o, size_t len)
+{
+ SharedBuffer* buf = SharedBuffer::alloc((len+1)*sizeof(char16_t));
+ ALOG_ASSERT(buf, "Unable to allocate shared buffer");
+ if (buf) {
+ char16_t* str = (char16_t*)buf->data();
+ memcpy(str, o, len*sizeof(char16_t));
+ str[len] = 0;
+ mString = str;
+ return;
+ }
+
+ mString = getEmptyString();
+}
+
+String16::String16(const String8& o)
+ : mString(allocFromUTF8(o.string(), o.size()))
+{
+}
+
+String16::String16(const char* o)
+ : mString(allocFromUTF8(o, strlen(o)))
+{
+}
+
+String16::String16(const char* o, size_t len)
+ : mString(allocFromUTF8(o, len))
+{
+}
+
+String16::~String16()
+{
+ SharedBuffer::bufferFromData(mString)->release();
+}
+
+void String16::setTo(const String16& other)
+{
+ SharedBuffer::bufferFromData(other.mString)->acquire();
+ SharedBuffer::bufferFromData(mString)->release();
+ mString = other.mString;
+}
+
+status_t String16::setTo(const String16& other, size_t len, size_t begin)
+{
+ const size_t N = other.size();
+ if (begin >= N) {
+ SharedBuffer::bufferFromData(mString)->release();
+ mString = getEmptyString();
+ return NO_ERROR;
+ }
+ if ((begin+len) > N) len = N-begin;
+ if (begin == 0 && len == N) {
+ setTo(other);
+ return NO_ERROR;
+ }
+
+ if (&other == this) {
+ LOG_ALWAYS_FATAL("Not implemented");
+ }
+
+ return setTo(other.string()+begin, len);
+}
+
+status_t String16::setTo(const char16_t* other)
+{
+ return setTo(other, strlen16(other));
+}
+
+status_t String16::setTo(const char16_t* other, size_t len)
+{
+ SharedBuffer* buf = SharedBuffer::bufferFromData(mString)
+ ->editResize((len+1)*sizeof(char16_t));
+ if (buf) {
+ char16_t* str = (char16_t*)buf->data();
+ memmove(str, other, len*sizeof(char16_t));
+ str[len] = 0;
+ mString = str;
+ return NO_ERROR;
+ }
+ return NO_MEMORY;
+}
+
+status_t String16::append(const String16& other)
+{
+ const size_t myLen = size();
+ const size_t otherLen = other.size();
+ if (myLen == 0) {
+ setTo(other);
+ return NO_ERROR;
+ } else if (otherLen == 0) {
+ return NO_ERROR;
+ }
+
+ SharedBuffer* buf = SharedBuffer::bufferFromData(mString)
+ ->editResize((myLen+otherLen+1)*sizeof(char16_t));
+ if (buf) {
+ char16_t* str = (char16_t*)buf->data();
+ memcpy(str+myLen, other, (otherLen+1)*sizeof(char16_t));
+ mString = str;
+ return NO_ERROR;
+ }
+ return NO_MEMORY;
+}
+
+status_t String16::append(const char16_t* chrs, size_t otherLen)
+{
+ const size_t myLen = size();
+ if (myLen == 0) {
+ setTo(chrs, otherLen);
+ return NO_ERROR;
+ } else if (otherLen == 0) {
+ return NO_ERROR;
+ }
+
+ SharedBuffer* buf = SharedBuffer::bufferFromData(mString)
+ ->editResize((myLen+otherLen+1)*sizeof(char16_t));
+ if (buf) {
+ char16_t* str = (char16_t*)buf->data();
+ memcpy(str+myLen, chrs, otherLen*sizeof(char16_t));
+ str[myLen+otherLen] = 0;
+ mString = str;
+ return NO_ERROR;
+ }
+ return NO_MEMORY;
+}
+
+status_t String16::insert(size_t pos, const char16_t* chrs)
+{
+ return insert(pos, chrs, strlen16(chrs));
+}
+
+status_t String16::insert(size_t pos, const char16_t* chrs, size_t len)
+{
+ const size_t myLen = size();
+ if (myLen == 0) {
+ return setTo(chrs, len);
+ return NO_ERROR;
+ } else if (len == 0) {
+ return NO_ERROR;
+ }
+
+ if (pos > myLen) pos = myLen;
+
+ #if 0
+ printf("Insert in to %s: pos=%d, len=%d, myLen=%d, chrs=%s\n",
+ String8(*this).string(), pos,
+ len, myLen, String8(chrs, len).string());
+ #endif
+
+ SharedBuffer* buf = SharedBuffer::bufferFromData(mString)
+ ->editResize((myLen+len+1)*sizeof(char16_t));
+ if (buf) {
+ char16_t* str = (char16_t*)buf->data();
+ if (pos < myLen) {
+ memmove(str+pos+len, str+pos, (myLen-pos)*sizeof(char16_t));
+ }
+ memcpy(str+pos, chrs, len*sizeof(char16_t));
+ str[myLen+len] = 0;
+ mString = str;
+ #if 0
+ printf("Result (%d chrs): %s\n", size(), String8(*this).string());
+ #endif
+ return NO_ERROR;
+ }
+ return NO_MEMORY;
+}
+
+ssize_t String16::findFirst(char16_t c) const
+{
+ const char16_t* str = string();
+ const char16_t* p = str;
+ const char16_t* e = p + size();
+ while (p < e) {
+ if (*p == c) {
+ return p-str;
+ }
+ p++;
+ }
+ return -1;
+}
+
+ssize_t String16::findLast(char16_t c) const
+{
+ const char16_t* str = string();
+ const char16_t* p = str;
+ const char16_t* e = p + size();
+ while (p < e) {
+ e--;
+ if (*e == c) {
+ return e-str;
+ }
+ }
+ return -1;
+}
+
+bool String16::startsWith(const String16& prefix) const
+{
+ const size_t ps = prefix.size();
+ if (ps > size()) return false;
+ return strzcmp16(mString, ps, prefix.string(), ps) == 0;
+}
+
+bool String16::startsWith(const char16_t* prefix) const
+{
+ const size_t ps = strlen16(prefix);
+ if (ps > size()) return false;
+ return strncmp16(mString, prefix, ps) == 0;
+}
+
+status_t String16::makeLower()
+{
+ const size_t N = size();
+ const char16_t* str = string();
+ char16_t* edit = NULL;
+ for (size_t i=0; i<N; i++) {
+ const char16_t v = str[i];
+ if (v >= 'A' && v <= 'Z') {
+ if (!edit) {
+ SharedBuffer* buf = SharedBuffer::bufferFromData(mString)->edit();
+ if (!buf) {
+ return NO_MEMORY;
+ }
+ edit = (char16_t*)buf->data();
+ mString = str = edit;
+ }
+ edit[i] = tolower((char)v);
+ }
+ }
+ return NO_ERROR;
+}
+
+status_t String16::replaceAll(char16_t replaceThis, char16_t withThis)
+{
+ const size_t N = size();
+ const char16_t* str = string();
+ char16_t* edit = NULL;
+ for (size_t i=0; i<N; i++) {
+ if (str[i] == replaceThis) {
+ if (!edit) {
+ SharedBuffer* buf = SharedBuffer::bufferFromData(mString)->edit();
+ if (!buf) {
+ return NO_MEMORY;
+ }
+ edit = (char16_t*)buf->data();
+ mString = str = edit;
+ }
+ edit[i] = withThis;
+ }
+ }
+ return NO_ERROR;
+}
+
+status_t String16::remove(size_t len, size_t begin)
+{
+ const size_t N = size();
+ if (begin >= N) {
+ SharedBuffer::bufferFromData(mString)->release();
+ mString = getEmptyString();
+ return NO_ERROR;
+ }
+ if ((begin+len) > N) len = N-begin;
+ if (begin == 0 && len == N) {
+ return NO_ERROR;
+ }
+
+ if (begin > 0) {
+ SharedBuffer* buf = SharedBuffer::bufferFromData(mString)
+ ->editResize((N+1)*sizeof(char16_t));
+ if (!buf) {
+ return NO_MEMORY;
+ }
+ char16_t* str = (char16_t*)buf->data();
+ memmove(str, str+begin, (N-begin+1)*sizeof(char16_t));
+ mString = str;
+ }
+ SharedBuffer* buf = SharedBuffer::bufferFromData(mString)
+ ->editResize((len+1)*sizeof(char16_t));
+ if (buf) {
+ char16_t* str = (char16_t*)buf->data();
+ str[len] = 0;
+ mString = str;
+ return NO_ERROR;
+ }
+ return NO_MEMORY;
+}
+
+}; // namespace stagefright
diff --git a/media/libstagefright/system/core/libutils/String8.cpp b/media/libstagefright/system/core/libutils/String8.cpp
new file mode 100644
index 000000000..3d4b28569
--- /dev/null
+++ b/media/libstagefright/system/core/libutils/String8.cpp
@@ -0,0 +1,646 @@
+/*
+ * Copyright (C) 2005 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <utils/String8.h>
+
+#include <utils/Log.h>
+#include <utils/Unicode.h>
+#include <utils/SharedBuffer.h>
+#include <utils/String16.h>
+#include <utils/threads.h>
+
+#include <ctype.h>
+
+/*
+ * Functions outside android is below the namespace stagefright, since they use
+ * functions and constants in android namespace.
+ */
+
+// ---------------------------------------------------------------------------
+
+namespace stagefright {
+
+// Separator used by resource paths. This is not platform dependent contrary
+// to OS_PATH_SEPARATOR.
+#define RES_PATH_SEPARATOR '/'
+
+static SharedBuffer* gEmptyStringBuf = NULL;
+static char* gEmptyString = NULL;
+
+extern int gDarwinCantLoadAllObjects;
+int gDarwinIsReallyAnnoying;
+
+void initialize_string8();
+
+static inline char* getEmptyString()
+{
+ gEmptyStringBuf->acquire();
+ return gEmptyString;
+}
+
+void initialize_string8()
+{
+ // HACK: This dummy dependency forces linking libutils Static.cpp,
+ // which is needed to initialize String8/String16 classes.
+ // These variables are named for Darwin, but are needed elsewhere too,
+ // including static linking on any platform.
+ gDarwinIsReallyAnnoying = gDarwinCantLoadAllObjects;
+
+ SharedBuffer* buf = SharedBuffer::alloc(1);
+ char* str = (char*)buf->data();
+ *str = 0;
+ gEmptyStringBuf = buf;
+ gEmptyString = str;
+}
+
+void terminate_string8()
+{
+ SharedBuffer::bufferFromData(gEmptyString)->release();
+ gEmptyStringBuf = NULL;
+ gEmptyString = NULL;
+}
+
+// ---------------------------------------------------------------------------
+
+static char* allocFromUTF8(const char* in, size_t len)
+{
+ if (len > 0) {
+ SharedBuffer* buf = SharedBuffer::alloc(len+1);
+ ALOG_ASSERT(buf, "Unable to allocate shared buffer");
+ if (buf) {
+ char* str = (char*)buf->data();
+ memcpy(str, in, len);
+ str[len] = 0;
+ return str;
+ }
+ return NULL;
+ }
+
+ return getEmptyString();
+}
+
+static char* allocFromUTF16(const char16_t* in, size_t len)
+{
+ if (len == 0) return getEmptyString();
+
+ const ssize_t bytes = utf16_to_utf8_length(in, len);
+ if (bytes < 0) {
+ return getEmptyString();
+ }
+
+ SharedBuffer* buf = SharedBuffer::alloc(bytes+1);
+ ALOG_ASSERT(buf, "Unable to allocate shared buffer");
+ if (!buf) {
+ return getEmptyString();
+ }
+
+ char* str = (char*)buf->data();
+ utf16_to_utf8(in, len, str);
+ return str;
+}
+
+static char* allocFromUTF32(const char32_t* in, size_t len)
+{
+ if (len == 0) {
+ return getEmptyString();
+ }
+
+ const ssize_t bytes = utf32_to_utf8_length(in, len);
+ if (bytes < 0) {
+ return getEmptyString();
+ }
+
+ SharedBuffer* buf = SharedBuffer::alloc(bytes+1);
+ ALOG_ASSERT(buf, "Unable to allocate shared buffer");
+ if (!buf) {
+ return getEmptyString();
+ }
+
+ char* str = (char*) buf->data();
+ utf32_to_utf8(in, len, str);
+
+ return str;
+}
+
+// ---------------------------------------------------------------------------
+
+String8::String8()
+ : mString(0)
+{
+ char* data = static_cast<char*>(
+ SharedBuffer::alloc(sizeof(char))->data());
+ data[0] = 0;
+ mString = data;
+}
+
+String8::String8(const String8& o)
+ : mString(o.mString)
+{
+ SharedBuffer::bufferFromData(mString)->acquire();
+}
+
+String8::String8(const char* o)
+ : mString(allocFromUTF8(o, strlen(o)))
+{
+ if (mString == NULL) {
+ mString = getEmptyString();
+ }
+}
+
+String8::String8(const char* o, size_t len)
+ : mString(allocFromUTF8(o, len))
+{
+ if (mString == NULL) {
+ mString = getEmptyString();
+ }
+}
+
+String8::String8(const String16& o)
+ : mString(allocFromUTF16(o.string(), o.size()))
+{
+}
+
+String8::String8(const char16_t* o)
+ : mString(allocFromUTF16(o, strlen16(o)))
+{
+}
+
+String8::String8(const char16_t* o, size_t len)
+ : mString(allocFromUTF16(o, len))
+{
+}
+
+String8::String8(const char32_t* o)
+ : mString(allocFromUTF32(o, strlen32(o)))
+{
+}
+
+String8::String8(const char32_t* o, size_t len)
+ : mString(allocFromUTF32(o, len))
+{
+}
+
+String8::~String8()
+{
+ SharedBuffer::bufferFromData(mString)->release();
+}
+
+String8 String8::format(const char* fmt, ...)
+{
+ va_list args;
+ va_start(args, fmt);
+
+ String8 result(formatV(fmt, args));
+
+ va_end(args);
+ return result;
+}
+
+String8 String8::formatV(const char* fmt, va_list args)
+{
+ String8 result;
+ result.appendFormatV(fmt, args);
+ return result;
+}
+
+void String8::clear() {
+ SharedBuffer::bufferFromData(mString)->release();
+ mString = getEmptyString();
+}
+
+void String8::setTo(const String8& other)
+{
+ SharedBuffer::bufferFromData(other.mString)->acquire();
+ SharedBuffer::bufferFromData(mString)->release();
+ mString = other.mString;
+}
+
+status_t String8::setTo(const char* other)
+{
+ const char *newString = allocFromUTF8(other, strlen(other));
+ SharedBuffer::bufferFromData(mString)->release();
+ mString = newString;
+ if (mString) return NO_ERROR;
+
+ mString = getEmptyString();
+ return NO_MEMORY;
+}
+
+status_t String8::setTo(const char* other, size_t len)
+{
+ const char *newString = allocFromUTF8(other, len);
+ SharedBuffer::bufferFromData(mString)->release();
+ mString = newString;
+ if (mString) return NO_ERROR;
+
+ mString = getEmptyString();
+ return NO_MEMORY;
+}
+
+status_t String8::setTo(const char16_t* other, size_t len)
+{
+ const char *newString = allocFromUTF16(other, len);
+ SharedBuffer::bufferFromData(mString)->release();
+ mString = newString;
+ if (mString) return NO_ERROR;
+
+ mString = getEmptyString();
+ return NO_MEMORY;
+}
+
+status_t String8::setTo(const char32_t* other, size_t len)
+{
+ const char *newString = allocFromUTF32(other, len);
+ SharedBuffer::bufferFromData(mString)->release();
+ mString = newString;
+ if (mString) return NO_ERROR;
+
+ mString = getEmptyString();
+ return NO_MEMORY;
+}
+
+status_t String8::append(const String8& other)
+{
+ const size_t otherLen = other.bytes();
+ if (bytes() == 0) {
+ setTo(other);
+ return NO_ERROR;
+ } else if (otherLen == 0) {
+ return NO_ERROR;
+ }
+
+ return real_append(other.string(), otherLen);
+}
+
+status_t String8::append(const char* other)
+{
+ return append(other, strlen(other));
+}
+
+status_t String8::append(const char* other, size_t otherLen)
+{
+ if (bytes() == 0) {
+ return setTo(other, otherLen);
+ } else if (otherLen == 0) {
+ return NO_ERROR;
+ }
+
+ return real_append(other, otherLen);
+}
+
+status_t String8::appendFormat(const char* fmt, ...)
+{
+ va_list args;
+ va_start(args, fmt);
+
+ status_t result = appendFormatV(fmt, args);
+
+ va_end(args);
+ return result;
+}
+
+status_t String8::appendFormatV(const char* fmt, va_list args)
+{
+ int result = NO_ERROR;
+#ifndef _MSC_VER
+ va_list o;
+ va_copy(o, args);
+#endif
+ int n = vsnprintf(NULL, 0, fmt, args);
+ if (n != 0) {
+ size_t oldLength = length();
+ char* buf = lockBuffer(oldLength + n);
+ if (buf) {
+#ifdef _MSC_VER
+ vsnprintf(buf + oldLength, n + 1, fmt, args);
+#else
+ vsnprintf(buf + oldLength, n + 1, fmt, o);
+#endif
+ } else {
+ result = NO_MEMORY;
+ }
+ }
+#ifndef _MSC_VER
+ va_end(o);
+#endif
+ return result;
+}
+
+status_t String8::real_append(const char* other, size_t otherLen)
+{
+ const size_t myLen = bytes();
+
+ SharedBuffer* buf = SharedBuffer::bufferFromData(mString)
+ ->editResize(myLen+otherLen+1);
+ if (buf) {
+ char* str = (char*)buf->data();
+ mString = str;
+ str += myLen;
+ memcpy(str, other, otherLen);
+ str[otherLen] = '\0';
+ return NO_ERROR;
+ }
+ return NO_MEMORY;
+}
+
+char* String8::lockBuffer(size_t size)
+{
+ SharedBuffer* buf = SharedBuffer::bufferFromData(mString)
+ ->editResize(size+1);
+ if (buf) {
+ char* str = (char*)buf->data();
+ mString = str;
+ return str;
+ }
+ return NULL;
+}
+
+void String8::unlockBuffer()
+{
+ unlockBuffer(strlen(mString));
+}
+
+status_t String8::unlockBuffer(size_t size)
+{
+ if (size != this->size()) {
+ SharedBuffer* buf = SharedBuffer::bufferFromData(mString)
+ ->editResize(size+1);
+ if (! buf) {
+ return NO_MEMORY;
+ }
+
+ char* str = (char*)buf->data();
+ str[size] = 0;
+ mString = str;
+ }
+
+ return NO_ERROR;
+}
+
+ssize_t String8::find(const char* other, size_t start) const
+{
+ size_t len = size();
+ if (start >= len) {
+ return -1;
+ }
+ const char* s = mString+start;
+ const char* p = strstr(s, other);
+ return p ? p-mString : -1;
+}
+
+void String8::toLower()
+{
+ toLower(0, size());
+}
+
+void String8::toLower(size_t start, size_t length)
+{
+ const size_t len = size();
+ if (start >= len) {
+ return;
+ }
+ if (start+length > len) {
+ length = len-start;
+ }
+ char* buf = lockBuffer(len);
+ buf += start;
+ while (length > 0) {
+ *buf = tolower(*buf);
+ buf++;
+ length--;
+ }
+ unlockBuffer(len);
+}
+
+void String8::toUpper()
+{
+ toUpper(0, size());
+}
+
+void String8::toUpper(size_t start, size_t length)
+{
+ const size_t len = size();
+ if (start >= len) {
+ return;
+ }
+ if (start+length > len) {
+ length = len-start;
+ }
+ char* buf = lockBuffer(len);
+ buf += start;
+ while (length > 0) {
+ *buf = toupper(*buf);
+ buf++;
+ length--;
+ }
+ unlockBuffer(len);
+}
+
+size_t String8::getUtf32Length() const
+{
+ return utf8_to_utf32_length(mString, length());
+}
+
+int32_t String8::getUtf32At(size_t index, size_t *next_index) const
+{
+ return utf32_from_utf8_at(mString, length(), index, next_index);
+}
+
+void String8::getUtf32(char32_t* dst) const
+{
+ utf8_to_utf32(mString, length(), dst);
+}
+
+// ---------------------------------------------------------------------------
+// Path functions
+
+#if 0
+
+void String8::setPathName(const char* name)
+{
+ setPathName(name, strlen(name));
+}
+
+void String8::setPathName(const char* name, size_t len)
+{
+ char* buf = lockBuffer(len);
+
+ memcpy(buf, name, len);
+
+ // remove trailing path separator, if present
+ if (len > 0 && buf[len-1] == OS_PATH_SEPARATOR)
+ len--;
+
+ buf[len] = '\0';
+
+ unlockBuffer(len);
+}
+
+String8 String8::getPathLeaf(void) const
+{
+ const char* cp;
+ const char*const buf = mString;
+
+ cp = strrchr(buf, OS_PATH_SEPARATOR);
+ if (cp == NULL)
+ return String8(*this);
+ else
+ return String8(cp+1);
+}
+
+String8 String8::getPathDir(void) const
+{
+ const char* cp;
+ const char*const str = mString;
+
+ cp = strrchr(str, OS_PATH_SEPARATOR);
+ if (cp == NULL)
+ return String8("");
+ else
+ return String8(str, cp - str);
+}
+
+String8 String8::walkPath(String8* outRemains) const
+{
+ const char* cp;
+ const char*const str = mString;
+ const char* buf = str;
+
+ cp = strchr(buf, OS_PATH_SEPARATOR);
+ if (cp == buf) {
+ // don't include a leading '/'.
+ buf = buf+1;
+ cp = strchr(buf, OS_PATH_SEPARATOR);
+ }
+
+ if (cp == NULL) {
+ String8 res = buf != str ? String8(buf) : *this;
+ if (outRemains) *outRemains = String8("");
+ return res;
+ }
+
+ String8 res(buf, cp-buf);
+ if (outRemains) *outRemains = String8(cp+1);
+ return res;
+}
+
+/*
+ * Helper function for finding the start of an extension in a pathname.
+ *
+ * Returns a pointer inside mString, or NULL if no extension was found.
+ */
+char* String8::find_extension(void) const
+{
+ const char* lastSlash;
+ const char* lastDot;
+ int extLen;
+ const char* const str = mString;
+
+ // only look at the filename
+ lastSlash = strrchr(str, OS_PATH_SEPARATOR);
+ if (lastSlash == NULL)
+ lastSlash = str;
+ else
+ lastSlash++;
+
+ // find the last dot
+ lastDot = strrchr(lastSlash, '.');
+ if (lastDot == NULL)
+ return NULL;
+
+ // looks good, ship it
+ return const_cast<char*>(lastDot);
+}
+
+String8 String8::getPathExtension(void) const
+{
+ char* ext;
+
+ ext = find_extension();
+ if (ext != NULL)
+ return String8(ext);
+ else
+ return String8("");
+}
+
+String8 String8::getBasePath(void) const
+{
+ char* ext;
+ const char* const str = mString;
+
+ ext = find_extension();
+ if (ext == NULL)
+ return String8(*this);
+ else
+ return String8(str, ext - str);
+}
+
+String8& String8::appendPath(const char* name)
+{
+ // TODO: The test below will fail for Win32 paths. Fix later or ignore.
+ if (name[0] != OS_PATH_SEPARATOR) {
+ if (*name == '\0') {
+ // nothing to do
+ return *this;
+ }
+
+ size_t len = length();
+ if (len == 0) {
+ // no existing filename, just use the new one
+ setPathName(name);
+ return *this;
+ }
+
+ // make room for oldPath + '/' + newPath
+ int newlen = strlen(name);
+
+ char* buf = lockBuffer(len+1+newlen);
+
+ // insert a '/' if needed
+ if (buf[len-1] != OS_PATH_SEPARATOR)
+ buf[len++] = OS_PATH_SEPARATOR;
+
+ memcpy(buf+len, name, newlen+1);
+ len += newlen;
+
+ unlockBuffer(len);
+
+ return *this;
+ } else {
+ setPathName(name);
+ return *this;
+ }
+}
+
+String8& String8::convertToResPath()
+{
+#if OS_PATH_SEPARATOR != RES_PATH_SEPARATOR
+ size_t len = length();
+ if (len > 0) {
+ char * buf = lockBuffer(len);
+ for (char * end = buf + len; buf < end; ++buf) {
+ if (*buf == OS_PATH_SEPARATOR)
+ *buf = RES_PATH_SEPARATOR;
+ }
+ unlockBuffer(len);
+ }
+#endif
+ return *this;
+}
+
+#endif
+
+}; // namespace stagefright
diff --git a/media/libstagefright/system/core/libutils/Unicode.cpp b/media/libstagefright/system/core/libutils/Unicode.cpp
new file mode 100644
index 000000000..b8aae5e04
--- /dev/null
+++ b/media/libstagefright/system/core/libutils/Unicode.cpp
@@ -0,0 +1,606 @@
+/*
+ * Copyright (C) 2005 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <utils/Unicode.h>
+
+#include <stddef.h>
+
+#ifdef HAVE_WINSOCK
+# undef nhtol
+# undef htonl
+# undef nhtos
+# undef htons
+
+# ifdef HAVE_LITTLE_ENDIAN
+# define ntohl(x) ( ((x) << 24) | (((x) >> 24) & 255) | (((x) << 8) & 0xff0000) | (((x) >> 8) & 0xff00) )
+# define htonl(x) ntohl(x)
+# define ntohs(x) ( (((x) << 8) & 0xff00) | (((x) >> 8) & 255) )
+# define htons(x) ntohs(x)
+# else
+# define ntohl(x) (x)
+# define htonl(x) (x)
+# define ntohs(x) (x)
+# define htons(x) (x)
+# endif
+#else
+# include <netinet/in.h>
+#endif
+
+extern "C" {
+
+static const char32_t kByteMask = 0x000000BF;
+static const char32_t kByteMark = 0x00000080;
+
+// Surrogates aren't valid for UTF-32 characters, so define some
+// constants that will let us screen them out.
+static const char32_t kUnicodeSurrogateHighStart = 0x0000D800;
+static const char32_t kUnicodeSurrogateHighEnd = 0x0000DBFF;
+static const char32_t kUnicodeSurrogateLowStart = 0x0000DC00;
+static const char32_t kUnicodeSurrogateLowEnd = 0x0000DFFF;
+static const char32_t kUnicodeSurrogateStart = kUnicodeSurrogateHighStart;
+static const char32_t kUnicodeSurrogateEnd = kUnicodeSurrogateLowEnd;
+static const char32_t kUnicodeMaxCodepoint = 0x0010FFFF;
+
+// Mask used to set appropriate bits in first byte of UTF-8 sequence,
+// indexed by number of bytes in the sequence.
+// 0xxxxxxx
+// -> (00-7f) 7bit. Bit mask for the first byte is 0x00000000
+// 110yyyyx 10xxxxxx
+// -> (c0-df)(80-bf) 11bit. Bit mask is 0x000000C0
+// 1110yyyy 10yxxxxx 10xxxxxx
+// -> (e0-ef)(80-bf)(80-bf) 16bit. Bit mask is 0x000000E0
+// 11110yyy 10yyxxxx 10xxxxxx 10xxxxxx
+// -> (f0-f7)(80-bf)(80-bf)(80-bf) 21bit. Bit mask is 0x000000F0
+static const char32_t kFirstByteMark[] = {
+ 0x00000000, 0x00000000, 0x000000C0, 0x000000E0, 0x000000F0
+};
+
+// --------------------------------------------------------------------------
+// UTF-32
+// --------------------------------------------------------------------------
+
+/**
+ * Return number of UTF-8 bytes required for the character. If the character
+ * is invalid, return size of 0.
+ */
+static inline size_t utf32_codepoint_utf8_length(char32_t srcChar)
+{
+ // Figure out how many bytes the result will require.
+ if (srcChar < 0x00000080) {
+ return 1;
+ } else if (srcChar < 0x00000800) {
+ return 2;
+ } else if (srcChar < 0x00010000) {
+ if ((srcChar < kUnicodeSurrogateStart) || (srcChar > kUnicodeSurrogateEnd)) {
+ return 3;
+ } else {
+ // Surrogates are invalid UTF-32 characters.
+ return 0;
+ }
+ }
+ // Max code point for Unicode is 0x0010FFFF.
+ else if (srcChar <= kUnicodeMaxCodepoint) {
+ return 4;
+ } else {
+ // Invalid UTF-32 character.
+ return 0;
+ }
+}
+
+// Write out the source character to <dstP>.
+
+static inline void utf32_codepoint_to_utf8(uint8_t* dstP, char32_t srcChar, size_t bytes)
+{
+ dstP += bytes;
+ switch (bytes)
+ { /* note: everything falls through. */
+ case 4: *--dstP = (uint8_t)((srcChar | kByteMark) & kByteMask); srcChar >>= 6;
+ case 3: *--dstP = (uint8_t)((srcChar | kByteMark) & kByteMask); srcChar >>= 6;
+ case 2: *--dstP = (uint8_t)((srcChar | kByteMark) & kByteMask); srcChar >>= 6;
+ case 1: *--dstP = (uint8_t)(srcChar | kFirstByteMark[bytes]);
+ }
+}
+
+size_t strlen32(const char32_t *s)
+{
+ const char32_t *ss = s;
+ while ( *ss )
+ ss++;
+ return ss-s;
+}
+
+size_t strnlen32(const char32_t *s, size_t maxlen)
+{
+ const char32_t *ss = s;
+ while ((maxlen > 0) && *ss) {
+ ss++;
+ maxlen--;
+ }
+ return ss-s;
+}
+
+static inline int32_t utf32_at_internal(const char* cur, size_t *num_read)
+{
+ const char first_char = *cur;
+ if ((first_char & 0x80) == 0) { // ASCII
+ *num_read = 1;
+ return *cur;
+ }
+ cur++;
+ char32_t mask, to_ignore_mask;
+ size_t num_to_read = 0;
+ char32_t utf32 = first_char;
+ for (num_to_read = 1, mask = 0x40, to_ignore_mask = 0xFFFFFF80;
+ (first_char & mask);
+ num_to_read++, to_ignore_mask |= mask, mask >>= 1) {
+ // 0x3F == 00111111
+ utf32 = (utf32 << 6) + (*cur++ & 0x3F);
+ }
+ to_ignore_mask |= mask;
+ utf32 &= ~(to_ignore_mask << (6 * (num_to_read - 1)));
+
+ *num_read = num_to_read;
+ return static_cast<int32_t>(utf32);
+}
+
+int32_t utf32_from_utf8_at(const char *src, size_t src_len, size_t index, size_t *next_index)
+{
+ if (index >= src_len) {
+ return -1;
+ }
+ size_t dummy_index;
+ if (next_index == NULL) {
+ next_index = &dummy_index;
+ }
+ size_t num_read;
+ int32_t ret = utf32_at_internal(src + index, &num_read);
+ if (ret >= 0) {
+ *next_index = index + num_read;
+ }
+
+ return ret;
+}
+
+ssize_t utf32_to_utf8_length(const char32_t *src, size_t src_len)
+{
+ if (src == NULL || src_len == 0) {
+ return -1;
+ }
+
+ size_t ret = 0;
+ const char32_t *end = src + src_len;
+ while (src < end) {
+ ret += utf32_codepoint_utf8_length(*src++);
+ }
+ return ret;
+}
+
+void utf32_to_utf8(const char32_t* src, size_t src_len, char* dst)
+{
+ if (src == NULL || src_len == 0 || dst == NULL) {
+ return;
+ }
+
+ const char32_t *cur_utf32 = src;
+ const char32_t *end_utf32 = src + src_len;
+ char *cur = dst;
+ while (cur_utf32 < end_utf32) {
+ size_t len = utf32_codepoint_utf8_length(*cur_utf32);
+ utf32_codepoint_to_utf8((uint8_t *)cur, *cur_utf32++, len);
+ cur += len;
+ }
+ *cur = '\0';
+}
+
+// --------------------------------------------------------------------------
+// UTF-16
+// --------------------------------------------------------------------------
+
+int strcmp16(const char16_t *s1, const char16_t *s2)
+{
+ char16_t ch;
+ int d = 0;
+
+ while ( 1 ) {
+ d = (int)(ch = *s1++) - (int)*s2++;
+ if ( d || !ch )
+ break;
+ }
+
+ return d;
+}
+
+int strncmp16(const char16_t *s1, const char16_t *s2, size_t n)
+{
+ char16_t ch;
+ int d = 0;
+
+ while ( n-- ) {
+ d = (int)(ch = *s1++) - (int)*s2++;
+ if ( d || !ch )
+ break;
+ }
+
+ return d;
+}
+
+char16_t *strcpy16(char16_t *dst, const char16_t *src)
+{
+ char16_t *q = dst;
+ const char16_t *p = src;
+ char16_t ch;
+
+ do {
+ *q++ = ch = *p++;
+ } while ( ch );
+
+ return dst;
+}
+
+size_t strlen16(const char16_t *s)
+{
+ const char16_t *ss = s;
+ while ( *ss )
+ ss++;
+ return ss-s;
+}
+
+
+char16_t *strncpy16(char16_t *dst, const char16_t *src, size_t n)
+{
+ char16_t *q = dst;
+ const char16_t *p = src;
+ char ch;
+
+ while (n) {
+ n--;
+ *q++ = ch = *p++;
+ if ( !ch )
+ break;
+ }
+
+ *q = 0;
+
+ return dst;
+}
+
+size_t strnlen16(const char16_t *s, size_t maxlen)
+{
+ const char16_t *ss = s;
+
+ /* Important: the maxlen test must precede the reference through ss;
+ since the byte beyond the maximum may segfault */
+ while ((maxlen > 0) && *ss) {
+ ss++;
+ maxlen--;
+ }
+ return ss-s;
+}
+
+int strzcmp16(const char16_t *s1, size_t n1, const char16_t *s2, size_t n2)
+{
+ const char16_t* e1 = s1+n1;
+ const char16_t* e2 = s2+n2;
+
+ while (s1 < e1 && s2 < e2) {
+ const int d = (int)*s1++ - (int)*s2++;
+ if (d) {
+ return d;
+ }
+ }
+
+ return n1 < n2
+ ? (0 - (int)*s2)
+ : (n1 > n2
+ ? ((int)*s1 - 0)
+ : 0);
+}
+
+int strzcmp16_h_n(const char16_t *s1H, size_t n1, const char16_t *s2N, size_t n2)
+{
+ const char16_t* e1 = s1H+n1;
+ const char16_t* e2 = s2N+n2;
+
+ while (s1H < e1 && s2N < e2) {
+ const char16_t c2 = ntohs(*s2N);
+ const int d = (int)*s1H++ - (int)c2;
+ s2N++;
+ if (d) {
+ return d;
+ }
+ }
+
+ return n1 < n2
+ ? (0 - (int)ntohs(*s2N))
+ : (n1 > n2
+ ? ((int)*s1H - 0)
+ : 0);
+}
+
+void utf16_to_utf8(const char16_t* src, size_t src_len, char* dst)
+{
+ if (src == NULL || src_len == 0 || dst == NULL) {
+ return;
+ }
+
+ const char16_t* cur_utf16 = src;
+ const char16_t* const end_utf16 = src + src_len;
+ char *cur = dst;
+ while (cur_utf16 < end_utf16) {
+ char32_t utf32;
+ // surrogate pairs
+ if ((*cur_utf16 & 0xFC00) == 0xD800) {
+ utf32 = (*cur_utf16++ - 0xD800) << 10;
+ utf32 |= *cur_utf16++ - 0xDC00;
+ utf32 += 0x10000;
+ } else {
+ utf32 = (char32_t) *cur_utf16++;
+ }
+ const size_t len = utf32_codepoint_utf8_length(utf32);
+ utf32_codepoint_to_utf8((uint8_t*)cur, utf32, len);
+ cur += len;
+ }
+ *cur = '\0';
+}
+
+// --------------------------------------------------------------------------
+// UTF-8
+// --------------------------------------------------------------------------
+
+ssize_t utf8_length(const char *src)
+{
+ const char *cur = src;
+ size_t ret = 0;
+ while (*cur != '\0') {
+ const char first_char = *cur++;
+ if ((first_char & 0x80) == 0) { // ASCII
+ ret += 1;
+ continue;
+ }
+ // (UTF-8's character must not be like 10xxxxxx,
+ // but 110xxxxx, 1110xxxx, ... or 1111110x)
+ if ((first_char & 0x40) == 0) {
+ return -1;
+ }
+
+ int32_t mask, to_ignore_mask;
+ size_t num_to_read = 0;
+ char32_t utf32 = 0;
+ for (num_to_read = 1, mask = 0x40, to_ignore_mask = 0x80;
+ num_to_read < 5 && (first_char & mask);
+ num_to_read++, to_ignore_mask |= mask, mask >>= 1) {
+ if ((*cur & 0xC0) != 0x80) { // must be 10xxxxxx
+ return -1;
+ }
+ // 0x3F == 00111111
+ utf32 = (utf32 << 6) + (*cur++ & 0x3F);
+ }
+ // "first_char" must be (110xxxxx - 11110xxx)
+ if (num_to_read == 5) {
+ return -1;
+ }
+ to_ignore_mask |= mask;
+ utf32 |= ((~to_ignore_mask) & first_char) << (6 * (num_to_read - 1));
+ if (utf32 > kUnicodeMaxCodepoint) {
+ return -1;
+ }
+
+ ret += num_to_read;
+ }
+ return ret;
+}
+
+ssize_t utf16_to_utf8_length(const char16_t *src, size_t src_len)
+{
+ if (src == NULL || src_len == 0) {
+ return -1;
+ }
+
+ size_t ret = 0;
+ const char16_t* const end = src + src_len;
+ while (src < end) {
+ if ((*src & 0xFC00) == 0xD800 && (src + 1) < end
+ && (*++src & 0xFC00) == 0xDC00) {
+ // surrogate pairs are always 4 bytes.
+ ret += 4;
+ src++;
+ } else {
+ ret += utf32_codepoint_utf8_length((char32_t) *src++);
+ }
+ }
+ return ret;
+}
+
+/**
+ * Returns 1-4 based on the number of leading bits.
+ *
+ * 1111 -> 4
+ * 1110 -> 3
+ * 110x -> 2
+ * 10xx -> 1
+ * 0xxx -> 1
+ */
+static inline size_t utf8_codepoint_len(uint8_t ch)
+{
+ return ((0xe5000000 >> ((ch >> 3) & 0x1e)) & 3) + 1;
+}
+
+static inline void utf8_shift_and_mask(uint32_t* codePoint, const uint8_t byte)
+{
+ *codePoint <<= 6;
+ *codePoint |= 0x3F & byte;
+}
+
+size_t utf8_to_utf32_length(const char *src, size_t src_len)
+{
+ if (src == NULL || src_len == 0) {
+ return 0;
+ }
+ size_t ret = 0;
+ const char* cur;
+ const char* end;
+ size_t num_to_skip;
+ for (cur = src, end = src + src_len, num_to_skip = 1;
+ cur < end;
+ cur += num_to_skip, ret++) {
+ const char first_char = *cur;
+ num_to_skip = 1;
+ if ((first_char & 0x80) == 0) { // ASCII
+ continue;
+ }
+ int32_t mask;
+
+ for (mask = 0x40; (first_char & mask); num_to_skip++, mask >>= 1) {
+ }
+ }
+ return ret;
+}
+
+void utf8_to_utf32(const char* src, size_t src_len, char32_t* dst)
+{
+ if (src == NULL || src_len == 0 || dst == NULL) {
+ return;
+ }
+
+ const char* cur = src;
+ const char* const end = src + src_len;
+ char32_t* cur_utf32 = dst;
+ while (cur < end) {
+ size_t num_read;
+ *cur_utf32++ = static_cast<char32_t>(utf32_at_internal(cur, &num_read));
+ cur += num_read;
+ }
+ *cur_utf32 = 0;
+}
+
+static inline uint32_t utf8_to_utf32_codepoint(const uint8_t *src, size_t length)
+{
+ uint32_t unicode;
+
+ switch (length)
+ {
+ case 1:
+ return src[0];
+ case 2:
+ unicode = src[0] & 0x1f;
+ utf8_shift_and_mask(&unicode, src[1]);
+ return unicode;
+ case 3:
+ unicode = src[0] & 0x0f;
+ utf8_shift_and_mask(&unicode, src[1]);
+ utf8_shift_and_mask(&unicode, src[2]);
+ return unicode;
+ case 4:
+ unicode = src[0] & 0x07;
+ utf8_shift_and_mask(&unicode, src[1]);
+ utf8_shift_and_mask(&unicode, src[2]);
+ utf8_shift_and_mask(&unicode, src[3]);
+ return unicode;
+ default:
+ return 0xffff;
+ }
+
+ //printf("Char at %p: len=%d, utf-16=%p\n", src, length, (void*)result);
+}
+
+ssize_t utf8_to_utf16_length(const uint8_t* u8str, size_t u8len)
+{
+ const uint8_t* const u8end = u8str + u8len;
+ const uint8_t* u8cur = u8str;
+
+ /* Validate that the UTF-8 is the correct len */
+ size_t u16measuredLen = 0;
+ while (u8cur < u8end) {
+ u16measuredLen++;
+ int u8charLen = utf8_codepoint_len(*u8cur);
+ uint32_t codepoint = utf8_to_utf32_codepoint(u8cur, u8charLen);
+ if (codepoint > 0xFFFF) u16measuredLen++; // this will be a surrogate pair in utf16
+ u8cur += u8charLen;
+ }
+
+ /**
+ * Make sure that we ended where we thought we would and the output UTF-16
+ * will be exactly how long we were told it would be.
+ */
+ if (u8cur != u8end) {
+ return -1;
+ }
+
+ return u16measuredLen;
+}
+
+char16_t* utf8_to_utf16_no_null_terminator(const uint8_t* u8str, size_t u8len, char16_t* u16str)
+{
+ const uint8_t* const u8end = u8str + u8len;
+ const uint8_t* u8cur = u8str;
+ char16_t* u16cur = u16str;
+
+ while (u8cur < u8end) {
+ size_t u8len = utf8_codepoint_len(*u8cur);
+ uint32_t codepoint = utf8_to_utf32_codepoint(u8cur, u8len);
+
+ // Convert the UTF32 codepoint to one or more UTF16 codepoints
+ if (codepoint <= 0xFFFF) {
+ // Single UTF16 character
+ *u16cur++ = (char16_t) codepoint;
+ } else {
+ // Multiple UTF16 characters with surrogates
+ codepoint = codepoint - 0x10000;
+ *u16cur++ = (char16_t) ((codepoint >> 10) + 0xD800);
+ *u16cur++ = (char16_t) ((codepoint & 0x3FF) + 0xDC00);
+ }
+
+ u8cur += u8len;
+ }
+ return u16cur;
+}
+
+void utf8_to_utf16(const uint8_t* u8str, size_t u8len, char16_t* u16str) {
+ char16_t* end = utf8_to_utf16_no_null_terminator(u8str, u8len, u16str);
+ *end = 0;
+}
+
+char16_t* utf8_to_utf16_n(const uint8_t* src, size_t srcLen, char16_t* dst, size_t dstLen) {
+ const uint8_t* const u8end = src + srcLen;
+ const uint8_t* u8cur = src;
+ const uint16_t* const u16end = (const uint16_t* const) dst + dstLen;
+ uint16_t* u16cur = (uint16_t*) dst;
+
+ while (u8cur < u8end && u16cur < u16end) {
+ size_t u8len = utf8_codepoint_len(*u8cur);
+ uint32_t codepoint = utf8_to_utf32_codepoint(u8cur, u8len);
+
+ // Convert the UTF32 codepoint to one or more UTF16 codepoints
+ if (codepoint <= 0xFFFF) {
+ // Single UTF16 character
+ *u16cur++ = (char16_t) codepoint;
+ } else {
+ // Multiple UTF16 characters with surrogates
+ codepoint = codepoint - 0x10000;
+ *u16cur++ = (char16_t) ((codepoint >> 10) + 0xD800);
+ if (u16cur >= u16end) {
+ // Ooops... not enough room for this surrogate pair.
+ return (char16_t*) u16cur-1;
+ }
+ *u16cur++ = (char16_t) ((codepoint & 0x3FF) + 0xDC00);
+ }
+
+ u8cur += u8len;
+ }
+ return (char16_t*) u16cur;
+}
+
+}
diff --git a/media/libstagefright/system/core/libutils/VectorImpl.cpp b/media/libstagefright/system/core/libutils/VectorImpl.cpp
new file mode 100644
index 000000000..b57d211d4
--- /dev/null
+++ b/media/libstagefright/system/core/libutils/VectorImpl.cpp
@@ -0,0 +1,643 @@
+/*
+ * Copyright (C) 2005 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "Vector"
+
+#include <limits.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include <cutils/log.h>
+
+#include <utils/Errors.h>
+#include <utils/SharedBuffer.h>
+#include <utils/VectorImpl.h>
+
+static const uint32_t kMAX_ALLOCATION =
+ ((SIZE_MAX > INT32_MAX ? INT32_MAX : SIZE_MAX) - 1);
+
+/*****************************************************************************/
+
+
+namespace stagefright {
+
+// ----------------------------------------------------------------------------
+
+const size_t kMinVectorCapacity = 4;
+
+static inline size_t max(size_t a, size_t b) {
+ return a>b ? a : b;
+}
+
+// ----------------------------------------------------------------------------
+
+VectorImpl::VectorImpl(size_t itemSize, uint32_t flags)
+ : mStorage(0), mCount(0), mFlags(flags), mItemSize(itemSize)
+{
+}
+
+VectorImpl::VectorImpl(const VectorImpl& rhs)
+ : mStorage(rhs.mStorage), mCount(rhs.mCount),
+ mFlags(rhs.mFlags), mItemSize(rhs.mItemSize)
+{
+ if (mStorage) {
+ SharedBuffer::bufferFromData(mStorage)->acquire();
+ }
+}
+
+VectorImpl::~VectorImpl()
+{
+ ALOGW_IF(mCount,
+ "[%p] subclasses of VectorImpl must call finish_vector()"
+ " in their destructor. Leaking %d bytes.",
+ this, (int)(mCount*mItemSize));
+ // We can't call _do_destroy() here because the vtable is already gone.
+}
+
+VectorImpl& VectorImpl::operator = (const VectorImpl& rhs)
+{
+ LOG_ALWAYS_FATAL_IF(mItemSize != rhs.mItemSize,
+ "Vector<> have different types (this=%p, rhs=%p)", this, &rhs);
+ if (this != &rhs) {
+ release_storage();
+ if (rhs.mCount) {
+ mStorage = rhs.mStorage;
+ mCount = rhs.mCount;
+ SharedBuffer::bufferFromData(mStorage)->acquire();
+ } else {
+ mStorage = 0;
+ mCount = 0;
+ }
+ }
+ return *this;
+}
+
+void* VectorImpl::editArrayImpl()
+{
+ if (mStorage) {
+ SharedBuffer* sb = SharedBuffer::bufferFromData(mStorage)->attemptEdit();
+ if (sb == 0) {
+ sb = SharedBuffer::alloc(capacity() * mItemSize);
+ assert(sb);
+ if (sb) {
+ _do_copy(sb->data(), mStorage, mCount);
+ release_storage();
+ mStorage = sb->data();
+ }
+ }
+ }
+ return mStorage;
+}
+
+size_t VectorImpl::capacity() const
+{
+ if (mStorage) {
+ return SharedBuffer::bufferFromData(mStorage)->size() / mItemSize;
+ }
+ return 0;
+}
+
+ssize_t VectorImpl::insertVectorAt(const VectorImpl& vector, size_t index)
+{
+ return insertArrayAt(vector.arrayImpl(), index, vector.size());
+}
+
+ssize_t VectorImpl::appendVector(const VectorImpl& vector)
+{
+ return insertVectorAt(vector, size());
+}
+
+ssize_t VectorImpl::insertArrayAt(const void* array, size_t index, size_t length)
+{
+ if (index > size())
+ return BAD_INDEX;
+ void* where = _grow(index, length);
+ if (where) {
+ _do_copy(where, array, length);
+ }
+ return where ? index : (ssize_t)NO_MEMORY;
+}
+
+ssize_t VectorImpl::appendArray(const void* array, size_t length)
+{
+ return insertArrayAt(array, size(), length);
+}
+
+ssize_t VectorImpl::insertAt(size_t index, size_t numItems)
+{
+ return insertAt(0, index, numItems);
+}
+
+ssize_t VectorImpl::insertAt(const void* item, size_t index, size_t numItems)
+{
+ if (index > size())
+ return BAD_INDEX;
+ void* where = _grow(index, numItems);
+ if (where) {
+ if (item) {
+ _do_splat(where, item, numItems);
+ } else {
+ _do_construct(where, numItems);
+ }
+ }
+ return where ? index : (ssize_t)NO_MEMORY;
+}
+
+static int sortProxy(const void* lhs, const void* rhs, void* func)
+{
+ return (*(VectorImpl::compar_t)func)(lhs, rhs);
+}
+
+status_t VectorImpl::sort(VectorImpl::compar_t cmp)
+{
+ return sort(sortProxy, (void*)cmp);
+}
+
+status_t VectorImpl::sort(VectorImpl::compar_r_t cmp, void* state)
+{
+ // the sort must be stable. we're using insertion sort which
+ // is well suited for small and already sorted arrays
+ // for big arrays, it could be better to use mergesort
+ const ssize_t count = size();
+ if (count > 1) {
+ void* array = const_cast<void*>(arrayImpl());
+ void* temp = 0;
+ ssize_t i = 1;
+ while (i < count) {
+ void* item = reinterpret_cast<char*>(array) + mItemSize*(i);
+ void* curr = reinterpret_cast<char*>(array) + mItemSize*(i-1);
+ if (cmp(curr, item, state) > 0) {
+
+ if (!temp) {
+ // we're going to have to modify the array...
+ array = editArrayImpl();
+ if (!array) return NO_MEMORY;
+ temp = malloc(mItemSize);
+ if (!temp) return NO_MEMORY;
+ item = reinterpret_cast<char*>(array) + mItemSize*(i);
+ curr = reinterpret_cast<char*>(array) + mItemSize*(i-1);
+ } else {
+ _do_destroy(temp, 1);
+ }
+
+ _do_copy(temp, item, 1);
+
+ ssize_t j = i-1;
+ void* next = reinterpret_cast<char*>(array) + mItemSize*(i);
+ do {
+ _do_destroy(next, 1);
+ _do_copy(next, curr, 1);
+ next = curr;
+ --j;
+ curr = reinterpret_cast<char*>(array) + mItemSize*(j);
+ } while (j>=0 && (cmp(curr, temp, state) > 0));
+
+ _do_destroy(next, 1);
+ _do_copy(next, temp, 1);
+ }
+ i++;
+ }
+
+ if (temp) {
+ _do_destroy(temp, 1);
+ free(temp);
+ }
+ }
+ return NO_ERROR;
+}
+
+void VectorImpl::pop()
+{
+ if (size())
+ removeItemsAt(size()-1, 1);
+}
+
+void VectorImpl::push()
+{
+ push(0);
+}
+
+void VectorImpl::push(const void* item)
+{
+ insertAt(item, size());
+}
+
+ssize_t VectorImpl::add()
+{
+ return add(0);
+}
+
+ssize_t VectorImpl::add(const void* item)
+{
+ return insertAt(item, size());
+}
+
+ssize_t VectorImpl::replaceAt(size_t index)
+{
+ return replaceAt(0, index);
+}
+
+ssize_t VectorImpl::replaceAt(const void* prototype, size_t index)
+{
+ ALOG_ASSERT(index<size(),
+ "[%p] replace: index=%d, size=%d", this, (int)index, (int)size());
+
+ if (index >= size()) {
+ return BAD_INDEX;
+ }
+
+ void* item = editItemLocation(index);
+ if (item != prototype) {
+ if (item == 0)
+ return NO_MEMORY;
+ _do_destroy(item, 1);
+ if (prototype == 0) {
+ _do_construct(item, 1);
+ } else {
+ _do_copy(item, prototype, 1);
+ }
+ }
+ return ssize_t(index);
+}
+
+ssize_t VectorImpl::removeItemsAt(size_t index, size_t count)
+{
+ ALOG_ASSERT((index+count)<=size(),
+ "[%p] remove: index=%d, count=%d, size=%d",
+ this, (int)index, (int)count, (int)size());
+
+ if ((index+count) > size())
+ return BAD_VALUE;
+ _shrink(index, count);
+ return index;
+}
+
+void VectorImpl::finish_vector()
+{
+ release_storage();
+ mStorage = 0;
+ mCount = 0;
+}
+
+void VectorImpl::clear()
+{
+ _shrink(0, mCount);
+}
+
+void* VectorImpl::editItemLocation(size_t index)
+{
+ ALOG_ASSERT(index<capacity(),
+ "[%p] editItemLocation: index=%d, capacity=%d, count=%d",
+ this, (int)index, (int)capacity(), (int)mCount);
+
+ if (index < capacity()) {
+ void* buffer = editArrayImpl();
+ if (buffer) {
+ return reinterpret_cast<char*>(buffer) + index*mItemSize;
+ }
+ }
+ return 0;
+}
+
+const void* VectorImpl::itemLocation(size_t index) const
+{
+ ALOG_ASSERT(index<capacity(),
+ "[%p] itemLocation: index=%d, capacity=%d, count=%d",
+ this, (int)index, (int)capacity(), (int)mCount);
+
+ if (index < capacity()) {
+ const void* buffer = arrayImpl();
+ if (buffer) {
+ return reinterpret_cast<const char*>(buffer) + index*mItemSize;
+ }
+ }
+ return 0;
+}
+
+ssize_t VectorImpl::setCapacity(size_t new_capacity)
+{
+ if (new_capacity <= size()) {
+ // we can't reduce the capacity
+ return capacity();
+ }
+ if (new_capacity >= (kMAX_ALLOCATION / mItemSize)) {
+ return NO_MEMORY;
+ }
+ SharedBuffer* sb = SharedBuffer::alloc(new_capacity * mItemSize);
+ if (sb) {
+ void* array = sb->data();
+ _do_copy(array, mStorage, size());
+ release_storage();
+ mStorage = const_cast<void*>(array);
+ } else {
+ return NO_MEMORY;
+ }
+ return new_capacity;
+}
+
+ssize_t VectorImpl::resize(size_t size) {
+ ssize_t result = NO_ERROR;
+ if (size > mCount) {
+ result = insertAt(mCount, size - mCount);
+ } else if (size < mCount) {
+ result = removeItemsAt(size, mCount - size);
+ }
+ return result < 0 ? result : size;
+}
+
+void VectorImpl::release_storage()
+{
+ if (mStorage) {
+ const SharedBuffer* sb = SharedBuffer::bufferFromData(mStorage);
+ if (sb->release(SharedBuffer::eKeepStorage) == 1) {
+ _do_destroy(mStorage, mCount);
+ SharedBuffer::dealloc(sb);
+ }
+ }
+}
+
+void* VectorImpl::_grow(size_t where, size_t amount)
+{
+// ALOGV("_grow(this=%p, where=%d, amount=%d) count=%d, capacity=%d",
+// this, (int)where, (int)amount, (int)mCount, (int)capacity());
+
+ ALOG_ASSERT(where <= mCount,
+ "[%p] _grow: where=%d, amount=%d, count=%d",
+ this, (int)where, (int)amount, (int)mCount); // caller already checked
+
+ const size_t new_size = mCount + amount;
+ assert(amount < kMAX_ALLOCATION - mCount);
+ if (capacity() < new_size) {
+ assert(new_size < (SIZE_MAX / 3 - 1));
+ const size_t new_capacity = max(kMinVectorCapacity, ((new_size*3)+1)/2);
+ assert(new_capacity < (kMAX_ALLOCATION / mItemSize));
+// ALOGV("grow vector %p, new_capacity=%d", this, (int)new_capacity);
+ if ((mStorage) &&
+ (mCount==where) &&
+ (mFlags & HAS_TRIVIAL_COPY) &&
+ (mFlags & HAS_TRIVIAL_DTOR))
+ {
+ const SharedBuffer* cur_sb = SharedBuffer::bufferFromData(mStorage);
+ assert(cur_sb);
+ SharedBuffer* sb = cur_sb->editResize(new_capacity * mItemSize);
+ assert(sb);
+ mStorage = sb->data();
+ } else {
+ SharedBuffer* sb = SharedBuffer::alloc(new_capacity * mItemSize);
+ assert(sb);
+ if (sb) {
+ void* array = sb->data();
+ if (where != 0) {
+ _do_copy(array, mStorage, where);
+ }
+ if (where != mCount) {
+ const void* from = reinterpret_cast<const uint8_t *>(mStorage) + where*mItemSize;
+ void* dest = reinterpret_cast<uint8_t *>(array) + (where+amount)*mItemSize;
+ _do_copy(dest, from, mCount-where);
+ }
+ release_storage();
+ mStorage = const_cast<void*>(array);
+ }
+ }
+ } else {
+ void* array = editArrayImpl();
+ if (where != mCount) {
+ const void* from = reinterpret_cast<const uint8_t *>(array) + where*mItemSize;
+ void* to = reinterpret_cast<uint8_t *>(array) + (where+amount)*mItemSize;
+ _do_move_forward(to, from, mCount - where);
+ }
+ }
+ mCount = new_size;
+ void* free_space = const_cast<void*>(itemLocation(where));
+ return free_space;
+}
+
+void VectorImpl::_shrink(size_t where, size_t amount)
+{
+ if (!mStorage)
+ return;
+
+// ALOGV("_shrink(this=%p, where=%d, amount=%d) count=%d, capacity=%d",
+// this, (int)where, (int)amount, (int)mCount, (int)capacity());
+
+ ALOG_ASSERT(where + amount <= mCount,
+ "[%p] _shrink: where=%d, amount=%d, count=%d",
+ this, (int)where, (int)amount, (int)mCount); // caller already checked
+
+ const size_t new_size = mCount - amount;
+ assert(new_size < (SIZE_MAX / 2));
+ if (new_size*2 < capacity()) {
+ const size_t new_capacity = max(kMinVectorCapacity, new_size*2);
+// ALOGV("shrink vector %p, new_capacity=%d", this, (int)new_capacity);
+ assert(new_capacity < (kMAX_ALLOCATION / mItemSize));
+ if ((where == new_size) &&
+ (mFlags & HAS_TRIVIAL_COPY) &&
+ (mFlags & HAS_TRIVIAL_DTOR))
+ {
+ const SharedBuffer* cur_sb = SharedBuffer::bufferFromData(mStorage);
+ assert(cur_sb);
+ SharedBuffer* sb = cur_sb->editResize(new_capacity * mItemSize);
+ assert(sb);
+ mStorage = sb->data();
+ } else {
+ SharedBuffer* sb = SharedBuffer::alloc(new_capacity * mItemSize);
+ assert(sb);
+ if (sb) {
+ void* array = sb->data();
+ if (where != 0) {
+ _do_copy(array, mStorage, where);
+ }
+ if (where != new_size) {
+ const void* from = reinterpret_cast<const uint8_t *>(mStorage) + (where+amount)*mItemSize;
+ void* dest = reinterpret_cast<uint8_t *>(array) + where*mItemSize;
+ _do_copy(dest, from, new_size - where);
+ }
+ release_storage();
+ mStorage = const_cast<void*>(array);
+ }
+ }
+ } else {
+ void* array = editArrayImpl();
+ assert(array);
+ void* to = reinterpret_cast<uint8_t *>(array) + where*mItemSize;
+ _do_destroy(to, amount);
+ if (where != new_size) {
+ const void* from = reinterpret_cast<uint8_t *>(array) + (where+amount)*mItemSize;
+ _do_move_backward(to, from, new_size - where);
+ }
+ }
+ mCount = new_size;
+}
+
+size_t VectorImpl::itemSize() const {
+ return mItemSize;
+}
+
+void VectorImpl::_do_construct(void* storage, size_t num) const
+{
+ if (!(mFlags & HAS_TRIVIAL_CTOR)) {
+ do_construct(storage, num);
+ }
+}
+
+void VectorImpl::_do_destroy(void* storage, size_t num) const
+{
+ if (!(mFlags & HAS_TRIVIAL_DTOR)) {
+ do_destroy(storage, num);
+ }
+}
+
+void VectorImpl::_do_copy(void* dest, const void* from, size_t num) const
+{
+ if (!(mFlags & HAS_TRIVIAL_COPY)) {
+ do_copy(dest, from, num);
+ } else {
+ memcpy(dest, from, num*itemSize());
+ }
+}
+
+void VectorImpl::_do_splat(void* dest, const void* item, size_t num) const {
+ do_splat(dest, item, num);
+}
+
+void VectorImpl::_do_move_forward(void* dest, const void* from, size_t num) const {
+ do_move_forward(dest, from, num);
+}
+
+void VectorImpl::_do_move_backward(void* dest, const void* from, size_t num) const {
+ do_move_backward(dest, from, num);
+}
+
+/*****************************************************************************/
+
+SortedVectorImpl::SortedVectorImpl(size_t itemSize, uint32_t flags)
+ : VectorImpl(itemSize, flags)
+{
+}
+
+SortedVectorImpl::SortedVectorImpl(const VectorImpl& rhs)
+: VectorImpl(rhs)
+{
+}
+
+SortedVectorImpl::~SortedVectorImpl()
+{
+}
+
+SortedVectorImpl& SortedVectorImpl::operator = (const SortedVectorImpl& rhs)
+{
+ return static_cast<SortedVectorImpl&>( VectorImpl::operator = (static_cast<const VectorImpl&>(rhs)) );
+}
+
+ssize_t SortedVectorImpl::indexOf(const void* item) const
+{
+ return _indexOrderOf(item);
+}
+
+size_t SortedVectorImpl::orderOf(const void* item) const
+{
+ size_t o;
+ _indexOrderOf(item, &o);
+ return o;
+}
+
+ssize_t SortedVectorImpl::_indexOrderOf(const void* item, size_t* order) const
+{
+ // binary search
+ ssize_t err = NAME_NOT_FOUND;
+ ssize_t l = 0;
+ ssize_t h = size()-1;
+ ssize_t mid;
+ const void* a = arrayImpl();
+ const size_t s = itemSize();
+ while (l <= h) {
+ mid = l + (h - l)/2;
+ const void* const curr = reinterpret_cast<const char *>(a) + (mid*s);
+ const int c = do_compare(curr, item);
+ if (c == 0) {
+ err = l = mid;
+ break;
+ } else if (c < 0) {
+ l = mid + 1;
+ } else {
+ h = mid - 1;
+ }
+ }
+ if (order) *order = l;
+ return err;
+}
+
+ssize_t SortedVectorImpl::add(const void* item)
+{
+ size_t order;
+ ssize_t index = _indexOrderOf(item, &order);
+ if (index < 0) {
+ index = VectorImpl::insertAt(item, order, 1);
+ } else {
+ index = VectorImpl::replaceAt(item, index);
+ }
+ return index;
+}
+
+ssize_t SortedVectorImpl::merge(const VectorImpl& vector)
+{
+ // naive merge...
+ if (!vector.isEmpty()) {
+ const void* buffer = vector.arrayImpl();
+ const size_t is = itemSize();
+ size_t s = vector.size();
+ for (size_t i=0 ; i<s ; i++) {
+ ssize_t err = add( reinterpret_cast<const char*>(buffer) + i*is );
+ if (err<0) {
+ return err;
+ }
+ }
+ }
+ return NO_ERROR;
+}
+
+ssize_t SortedVectorImpl::merge(const SortedVectorImpl& vector)
+{
+ // we've merging a sorted vector... nice!
+ ssize_t err = NO_ERROR;
+ if (!vector.isEmpty()) {
+ // first take care of the case where the vectors are sorted together
+ if (do_compare(vector.itemLocation(vector.size()-1), arrayImpl()) <= 0) {
+ err = VectorImpl::insertVectorAt(static_cast<const VectorImpl&>(vector), 0);
+ } else if (do_compare(vector.arrayImpl(), itemLocation(size()-1)) >= 0) {
+ err = VectorImpl::appendVector(static_cast<const VectorImpl&>(vector));
+ } else {
+ // this could be made a little better
+ err = merge(static_cast<const VectorImpl&>(vector));
+ }
+ }
+ return err;
+}
+
+ssize_t SortedVectorImpl::remove(const void* item)
+{
+ ssize_t i = indexOf(item);
+ if (i>=0) {
+ VectorImpl::removeItemsAt(i, 1);
+ }
+ return i;
+}
+
+/*****************************************************************************/
+
+}; // namespace stagefright
+
diff --git a/media/libstagefright/update-patches.sh b/media/libstagefright/update-patches.sh
new file mode 100755
index 000000000..f9f498b77
--- /dev/null
+++ b/media/libstagefright/update-patches.sh
@@ -0,0 +1,13 @@
+#!/bin/bash -e
+cd `dirname "$0"`
+rm -fR patches
+for DIR in `find android/ -name .git`
+do
+ DIR=`dirname ${DIR}`
+ DST=patches/${DIR:8}
+ echo ${DST}
+ mkdir -p `dirname ${DST}`
+ cp -a ${DIR:8} `dirname ${DIR}`
+ (cd ${DIR} && git diff) > ${DST}.patch
+ (cd ${DIR} && git rev-parse HEAD) > ${DST}.tag
+done