From 5f8de423f190bbb79a62f804151bc24824fa32d8 Mon Sep 17 00:00:00 2001 From: "Matt A. Tobin" Date: Fri, 2 Feb 2018 04:16:08 -0500 Subject: Add m-esr52 at 52.6.0 --- .../av/include/media/stagefright/DataSource.h | 100 + .../av/include/media/stagefright/MediaBuffer.h | 128 + .../av/include/media/stagefright/MediaDefs.h | 64 + .../av/include/media/stagefright/MediaErrors.h | 69 + .../av/include/media/stagefright/MediaExtractor.h | 82 + .../av/include/media/stagefright/MediaSource.h | 61 + .../av/include/media/stagefright/MetaData.h | 275 ++ .../av/include/media/stagefright/Utils.h | 55 + .../media/stagefright/foundation/AAtomizer.h | 51 + .../include/media/stagefright/foundation/ABase.h | 25 + .../media/stagefright/foundation/ABitReader.h | 54 + .../include/media/stagefright/foundation/ABuffer.h | 71 + .../include/media/stagefright/foundation/ADebug.h | 86 + .../media/stagefright/foundation/AHandler.h | 56 + .../include/media/stagefright/foundation/AString.h | 95 + .../include/media/stagefright/foundation/hexdump.h | 33 + .../av/media/libstagefright/DataSource.cpp | 219 ++ .../frameworks/av/media/libstagefright/ESDS.cpp | 244 ++ .../av/media/libstagefright/MPEG4Extractor.cpp | 2972 ++++++++++++++++++++ .../av/media/libstagefright/MediaBuffer.cpp | 215 ++ .../av/media/libstagefright/MediaDefs.cpp | 60 + .../av/media/libstagefright/MediaSource.cpp | 25 + .../av/media/libstagefright/MetaData.cpp | 367 +++ .../av/media/libstagefright/SampleIterator.cpp | 331 +++ .../av/media/libstagefright/SampleTable.cpp | 1170 ++++++++ .../frameworks/av/media/libstagefright/Utils.cpp | 619 ++++ .../media/libstagefright/foundation/AAtomizer.cpp | 67 + .../media/libstagefright/foundation/ABitReader.cpp | 103 + .../av/media/libstagefright/foundation/ABuffer.cpp | 76 + .../av/media/libstagefright/foundation/AString.cpp | 347 +++ .../av/media/libstagefright/foundation/hexdump.cpp | 96 + .../av/media/libstagefright/include/AMRExtractor.h | 62 + .../av/media/libstagefright/include/ESDS.h | 72 + .../media/libstagefright/include/MPEG4Extractor.h | 136 + .../media/libstagefright/include/SampleIterator.h | 83 + .../av/media/libstagefright/include/SampleTable.h | 188 ++ 36 files changed, 8757 insertions(+) create mode 100644 media/libstagefright/frameworks/av/include/media/stagefright/DataSource.h create mode 100644 media/libstagefright/frameworks/av/include/media/stagefright/MediaBuffer.h create mode 100644 media/libstagefright/frameworks/av/include/media/stagefright/MediaDefs.h create mode 100644 media/libstagefright/frameworks/av/include/media/stagefright/MediaErrors.h create mode 100644 media/libstagefright/frameworks/av/include/media/stagefright/MediaExtractor.h create mode 100644 media/libstagefright/frameworks/av/include/media/stagefright/MediaSource.h create mode 100644 media/libstagefright/frameworks/av/include/media/stagefright/MetaData.h create mode 100644 media/libstagefright/frameworks/av/include/media/stagefright/Utils.h create mode 100644 media/libstagefright/frameworks/av/include/media/stagefright/foundation/AAtomizer.h create mode 100644 media/libstagefright/frameworks/av/include/media/stagefright/foundation/ABase.h create mode 100644 media/libstagefright/frameworks/av/include/media/stagefright/foundation/ABitReader.h create mode 100644 media/libstagefright/frameworks/av/include/media/stagefright/foundation/ABuffer.h create mode 100644 media/libstagefright/frameworks/av/include/media/stagefright/foundation/ADebug.h create mode 100644 media/libstagefright/frameworks/av/include/media/stagefright/foundation/AHandler.h create mode 100644 media/libstagefright/frameworks/av/include/media/stagefright/foundation/AString.h create mode 100644 media/libstagefright/frameworks/av/include/media/stagefright/foundation/hexdump.h create mode 100644 media/libstagefright/frameworks/av/media/libstagefright/DataSource.cpp create mode 100644 media/libstagefright/frameworks/av/media/libstagefright/ESDS.cpp create mode 100644 media/libstagefright/frameworks/av/media/libstagefright/MPEG4Extractor.cpp create mode 100644 media/libstagefright/frameworks/av/media/libstagefright/MediaBuffer.cpp create mode 100644 media/libstagefright/frameworks/av/media/libstagefright/MediaDefs.cpp create mode 100644 media/libstagefright/frameworks/av/media/libstagefright/MediaSource.cpp create mode 100644 media/libstagefright/frameworks/av/media/libstagefright/MetaData.cpp create mode 100644 media/libstagefright/frameworks/av/media/libstagefright/SampleIterator.cpp create mode 100644 media/libstagefright/frameworks/av/media/libstagefright/SampleTable.cpp create mode 100644 media/libstagefright/frameworks/av/media/libstagefright/Utils.cpp create mode 100644 media/libstagefright/frameworks/av/media/libstagefright/foundation/AAtomizer.cpp create mode 100644 media/libstagefright/frameworks/av/media/libstagefright/foundation/ABitReader.cpp create mode 100644 media/libstagefright/frameworks/av/media/libstagefright/foundation/ABuffer.cpp create mode 100644 media/libstagefright/frameworks/av/media/libstagefright/foundation/AString.cpp create mode 100644 media/libstagefright/frameworks/av/media/libstagefright/foundation/hexdump.cpp create mode 100644 media/libstagefright/frameworks/av/media/libstagefright/include/AMRExtractor.h create mode 100644 media/libstagefright/frameworks/av/media/libstagefright/include/ESDS.h create mode 100644 media/libstagefright/frameworks/av/media/libstagefright/include/MPEG4Extractor.h create mode 100644 media/libstagefright/frameworks/av/media/libstagefright/include/SampleIterator.h create mode 100644 media/libstagefright/frameworks/av/media/libstagefright/include/SampleTable.h (limited to 'media/libstagefright/frameworks/av') 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 + +#include +#include + +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 *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 &source, String8 *mimeType, + float *confidence, sp *meta); + + static void RegisterSniffer(SnifferFunc func); + static void RegisterDefaultSniffers(); + + // for DRM + virtual sp DrmInitialization(const char *mime = NULL) { + return NULL; + } + virtual void getDrmInfo(sp &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 + +#include +#include +#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); + + MediaBuffer(const sp &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() const; + + sp 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 mGraphicBuffer; + sp mBuffer; + + bool mOwnsData; + + sp mMetaData; + + MediaBuffer *mOriginal; + + void setNextBuffer(MediaBuffer *buffer); + MediaBuffer *nextBuffer(); + + MediaBuffer(const MediaBuffer &); + MediaBuffer &operator=(const MediaBuffer &); + + FallibleTArray 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 + +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 + +namespace stagefright { + +class DataSource; +struct MediaSource; +class MetaData; + +class MediaExtractor : public RefBase { +public: + static sp Create( + const sp &source, const char *mime = NULL); + + virtual size_t countTracks() = 0; + virtual sp getTrack(size_t index) = 0; + + enum GetTrackMetaDataFlags { + kIncludeExtensiveMetaData = 1 + }; + virtual sp getTrackMetaData( + size_t index, uint32_t flags = 0) = 0; + + // Return container specific meta-data. The default implementation + // returns an empty metadata object. + virtual sp 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 + +#include +#include +#include +#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 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 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 + +#include + +#include +#include +#include + +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 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 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 +#include +#include +#include +#include +#include + +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 &meta, sp *format); +void convertMessageToMetaData( + const sp &format, sp &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 + +#include +#include +#include +#include +#include + +namespace stagefright { + +struct AAtomizer { + static const char *Atomize(const char *name); + +private: + static AAtomizer gAtomizer; + + Mutex mLock; + Vector > 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 + +#include +#include + +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 +#include + +#include +#include + +namespace stagefright { + +struct AMessage; + +struct ABuffer : public RefBase { + ABuffer(size_t capacity); + ABuffer(void *data, size_t capacity); + + void setFarewellMessage(const sp 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 meta(); + +protected: + virtual ~ABuffer(); + +private: + sp mFarewell; + sp 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 + +#include +#include +#include + +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 \ + 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 +#include + +namespace stagefright { + +struct AMessage; + +struct AHandler : public RefBase { + AHandler() + : mID(0) { + } + + ALooper::handler_id id() const { + return mID; + } + + sp looper(); + +protected: + virtual void onMessageReceived(const sp &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 + +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 + +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 +#include + +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::gSniffers; + +bool DataSource::sniff( + String8 *mimeType, float *confidence, sp *meta) { + *mimeType = ""; + *confidence = 0.0f; + meta->clear(); + + Mutex::Autolock autoLock(gSnifferMutex); + for (List::iterator it = gSniffers.begin(); + it != gSniffers.end(); ++it) { + String8 newMimeType; + float newConfidence; + sp 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::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::CreateFromURI( + const char *uri, const KeyedVector *headers) { + bool isWidevine = !strncasecmp("widevine://", uri, 11); + + sp source; + if (!strncasecmp("file://", uri, 7)) { + source = new FileSource(uri + 7); + } else if (!strncasecmp("http://", uri, 7) + || !strncasecmp("https://", uri, 8) + || isWidevine) { + sp 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 copy = *headers; + NuCachedSource2::RemoveCacheSpecificHeaders( + ©, &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 + +#include "include/ESDS.h" + +#include + +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 + +#include "include/MPEG4Extractor.h" +#include "include/SampleTable.h" +#include "include/ESDS.h" + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +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 &format, + uint32_t timeScale, + const sp &sampleTable); + + sp getFormat() override; + + nsTArray exportIndex() override; + +protected: + virtual ~MPEG4Source(); + +private: + sp mFormat; + uint32_t mTimescale; + sp 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 &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 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 &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 &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 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 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, 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 *path, uint32_t chunkType) + : mPath(path) { + mPath->AppendElement(chunkType); + } + + ~PathAdder() { + mPath->RemoveElementAt(mPath->Length() - 1); + } + +private: + nsTArray *mPath; + + PathAdder(const PathAdder &); + PathAdder &operator=(const PathAdder &); +}; + +static bool underMetaDataPath(const nsTArray &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 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, ×cale, 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(&i_value); + sample_rate = uint32_t(*reinterpret_cast(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 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 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 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 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(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 &format, + uint32_t timeScale, + const sp &sampleTable) + : mFormat(format), + mTimescale(timeScale), + mSampleTable(sampleTable) { +} + +MPEG4Source::~MPEG4Source() { +} + +sp 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 MPEG4Source::exportIndex() +{ + nsTArray 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 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 + +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +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) + : 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 &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 MediaBuffer::graphicBuffer() const { + return mGraphicBuffer; +} + +sp 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 + +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 + +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 + +#include +#include + +#include +#include +#include +#include + +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 + +#include "include/SampleIterator.h" + +#include + +#include +#include +#include + +#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 + +#include "include/SampleTable.h" +#include "include/SampleIterator.h" + +#include + +#include +#include +#include + +#include + +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 &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& 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& clear_sizes, + nsTArray& 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 + +#include "include/ESDS.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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 &meta, sp *format) { + format->clear(); + + const char *mime; + CHECK(meta->findCString(kKeyMIMEType, &mime)); + + sp 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 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 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 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 &csd0, const sp 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 &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 &msg, sp &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 csd0; + if (msg->findBuffer("csd-0", &csd0)) { + if (mime.startsWith("video/")) { // do we need to be stricter than this? + sp 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& sink, + const sp& 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& 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 + +#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()); + } +} + +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 &entry = mAtoms.editItemAt(index); + List::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 +#include + +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 msg) { + mFarewell = msg; +} + +sp 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 +#include +#include +#include +#include + +#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 + +#include "hexdump.h" + +#include "ADebug.h" +#include "AString.h" + +#include +#include +#include + +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 +#include + +namespace stagefright { + +struct AMessage; +class String8; +#define OFFSET_TABLE_LEN 300 + +class AMRExtractor : public MediaExtractor { +public: + AMRExtractor(const sp &source); + + virtual size_t countTracks(); + virtual sp getTrack(size_t index); + virtual sp getTrackMetaData(size_t index, uint32_t flags); + + virtual sp getMetaData(); + +protected: + virtual ~AMRExtractor(); + +private: + sp mDataSource; + sp 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 &source, String8 *mimeType, float *confidence, + sp *); + +} // 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 + +#include + +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 + +#include +#include +#include +#include +#include +#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 &source); + + size_t countTracks() override; + sp getTrack(size_t index) override; + sp getTrackMetaData(size_t index, uint32_t flags) override; + + sp 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 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; + bool includes_expensive_metadata; + bool skipTrack; + }; + + nsTArray mSidxEntries; + uint64_t mSidxDuration; + + nsTArray mPssh; + + sp mDataSource; + status_t mInitCheck; + bool mHasVideo; + uint32_t mHeaderTimescale; + + Track *mFirstTrack, *mLastTrack; + + sp mFileMetaData; + + nsTArray 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 + +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 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 +#include + +#include +#include +#include + +namespace stagefright { + +class DataSource; +struct SampleIterator; + +class SampleTable : public RefBase { +public: + SampleTable(const sp &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& aClearSizes, + nsTArray& 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 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 mCencSizes; + FallibleTArray 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_ -- cgit v1.2.3