diff options
Diffstat (limited to 'media')
24 files changed, 1377 insertions, 400 deletions
diff --git a/media/libeffects/lvm/wrapper/Reverb/EffectReverb.cpp b/media/libeffects/lvm/wrapper/Reverb/EffectReverb.cpp index 26c5aca..9097e20 100755 --- a/media/libeffects/lvm/wrapper/Reverb/EffectReverb.cpp +++ b/media/libeffects/lvm/wrapper/Reverb/EffectReverb.cpp @@ -166,7 +166,7 @@ enum { REVERB_VOLUME_RAMP, }; -#define REVERB_DEFAULT_PRESET REVERB_PRESET_MEDIUMROOM +#define REVERB_DEFAULT_PRESET REVERB_PRESET_NONE #define REVERB_SEND_LEVEL (0x0C00) // 0.75 in 4.12 format diff --git a/media/libstagefright/AMRExtractor.cpp b/media/libstagefright/AMRExtractor.cpp index ac87c29..7eca5e4 100644 --- a/media/libstagefright/AMRExtractor.cpp +++ b/media/libstagefright/AMRExtractor.cpp @@ -35,8 +35,9 @@ class AMRSource : public MediaSource { public: AMRSource(const sp<DataSource> &source, const sp<MetaData> &meta, - size_t frameSize, - bool isWide); + bool isWide, + const off64_t *offset_table, + size_t offset_table_length); virtual status_t start(MetaData *params = NULL); virtual status_t stop(); @@ -52,7 +53,6 @@ protected: private: sp<DataSource> mDataSource; sp<MetaData> mMeta; - size_t mFrameSize; bool mIsWide; off64_t mOffset; @@ -60,6 +60,9 @@ private: bool mStarted; MediaBufferGroup *mGroup; + off64_t mOffsetTable[OFFSET_TABLE_LEN]; + size_t mOffsetTableLength; + AMRSource(const AMRSource &); AMRSource &operator=(const AMRSource &); }; @@ -67,13 +70,25 @@ private: //////////////////////////////////////////////////////////////////////////////// static size_t getFrameSize(bool isWide, unsigned FT) { - static const size_t kFrameSizeNB[8] = { - 95, 103, 118, 134, 148, 159, 204, 244 + static const size_t kFrameSizeNB[16] = { + 95, 103, 118, 134, 148, 159, 204, 244, + 39, 43, 38, 37, // SID + 0, 0, 0, // future use + 0 // no data }; - static const size_t kFrameSizeWB[9] = { - 132, 177, 253, 285, 317, 365, 397, 461, 477 + static const size_t kFrameSizeWB[16] = { + 132, 177, 253, 285, 317, 365, 397, 461, 477, + 40, // SID + 0, 0, 0, 0, // future use + 0, // speech lost + 0 // no data }; + if (FT > 15 || (isWide && FT > 9 && FT < 14) || (!isWide && FT > 11 && FT < 15)) { + LOGE("illegal AMR frame type %d", FT); + return 0; + } + size_t frameSize = isWide ? kFrameSizeWB[FT] : kFrameSizeNB[FT]; // Round up bits to bytes and add 1 for the header byte. @@ -82,9 +97,26 @@ static size_t getFrameSize(bool isWide, unsigned FT) { return frameSize; } +static status_t getFrameSizeByOffset(const sp<DataSource> &source, + off64_t offset, bool isWide, size_t *frameSize) { + uint8_t header; + if (source->readAt(offset, &header, 1) < 1) { + return ERROR_IO; + } + + unsigned FT = (header >> 3) & 0x0f; + + *frameSize = getFrameSize(isWide, FT); + if (*frameSize == 0) { + return ERROR_MALFORMED; + } + return OK; +} + AMRExtractor::AMRExtractor(const sp<DataSource> &source) : mDataSource(source), - mInitCheck(NO_INIT) { + mInitCheck(NO_INIT), + mOffsetTableLength(0) { String8 mimeType; float confidence; if (!SniffAMR(mDataSource, &mimeType, &confidence, NULL)) { @@ -101,25 +133,29 @@ AMRExtractor::AMRExtractor(const sp<DataSource> &source) mMeta->setInt32(kKeyChannelCount, 1); mMeta->setInt32(kKeySampleRate, mIsWide ? 16000 : 8000); - size_t offset = mIsWide ? 9 : 6; - uint8_t header; - if (mDataSource->readAt(offset, &header, 1) != 1) { - return; - } - - unsigned FT = (header >> 3) & 0x0f; - - if (FT > 8 || (!mIsWide && FT > 7)) { - return; - } - - mFrameSize = getFrameSize(mIsWide, FT); - + off64_t offset = mIsWide ? 9 : 6; off64_t streamSize; - if (mDataSource->getSize(&streamSize) == OK) { - off64_t numFrames = streamSize / mFrameSize; + size_t frameSize, numFrames = 0; + int64_t duration = 0; - mMeta->setInt64(kKeyDuration, 20000ll * numFrames); + if (mDataSource->getSize(&streamSize) == OK) { + while (offset < streamSize) { + if (getFrameSizeByOffset(source, offset, mIsWide, &frameSize) != OK) { + return; + } + + if ((numFrames % 50 == 0) && (numFrames / 50 < OFFSET_TABLE_LEN)) { + CHECK_EQ(mOffsetTableLength, numFrames / 50); + mOffsetTable[mOffsetTableLength] = offset - (mIsWide ? 9: 6); + mOffsetTableLength ++; + } + + offset += frameSize; + duration += 20000; // Each frame is 20ms + numFrames ++; + } + + mMeta->setInt64(kKeyDuration, duration); } mInitCheck = OK; @@ -149,7 +185,8 @@ sp<MediaSource> AMRExtractor::getTrack(size_t index) { return NULL; } - return new AMRSource(mDataSource, mMeta, mFrameSize, mIsWide); + return new AMRSource(mDataSource, mMeta, mIsWide, + mOffsetTable, mOffsetTableLength); } sp<MetaData> AMRExtractor::getTrackMetaData(size_t index, uint32_t flags) { @@ -164,15 +201,18 @@ sp<MetaData> AMRExtractor::getTrackMetaData(size_t index, uint32_t flags) { AMRSource::AMRSource( const sp<DataSource> &source, const sp<MetaData> &meta, - size_t frameSize, bool isWide) + bool isWide, const off64_t *offset_table, size_t offset_table_length) : mDataSource(source), mMeta(meta), - mFrameSize(frameSize), mIsWide(isWide), mOffset(mIsWide ? 9 : 6), mCurrentTimeUs(0), mStarted(false), - mGroup(NULL) { + mGroup(NULL), + mOffsetTableLength(offset_table_length) { + if (mOffsetTableLength > 0 && mOffsetTableLength <= OFFSET_TABLE_LEN) { + memcpy ((char*)mOffsetTable, (char*)offset_table, sizeof(off64_t) * mOffsetTableLength); + } } AMRSource::~AMRSource() { @@ -214,9 +254,25 @@ status_t AMRSource::read( int64_t seekTimeUs; ReadOptions::SeekMode mode; if (options && options->getSeekTo(&seekTimeUs, &mode)) { + size_t size; int64_t seekFrame = seekTimeUs / 20000ll; // 20ms per frame. mCurrentTimeUs = seekFrame * 20000ll; - mOffset = seekFrame * mFrameSize + (mIsWide ? 9 : 6); + + int index = seekFrame / 50; + if (index >= mOffsetTableLength) { + index = mOffsetTableLength - 1; + } + + mOffset = mOffsetTable[index] + (mIsWide ? 9 : 6); + + for (int i = 0; i< seekFrame - index * 50; i++) { + status_t err; + if ((err = getFrameSizeByOffset(mDataSource, mOffset, + mIsWide, &size)) != OK) { + return err; + } + mOffset += size; + } } uint8_t header; @@ -236,16 +292,11 @@ status_t AMRSource::read( unsigned FT = (header >> 3) & 0x0f; - if (FT > 8 || (!mIsWide && FT > 7)) { - - LOGE("illegal AMR frame type %d", FT); - + size_t frameSize = getFrameSize(mIsWide, FT); + if (frameSize == 0) { return ERROR_MALFORMED; } - size_t frameSize = getFrameSize(mIsWide, FT); - CHECK_EQ(frameSize, mFrameSize); - MediaBuffer *buffer; status_t err = mGroup->acquire_buffer(&buffer); if (err != OK) { diff --git a/media/libstagefright/Android.mk b/media/libstagefright/Android.mk index 2d486e3..029b238 100644 --- a/media/libstagefright/Android.mk +++ b/media/libstagefright/Android.mk @@ -17,6 +17,7 @@ LOCAL_SRC_FILES:= \ DRMExtractor.cpp \ ESDS.cpp \ FileSource.cpp \ + FLACExtractor.cpp \ HTTPStream.cpp \ JPEGSource.cpp \ MP3Extractor.cpp \ @@ -54,6 +55,7 @@ LOCAL_SRC_FILES:= \ LOCAL_C_INCLUDES:= \ $(JNI_H_INCLUDE) \ $(TOP)/frameworks/base/include/media/stagefright/openmax \ + $(TOP)/external/flac/include \ $(TOP)/external/tremolo \ $(TOP)/frameworks/base/media/libstagefright/rtsp @@ -93,6 +95,7 @@ LOCAL_STATIC_LIBRARIES := \ libstagefright_rtsp \ libstagefright_id3 \ libstagefright_g711dec \ + libFLAC \ LOCAL_SHARED_LIBRARIES += \ libstagefright_amrnb_common \ diff --git a/media/libstagefright/AwesomePlayer.cpp b/media/libstagefright/AwesomePlayer.cpp index 89b3dab..11ac56c 100644 --- a/media/libstagefright/AwesomePlayer.cpp +++ b/media/libstagefright/AwesomePlayer.cpp @@ -378,11 +378,14 @@ status_t AwesomePlayer::setDataSource_l(const sp<MediaExtractor> &extractor) { } void AwesomePlayer::reset() { + LOGI("reset"); + Mutex::Autolock autoLock(mLock); reset_l(); } void AwesomePlayer::reset_l() { + LOGI("reset_l"); mDisplayWidth = 0; mDisplayHeight = 0; @@ -408,6 +411,10 @@ void AwesomePlayer::reset_l() { } } + if (mFlags & PREPARING) { + LOGI("waiting until preparation is completes."); + } + while (mFlags & PREPARING) { mPreparedCondition.wait(mLock); } @@ -431,6 +438,8 @@ void AwesomePlayer::reset_l() { } mAudioSource.clear(); + LOGI("audio source cleared"); + mTimeSource = NULL; delete mAudioPlayer; @@ -471,6 +480,8 @@ void AwesomePlayer::reset_l() { IPCThreadState::self()->flushCommands(); } + LOGI("video source cleared"); + mDurationUs = -1; mFlags = 0; mExtractorFlags = 0; @@ -487,6 +498,8 @@ void AwesomePlayer::reset_l() { mFileSource.clear(); mBitrate = -1; + + LOGI("reset_l completed"); } void AwesomePlayer::notifyListener_l(int msg, int ext1, int ext2) { diff --git a/media/libstagefright/DataSource.cpp b/media/libstagefright/DataSource.cpp index ee0d792..e06fa81 100644 --- a/media/libstagefright/DataSource.cpp +++ b/media/libstagefright/DataSource.cpp @@ -23,6 +23,7 @@ #include "include/NuCachedSource2.h" #include "include/NuHTTPDataSource.h" #include "include/DRMExtractor.h" +#include "include/FLACExtractor.h" #include "matroska/MatroskaExtractor.h" @@ -104,6 +105,7 @@ void DataSource::RegisterDefaultSniffers() { RegisterSniffer(SniffMatroska); RegisterSniffer(SniffOgg); RegisterSniffer(SniffWAV); + RegisterSniffer(SniffFLAC); RegisterSniffer(SniffAMR); RegisterSniffer(SniffMPEG2TS); RegisterSniffer(SniffMP3); diff --git a/media/libstagefright/FLACExtractor.cpp b/media/libstagefright/FLACExtractor.cpp new file mode 100644 index 0000000..8ba5a2d --- /dev/null +++ b/media/libstagefright/FLACExtractor.cpp @@ -0,0 +1,813 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +//#define LOG_NDEBUG 0 +#define LOG_TAG "FLACExtractor" +#include <utils/Log.h> + +#include "include/FLACExtractor.h" +// Vorbis comments +#include "include/OggExtractor.h" +// libFLAC parser +#include "FLAC/stream_decoder.h" + +#include <media/stagefright/foundation/ADebug.h> +#include <media/stagefright/DataSource.h> +#include <media/stagefright/MediaBufferGroup.h> +#include <media/stagefright/MediaDefs.h> +#include <media/stagefright/MetaData.h> +#include <media/stagefright/MediaSource.h> +#include <media/stagefright/MediaBuffer.h> + +namespace android { + +class FLACParser; + +class FLACSource : public MediaSource { + +public: + FLACSource( + const sp<DataSource> &dataSource, + const sp<MetaData> &trackMetadata); + + virtual status_t start(MetaData *params); + virtual status_t stop(); + virtual sp<MetaData> getFormat(); + + virtual status_t read( + MediaBuffer **buffer, const ReadOptions *options = NULL); + +protected: + virtual ~FLACSource(); + +private: + sp<DataSource> mDataSource; + sp<MetaData> mTrackMetadata; + sp<FLACParser> mParser; + bool mInitCheck; + bool mStarted; + + status_t init(); + + // no copy constructor or assignment + FLACSource(const FLACSource &); + FLACSource &operator=(const FLACSource &); + +}; + +// FLACParser wraps a C libFLAC parser aka stream decoder + +class FLACParser : public RefBase { + +public: + FLACParser( + const sp<DataSource> &dataSource, + // If metadata pointers aren't provided, we don't fill them + const sp<MetaData> &fileMetadata = 0, + const sp<MetaData> &trackMetadata = 0); + + status_t initCheck() const { + return mInitCheck; + } + + // stream properties + unsigned getMaxBlockSize() const { + return mStreamInfo.max_blocksize; + } + unsigned getSampleRate() const { + return mStreamInfo.sample_rate; + } + unsigned getChannels() const { + return mStreamInfo.channels; + } + unsigned getBitsPerSample() const { + return mStreamInfo.bits_per_sample; + } + FLAC__uint64 getTotalSamples() const { + return mStreamInfo.total_samples; + } + + // media buffers + void allocateBuffers(); + void releaseBuffers(); + MediaBuffer *readBuffer() { + return readBuffer(false, 0LL); + } + MediaBuffer *readBuffer(FLAC__uint64 sample) { + return readBuffer(true, sample); + } + +protected: + virtual ~FLACParser(); + +private: + sp<DataSource> mDataSource; + sp<MetaData> mFileMetadata; + sp<MetaData> mTrackMetadata; + bool mInitCheck; + + // media buffers + size_t mMaxBufferSize; + MediaBufferGroup *mGroup; + void (*mCopy)(short *dst, const int *const *src, unsigned nSamples); + + // handle to underlying libFLAC parser + FLAC__StreamDecoder *mDecoder; + + // current position within the data source + off64_t mCurrentPos; + bool mEOF; + + // cached when the STREAMINFO metadata is parsed by libFLAC + FLAC__StreamMetadata_StreamInfo mStreamInfo; + bool mStreamInfoValid; + + // cached when a decoded PCM block is "written" by libFLAC parser + bool mWriteRequested; + bool mWriteCompleted; + FLAC__FrameHeader mWriteHeader; + const FLAC__int32 * const *mWriteBuffer; + + // most recent error reported by libFLAC parser + FLAC__StreamDecoderErrorStatus mErrorStatus; + + status_t init(); + MediaBuffer *readBuffer(bool doSeek, FLAC__uint64 sample); + + // no copy constructor or assignment + FLACParser(const FLACParser &); + FLACParser &operator=(const FLACParser &); + + // FLAC parser callbacks as C++ instance methods + FLAC__StreamDecoderReadStatus readCallback( + FLAC__byte buffer[], size_t *bytes); + FLAC__StreamDecoderSeekStatus seekCallback( + FLAC__uint64 absolute_byte_offset); + FLAC__StreamDecoderTellStatus tellCallback( + FLAC__uint64 *absolute_byte_offset); + FLAC__StreamDecoderLengthStatus lengthCallback( + FLAC__uint64 *stream_length); + FLAC__bool eofCallback(); + FLAC__StreamDecoderWriteStatus writeCallback( + const FLAC__Frame *frame, const FLAC__int32 * const buffer[]); + void metadataCallback(const FLAC__StreamMetadata *metadata); + void errorCallback(FLAC__StreamDecoderErrorStatus status); + + // FLAC parser callbacks as C-callable functions + static FLAC__StreamDecoderReadStatus read_callback( + const FLAC__StreamDecoder *decoder, + FLAC__byte buffer[], size_t *bytes, + void *client_data); + static FLAC__StreamDecoderSeekStatus seek_callback( + const FLAC__StreamDecoder *decoder, + FLAC__uint64 absolute_byte_offset, + void *client_data); + static FLAC__StreamDecoderTellStatus tell_callback( + const FLAC__StreamDecoder *decoder, + FLAC__uint64 *absolute_byte_offset, + void *client_data); + static FLAC__StreamDecoderLengthStatus length_callback( + const FLAC__StreamDecoder *decoder, + FLAC__uint64 *stream_length, + void *client_data); + static FLAC__bool eof_callback( + const FLAC__StreamDecoder *decoder, + void *client_data); + static FLAC__StreamDecoderWriteStatus write_callback( + const FLAC__StreamDecoder *decoder, + const FLAC__Frame *frame, const FLAC__int32 * const buffer[], + void *client_data); + static void metadata_callback( + const FLAC__StreamDecoder *decoder, + const FLAC__StreamMetadata *metadata, + void *client_data); + static void error_callback( + const FLAC__StreamDecoder *decoder, + FLAC__StreamDecoderErrorStatus status, + void *client_data); + +}; + +// The FLAC parser calls our C++ static callbacks using C calling conventions, +// inside FLAC__stream_decoder_process_until_end_of_metadata +// and FLAC__stream_decoder_process_single. +// We immediately then call our corresponding C++ instance methods +// with the same parameter list, but discard redundant information. + +FLAC__StreamDecoderReadStatus FLACParser::read_callback( + const FLAC__StreamDecoder *decoder, FLAC__byte buffer[], + size_t *bytes, void *client_data) +{ + return ((FLACParser *) client_data)->readCallback(buffer, bytes); +} + +FLAC__StreamDecoderSeekStatus FLACParser::seek_callback( + const FLAC__StreamDecoder *decoder, + FLAC__uint64 absolute_byte_offset, void *client_data) +{ + return ((FLACParser *) client_data)->seekCallback(absolute_byte_offset); +} + +FLAC__StreamDecoderTellStatus FLACParser::tell_callback( + const FLAC__StreamDecoder *decoder, + FLAC__uint64 *absolute_byte_offset, void *client_data) +{ + return ((FLACParser *) client_data)->tellCallback(absolute_byte_offset); +} + +FLAC__StreamDecoderLengthStatus FLACParser::length_callback( + const FLAC__StreamDecoder *decoder, + FLAC__uint64 *stream_length, void *client_data) +{ + return ((FLACParser *) client_data)->lengthCallback(stream_length); +} + +FLAC__bool FLACParser::eof_callback( + const FLAC__StreamDecoder *decoder, void *client_data) +{ + return ((FLACParser *) client_data)->eofCallback(); +} + +FLAC__StreamDecoderWriteStatus FLACParser::write_callback( + const FLAC__StreamDecoder *decoder, const FLAC__Frame *frame, + const FLAC__int32 * const buffer[], void *client_data) +{ + return ((FLACParser *) client_data)->writeCallback(frame, buffer); +} + +void FLACParser::metadata_callback( + const FLAC__StreamDecoder *decoder, + const FLAC__StreamMetadata *metadata, void *client_data) +{ + ((FLACParser *) client_data)->metadataCallback(metadata); +} + +void FLACParser::error_callback( + const FLAC__StreamDecoder *decoder, + FLAC__StreamDecoderErrorStatus status, void *client_data) +{ + ((FLACParser *) client_data)->errorCallback(status); +} + +// These are the corresponding callbacks with C++ calling conventions + +FLAC__StreamDecoderReadStatus FLACParser::readCallback( + FLAC__byte buffer[], size_t *bytes) +{ + size_t requested = *bytes; + ssize_t actual = mDataSource->readAt(mCurrentPos, buffer, requested); + if (0 > actual) { + *bytes = 0; + return FLAC__STREAM_DECODER_READ_STATUS_ABORT; + } else if (0 == actual) { + *bytes = 0; + mEOF = true; + return FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM; + } else { + assert(actual <= requested); + *bytes = actual; + mCurrentPos += actual; + return FLAC__STREAM_DECODER_READ_STATUS_CONTINUE; + } +} + +FLAC__StreamDecoderSeekStatus FLACParser::seekCallback( + FLAC__uint64 absolute_byte_offset) +{ + mCurrentPos = absolute_byte_offset; + mEOF = false; + return FLAC__STREAM_DECODER_SEEK_STATUS_OK; +} + +FLAC__StreamDecoderTellStatus FLACParser::tellCallback( + FLAC__uint64 *absolute_byte_offset) +{ + *absolute_byte_offset = mCurrentPos; + return FLAC__STREAM_DECODER_TELL_STATUS_OK; +} + +FLAC__StreamDecoderLengthStatus FLACParser::lengthCallback( + FLAC__uint64 *stream_length) +{ + off64_t size; + if (OK == mDataSource->getSize(&size)) { + *stream_length = size; + return FLAC__STREAM_DECODER_LENGTH_STATUS_OK; + } else { + return FLAC__STREAM_DECODER_LENGTH_STATUS_UNSUPPORTED; + } +} + +FLAC__bool FLACParser::eofCallback() +{ + return mEOF; +} + +FLAC__StreamDecoderWriteStatus FLACParser::writeCallback( + const FLAC__Frame *frame, const FLAC__int32 * const buffer[]) +{ + if (mWriteRequested) { + mWriteRequested = false; + // FLAC parser doesn't free or realloc buffer until next frame or finish + mWriteHeader = frame->header; + mWriteBuffer = buffer; + mWriteCompleted = true; + return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE; + } else { + LOGE("FLACParser::writeCallback unexpected"); + return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT; + } +} + +void FLACParser::metadataCallback(const FLAC__StreamMetadata *metadata) +{ + switch (metadata->type) { + case FLAC__METADATA_TYPE_STREAMINFO: + if (!mStreamInfoValid) { + mStreamInfo = metadata->data.stream_info; + mStreamInfoValid = true; + } else { + LOGE("FLACParser::metadataCallback unexpected STREAMINFO"); + } + break; + case FLAC__METADATA_TYPE_VORBIS_COMMENT: + { + const FLAC__StreamMetadata_VorbisComment *vc; + vc = &metadata->data.vorbis_comment; + for (FLAC__uint32 i = 0; i < vc->num_comments; ++i) { + FLAC__StreamMetadata_VorbisComment_Entry *vce; + vce = &vc->comments[i]; + if (mFileMetadata != 0) { + parseVorbisComment(mFileMetadata, (const char *) vce->entry, + vce->length); + } + } + } + break; + case FLAC__METADATA_TYPE_PICTURE: + if (mFileMetadata != 0) { + const FLAC__StreamMetadata_Picture *p = &metadata->data.picture; + mFileMetadata->setData(kKeyAlbumArt, + MetaData::TYPE_NONE, p->data, p->data_length); + mFileMetadata->setCString(kKeyAlbumArtMIME, p->mime_type); + } + break; + default: + LOGW("FLACParser::metadataCallback unexpected type %u", metadata->type); + break; + } +} + +void FLACParser::errorCallback(FLAC__StreamDecoderErrorStatus status) +{ + LOGE("FLACParser::errorCallback status=%d", status); + mErrorStatus = status; +} + +// Copy samples from FLAC native 32-bit non-interleaved to 16-bit interleaved. +// These are candidates for optimization if needed. + +static void copyMono8(short *dst, const int *const *src, unsigned nSamples) +{ + for (unsigned i = 0; i < nSamples; ++i) { + *dst++ = src[0][i] << 8; + } +} + +static void copyStereo8(short *dst, const int *const *src, unsigned nSamples) +{ + for (unsigned i = 0; i < nSamples; ++i) { + *dst++ = src[0][i] << 8; + *dst++ = src[1][i] << 8; + } +} + +static void copyMono16(short *dst, const int *const *src, unsigned nSamples) +{ + for (unsigned i = 0; i < nSamples; ++i) { + *dst++ = src[0][i]; + } +} + +static void copyStereo16(short *dst, const int *const *src, unsigned nSamples) +{ + for (unsigned i = 0; i < nSamples; ++i) { + *dst++ = src[0][i]; + *dst++ = src[1][i]; + } +} + +// 24-bit versions should do dithering or noise-shaping, here or in AudioFlinger + +static void copyMono24(short *dst, const int *const *src, unsigned nSamples) +{ + for (unsigned i = 0; i < nSamples; ++i) { + *dst++ = src[0][i] >> 8; + } +} + +static void copyStereo24(short *dst, const int *const *src, unsigned nSamples) +{ + for (unsigned i = 0; i < nSamples; ++i) { + *dst++ = src[0][i] >> 8; + *dst++ = src[1][i] >> 8; + } +} + +static void copyTrespass(short *dst, const int *const *src, unsigned nSamples) +{ + TRESPASS(); +} + +// FLACParser + +FLACParser::FLACParser( + const sp<DataSource> &dataSource, + const sp<MetaData> &fileMetadata, + const sp<MetaData> &trackMetadata) + : mDataSource(dataSource), + mFileMetadata(fileMetadata), + mTrackMetadata(trackMetadata), + mInitCheck(false), + mMaxBufferSize(0), + mGroup(NULL), + mCopy(copyTrespass), + mDecoder(NULL), + mCurrentPos(0LL), + mEOF(false), + mStreamInfoValid(false), + mWriteRequested(false), + mWriteCompleted(false), + mWriteBuffer(NULL), + mErrorStatus((FLAC__StreamDecoderErrorStatus) -1) +{ + LOGV("FLACParser::FLACParser"); + memset(&mStreamInfo, 0, sizeof(mStreamInfo)); + memset(&mWriteHeader, 0, sizeof(mWriteHeader)); + mInitCheck = init(); +} + +FLACParser::~FLACParser() +{ + LOGV("FLACParser::~FLACParser"); + if (mDecoder != NULL) { + FLAC__stream_decoder_delete(mDecoder); + mDecoder = NULL; + } +} + +status_t FLACParser::init() +{ + // setup libFLAC parser + mDecoder = FLAC__stream_decoder_new(); + if (mDecoder == NULL) { + // The new should succeed, since probably all it does is a malloc + // that always succeeds in Android. But to avoid dependence on the + // libFLAC internals, we check and log here. + LOGE("new failed"); + return NO_INIT; + } + FLAC__stream_decoder_set_md5_checking(mDecoder, false); + FLAC__stream_decoder_set_metadata_ignore_all(mDecoder); + FLAC__stream_decoder_set_metadata_respond( + mDecoder, FLAC__METADATA_TYPE_STREAMINFO); + FLAC__stream_decoder_set_metadata_respond( + mDecoder, FLAC__METADATA_TYPE_PICTURE); + FLAC__stream_decoder_set_metadata_respond( + mDecoder, FLAC__METADATA_TYPE_VORBIS_COMMENT); + FLAC__StreamDecoderInitStatus initStatus; + initStatus = FLAC__stream_decoder_init_stream( + mDecoder, + read_callback, seek_callback, tell_callback, + length_callback, eof_callback, write_callback, + metadata_callback, error_callback, (void *) this); + if (initStatus != FLAC__STREAM_DECODER_INIT_STATUS_OK) { + // A failure here probably indicates a programming error and so is + // unlikely to happen. But we check and log here similarly to above. + LOGE("init_stream failed %d", initStatus); + return NO_INIT; + } + // parse all metadata + if (!FLAC__stream_decoder_process_until_end_of_metadata(mDecoder)) { + LOGE("end_of_metadata failed"); + return NO_INIT; + } + if (mStreamInfoValid) { + // check channel count + switch (getChannels()) { + case 1: + case 2: + break; + default: + LOGE("unsupported channel count %u", getChannels()); + return NO_INIT; + } + // check bit depth + switch (getBitsPerSample()) { + case 8: + case 16: + case 24: + break; + default: + LOGE("unsupported bits per sample %u", getBitsPerSample()); + return NO_INIT; + } + // check sample rate + switch (getSampleRate()) { + case 8000: + case 11025: + case 12000: + case 16000: + case 22050: + case 24000: + case 32000: + case 44100: + case 48000: + break; + default: + // 96000 would require a proper downsampler in AudioFlinger + LOGE("unsupported sample rate %u", getSampleRate()); + return NO_INIT; + } + // configure the appropriate copy function, defaulting to trespass + static const struct { + unsigned mChannels; + unsigned mBitsPerSample; + void (*mCopy)(short *dst, const int *const *src, unsigned nSamples); + } table[] = { + { 1, 8, copyMono8 }, + { 2, 8, copyStereo8 }, + { 1, 16, copyMono16 }, + { 2, 16, copyStereo16 }, + { 1, 24, copyMono24 }, + { 2, 24, copyStereo24 }, + }; + for (unsigned i = 0; i < sizeof(table)/sizeof(table[0]); ++i) { + if (table[i].mChannels == getChannels() && + table[i].mBitsPerSample == getBitsPerSample()) { + mCopy = table[i].mCopy; + break; + } + } + // populate track metadata + if (mTrackMetadata != 0) { + mTrackMetadata->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_RAW); + mTrackMetadata->setInt32(kKeyChannelCount, getChannels()); + mTrackMetadata->setInt32(kKeySampleRate, getSampleRate()); + // sample rate is non-zero, so division by zero not possible + mTrackMetadata->setInt64(kKeyDuration, + (getTotalSamples() * 1000000LL) / getSampleRate()); + } + } else { + LOGE("missing STREAMINFO"); + return NO_INIT; + } + if (mFileMetadata != 0) { + mFileMetadata->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_FLAC); + } + return OK; +} + +void FLACParser::allocateBuffers() +{ + CHECK(mGroup == NULL); + mGroup = new MediaBufferGroup; + mMaxBufferSize = getMaxBlockSize() * getChannels() * sizeof(short); + mGroup->add_buffer(new MediaBuffer(mMaxBufferSize)); +} + +void FLACParser::releaseBuffers() +{ + CHECK(mGroup != NULL); + delete mGroup; + mGroup = NULL; +} + +MediaBuffer *FLACParser::readBuffer(bool doSeek, FLAC__uint64 sample) +{ + mWriteRequested = true; + mWriteCompleted = false; + if (doSeek) { + // We implement the seek callback, so this works without explicit flush + if (!FLAC__stream_decoder_seek_absolute(mDecoder, sample)) { + LOGE("FLACParser::readBuffer seek to sample %llu failed", sample); + return NULL; + } + LOGV("FLACParser::readBuffer seek to sample %llu succeeded", sample); + } else { + if (!FLAC__stream_decoder_process_single(mDecoder)) { + LOGE("FLACParser::readBuffer process_single failed"); + return NULL; + } + } + if (!mWriteCompleted) { + LOGV("FLACParser::readBuffer write did not complete"); + return NULL; + } + // verify that block header keeps the promises made by STREAMINFO + unsigned blocksize = mWriteHeader.blocksize; + if (blocksize == 0 || blocksize > getMaxBlockSize()) { + LOGE("FLACParser::readBuffer write invalid blocksize %u", blocksize); + return NULL; + } + if (mWriteHeader.sample_rate != getSampleRate() || + mWriteHeader.channels != getChannels() || + mWriteHeader.bits_per_sample != getBitsPerSample()) { + LOGE("FLACParser::readBuffer write changed parameters mid-stream"); + } + // acquire a media buffer + CHECK(mGroup != NULL); + MediaBuffer *buffer; + status_t err = mGroup->acquire_buffer(&buffer); + if (err != OK) { + return NULL; + } + size_t bufferSize = blocksize * getChannels() * sizeof(short); + CHECK(bufferSize <= mMaxBufferSize); + short *data = (short *) buffer->data(); + buffer->set_range(0, bufferSize); + // copy PCM from FLAC write buffer to our media buffer, with interleaving + (*mCopy)(data, mWriteBuffer, blocksize); + // fill in buffer metadata + CHECK(mWriteHeader.number_type == FLAC__FRAME_NUMBER_TYPE_SAMPLE_NUMBER); + FLAC__uint64 sampleNumber = mWriteHeader.number.sample_number; + int64_t timeUs = (1000000LL * sampleNumber) / getSampleRate(); + buffer->meta_data()->setInt64(kKeyTime, timeUs); + buffer->meta_data()->setInt32(kKeyIsSyncFrame, 1); + return buffer; +} + +// FLACsource + +FLACSource::FLACSource( + const sp<DataSource> &dataSource, + const sp<MetaData> &trackMetadata) + : mDataSource(dataSource), + mTrackMetadata(trackMetadata), + mParser(0), + mInitCheck(false), + mStarted(false) +{ + LOGV("FLACSource::FLACSource"); + mInitCheck = init(); +} + +FLACSource::~FLACSource() +{ + LOGV("~FLACSource::FLACSource"); + if (mStarted) { + stop(); + } +} + +status_t FLACSource::start(MetaData *params) +{ + LOGV("FLACSource::start"); + + CHECK(!mStarted); + mParser->allocateBuffers(); + mStarted = true; + + return OK; +} + +status_t FLACSource::stop() +{ + LOGV("FLACSource::stop"); + + CHECK(mStarted); + mParser->releaseBuffers(); + mStarted = false; + + return OK; +} + +sp<MetaData> FLACSource::getFormat() +{ + return mTrackMetadata; +} + +status_t FLACSource::read( + MediaBuffer **outBuffer, const ReadOptions *options) +{ + MediaBuffer *buffer; + // process an optional seek request + int64_t seekTimeUs; + ReadOptions::SeekMode mode; + if ((NULL != options) && options->getSeekTo(&seekTimeUs, &mode)) { + FLAC__uint64 sample; + if (seekTimeUs <= 0LL) { + sample = 0LL; + } else { + // sample and total samples are both zero-based, and seek to EOF ok + sample = (seekTimeUs * mParser->getSampleRate()) / 1000000LL; + if (sample >= mParser->getTotalSamples()) { + sample = mParser->getTotalSamples(); + } + } + buffer = mParser->readBuffer(sample); + // otherwise read sequentially + } else { + buffer = mParser->readBuffer(); + } + *outBuffer = buffer; + return buffer != NULL ? (status_t) OK : (status_t) ERROR_END_OF_STREAM; +} + +status_t FLACSource::init() +{ + LOGV("FLACSource::init"); + // re-use the same track metadata passed into constructor from FLACExtractor + mParser = new FLACParser(mDataSource); + return mParser->initCheck(); +} + +// FLACExtractor + +FLACExtractor::FLACExtractor( + const sp<DataSource> &dataSource) + : mDataSource(dataSource), + mInitCheck(false) +{ + LOGV("FLACExtractor::FLACExtractor"); + mInitCheck = init(); +} + +FLACExtractor::~FLACExtractor() +{ + LOGV("~FLACExtractor::FLACExtractor"); +} + +size_t FLACExtractor::countTracks() +{ + return mInitCheck == OK ? 1 : 0; +} + +sp<MediaSource> FLACExtractor::getTrack(size_t index) +{ + if (mInitCheck != OK || index > 0) { + return NULL; + } + return new FLACSource(mDataSource, mTrackMetadata); +} + +sp<MetaData> FLACExtractor::getTrackMetaData( + size_t index, uint32_t flags) +{ + if (mInitCheck != OK || index > 0) { + return NULL; + } + return mTrackMetadata; +} + +status_t FLACExtractor::init() +{ + mFileMetadata = new MetaData; + mTrackMetadata = new MetaData; + // FLACParser will fill in the metadata for us + mParser = new FLACParser(mDataSource, mFileMetadata, mTrackMetadata); + return mParser->initCheck(); +} + +sp<MetaData> FLACExtractor::getMetaData() +{ + return mFileMetadata; +} + +// Sniffer + +bool SniffFLAC( + const sp<DataSource> &source, String8 *mimeType, float *confidence, + sp<AMessage> *) +{ + // first 4 is the signature word + // second 4 is the sizeof STREAMINFO + // 042 is the mandatory STREAMINFO + // no need to read rest of the header, as a premature EOF will be caught later + uint8_t header[4+4]; + if (source->readAt(0, header, sizeof(header)) != sizeof(header) + || memcmp("fLaC\0\0\0\042", header, 4+4)) + { + return false; + } + + *mimeType = MEDIA_MIMETYPE_AUDIO_FLAC; + *confidence = 0.5; + + return true; +} + +} // namespace android diff --git a/media/libstagefright/MPEG4Extractor.cpp b/media/libstagefright/MPEG4Extractor.cpp index e6e98aa..108a1d1 100644 --- a/media/libstagefright/MPEG4Extractor.cpp +++ b/media/libstagefright/MPEG4Extractor.cpp @@ -1074,6 +1074,20 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { break; } + case FOURCC('c', 't', 't', 's'): + { + 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'): { status_t err = diff --git a/media/libstagefright/MediaDefs.cpp b/media/libstagefright/MediaDefs.cpp index 4599fca..b50af89 100644 --- a/media/libstagefright/MediaDefs.cpp +++ b/media/libstagefright/MediaDefs.cpp @@ -35,6 +35,7 @@ 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_CONTAINER_MPEG4 = "video/mpeg4"; const char *MEDIA_MIMETYPE_CONTAINER_WAV = "audio/wav"; diff --git a/media/libstagefright/MediaExtractor.cpp b/media/libstagefright/MediaExtractor.cpp index d12ac64..08ed206 100644 --- a/media/libstagefright/MediaExtractor.cpp +++ b/media/libstagefright/MediaExtractor.cpp @@ -26,6 +26,7 @@ #include "include/MPEG2TSExtractor.h" #include "include/DRMExtractor.h" #include "include/WVMExtractor.h" +#include "include/FLACExtractor.h" #include "matroska/MatroskaExtractor.h" @@ -85,6 +86,8 @@ sp<MediaExtractor> MediaExtractor::Create( } else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AMR_NB) || !strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AMR_WB)) { return new AMRExtractor(source); + } else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_FLAC)) { + return new FLACExtractor(source); } else if (!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_WAV)) { return new WAVExtractor(source); } else if (!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_OGG)) { diff --git a/media/libstagefright/OMXCodec.cpp b/media/libstagefright/OMXCodec.cpp index 2a19b25..247ace7 100644 --- a/media/libstagefright/OMXCodec.cpp +++ b/media/libstagefright/OMXCodec.cpp @@ -3269,7 +3269,7 @@ status_t OMXCodec::start(MetaData *meta) { } status_t OMXCodec::stop() { - CODEC_LOGV("stop mState=%d", mState); + CODEC_LOGI("stop mState=%d", mState); Mutex::Autolock autoLock(mLock); diff --git a/media/libstagefright/OggExtractor.cpp b/media/libstagefright/OggExtractor.cpp index cf622af..0e51caf 100644 --- a/media/libstagefright/OggExtractor.cpp +++ b/media/libstagefright/OggExtractor.cpp @@ -114,7 +114,6 @@ private: MediaBuffer *buffer, uint8_t type); void parseFileMetaData(); - void extractAlbumArt(const void *data, size_t size); uint64_t findPrevGranulePosition(off64_t pageOffset); @@ -122,6 +121,9 @@ private: MyVorbisExtractor &operator=(const MyVorbisExtractor &); }; +static void extractAlbumArt( + const sp<MetaData> &fileMeta, const void *data, size_t size); + //////////////////////////////////////////////////////////////////////////////// OggSource::OggSource(const sp<OggExtractor> &extractor) @@ -654,6 +656,17 @@ void MyVorbisExtractor::parseFileMetaData() { mFileMeta = new MetaData; mFileMeta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_CONTAINER_OGG); + for (int i = 0; i < mVc.comments; ++i) { + const char *comment = mVc.user_comments[i]; + size_t commentLength = mVc.comment_lengths[i]; + parseVorbisComment(mFileMeta, comment, commentLength); + //LOGI("comment #%d: '%s'", i + 1, mVc.user_comments[i]); + } +} + +void parseVorbisComment( + const sp<MetaData> &fileMeta, const char *comment, size_t commentLength) +{ struct { const char *const mTag; uint32_t mKey; @@ -675,33 +688,25 @@ void MyVorbisExtractor::parseFileMetaData() { { "ANDROID_LOOP", kKeyAutoLoop }, }; - for (int i = 0; i < mVc.comments; ++i) { - const char *comment = mVc.user_comments[i]; - for (size_t j = 0; j < sizeof(kMap) / sizeof(kMap[0]); ++j) { size_t tagLen = strlen(kMap[j].mTag); if (!strncasecmp(kMap[j].mTag, comment, tagLen) && comment[tagLen] == '=') { if (kMap[j].mKey == kKeyAlbumArt) { extractAlbumArt( + fileMeta, &comment[tagLen + 1], - mVc.comment_lengths[i] - tagLen - 1); + commentLength - tagLen - 1); } else if (kMap[j].mKey == kKeyAutoLoop) { if (!strcasecmp(&comment[tagLen + 1], "true")) { - mFileMeta->setInt32(kKeyAutoLoop, true); + fileMeta->setInt32(kKeyAutoLoop, true); } } else { - mFileMeta->setCString(kMap[j].mKey, &comment[tagLen + 1]); + fileMeta->setCString(kMap[j].mKey, &comment[tagLen + 1]); } } } - } -#if 0 - for (int i = 0; i < mVc.comments; ++i) { - LOGI("comment #%d: '%s'", i + 1, mVc.user_comments[i]); - } -#endif } // The returned buffer should be free()d. @@ -769,7 +774,8 @@ static uint8_t *DecodeBase64(const char *s, size_t size, size_t *outSize) { return (uint8_t *)buffer; } -void MyVorbisExtractor::extractAlbumArt(const void *data, size_t size) { +static void extractAlbumArt( + const sp<MetaData> &fileMeta, const void *data, size_t size) { LOGV("extractAlbumArt from '%s'", (const char *)data); size_t flacSize; @@ -833,10 +839,10 @@ void MyVorbisExtractor::extractAlbumArt(const void *data, size_t size) { LOGV("got image data, %d trailing bytes", flacSize - 32 - typeLen - descLen - dataLen); - mFileMeta->setData( + fileMeta->setData( kKeyAlbumArt, 0, &flac[8 + typeLen + 4 + descLen + 20], dataLen); - mFileMeta->setCString(kKeyAlbumArtMIME, type); + fileMeta->setCString(kKeyAlbumArtMIME, type); exit: free(flac); diff --git a/media/libstagefright/SampleIterator.cpp b/media/libstagefright/SampleIterator.cpp index 062ab9b..c7b00b1 100644 --- a/media/libstagefright/SampleIterator.cpp +++ b/media/libstagefright/SampleIterator.cpp @@ -307,6 +307,8 @@ status_t SampleIterator::findSampleTime( *time = mTTSSampleTime + mTTSDuration * (sampleIndex - mTTSSampleIndex); + *time += mTable->getCompositionTimeOffset(sampleIndex); + return OK; } diff --git a/media/libstagefright/SampleTable.cpp b/media/libstagefright/SampleTable.cpp index a9163fc..423df70 100644 --- a/media/libstagefright/SampleTable.cpp +++ b/media/libstagefright/SampleTable.cpp @@ -53,6 +53,8 @@ SampleTable::SampleTable(const sp<DataSource> &source) mNumSampleSizes(0), mTimeToSampleCount(0), mTimeToSample(NULL), + mCompositionTimeDeltaEntries(NULL), + mNumCompositionTimeDeltaEntries(0), mSyncSampleOffset(-1), mNumSyncSamples(0), mSyncSamples(NULL), @@ -68,6 +70,9 @@ SampleTable::~SampleTable() { delete[] mSyncSamples; mSyncSamples = NULL; + delete[] mCompositionTimeDeltaEntries; + mCompositionTimeDeltaEntries = NULL; + delete[] mTimeToSample; mTimeToSample = NULL; @@ -260,6 +265,51 @@ status_t SampleTable::setTimeToSampleParams( return OK; } +status_t SampleTable::setCompositionTimeToSampleParams( + off64_t data_offset, size_t data_size) { + LOGI("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; + } + + if (U32_AT(header) != 0) { + // Expected version = 0, flags = 0. + return ERROR_MALFORMED; + } + + size_t numEntries = U32_AT(&header[4]); + + if (data_size != (numEntries + 1) * 8) { + return ERROR_MALFORMED; + } + + mNumCompositionTimeDeltaEntries = numEntries; + mCompositionTimeDeltaEntries = new uint32_t[2 * numEntries]; + + 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]); + } + + return OK; +} + status_t SampleTable::setSyncSampleParams(off64_t data_offset, size_t data_size) { if (mSyncSampleOffset >= 0 || data_size < 8) { return ERROR_MALFORMED; @@ -333,6 +383,8 @@ uint32_t abs_difference(uint32_t time1, uint32_t time2) { status_t SampleTable::findSampleAtTime( uint32_t req_time, uint32_t *sample_index, uint32_t flags) { + // XXX this currently uses decoding time, instead of composition time. + *sample_index = 0; Mutex::Autolock autoLock(mLock); @@ -607,5 +659,26 @@ status_t SampleTable::getMetaDataForSample( return OK; } +uint32_t SampleTable::getCompositionTimeOffset(uint32_t sampleIndex) const { + if (mCompositionTimeDeltaEntries == NULL) { + return 0; + } + + uint32_t curSample = 0; + for (size_t i = 0; i < mNumCompositionTimeDeltaEntries; ++i) { + uint32_t sampleCount = mCompositionTimeDeltaEntries[2 * i]; + + if (sampleIndex < curSample + sampleCount) { + uint32_t sampleDelta = mCompositionTimeDeltaEntries[2 * i + 1]; + + return sampleDelta; + } + + curSample += sampleCount; + } + + return 0; +} + } // namespace android diff --git a/media/libstagefright/StagefrightMediaScanner.cpp b/media/libstagefright/StagefrightMediaScanner.cpp index be3df7c..84f65ff 100644 --- a/media/libstagefright/StagefrightMediaScanner.cpp +++ b/media/libstagefright/StagefrightMediaScanner.cpp @@ -37,7 +37,7 @@ static bool FileHasAcceptableExtension(const char *extension) { ".mp3", ".mp4", ".m4a", ".3gp", ".3gpp", ".3g2", ".3gpp2", ".mpeg", ".ogg", ".mid", ".smf", ".imy", ".wma", ".aac", ".wav", ".amr", ".midi", ".xmf", ".rtttl", ".rtx", ".ota", - ".mkv", ".mka", ".webm", ".ts", ".fl" + ".mkv", ".mka", ".webm", ".ts", ".fl", ".flac" }; static const size_t kNumValidExtensions = sizeof(kValidExtensions) / sizeof(kValidExtensions[0]); diff --git a/media/libstagefright/include/AMRExtractor.h b/media/libstagefright/include/AMRExtractor.h index 1cdf36d..589d837 100644 --- a/media/libstagefright/include/AMRExtractor.h +++ b/media/libstagefright/include/AMRExtractor.h @@ -24,6 +24,7 @@ namespace android { struct AMessage; class String8; +#define OFFSET_TABLE_LEN 300 class AMRExtractor : public MediaExtractor { public: @@ -42,9 +43,11 @@ private: sp<DataSource> mDataSource; sp<MetaData> mMeta; status_t mInitCheck; - size_t mFrameSize; bool mIsWide; + off64_t mOffsetTable[OFFSET_TABLE_LEN]; //5 min + size_t mOffsetTableLength; + AMRExtractor(const AMRExtractor &); AMRExtractor &operator=(const AMRExtractor &); }; diff --git a/media/libstagefright/include/FLACExtractor.h b/media/libstagefright/include/FLACExtractor.h new file mode 100644 index 0000000..ded91c2 --- /dev/null +++ b/media/libstagefright/include/FLACExtractor.h @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FLAC_EXTRACTOR_H_ +#define FLAC_EXTRACTOR_H_ + +#include <media/stagefright/DataSource.h> +#include <media/stagefright/MediaExtractor.h> +#include <utils/String8.h> + +namespace android { + +class FLACParser; + +class FLACExtractor : public MediaExtractor { + +public: + // Extractor assumes ownership of source + FLACExtractor(const sp<DataSource> &source); + + virtual size_t countTracks(); + virtual sp<MediaSource> getTrack(size_t index); + virtual sp<MetaData> getTrackMetaData(size_t index, uint32_t flags); + + virtual sp<MetaData> getMetaData(); + +protected: + virtual ~FLACExtractor(); + +private: + sp<DataSource> mDataSource; + sp<FLACParser> mParser; + status_t mInitCheck; + sp<MetaData> mFileMetadata; + + // There is only one track + sp<MetaData> mTrackMetadata; + + status_t init(); + + FLACExtractor(const FLACExtractor &); + FLACExtractor &operator=(const FLACExtractor &); + +}; + +bool SniffFLAC(const sp<DataSource> &source, String8 *mimeType, + float *confidence, sp<AMessage> *); + +} // namespace android + +#endif // FLAC_EXTRACTOR_H_ diff --git a/media/libstagefright/include/OggExtractor.h b/media/libstagefright/include/OggExtractor.h index 1eda025..a41f681 100644 --- a/media/libstagefright/include/OggExtractor.h +++ b/media/libstagefright/include/OggExtractor.h @@ -57,6 +57,9 @@ bool SniffOgg( const sp<DataSource> &source, String8 *mimeType, float *confidence, sp<AMessage> *); +void parseVorbisComment( + const sp<MetaData> &fileMeta, const char *comment, size_t commentLength); + } // namespace android #endif // OGG_EXTRACTOR_H_ diff --git a/media/libstagefright/include/SampleTable.h b/media/libstagefright/include/SampleTable.h index c5e8136..2f95de9 100644 --- a/media/libstagefright/include/SampleTable.h +++ b/media/libstagefright/include/SampleTable.h @@ -46,6 +46,9 @@ public: 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); //////////////////////////////////////////////////////////////////////////// @@ -104,6 +107,9 @@ private: uint32_t mTimeToSampleCount; uint32_t *mTimeToSample; + uint32_t *mCompositionTimeDeltaEntries; + size_t mNumCompositionTimeDeltaEntries; + off64_t mSyncSampleOffset; uint32_t mNumSyncSamples; uint32_t *mSyncSamples; @@ -122,6 +128,8 @@ private: status_t getSampleSize_l(uint32_t sample_index, size_t *sample_size); + uint32_t getCompositionTimeOffset(uint32_t sampleIndex) const; + SampleTable(const SampleTable &); SampleTable &operator=(const SampleTable &); }; diff --git a/media/mtp/Android.mk b/media/mtp/Android.mk index 70dc340..c25285e 100644 --- a/media/mtp/Android.mk +++ b/media/mtp/Android.mk @@ -21,7 +21,6 @@ ifneq ($(TARGET_SIMULATOR),true) include $(CLEAR_VARS) LOCAL_SRC_FILES:= \ - MtpClient.cpp \ MtpDataPacket.cpp \ MtpDebug.cpp \ MtpDevice.cpp \ @@ -53,7 +52,6 @@ ifeq ($(HOST_OS),linux) include $(CLEAR_VARS) LOCAL_SRC_FILES:= \ - MtpClient.cpp \ MtpDataPacket.cpp \ MtpDebug.cpp \ MtpDevice.cpp \ diff --git a/media/mtp/MtpClient.cpp b/media/mtp/MtpClient.cpp deleted file mode 100644 index c830540..0000000 --- a/media/mtp/MtpClient.cpp +++ /dev/null @@ -1,251 +0,0 @@ -/* - * 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_TAG "MtpClient" - -#include "MtpDebug.h" -#include "MtpClient.h" -#include "MtpDevice.h" - -#include <stdio.h> -#include <stdlib.h> -#include <sys/types.h> -#include <sys/ioctl.h> -#include <sys/stat.h> -#include <fcntl.h> -#include <errno.h> - -#include <usbhost/usbhost.h> - -struct usb_device; - -namespace android { - -static bool isMtpDevice(uint16_t vendor, uint16_t product) { - // Sandisk Sansa Fuze - if (vendor == 0x0781 && product == 0x74c2) - return true; - // Samsung YP-Z5 - if (vendor == 0x04e8 && product == 0x503c) - return true; - return false; -} - -class MtpClientThread : public Thread { -private: - MtpClient* mClient; - -public: - MtpClientThread(MtpClient* client) - : mClient(client) - { - } - - virtual bool threadLoop() { - return mClient->threadLoop(); - } -}; - - -MtpClient::MtpClient() - : mThread(NULL), - mUsbHostContext(NULL), - mDone(false) -{ -} - -MtpClient::~MtpClient() { - usb_host_cleanup(mUsbHostContext); -} - -bool MtpClient::start() { - Mutex::Autolock autoLock(mMutex); - - if (mThread) - return true; - - mUsbHostContext = usb_host_init(); - if (!mUsbHostContext) - return false; - - mThread = new MtpClientThread(this); - mThread->run("MtpClientThread"); - // wait for the thread to do initial device discovery before returning - mThreadStartCondition.wait(mMutex); - - return true; -} - -void MtpClient::stop() { - mDone = true; -} - -MtpDevice* MtpClient::getDevice(int id) { - for (int i = 0; i < mDeviceList.size(); i++) { - MtpDevice* device = mDeviceList[i]; - if (device->getID() == id) - return device; - } - return NULL; -} - -bool MtpClient::usbDeviceAdded(const char *devname) { - struct usb_descriptor_header* desc; - struct usb_descriptor_iter iter; - - struct usb_device *device = usb_device_open(devname); - if (!device) { - LOGE("usb_device_open failed\n"); - return mDone; - } - - usb_descriptor_iter_init(device, &iter); - - while ((desc = usb_descriptor_iter_next(&iter)) != NULL) { - if (desc->bDescriptorType == USB_DT_INTERFACE) { - struct usb_interface_descriptor *interface = (struct usb_interface_descriptor *)desc; - - if (interface->bInterfaceClass == USB_CLASS_STILL_IMAGE && - interface->bInterfaceSubClass == 1 && // Still Image Capture - interface->bInterfaceProtocol == 1) // Picture Transfer Protocol (PIMA 15470) - { - LOGD("Found camera: \"%s\" \"%s\"\n", usb_device_get_manufacturer_name(device), - usb_device_get_product_name(device)); - } else if (interface->bInterfaceClass == 0xFF && - interface->bInterfaceSubClass == 0xFF && - interface->bInterfaceProtocol == 0) { - char* interfaceName = usb_device_get_string(device, interface->iInterface); - if (!interfaceName || strcmp(interfaceName, "MTP")) - continue; - // Looks like an android style MTP device - LOGD("Found MTP device: \"%s\" \"%s\"\n", usb_device_get_manufacturer_name(device), - usb_device_get_product_name(device)); - } else { - // look for special cased devices based on vendor/product ID - // we are doing this mainly for testing purposes - uint16_t vendor = usb_device_get_vendor_id(device); - uint16_t product = usb_device_get_product_id(device); - if (!isMtpDevice(vendor, product)) { - // not an MTP or PTP device - continue; - } - // request MTP OS string and descriptor - // some music players need to see this before entering MTP mode. - char buffer[256]; - memset(buffer, 0, sizeof(buffer)); - int ret = usb_device_send_control(device, - USB_DIR_IN|USB_RECIP_DEVICE|USB_TYPE_STANDARD, - USB_REQ_GET_DESCRIPTOR, (USB_DT_STRING << 8) | 0xEE, - 0, sizeof(buffer), buffer); - printf("usb_device_send_control returned %d errno: %d\n", ret, errno); - if (ret > 0) { - printf("got MTP string %s\n", buffer); - ret = usb_device_send_control(device, - USB_DIR_IN|USB_RECIP_DEVICE|USB_TYPE_VENDOR, 1, - 0, 4, sizeof(buffer), buffer); - printf("OS descriptor got %d\n", ret); - } else { - printf("no MTP string\n"); - } - } - - // if we got here, then we have a likely MTP or PTP device - - // interface should be followed by three endpoints - struct usb_endpoint_descriptor *ep; - struct usb_endpoint_descriptor *ep_in_desc = NULL; - struct usb_endpoint_descriptor *ep_out_desc = NULL; - struct usb_endpoint_descriptor *ep_intr_desc = NULL; - for (int i = 0; i < 3; i++) { - ep = (struct usb_endpoint_descriptor *)usb_descriptor_iter_next(&iter); - if (!ep || ep->bDescriptorType != USB_DT_ENDPOINT) { - LOGE("endpoints not found\n"); - return mDone; - } - if (ep->bmAttributes == USB_ENDPOINT_XFER_BULK) { - if (ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK) - ep_in_desc = ep; - else - ep_out_desc = ep; - } else if (ep->bmAttributes == USB_ENDPOINT_XFER_INT && - ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK) { - ep_intr_desc = ep; - } - } - if (!ep_in_desc || !ep_out_desc || !ep_intr_desc) { - LOGE("endpoints not found\n"); - return mDone; - } - - if (usb_device_claim_interface(device, interface->bInterfaceNumber)) { - LOGE("usb_device_claim_interface failed errno: %d\n", errno); - return mDone; - } - - MtpDevice* mtpDevice = new MtpDevice(device, interface->bInterfaceNumber, - ep_in_desc, ep_out_desc, ep_intr_desc); - mDeviceList.add(mtpDevice); - mtpDevice->initialize(); - deviceAdded(mtpDevice); - return mDone; - } - } - - usb_device_close(device); - return mDone; -} - -bool MtpClient::usbDeviceRemoved(const char *devname) { - for (int i = 0; i < mDeviceList.size(); i++) { - MtpDevice* device = mDeviceList[i]; - if (!strcmp(devname, device->getDeviceName())) { - deviceRemoved(device); - mDeviceList.removeAt(i); - delete device; - LOGD("Camera removed!\n"); - break; - } - } - return mDone; -} - -bool MtpClient::usbDiscoveryDone() { - Mutex::Autolock autoLock(mMutex); - mThreadStartCondition.signal(); - return mDone; -} - -bool MtpClient::threadLoop() { - usb_host_run(mUsbHostContext, usb_device_added, usb_device_removed, usb_discovery_done, this); - return false; -} - -int MtpClient::usb_device_added(const char *devname, void* client_data) { - LOGD("usb_device_added %s\n", devname); - return ((MtpClient *)client_data)->usbDeviceAdded(devname); -} - -int MtpClient::usb_device_removed(const char *devname, void* client_data) { - LOGD("usb_device_removed %s\n", devname); - return ((MtpClient *)client_data)->usbDeviceRemoved(devname); -} - -int MtpClient::usb_discovery_done(void* client_data) { - LOGD("usb_discovery_done\n"); - return ((MtpClient *)client_data)->usbDiscoveryDone(); -} - -} // namespace android diff --git a/media/mtp/MtpClient.h b/media/mtp/MtpClient.h deleted file mode 100644 index fa5c527..0000000 --- a/media/mtp/MtpClient.h +++ /dev/null @@ -1,68 +0,0 @@ -/* - * 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 _MTP_CLIENT_H -#define _MTP_CLIENT_H - -#include "MtpTypes.h" - -#include <utils/threads.h> - -struct usb_host_context; - -namespace android { - -class MtpClientThread; - -class MtpClient { -private: - MtpDeviceList mDeviceList; - MtpClientThread* mThread; - Condition mThreadStartCondition; - Mutex mMutex; - struct usb_host_context* mUsbHostContext; - bool mDone; - -public: - MtpClient(); - virtual ~MtpClient(); - - bool start(); - void stop(); - - inline MtpDeviceList& getDeviceList() { return mDeviceList; } - MtpDevice* getDevice(int id); - - - virtual void deviceAdded(MtpDevice *device) = 0; - virtual void deviceRemoved(MtpDevice *device) = 0; - -private: - // these return true if we should stop monitoring USB and clean up - bool usbDeviceAdded(const char *devname); - bool usbDeviceRemoved(const char *devname); - bool usbDiscoveryDone(); - - friend class MtpClientThread; - bool threadLoop(); - static int usb_device_added(const char *devname, void* client_data); - static int usb_device_removed(const char *devname, void* client_data); - static int usb_discovery_done(void* client_data); -}; - -}; // namespace android - -#endif // _MTP_CLIENT_H diff --git a/media/mtp/MtpDevice.cpp b/media/mtp/MtpDevice.cpp index d22c72f..fb1b073 100644 --- a/media/mtp/MtpDevice.cpp +++ b/media/mtp/MtpDevice.cpp @@ -38,6 +38,136 @@ namespace android { +static bool isMtpDevice(uint16_t vendor, uint16_t product) { + // Sandisk Sansa Fuze + if (vendor == 0x0781 && product == 0x74c2) + return true; + // Samsung YP-Z5 + if (vendor == 0x04e8 && product == 0x503c) + return true; + return false; +} + +MtpDevice* MtpDevice::open(const char* deviceName, int fd) { + struct usb_device *device = usb_device_new(deviceName, fd); + if (!device) { + LOGE("usb_device_new failed for %s", deviceName); + return NULL; + } + + struct usb_descriptor_header* desc; + struct usb_descriptor_iter iter; + + usb_descriptor_iter_init(device, &iter); + + while ((desc = usb_descriptor_iter_next(&iter)) != NULL) { + if (desc->bDescriptorType == USB_DT_INTERFACE) { + struct usb_interface_descriptor *interface = (struct usb_interface_descriptor *)desc; + + if (interface->bInterfaceClass == USB_CLASS_STILL_IMAGE && + interface->bInterfaceSubClass == 1 && // Still Image Capture + interface->bInterfaceProtocol == 1) // Picture Transfer Protocol (PIMA 15470) + { + char* manufacturerName = usb_device_get_manufacturer_name(device); + char* productName = usb_device_get_product_name(device); + LOGD("Found camera: \"%s\" \"%s\"\n", manufacturerName, productName); + free(manufacturerName); + free(productName); + } else if (interface->bInterfaceClass == 0xFF && + interface->bInterfaceSubClass == 0xFF && + interface->bInterfaceProtocol == 0) { + char* interfaceName = usb_device_get_string(device, interface->iInterface); + if (!interfaceName) { + continue; + } else if (strcmp(interfaceName, "MTP")) { + free(interfaceName); + continue; + } + free(interfaceName); + + // Looks like an android style MTP device + char* manufacturerName = usb_device_get_manufacturer_name(device); + char* productName = usb_device_get_product_name(device); + LOGD("Found MTP device: \"%s\" \"%s\"\n", manufacturerName, productName); + free(manufacturerName); + free(productName); + } else { + // look for special cased devices based on vendor/product ID + // we are doing this mainly for testing purposes + uint16_t vendor = usb_device_get_vendor_id(device); + uint16_t product = usb_device_get_product_id(device); + if (!isMtpDevice(vendor, product)) { + // not an MTP or PTP device + continue; + } + // request MTP OS string and descriptor + // some music players need to see this before entering MTP mode. + char buffer[256]; + memset(buffer, 0, sizeof(buffer)); + int ret = usb_device_control_transfer(device, + USB_DIR_IN|USB_RECIP_DEVICE|USB_TYPE_STANDARD, + USB_REQ_GET_DESCRIPTOR, (USB_DT_STRING << 8) | 0xEE, + 0, buffer, sizeof(buffer), 0); + printf("usb_device_control_transfer returned %d errno: %d\n", ret, errno); + if (ret > 0) { + printf("got MTP string %s\n", buffer); + ret = usb_device_control_transfer(device, + USB_DIR_IN|USB_RECIP_DEVICE|USB_TYPE_VENDOR, 1, + 0, 4, buffer, sizeof(buffer), 0); + printf("OS descriptor got %d\n", ret); + } else { + printf("no MTP string\n"); + } + } + + // if we got here, then we have a likely MTP or PTP device + + // interface should be followed by three endpoints + struct usb_endpoint_descriptor *ep; + struct usb_endpoint_descriptor *ep_in_desc = NULL; + struct usb_endpoint_descriptor *ep_out_desc = NULL; + struct usb_endpoint_descriptor *ep_intr_desc = NULL; + for (int i = 0; i < 3; i++) { + ep = (struct usb_endpoint_descriptor *)usb_descriptor_iter_next(&iter); + if (!ep || ep->bDescriptorType != USB_DT_ENDPOINT) { + LOGE("endpoints not found\n"); + usb_device_close(device); + return NULL; + } + if (ep->bmAttributes == USB_ENDPOINT_XFER_BULK) { + if (ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK) + ep_in_desc = ep; + else + ep_out_desc = ep; + } else if (ep->bmAttributes == USB_ENDPOINT_XFER_INT && + ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK) { + ep_intr_desc = ep; + } + } + if (!ep_in_desc || !ep_out_desc || !ep_intr_desc) { + LOGE("endpoints not found\n"); + usb_device_close(device); + return NULL; + } + + if (usb_device_claim_interface(device, interface->bInterfaceNumber)) { + LOGE("usb_device_claim_interface failed errno: %d\n", errno); + usb_device_close(device); + return NULL; + } + + MtpDevice* mtpDevice = new MtpDevice(device, interface->bInterfaceNumber, + ep_in_desc, ep_out_desc, ep_intr_desc); + mtpDevice->initialize(); + return mtpDevice; + } + } + + usb_device_close(device); + LOGE("device not found"); + return NULL; +} + MtpDevice::MtpDevice(struct usb_device* device, int interface, const struct usb_endpoint_descriptor *ep_in, const struct usb_endpoint_descriptor *ep_out, @@ -49,7 +179,6 @@ MtpDevice::MtpDevice(struct usb_device* device, int interface, mRequestOut(NULL), mRequestIntr(NULL), mDeviceInfo(NULL), - mID(usb_device_get_unique_id(device)), mSessionID(0), mTransactionID(0), mReceivedResponse(false) @@ -106,6 +235,7 @@ void MtpDevice::print() { MtpProperty* property = getDevicePropDesc(propCode); if (property) { property->print(); + delete property; } } } @@ -122,11 +252,13 @@ void MtpDevice::print() { for (int j = 0; j < props->size(); j++) { MtpObjectProperty prop = (*props)[j]; MtpProperty* property = getObjectPropDesc(prop, format); - if (property) + if (property) { property->print(); - else + delete property; + } else { LOGE("could not fetch property: %s", MtpDebug::getObjectPropCodeName(prop)); + } } } } @@ -362,18 +494,24 @@ bool MtpDevice::deleteObject(MtpObjectHandle handle) { MtpObjectHandle MtpDevice::getParent(MtpObjectHandle handle) { MtpObjectInfo* info = getObjectInfo(handle); - if (info) - return info->mParent; - else + if (info) { + MtpObjectHandle parent = info->mParent; + delete info; + return parent; + } else { return -1; + } } MtpObjectHandle MtpDevice::getStorageID(MtpObjectHandle handle) { MtpObjectInfo* info = getObjectInfo(handle); - if (info) - return info->mStorageID; - else + if (info) { + MtpObjectHandle storageId = info->mStorageID; + delete info; + return storageId; + } else { return -1; + } } MtpObjectPropertyList* MtpDevice::getObjectPropsSupported(MtpObjectFormat format) { @@ -430,6 +568,98 @@ MtpProperty* MtpDevice::getObjectPropDesc(MtpObjectProperty code, MtpObjectForma return NULL; } +bool MtpDevice::readObject(MtpObjectHandle handle, + bool (* callback)(void* data, int offset, int length, void* clientData), + int objectSize, void* clientData) { + Mutex::Autolock autoLock(mMutex); + bool result = false; + + mRequest.reset(); + mRequest.setParameter(1, handle); + if (sendRequest(MTP_OPERATION_GET_OBJECT) + && mData.readDataHeader(mRequestIn1)) { + uint32_t length = mData.getContainerLength(); + if (length - MTP_CONTAINER_HEADER_SIZE != objectSize) { + LOGE("readObject error objectSize: %d, length: %d", + objectSize, length); + goto fail; + } + length -= MTP_CONTAINER_HEADER_SIZE; + uint32_t remaining = length; + int offset = 0; + + int initialDataLength = 0; + void* initialData = mData.getData(initialDataLength); + if (initialData) { + if (initialDataLength > 0) { + if (!callback(initialData, 0, initialDataLength, clientData)) + goto fail; + remaining -= initialDataLength; + offset += initialDataLength; + } + free(initialData); + } + + // USB reads greater than 16K don't work + char buffer1[16384], buffer2[16384]; + mRequestIn1->buffer = buffer1; + mRequestIn2->buffer = buffer2; + struct usb_request* req = mRequestIn1; + void* writeBuffer = NULL; + int writeLength = 0; + + while (remaining > 0 || writeBuffer) { + if (remaining > 0) { + // queue up a read request + req->buffer_length = (remaining > sizeof(buffer1) ? sizeof(buffer1) : remaining); + if (mData.readDataAsync(req)) { + LOGE("readDataAsync failed"); + goto fail; + } + } else { + req = NULL; + } + + if (writeBuffer) { + // write previous buffer + if (!callback(writeBuffer, offset, writeLength, clientData)) { + LOGE("write failed"); + // wait for pending read before failing + if (req) + mData.readDataWait(mDevice); + goto fail; + } + offset += writeLength; + writeBuffer = NULL; + } + + // wait for read to complete + if (req) { + int read = mData.readDataWait(mDevice); + if (read < 0) + goto fail; + + if (read > 0) { + writeBuffer = req->buffer; + writeLength = read; + remaining -= read; + req = (req == mRequestIn1 ? mRequestIn2 : mRequestIn1); + } else { + writeBuffer = NULL; + } + } + } + + MtpResponseCode response = readResponse(); + if (response == MTP_RESPONSE_OK) + result = true; + } + +fail: + return result; +} + + // reads the object's data and writes it to the specified file path bool MtpDevice::readObject(MtpObjectHandle handle, const char* destPath, int group, int perm) { LOGD("readObject: %s", destPath); @@ -462,8 +692,10 @@ bool MtpDevice::readObject(MtpObjectHandle handle, const char* destPath, int gro void* initialData = mData.getData(initialDataLength); if (initialData) { if (initialDataLength > 0) { - if (write(fd, initialData, initialDataLength) != initialDataLength) + if (write(fd, initialData, initialDataLength) != initialDataLength) { + free(initialData); goto fail; + } remaining -= initialDataLength; } free(initialData); @@ -507,10 +739,14 @@ bool MtpDevice::readObject(MtpObjectHandle handle, const char* destPath, int gro if (read < 0) goto fail; - writeBuffer = req->buffer; - writeLength = read; - remaining -= read; - req = (req == mRequestIn1 ? mRequestIn2 : mRequestIn1); + if (read > 0) { + writeBuffer = req->buffer; + writeLength = read; + remaining -= read; + req = (req == mRequestIn1 ? mRequestIn2 : mRequestIn1); + } else { + writeBuffer = NULL; + } } } diff --git a/media/mtp/MtpDevice.h b/media/mtp/MtpDevice.h index d0a0fb3..b69203e 100644 --- a/media/mtp/MtpDevice.h +++ b/media/mtp/MtpDevice.h @@ -45,9 +45,6 @@ private: MtpDeviceInfo* mDeviceInfo; MtpPropertyList mDeviceProperties; - // a unique ID for the device - int mID; - // current session ID MtpSessionID mSessionID; // current transaction ID @@ -67,9 +64,10 @@ public: const struct usb_endpoint_descriptor *ep_in, const struct usb_endpoint_descriptor *ep_out, const struct usb_endpoint_descriptor *ep_intr); - virtual ~MtpDevice(); - inline int getID() const { return mID; } + static MtpDevice* open(const char* deviceName, int fd); + + virtual ~MtpDevice(); void initialize(); void close(); @@ -97,7 +95,11 @@ public: MtpProperty* getDevicePropDesc(MtpDeviceProperty code); MtpProperty* getObjectPropDesc(MtpObjectProperty code, MtpObjectFormat format); - bool readObject(MtpObjectHandle handle, const char* destPath, int group, + bool readObject(MtpObjectHandle handle, + bool (* callback)(void* data, int offset, + int length, void* clientData), + int objectSize, void* clientData); + bool readObject(MtpObjectHandle handle, const char* destPath, int group, int perm); private: diff --git a/media/mtp/MtpServer.cpp b/media/mtp/MtpServer.cpp index be004d2..853a5af 100644 --- a/media/mtp/MtpServer.cpp +++ b/media/mtp/MtpServer.cpp @@ -343,8 +343,9 @@ MtpResponseCode MtpServer::doGetDeviceInfo() { mData.putAUInt16(deviceProperties); // Device Properties Supported mData.putAUInt16(captureFormats); // Capture Formats mData.putAUInt16(playbackFormats); // Playback Formats - // FIXME - string.set("Google, Inc."); + + property_get("ro.product.manufacturer", prop_value, "unknown manufacturer"); + string.set(prop_value); mData.putString(string); // Manufacturer property_get("ro.product.model", prop_value, "MTP Device"); |