diff options
61 files changed, 1053 insertions, 153 deletions
diff --git a/cmds/stagefright/Android.mk b/cmds/stagefright/Android.mk index e41b666..8b55c8f 100644 --- a/cmds/stagefright/Android.mk +++ b/cmds/stagefright/Android.mk @@ -7,11 +7,10 @@ LOCAL_SRC_FILES:= \ SineSource.cpp LOCAL_SHARED_LIBRARIES := \ - libstagefright libmedia libutils libbinder libstagefright_foundation \ + libstagefright libmedia libmedia_native libutils libbinder libstagefright_foundation \ libskia libgui LOCAL_C_INCLUDES:= \ - $(JNI_H_INCLUDE) \ frameworks/base/media/libstagefright \ frameworks/base/media/libstagefright/include \ $(TOP)/frameworks/native/include/media/openmax \ @@ -38,7 +37,6 @@ LOCAL_SHARED_LIBRARIES := \ libstagefright liblog libutils libbinder libstagefright_foundation LOCAL_C_INCLUDES:= \ - $(JNI_H_INCLUDE) \ frameworks/base/media/libstagefright \ $(TOP)/frameworks/native/include/media/openmax @@ -62,7 +60,6 @@ LOCAL_SHARED_LIBRARIES := \ libstagefright liblog libutils libbinder libstagefright_foundation LOCAL_C_INCLUDES:= \ - $(JNI_H_INCLUDE) \ frameworks/base/media/libstagefright \ $(TOP)/frameworks/native/include/media/openmax @@ -87,7 +84,6 @@ LOCAL_SHARED_LIBRARIES := \ libstagefright liblog libutils libbinder libstagefright_foundation LOCAL_C_INCLUDES:= \ - $(JNI_H_INCLUDE) \ frameworks/base/media/libstagefright \ $(TOP)/frameworks/native/include/media/openmax @@ -108,10 +104,9 @@ LOCAL_SRC_FILES:= \ LOCAL_SHARED_LIBRARIES := \ libstagefright liblog libutils libbinder libgui \ - libstagefright_foundation libmedia + libstagefright_foundation libmedia libmedia_native LOCAL_C_INCLUDES:= \ - $(JNI_H_INCLUDE) \ frameworks/base/media/libstagefright \ $(TOP)/frameworks/native/include/media/openmax @@ -132,10 +127,9 @@ LOCAL_SRC_FILES:= \ LOCAL_SHARED_LIBRARIES := \ libstagefright liblog libutils libbinder libstagefright_foundation \ - libmedia libgui libcutils libui + libmedia libmedia_native libgui libcutils libui LOCAL_C_INCLUDES:= \ - $(JNI_H_INCLUDE) \ frameworks/base/media/libstagefright \ $(TOP)/frameworks/native/include/media/openmax @@ -157,10 +151,9 @@ LOCAL_SRC_FILES:= \ LOCAL_SHARED_LIBRARIES := \ libstagefright liblog libutils libbinder libstagefright_foundation \ - libmedia libgui libcutils libui + libmedia libmedia_native libgui libcutils libui LOCAL_C_INCLUDES:= \ - $(JNI_H_INCLUDE) \ frameworks/base/media/libstagefright \ $(TOP)/frameworks/native/include/media/openmax diff --git a/cmds/stagefright/sf2.cpp b/cmds/stagefright/sf2.cpp index e47cdc0..64df5d1 100644 --- a/cmds/stagefright/sf2.cpp +++ b/cmds/stagefright/sf2.cpp @@ -176,8 +176,9 @@ protected: } onDrainThisBuffer(msg); - } else if (what == ACodec::kWhatEOS) { - printf("$\n"); + } else if (what == ACodec::kWhatEOS + || what == ACodec::kWhatError) { + printf((what == ACodec::kWhatEOS) ? "$\n" : "E\n"); int64_t delayUs = ALooper::GetNowUs() - mStartTimeUs; @@ -412,7 +413,8 @@ private: sp<AMessage> reply; CHECK(msg->findMessage("reply", &reply)); - if (mSeekState == SEEK_FLUSHING) { + if (mSource == NULL || mSeekState == SEEK_FLUSHING) { + reply->setInt32("err", ERROR_END_OF_STREAM); reply->post(); return; } diff --git a/include/common_time/local_clock.h b/include/common_time/local_clock.h index 845d1c2..384c3de 100644 --- a/include/common_time/local_clock.h +++ b/include/common_time/local_clock.h @@ -28,7 +28,7 @@ namespace android { class LocalClock { public: - LocalClock(); + LocalClock(); bool initCheck(); diff --git a/include/media/AudioTrack.h b/include/media/AudioTrack.h index 552e829..7d5d772 100644 --- a/include/media/AudioTrack.h +++ b/include/media/AudioTrack.h @@ -169,7 +169,7 @@ public: callback_t cbf = 0, void* user = 0, int notificationFrames = 0, - int sessionId = 0); + int sessionId = 0); /* Creates an audio track and registers it with AudioFlinger. With this constructor, * the PCM data to be rendered by AudioTrack is passed in a shared memory buffer @@ -215,7 +215,7 @@ public: int notificationFrames = 0, const sp<IMemory>& sharedBuffer = 0, bool threadCanCallJava = false, - int sessionId = 0); + int sessionId = 0); /* Result of constructing the AudioTrack. This must be checked @@ -468,6 +468,7 @@ protected: // body of AudioTrackThread::threadLoop() bool processAudioBuffer(const sp<AudioTrackThread>& thread); + status_t createTrack_l(audio_stream_type_t streamType, uint32_t sampleRate, audio_format_t format, @@ -475,8 +476,7 @@ protected: int frameCount, audio_policy_output_flags_t flags, const sp<IMemory>& sharedBuffer, - audio_io_handle_t output, - bool enforceFrameCount); + audio_io_handle_t output); void flush_l(); status_t setLoop_l(uint32_t loopStart, uint32_t loopEnd, int loopCount); audio_io_handle_t getOutput_l(); diff --git a/include/media/IOMX.h b/include/media/IOMX.h index a295e9a..be1b2fc 100644 --- a/include/media/IOMX.h +++ b/include/media/IOMX.h @@ -26,8 +26,6 @@ #include <OMX_Core.h> #include <OMX_Video.h> -#include "jni.h" - namespace android { class IMemory; diff --git a/include/media/stagefright/MetaData.h b/include/media/stagefright/MetaData.h index 8b4b8ed..c3ccb56 100644 --- a/include/media/stagefright/MetaData.h +++ b/include/media/stagefright/MetaData.h @@ -24,6 +24,7 @@ #include <utils/RefBase.h> #include <utils/KeyedVector.h> +#include <utils/String8.h> namespace android { @@ -70,6 +71,8 @@ enum { kKeyThumbnailTime = 'thbT', // int64_t (usecs) kKeyTrackID = 'trID', kKeyIsDRM = 'idrm', // int32_t (bool) + kKeyEncoderDelay = 'encd', // int32_t (frames) + kKeyEncoderPadding = 'encp', // int32_t (frames) kKeyAlbum = 'albu', // cstring kKeyArtist = 'arti', // cstring @@ -178,6 +181,8 @@ public: bool findData(uint32_t key, uint32_t *type, const void **data, size_t *size) const; + void dumpToLog() const; + protected: virtual ~MetaData(); @@ -192,6 +197,7 @@ private: 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; diff --git a/include/media/stagefright/OMXCodec.h b/include/media/stagefright/OMXCodec.h index 392ea87..7c612ba 100644 --- a/include/media/stagefright/OMXCodec.h +++ b/include/media/stagefright/OMXCodec.h @@ -30,6 +30,7 @@ struct MediaCodecList; class MemoryDealer; struct OMXCodecObserver; struct CodecProfileLevel; +class SkipCutBuffer; struct OMXCodec : public MediaSource, public MediaBufferObserver { @@ -201,6 +202,7 @@ private: ReadOptions::SeekMode mSeekMode; int64_t mTargetTimeUs; bool mOutputPortSettingsChangedPending; + SkipCutBuffer *mSkipCutBuffer; MediaBuffer *mLeftOverBuffer; @@ -378,6 +380,7 @@ status_t QueryCodecs( const char *mimeType, bool queryDecoders, Vector<CodecCapabilities> *results); + } // namespace android #endif // OMX_CODEC_H_ diff --git a/include/media/stagefright/SkipCutBuffer.h b/include/media/stagefright/SkipCutBuffer.h new file mode 100644 index 0000000..5c7cd47 --- /dev/null +++ b/include/media/stagefright/SkipCutBuffer.h @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SKIP_CUT_BUFFER_H_ + +#define SKIP_CUT_BUFFER_H_ + +#include <media/stagefright/MediaBuffer.h> + +namespace android { + +/** + * utility class to cut the start and end off a stream of data in MediaBuffers + * + */ +class SkipCutBuffer { + public: + // 'skip' is the number of bytes to skip from the beginning + // 'cut' is the number of bytes to cut from the end + // 'output_size' is the size in bytes of the MediaBuffers that will be used + SkipCutBuffer(int32_t skip, int32_t cut, int32_t output_size); + virtual ~SkipCutBuffer(); + + // Submit one MediaBuffer for skipping and cutting. This may consume all or + // some of the data in the buffer, or it may add data to it. + // After this, the caller should continue processing the buffer as usual. + void submit(MediaBuffer *buffer); + void clear(); + size_t size(); // how many bytes are currently stored in the buffer + + private: + void write(const char *src, size_t num); + size_t read(char *dst, size_t num); + int32_t mFrontPadding; + int32_t mBackPadding; + int32_t mWriteHead; + int32_t mReadHead; + int32_t mCapacity; + char* mCutBuffer; + DISALLOW_EVIL_CONSTRUCTORS(SkipCutBuffer); +}; + +} // namespace android + +#endif // OMX_CODEC_H_ diff --git a/include/media/stagefright/SurfaceMediaSource.h b/include/media/stagefright/SurfaceMediaSource.h index 54baab6..936b057 100644 --- a/include/media/stagefright/SurfaceMediaSource.h +++ b/include/media/stagefright/SurfaceMediaSource.h @@ -111,7 +111,7 @@ public: // Make sure this is called when the mutex is locked virtual status_t onFrameReceivedLocked(); - virtual status_t setScalingMode(int mode) { } // no op for encoding + virtual status_t setScalingMode(int mode) { return OK; } // no op for encoding virtual int query(int what, int* value); // Just confirming to the ISurfaceTexture interface as of now diff --git a/media/libaah_rtp/Android.mk b/media/libaah_rtp/Android.mk index 1e87cf0..6c927ba 100644 --- a/media/libaah_rtp/Android.mk +++ b/media/libaah_rtp/Android.mk @@ -29,6 +29,7 @@ LOCAL_SHARED_LIBRARIES := \ libcommon_time_client \ libbinder \ libmedia \ + libmedia_native \ libstagefright \ libstagefright_foundation \ libutils @@ -37,4 +38,3 @@ LOCAL_LDLIBS := \ -lpthread include $(BUILD_SHARED_LIBRARY) - diff --git a/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp b/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp index ca93ce5..40dffd4 100644 --- a/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp +++ b/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp @@ -1493,7 +1493,7 @@ int VolumeSetVolumeLevel(EffectContext *pContext, int16_t level){ pContext->pBundledContext->firstVolume = LVM_FALSE; } return 0; -} /* end setVolumeLevel */ +} /* end VolumeSetVolumeLevel */ //---------------------------------------------------------------------------- // VolumeGetVolumeLevel() diff --git a/media/libmedia/Android.mk b/media/libmedia/Android.mk index 8b009aa..c34e23b 100644 --- a/media/libmedia/Android.mk +++ b/media/libmedia/Android.mk @@ -48,14 +48,13 @@ LOCAL_SRC_FILES:= \ LOCAL_SHARED_LIBRARIES := \ libui libcutils libutils libbinder libsonivox libicuuc libexpat \ libcamera_client libstagefright_foundation \ - libgui libdl libaudioutils + libgui libdl libaudioutils libmedia_native LOCAL_WHOLE_STATIC_LIBRARY := libmedia_helper LOCAL_MODULE:= libmedia LOCAL_C_INCLUDES := \ - $(JNI_H_INCLUDE) \ $(call include-path-for, graphics corecg) \ $(TOP)/frameworks/native/include/media/openmax \ external/icu4c/common \ diff --git a/media/libmedia/AudioTrack.cpp b/media/libmedia/AudioTrack.cpp index a73f035..bafde3a 100644 --- a/media/libmedia/AudioTrack.cpp +++ b/media/libmedia/AudioTrack.cpp @@ -104,9 +104,10 @@ AudioTrack::AudioTrack( { mStatus = set(streamType, sampleRate, format, channelMask, frameCount, flags, cbf, user, notificationFrames, - 0, false, sessionId); + 0 /*sharedBuffer*/, false /*threadCanCallJava*/, sessionId); } +// DEPRECATED AudioTrack::AudioTrack( int streamType, uint32_t sampleRate, @@ -124,7 +125,7 @@ AudioTrack::AudioTrack( { mStatus = set((audio_stream_type_t)streamType, sampleRate, (audio_format_t)format, channelMask, frameCount, (audio_policy_output_flags_t)flags, cbf, user, notificationFrames, - 0, false, sessionId); + 0 /*sharedBuffer*/, false /*threadCanCallJava*/, sessionId); } AudioTrack::AudioTrack( @@ -144,8 +145,8 @@ AudioTrack::AudioTrack( mPreviousSchedulingGroup(ANDROID_TGROUP_DEFAULT) { mStatus = set(streamType, sampleRate, format, channelMask, - 0, flags, cbf, user, notificationFrames, - sharedBuffer, false, sessionId); + 0 /*frameCount*/, flags, cbf, user, notificationFrames, + sharedBuffer, false /*threadCanCallJava*/, sessionId); } AudioTrack::~AudioTrack() @@ -194,6 +195,7 @@ status_t AudioTrack::set( if (AudioSystem::getOutputSamplingRate(&afSampleRate, streamType) != NO_ERROR) { return NO_INIT; } + uint32_t afLatency; if (AudioSystem::getOutputLatency(&afLatency, streamType) != NO_ERROR) { return NO_INIT; @@ -203,9 +205,11 @@ status_t AudioTrack::set( if (streamType == AUDIO_STREAM_DEFAULT) { streamType = AUDIO_STREAM_MUSIC; } + if (sampleRate == 0) { sampleRate = afSampleRate; } + // these below should probably come from the audioFlinger too... if (format == AUDIO_FORMAT_DEFAULT) { format = AUDIO_FORMAT_PCM_16_BIT; @@ -257,8 +261,7 @@ status_t AudioTrack::set( frameCount, flags, sharedBuffer, - output, - true); + output); if (status != NO_ERROR) { return status; @@ -737,8 +740,7 @@ status_t AudioTrack::createTrack_l( int frameCount, audio_policy_output_flags_t flags, const sp<IMemory>& sharedBuffer, - audio_io_handle_t output, - bool enforceFrameCount) + audio_io_handle_t output) { status_t status; const sp<IAudioFlinger>& audioFlinger = AudioSystem::get_audio_flinger(); @@ -785,7 +787,8 @@ status_t AudioTrack::createTrack_l( mNotificationFramesAct = frameCount/2; } if (frameCount < minFrameCount) { - ALOGW_IF(enforceFrameCount, "Minimum buffer size corrected from %d to %d", + // not ALOGW because it happens all the time when playing key clicks over A2DP + ALOGV("Minimum buffer size corrected from %d to %d", frameCount, minFrameCount); frameCount = minFrameCount; } @@ -1248,8 +1251,7 @@ status_t AudioTrack::restoreTrack_l(audio_track_cblk_t*& cblk, bool fromStart) mFrameCount, mFlags, mSharedBuffer, - getOutput_l(), - false); + getOutput_l()); if (result == NO_ERROR) { uint32_t user = cblk->user; diff --git a/media/libmedia/JetPlayer.cpp b/media/libmedia/JetPlayer.cpp index f1f62f7..7fa6bb7 100644 --- a/media/libmedia/JetPlayer.cpp +++ b/media/libmedia/JetPlayer.cpp @@ -89,7 +89,7 @@ int JetPlayer::init() // create the output AudioTrack mAudioTrack = new AudioTrack(); - mAudioTrack->set(AUDIO_STREAM_MUSIC, //TODO parametrize this + mAudioTrack->set(AUDIO_STREAM_MUSIC, //TODO parameterize this pLibConfig->sampleRate, AUDIO_FORMAT_PCM_16_BIT, audio_channel_out_mask_from_count(pLibConfig->numChannels), diff --git a/media/libmediaplayerservice/Android.mk b/media/libmediaplayerservice/Android.mk index 41bcab0..4c6e0bd 100644 --- a/media/libmediaplayerservice/Android.mk +++ b/media/libmediaplayerservice/Android.mk @@ -23,6 +23,7 @@ LOCAL_SHARED_LIBRARIES := \ libvorbisidec \ libsonivox \ libmedia \ + libmedia_native \ libcamera_client \ libandroid_runtime \ libstagefright \ @@ -37,7 +38,6 @@ LOCAL_STATIC_LIBRARIES := \ libstagefright_rtsp \ LOCAL_C_INCLUDES := \ - $(JNI_H_INCLUDE) \ $(call include-path-for, graphics corecg) \ $(TOP)/frameworks/base/media/libstagefright/include \ $(TOP)/frameworks/base/media/libstagefright/rtsp \ diff --git a/media/libmediaplayerservice/MediaPlayerService.cpp b/media/libmediaplayerservice/MediaPlayerService.cpp index 8f62ee4..840e475 100644 --- a/media/libmediaplayerservice/MediaPlayerService.cpp +++ b/media/libmediaplayerservice/MediaPlayerService.cpp @@ -541,6 +541,12 @@ void MediaPlayerService::Client::disconnect() } static player_type getDefaultPlayerType() { + char value[PROPERTY_VALUE_MAX]; + if (property_get("media.stagefright.use-nuplayer", value, NULL) + && (!strcmp("1", value) || !strcasecmp("true", value))) { + return NU_PLAYER; + } + return STAGEFRIGHT_PLAYER; } @@ -1571,7 +1577,7 @@ status_t MediaPlayerService::AudioOutput::open( AUDIO_POLICY_OUTPUT_FLAG_NONE, CallbackWrapper, mCallbackData, - 0, + 0, // notification frames mSessionId); } else { t = new AudioTrack( diff --git a/media/libmediaplayerservice/MetadataRetrieverClient.cpp b/media/libmediaplayerservice/MetadataRetrieverClient.cpp index 7dbb57f..776d288 100644 --- a/media/libmediaplayerservice/MetadataRetrieverClient.cpp +++ b/media/libmediaplayerservice/MetadataRetrieverClient.cpp @@ -87,6 +87,7 @@ static sp<MediaMetadataRetrieverBase> createRetriever(player_type playerType) sp<MediaMetadataRetrieverBase> p; switch (playerType) { case STAGEFRIGHT_PLAYER: + case NU_PLAYER: { p = new StagefrightMetadataRetriever; break; diff --git a/media/libmediaplayerservice/nuplayer/Android.mk b/media/libmediaplayerservice/nuplayer/Android.mk index 9b485d7..73336ef 100644 --- a/media/libmediaplayerservice/nuplayer/Android.mk +++ b/media/libmediaplayerservice/nuplayer/Android.mk @@ -2,6 +2,7 @@ LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) LOCAL_SRC_FILES:= \ + GenericSource.cpp \ HTTPLiveSource.cpp \ NuPlayer.cpp \ NuPlayerDecoder.cpp \ diff --git a/media/libmediaplayerservice/nuplayer/GenericSource.cpp b/media/libmediaplayerservice/nuplayer/GenericSource.cpp new file mode 100644 index 0000000..99569c9 --- /dev/null +++ b/media/libmediaplayerservice/nuplayer/GenericSource.cpp @@ -0,0 +1,265 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "GenericSource.h" + +#include "AnotherPacketSource.h" + +#include <media/stagefright/foundation/ABuffer.h> +#include <media/stagefright/foundation/ADebug.h> +#include <media/stagefright/foundation/AMessage.h> +#include <media/stagefright/DataSource.h> +#include <media/stagefright/FileSource.h> +#include <media/stagefright/MediaBuffer.h> +#include <media/stagefright/MediaDefs.h> +#include <media/stagefright/MediaExtractor.h> +#include <media/stagefright/MediaSource.h> +#include <media/stagefright/MetaData.h> + +namespace android { + +NuPlayer::GenericSource::GenericSource( + const char *url, + const KeyedVector<String8, String8> *headers, + bool uidValid, + uid_t uid) + : mDurationUs(0ll), + mAudioIsVorbis(false) { + DataSource::RegisterDefaultSniffers(); + + sp<DataSource> dataSource = + DataSource::CreateFromURI(url, headers); + CHECK(dataSource != NULL); + + initFromDataSource(dataSource); +} + +NuPlayer::GenericSource::GenericSource( + int fd, int64_t offset, int64_t length) + : mDurationUs(0ll), + mAudioIsVorbis(false) { + DataSource::RegisterDefaultSniffers(); + + sp<DataSource> dataSource = new FileSource(dup(fd), offset, length); + + initFromDataSource(dataSource); +} + +void NuPlayer::GenericSource::initFromDataSource( + const sp<DataSource> &dataSource) { + sp<MediaExtractor> extractor = MediaExtractor::Create(dataSource); + + CHECK(extractor != NULL); + + for (size_t i = 0; i < extractor->countTracks(); ++i) { + sp<MetaData> meta = extractor->getTrackMetaData(i); + + const char *mime; + CHECK(meta->findCString(kKeyMIMEType, &mime)); + + sp<MediaSource> track; + + if (!strncasecmp(mime, "audio/", 6)) { + if (mAudioTrack.mSource == NULL) { + mAudioTrack.mSource = track = extractor->getTrack(i); + + if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_VORBIS)) { + mAudioIsVorbis = true; + } else { + mAudioIsVorbis = false; + } + } + } else if (!strncasecmp(mime, "video/", 6)) { + if (mVideoTrack.mSource == NULL) { + mVideoTrack.mSource = track = extractor->getTrack(i); + } + } + + if (track != NULL) { + int64_t durationUs; + if (meta->findInt64(kKeyDuration, &durationUs)) { + if (durationUs > mDurationUs) { + mDurationUs = durationUs; + } + } + } + } +} + +NuPlayer::GenericSource::~GenericSource() { +} + +void NuPlayer::GenericSource::start() { + ALOGI("start"); + + if (mAudioTrack.mSource != NULL) { + CHECK_EQ(mAudioTrack.mSource->start(), (status_t)OK); + + mAudioTrack.mPackets = + new AnotherPacketSource(mAudioTrack.mSource->getFormat()); + + readBuffer(true /* audio */); + } + + if (mVideoTrack.mSource != NULL) { + CHECK_EQ(mVideoTrack.mSource->start(), (status_t)OK); + + mVideoTrack.mPackets = + new AnotherPacketSource(mVideoTrack.mSource->getFormat()); + + readBuffer(false /* audio */); + } +} + +status_t NuPlayer::GenericSource::feedMoreTSData() { + return OK; +} + +sp<MetaData> NuPlayer::GenericSource::getFormat(bool audio) { + sp<MediaSource> source = audio ? mAudioTrack.mSource : mVideoTrack.mSource; + + if (source == NULL) { + return NULL; + } + + return source->getFormat(); +} + +status_t NuPlayer::GenericSource::dequeueAccessUnit( + bool audio, sp<ABuffer> *accessUnit) { + Track *track = audio ? &mAudioTrack : &mVideoTrack; + + if (track->mSource == NULL) { + return -EWOULDBLOCK; + } + + status_t finalResult; + if (!track->mPackets->hasBufferAvailable(&finalResult)) { + return finalResult == OK ? -EWOULDBLOCK : finalResult; + } + + status_t result = track->mPackets->dequeueAccessUnit(accessUnit); + + readBuffer(audio, -1ll); + + return result; +} + +status_t NuPlayer::GenericSource::getDuration(int64_t *durationUs) { + *durationUs = mDurationUs; + return OK; +} + +status_t NuPlayer::GenericSource::seekTo(int64_t seekTimeUs) { + if (mVideoTrack.mSource != NULL) { + int64_t actualTimeUs; + readBuffer(false /* audio */, seekTimeUs, &actualTimeUs); + + seekTimeUs = actualTimeUs; + } + + if (mAudioTrack.mSource != NULL) { + readBuffer(true /* audio */, seekTimeUs); + } + + return OK; +} + +void NuPlayer::GenericSource::readBuffer( + bool audio, int64_t seekTimeUs, int64_t *actualTimeUs) { + Track *track = audio ? &mAudioTrack : &mVideoTrack; + CHECK(track->mSource != NULL); + + if (actualTimeUs) { + *actualTimeUs = seekTimeUs; + } + + MediaSource::ReadOptions options; + + bool seeking = false; + + if (seekTimeUs >= 0) { + options.setSeekTo(seekTimeUs); + seeking = true; + } + + for (;;) { + MediaBuffer *mbuf; + status_t err = track->mSource->read(&mbuf, &options); + + options.clearSeekTo(); + + if (err == OK) { + size_t outLength = mbuf->range_length(); + + if (audio && mAudioIsVorbis) { + outLength += sizeof(int32_t); + } + + sp<ABuffer> buffer = new ABuffer(outLength); + + memcpy(buffer->data(), + (const uint8_t *)mbuf->data() + mbuf->range_offset(), + mbuf->range_length()); + + if (audio && mAudioIsVorbis) { + int32_t numPageSamples; + if (!mbuf->meta_data()->findInt32( + kKeyValidSamples, &numPageSamples)) { + numPageSamples = -1; + } + + memcpy(buffer->data() + mbuf->range_length(), + &numPageSamples, + sizeof(numPageSamples)); + } + + int64_t timeUs; + CHECK(mbuf->meta_data()->findInt64(kKeyTime, &timeUs)); + + buffer->meta()->setInt64("timeUs", timeUs); + + if (actualTimeUs) { + *actualTimeUs = timeUs; + } + + mbuf->release(); + mbuf = NULL; + + if (seeking) { + track->mPackets->queueDiscontinuity( + ATSParser::DISCONTINUITY_SEEK, NULL); + } + + track->mPackets->queueAccessUnit(buffer); + break; + } else if (err == INFO_FORMAT_CHANGED) { +#if 0 + track->mPackets->queueDiscontinuity( + ATSParser::DISCONTINUITY_FORMATCHANGE, NULL); +#endif + } else { + track->mPackets->signalEOS(err); + break; + } + } +} + +bool NuPlayer::GenericSource::isSeekable() { + return true; +} + +} // namespace android diff --git a/media/libmediaplayerservice/nuplayer/GenericSource.h b/media/libmediaplayerservice/nuplayer/GenericSource.h new file mode 100644 index 0000000..aaa5876 --- /dev/null +++ b/media/libmediaplayerservice/nuplayer/GenericSource.h @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef GENERIC_SOURCE_H_ + +#define GENERIC_SOURCE_H_ + +#include "NuPlayer.h" +#include "NuPlayerSource.h" + +#include "ATSParser.h" + +namespace android { + +struct AnotherPacketSource; +struct ARTSPController; +struct DataSource; +struct MediaSource; + +struct NuPlayer::GenericSource : public NuPlayer::Source { + GenericSource( + const char *url, + const KeyedVector<String8, String8> *headers, + bool uidValid = false, + uid_t uid = 0); + + GenericSource(int fd, int64_t offset, int64_t length); + + virtual void start(); + + virtual status_t feedMoreTSData(); + + virtual sp<MetaData> getFormat(bool audio); + virtual status_t dequeueAccessUnit(bool audio, sp<ABuffer> *accessUnit); + + virtual status_t getDuration(int64_t *durationUs); + virtual status_t seekTo(int64_t seekTimeUs); + virtual bool isSeekable(); + +protected: + virtual ~GenericSource(); + +private: + struct Track { + sp<MediaSource> mSource; + sp<AnotherPacketSource> mPackets; + }; + + Track mAudioTrack; + Track mVideoTrack; + + int64_t mDurationUs; + bool mAudioIsVorbis; + + void initFromDataSource(const sp<DataSource> &dataSource); + + void readBuffer( + bool audio, + int64_t seekTimeUs = -1ll, int64_t *actualTimeUs = NULL); + + DISALLOW_EVIL_CONSTRUCTORS(GenericSource); +}; + +} // namespace android + +#endif // GENERIC_SOURCE_H_ diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp index 526120a..544d501 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp +++ b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp @@ -27,6 +27,7 @@ #include "NuPlayerSource.h" #include "RTSPSource.h" #include "StreamingSource.h" +#include "GenericSource.h" #include "ATSParser.h" @@ -84,18 +85,44 @@ void NuPlayer::setDataSource(const sp<IStreamSource> &source) { msg->post(); } +static bool IsHTTPLiveURL(const char *url) { + if (!strncasecmp("http://", url, 7) + || !strncasecmp("https://", url, 8)) { + size_t len = strlen(url); + if (len >= 5 && !strcasecmp(".m3u8", &url[len - 5])) { + return true; + } + + if (strstr(url,"m3u8")) { + return true; + } + } + + return false; +} + void NuPlayer::setDataSource( const char *url, const KeyedVector<String8, String8> *headers) { sp<AMessage> msg = new AMessage(kWhatSetDataSource, id()); - if (!strncasecmp(url, "rtsp://", 7)) { - msg->setObject( - "source", new RTSPSource(url, headers, mUIDValid, mUID)); + sp<Source> source; + if (IsHTTPLiveURL(url)) { + source = new HTTPLiveSource(url, headers, mUIDValid, mUID); + } else if (!strncasecmp(url, "rtsp://", 7)) { + source = new RTSPSource(url, headers, mUIDValid, mUID); } else { - msg->setObject( - "source", new HTTPLiveSource(url, headers, mUIDValid, mUID)); + source = new GenericSource(url, headers, mUIDValid, mUID); } + msg->setObject("source", source); + msg->post(); +} + +void NuPlayer::setDataSource(int fd, int64_t offset, int64_t length) { + sp<AMessage> msg = new AMessage(kWhatSetDataSource, id()); + + sp<Source> source = new GenericSource(fd, offset, length); + msg->setObject("source", source); msg->post(); } diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.h b/media/libmediaplayerservice/nuplayer/NuPlayer.h index 6be14be..25766e0 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayer.h +++ b/media/libmediaplayerservice/nuplayer/NuPlayer.h @@ -40,6 +40,8 @@ struct NuPlayer : public AHandler { void setDataSource( const char *url, const KeyedVector<String8, String8> *headers); + void setDataSource(int fd, int64_t offset, int64_t length); + void setVideoSurfaceTexture(const sp<ISurfaceTexture> &surfaceTexture); void setAudioSink(const sp<MediaPlayerBase::AudioSink> &sink); void start(); @@ -60,12 +62,13 @@ protected: private: struct Decoder; + struct GenericSource; struct HTTPLiveSource; struct NuPlayerStreamListener; struct Renderer; + struct RTSPSource; struct Source; struct StreamingSource; - struct RTSPSource; enum { kWhatSetDataSource = '=DaS', diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp index 460fc98..1600141 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp +++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp @@ -228,6 +228,20 @@ sp<AMessage> NuPlayer::Decoder::makeFormat(const sp<MetaData> &meta) { buffer->meta()->setInt32("csd", true); mCSD.push(buffer); + } else if (meta->findData(kKeyVorbisInfo, &type, &data, &size)) { + sp<ABuffer> buffer = new ABuffer(size); + memcpy(buffer->data(), data, size); + + buffer->meta()->setInt32("csd", true); + mCSD.push(buffer); + + CHECK(meta->findData(kKeyVorbisBooks, &type, &data, &size)); + + buffer = new ABuffer(size); + memcpy(buffer->data(), data, size); + + buffer->meta()->setInt32("csd", true); + mCSD.push(buffer); } return msg; diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp index 5aa99bf..253bc2f 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp +++ b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp @@ -76,7 +76,13 @@ status_t NuPlayerDriver::setDataSource( } status_t NuPlayerDriver::setDataSource(int fd, int64_t offset, int64_t length) { - return INVALID_OPERATION; + CHECK_EQ((int)mState, (int)UNINITIALIZED); + + mPlayer->setDataSource(fd, offset, length); + + mState = STOPPED; + + return OK; } status_t NuPlayerDriver::setDataSource(const sp<IStreamSource> &source) { @@ -97,13 +103,16 @@ status_t NuPlayerDriver::setVideoSurfaceTexture( } status_t NuPlayerDriver::prepare() { + sendEvent(MEDIA_SET_VIDEO_SIZE, 320, 240); return OK; } status_t NuPlayerDriver::prepareAsync() { + status_t err = prepare(); + notifyListener(MEDIA_PREPARED); - return OK; + return err; } status_t NuPlayerDriver::start() { diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp index 5738ecb..ecbc428 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp +++ b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp @@ -376,7 +376,8 @@ void NuPlayer::Renderer::onDrainVideoQueue() { bool tooLate = (mVideoLateByUs > 40000); if (tooLate) { - ALOGV("video late by %lld us (%.2f secs)", mVideoLateByUs, mVideoLateByUs / 1E6); + ALOGV("video late by %lld us (%.2f secs)", + mVideoLateByUs, mVideoLateByUs / 1E6); } else { ALOGV("rendering video at media time %.2f secs", mediaTimeUs / 1E6); } diff --git a/media/libstagefright/Android.mk b/media/libstagefright/Android.mk index 047b4cb..65a947a 100644 --- a/media/libstagefright/Android.mk +++ b/media/libstagefright/Android.mk @@ -42,6 +42,7 @@ LOCAL_SRC_FILES:= \ OggExtractor.cpp \ SampleIterator.cpp \ SampleTable.cpp \ + SkipCutBuffer.cpp \ StagefrightMediaScanner.cpp \ StagefrightMetadataRetriever.cpp \ SurfaceMediaSource.cpp \ @@ -56,7 +57,6 @@ LOCAL_SRC_FILES:= \ avc_utils.cpp \ LOCAL_C_INCLUDES:= \ - $(JNI_H_INCLUDE) \ $(TOP)/frameworks/base/include/media/stagefright/timedtext \ $(TOP)/frameworks/native/include/media/hardware \ $(TOP)/frameworks/native/include/media/openmax \ @@ -79,6 +79,7 @@ LOCAL_SHARED_LIBRARIES := \ libicuuc \ liblog \ libmedia \ + libmedia_native \ libsonivox \ libssl \ libstagefright_omx \ diff --git a/media/libstagefright/CameraSource.cpp b/media/libstagefright/CameraSource.cpp index 0d67800..fd3f892 100755 --- a/media/libstagefright/CameraSource.cpp +++ b/media/libstagefright/CameraSource.cpp @@ -515,9 +515,13 @@ status_t CameraSource::initWithCameraAccess( return err; } - // This CHECK is good, since we just passed the lock/unlock - // check earlier by calling mCamera->setParameters(). - CHECK_EQ((status_t)OK, mCamera->setPreviewDisplay(mSurface)); + // Set the preview display. Skip this if mSurface is null because + // applications may already set a surface to the camera. + if (mSurface != NULL) { + // This CHECK is good, since we just passed the lock/unlock + // check earlier by calling mCamera->setParameters(). + CHECK_EQ((status_t)OK, mCamera->setPreviewDisplay(mSurface)); + } // By default, do not store metadata in video buffers mIsMetaDataStoredInVideoBuffers = false; diff --git a/media/libstagefright/MP3Extractor.cpp b/media/libstagefright/MP3Extractor.cpp index 69209b5..6abaf23 100644 --- a/media/libstagefright/MP3Extractor.cpp +++ b/media/libstagefright/MP3Extractor.cpp @@ -311,10 +311,18 @@ MP3Extractor::MP3Extractor( mMeta->setInt32(kKeyBitRate, bitrate * 1000); mMeta->setInt32(kKeyChannelCount, num_channels); - mSeeker = XINGSeeker::CreateFromSource(mDataSource, mFirstFramePos); + sp<XINGSeeker> seeker = XINGSeeker::CreateFromSource(mDataSource, mFirstFramePos); - if (mSeeker == NULL) { + if (seeker == NULL) { mSeeker = VBRISeeker::CreateFromSource(mDataSource, post_id3_pos); + } else { + mSeeker = seeker; + int encd = seeker->getEncoderDelay(); + int encp = seeker->getEncoderPadding(); + if (encd != 0 || encp != 0) { + mMeta->setInt32(kKeyEncoderDelay, encd); + mMeta->setInt32(kKeyEncoderPadding, encp); + } } if (mSeeker != NULL) { @@ -340,6 +348,37 @@ MP3Extractor::MP3Extractor( } mInitCheck = OK; + + // get iTunes-style gapless info if present + ID3 id3(mDataSource); + if (id3.isValid()) { + ID3::Iterator *com = new ID3::Iterator(id3, "COM"); + if (com->done()) { + delete com; + com = new ID3::Iterator(id3, "COMM"); + } + while(!com->done()) { + String8 commentdesc; + String8 commentvalue; + com->getString(&commentdesc, &commentvalue); + const char * desc = commentdesc.string(); + const char * value = commentvalue.string(); + + // first 3 characters are the language, which we don't care about + if(strlen(desc) > 3 && strcmp(desc + 3, "iTunSMPB") == 0) { + + int32_t delay, padding; + if (sscanf(value, " %*x %x %x %*x", &delay, &padding) == 2) { + mMeta->setInt32(kKeyEncoderDelay, delay); + mMeta->setInt32(kKeyEncoderPadding, padding); + } + break; + } + com->next(); + } + delete com; + com = NULL; + } } size_t MP3Extractor::countTracks() { diff --git a/media/libstagefright/MPEG4Extractor.cpp b/media/libstagefright/MPEG4Extractor.cpp index 6c95d4e..9385b8a 100644 --- a/media/libstagefright/MPEG4Extractor.cpp +++ b/media/libstagefright/MPEG4Extractor.cpp @@ -373,7 +373,8 @@ status_t MPEG4Extractor::readMetaData() { if (mInitCheck == OK) { if (mHasVideo) { - mFileMetaData->setCString(kKeyMIMEType, "video/mp4"); + mFileMetaData->setCString( + kKeyMIMEType, MEDIA_MIMETYPE_CONTAINER_MPEG4); } else { mFileMetaData->setCString(kKeyMIMEType, "audio/mp4"); } @@ -599,6 +600,7 @@ static void convertTimeToDate(int64_t time_1904, String8 *s) { } 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, 8) < 8) { return ERROR_IO; @@ -625,6 +627,7 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { char chunk[5]; MakeFourCCString(chunk_type, chunk); + ALOGV("chunk: %s @ %lld", chunk, *offset); #if 0 static const char kWhitespace[] = " "; @@ -1302,6 +1305,8 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { break; } + case FOURCC('m', 'e', 'a', 'n'): + case FOURCC('n', 'a', 'm', 'e'): case FOURCC('d', 'a', 't', 'a'): { if (mPath.size() == 6 && underMetaDataPath(mPath)) { @@ -1437,6 +1442,15 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { break; } + case FOURCC('-', '-', '-', '-'): + { + mLastCommentMean.clear(); + mLastCommentName.clear(); + mLastCommentData.clear(); + *offset += chunk_size; + break; + } + default: { *offset += chunk_size; @@ -1553,6 +1567,9 @@ status_t MPEG4Extractor::parseMetaData(off64_t offset, size_t size) { 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'): { @@ -1632,6 +1649,35 @@ status_t MPEG4Extractor::parseMetaData(off64_t offset, size_t size) { } 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; + } + if (mLastCommentMean == "com.apple.iTunes" + && mLastCommentName == "iTunSMPB" + && mLastCommentData.length() != 0) { + int32_t delay, padding; + if (sscanf(mLastCommentData, + " %*x %x %x %*x", &delay, &padding) == 2) { + mLastTrack->meta->setInt32(kKeyEncoderDelay, delay); + mLastTrack->meta->setInt32(kKeyEncoderPadding, padding); + } + mLastCommentMean.clear(); + mLastCommentName.clear(); + mLastCommentData.clear(); + } + break; + } default: break; diff --git a/media/libstagefright/MediaDefs.cpp b/media/libstagefright/MediaDefs.cpp index 2549de6..2740d6b 100644 --- a/media/libstagefright/MediaDefs.cpp +++ b/media/libstagefright/MediaDefs.cpp @@ -41,7 +41,7 @@ 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_CONTAINER_MPEG4 = "video/mpeg4"; +const char *MEDIA_MIMETYPE_CONTAINER_MPEG4 = "video/mp4"; const char *MEDIA_MIMETYPE_CONTAINER_WAV = "audio/wav"; const char *MEDIA_MIMETYPE_CONTAINER_OGG = "application/ogg"; const char *MEDIA_MIMETYPE_CONTAINER_MATROSKA = "video/x-matroska"; diff --git a/media/libstagefright/MetaData.cpp b/media/libstagefright/MetaData.cpp index 66dec90..755594a 100644 --- a/media/libstagefright/MetaData.cpp +++ b/media/libstagefright/MetaData.cpp @@ -14,6 +14,10 @@ * limitations under the License. */ +//#define LOG_NDEBUG 0 +#define LOG_TAG "MetaData" +#include <utils/Log.h> + #include <stdlib.h> #include <string.h> @@ -282,5 +286,60 @@ void MetaData::typed_data::freeStorage() { 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); + 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 android diff --git a/media/libstagefright/OMXCodec.cpp b/media/libstagefright/OMXCodec.cpp index d5e6bec..8b6e9d5 100755 --- a/media/libstagefright/OMXCodec.cpp +++ b/media/libstagefright/OMXCodec.cpp @@ -38,6 +38,7 @@ #include <media/stagefright/MetaData.h> #include <media/stagefright/OMXCodec.h> #include <media/stagefright/Utils.h> +#include <media/stagefright/SkipCutBuffer.h> #include <utils/Vector.h> #include <OMX_Audio.h> @@ -1303,6 +1304,7 @@ OMXCodec::OMXCodec( mSeekMode(ReadOptions::SEEK_CLOSEST_SYNC), mTargetTimeUs(-1), mOutputPortSettingsChangedPending(false), + mSkipCutBuffer(NULL), mLeftOverBuffer(NULL), mPaused(false), mNativeWindow( @@ -1413,6 +1415,9 @@ OMXCodec::~OMXCodec() { free(mMIME); mMIME = NULL; + + delete mSkipCutBuffer; + mSkipCutBuffer = NULL; } status_t OMXCodec::init() { @@ -1573,6 +1578,34 @@ status_t OMXCodec::allocateBuffersOnPort(OMX_U32 portIndex) { portIndex == kPortIndexInput ? "input" : "output"); } + if (portIndex == kPortIndexOutput) { + + sp<MetaData> meta = mSource->getFormat(); + int32_t delay = 0; + if (!meta->findInt32(kKeyEncoderDelay, &delay)) { + delay = 0; + } + int32_t padding = 0; + if (!meta->findInt32(kKeyEncoderPadding, &padding)) { + padding = 0; + } + int32_t numchannels = 0; + if (delay + padding) { + if (meta->findInt32(kKeyChannelCount, &numchannels)) { + size_t frameSize = numchannels * sizeof(int16_t); + if (mSkipCutBuffer) { + size_t prevbuffersize = mSkipCutBuffer->size(); + if (prevbuffersize != 0) { + ALOGW("Replacing SkipCutBuffer holding %d bytes", prevbuffersize); + } + delete mSkipCutBuffer; + } + mSkipCutBuffer = new SkipCutBuffer(delay * frameSize, padding * frameSize, + def.nBufferSize); + } + } + } + // dumpPortStatus(portIndex); if (portIndex == kPortIndexInput && (mFlags & kUseSecureInputBuffers)) { @@ -2490,6 +2523,10 @@ void OMXCodec::onCmdComplete(OMX_COMMANDTYPE cmd, OMX_U32 data) { CHECK_EQ(countBuffersWeOwn(mPortBuffers[portIndex]), mPortBuffers[portIndex].size()); + if (mSkipCutBuffer && mPortStatus[kPortIndexOutput] == ENABLED) { + mSkipCutBuffer->clear(); + } + if (mState == RECONFIGURING) { CHECK_EQ(portIndex, (OMX_U32)kPortIndexOutput); @@ -3800,6 +3837,9 @@ status_t OMXCodec::read( info->mStatus = OWNED_BY_CLIENT; info->mMediaBuffer->add_ref(); + if (mSkipCutBuffer) { + mSkipCutBuffer->submit(info->mMediaBuffer); + } *buffer = info->mMediaBuffer; return OK; diff --git a/media/libstagefright/SkipCutBuffer.cpp b/media/libstagefright/SkipCutBuffer.cpp new file mode 100755 index 0000000..6d331b0 --- /dev/null +++ b/media/libstagefright/SkipCutBuffer.cpp @@ -0,0 +1,130 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +//#define LOG_NDEBUG 0 +#define LOG_TAG "SkipCutBuffer" +#include <utils/Log.h> + +#include <media/stagefright/foundation/ADebug.h> +#include <media/stagefright/MediaBuffer.h> +#include <media/stagefright/SkipCutBuffer.h> + +namespace android { + +SkipCutBuffer::SkipCutBuffer(int32_t skip, int32_t cut, int32_t output_size) { + mFrontPadding = skip; + mBackPadding = cut; + mWriteHead = 0; + mReadHead = 0; + mCapacity = cut + output_size; + mCutBuffer = new char[mCapacity]; + ALOGV("skipcutbuffer %d %d %d", skip, cut, mCapacity); +} + +SkipCutBuffer::~SkipCutBuffer() { + delete[] mCutBuffer; +} + +void SkipCutBuffer::submit(MediaBuffer *buffer) { + int32_t offset = buffer->range_offset(); + int32_t buflen = buffer->range_length(); + + // drop the initial data from the buffer if needed + if (mFrontPadding > 0) { + // still data left to drop + int32_t to_drop = (buflen < mFrontPadding) ? buflen : mFrontPadding; + offset += to_drop; + buflen -= to_drop; + buffer->set_range(offset, buflen); + mFrontPadding -= to_drop; + } + + + // append data to cutbuffer + char *src = ((char*) buffer->data()) + offset; + write(src, buflen); + + + // the mediabuffer is now empty. Fill it from cutbuffer, always leaving + // at least mBackPadding bytes in the cutbuffer + char *dst = (char*) buffer->data(); + size_t copied = read(dst, buffer->size()); + buffer->set_range(0, copied); +} + +void SkipCutBuffer::clear() { + mWriteHead = mReadHead = 0; +} + +void SkipCutBuffer::write(const char *src, size_t num) { + int32_t sizeused = (mWriteHead - mReadHead); + if (sizeused < 0) sizeused += mCapacity; + + // everything must fit + CHECK_GE((mCapacity - size_t(sizeused)), num); + + size_t copyfirst = (mCapacity - mWriteHead); + if (copyfirst > num) copyfirst = num; + if (copyfirst) { + memcpy(mCutBuffer + mWriteHead, src, copyfirst); + num -= copyfirst; + src += copyfirst; + mWriteHead += copyfirst; + CHECK_LE(mWriteHead, mCapacity); + if (mWriteHead == mCapacity) mWriteHead = 0; + if (num) { + memcpy(mCutBuffer, src, num); + mWriteHead += num; + } + } +} + +size_t SkipCutBuffer::read(char *dst, size_t num) { + int32_t available = (mWriteHead - mReadHead); + if (available < 0) available += mCapacity; + + available -= mBackPadding; + if (available <=0) { + return 0; + } + if (available < num) { + num = available; + } + + size_t copyfirst = (mCapacity - mReadHead); + if (copyfirst > num) copyfirst = num; + if (copyfirst) { + memcpy(dst, mCutBuffer + mReadHead, copyfirst); + num -= copyfirst; + dst += copyfirst; + mReadHead += copyfirst; + CHECK_LE(mReadHead, mCapacity); + if (mReadHead == mCapacity) mReadHead = 0; + if (num) { + memcpy(dst, mCutBuffer, num); + mReadHead += num; + } + } + return available; +} + +size_t SkipCutBuffer::size() { + int32_t available = (mWriteHead - mReadHead); + if (available < 0) available += mCapacity; + return available; +} + +} // namespace android diff --git a/media/libstagefright/XINGSeeker.cpp b/media/libstagefright/XINGSeeker.cpp index 8c99c76..9c91134 100644 --- a/media/libstagefright/XINGSeeker.cpp +++ b/media/libstagefright/XINGSeeker.cpp @@ -14,6 +14,9 @@ * limitations under the License. */ +#define LOG_TAG "XINGSEEKER" +#include <utils/Log.h> + #include "include/XINGSeeker.h" #include "include/avc_utils.h" @@ -24,7 +27,9 @@ namespace android { XINGSeeker::XINGSeeker() : mDurationUs(-1), - mSizeBytes(0) { + mSizeBytes(0), + mEncoderDelay(0), + mEncoderPadding(0) { } bool XINGSeeker::getDuration(int64_t *durationUs) { @@ -76,8 +81,6 @@ sp<XINGSeeker> XINGSeeker::CreateFromSource( seeker->mFirstFramePos = first_frame_pos; - ALOGI("xingseeker first frame pos: %lld", first_frame_pos); - seeker->mSizeBytes = 0; seeker->mTOCValid = false; seeker->mDurationUs = 0; @@ -111,6 +114,8 @@ sp<XINGSeeker> XINGSeeker::CreateFromSource( else offset += 9; } + int xingbase = offset; + if (source->readAt(offset, &buffer, 4) < 4) { // XING header ID return NULL; } @@ -161,10 +166,31 @@ sp<XINGSeeker> XINGSeeker::CreateFromSource( // do something with the quality indicator offset += 4; } + + if (source->readAt(xingbase + 0xaf - 0x24, &buffer, 1) < 1) { // encoding flags + return false; + } + + ALOGV("nogap preceding: %s, nogap continued in next: %s", + (buffer[0] & 0x80) ? "true" : "false", + (buffer[0] & 0x40) ? "true" : "false"); #endif + if (source->readAt(xingbase + 0xb1 - 0x24, &buffer, 3) == 3) { + seeker->mEncoderDelay = (buffer[0] << 4) + (buffer[1] >> 4); + seeker->mEncoderPadding = ((buffer[1] & 0xf) << 8) + buffer[2]; + } + return seeker; } +int32_t XINGSeeker::getEncoderDelay() { + return mEncoderDelay; +} + +int32_t XINGSeeker::getEncoderPadding() { + return mEncoderPadding; +} + } // namespace android diff --git a/media/libstagefright/chromium_http/Android.mk b/media/libstagefright/chromium_http/Android.mk index e37b4a8..6d5dcfb 100644 --- a/media/libstagefright/chromium_http/Android.mk +++ b/media/libstagefright/chromium_http/Android.mk @@ -8,7 +8,6 @@ LOCAL_SRC_FILES:= \ support.cpp LOCAL_C_INCLUDES:= \ - $(JNI_H_INCLUDE) \ frameworks/base/media/libstagefright \ $(TOP)/frameworks/native/include/media/openmax \ external/chromium \ diff --git a/media/libstagefright/codecs/mp3dec/SoftMP3.cpp b/media/libstagefright/codecs/mp3dec/SoftMP3.cpp index ad55295..92009ee 100644 --- a/media/libstagefright/codecs/mp3dec/SoftMP3.cpp +++ b/media/libstagefright/codecs/mp3dec/SoftMP3.cpp @@ -115,6 +115,7 @@ void SoftMP3::initDecoder() { mDecoderBuf = malloc(memRequirements); pvmp3_InitDecoder(mConfig, mDecoderBuf); + mIsFirst = true; } OMX_ERRORTYPE SoftMP3::internalGetParameter( @@ -190,7 +191,10 @@ void SoftMP3::onQueueFilled(OMX_U32 portIndex) { inInfo->mOwnedByUs = false; notifyEmptyBufferDone(inHeader); - outHeader->nFilledLen = 0; + // pad the end of the stream with 529 samples, since that many samples + // were trimmed off the beginning when decoding started + outHeader->nFilledLen = kPVMP3DecoderDelay * mNumChannels * sizeof(int16_t); + memset(outHeader->pBuffer, 0, outHeader->nFilledLen); outHeader->nFlags = OMX_BUFFERFLAG_EOS; outQueue.erase(outQueue.begin()); @@ -251,8 +255,17 @@ void SoftMP3::onQueueFilled(OMX_U32 portIndex) { return; } - outHeader->nOffset = 0; - outHeader->nFilledLen = mConfig->outputFrameSize * sizeof(int16_t); + if (mIsFirst) { + mIsFirst = false; + // The decoder delay is 529 samples, so trim that many samples off + // the start of the first output buffer. This essentially makes this + // decoder have zero delay, which the rest of the pipeline assumes. + outHeader->nOffset = kPVMP3DecoderDelay * mNumChannels * sizeof(int16_t); + outHeader->nFilledLen = mConfig->outputFrameSize * sizeof(int16_t) - outHeader->nOffset; + } else { + outHeader->nOffset = 0; + outHeader->nFilledLen = mConfig->outputFrameSize * sizeof(int16_t); + } outHeader->nTimeStamp = mAnchorTimeUs @@ -288,6 +301,7 @@ void SoftMP3::onPortFlushCompleted(OMX_U32 portIndex) { // Make sure that the next buffer output does not still // depend on fragments from the last one decoded. pvmp3_InitDecoder(mConfig, mDecoderBuf); + mIsFirst = true; } } diff --git a/media/libstagefright/codecs/mp3dec/SoftMP3.h b/media/libstagefright/codecs/mp3dec/SoftMP3.h index 70d0682..3a05466 100644 --- a/media/libstagefright/codecs/mp3dec/SoftMP3.h +++ b/media/libstagefright/codecs/mp3dec/SoftMP3.h @@ -46,7 +46,8 @@ protected: private: enum { kNumBuffers = 4, - kOutputBufferSize = 4608 * 2 + kOutputBufferSize = 4608 * 2, + kPVMP3DecoderDelay = 529 // frames }; tPVMP3DecoderExternal *mConfig; @@ -57,8 +58,7 @@ private: int32_t mNumChannels; int32_t mSamplingRate; - bool mConfigured; - + bool mIsFirst; bool mSignalledError; enum { diff --git a/media/libstagefright/httplive/Android.mk b/media/libstagefright/httplive/Android.mk index a5990c3..90cb448 100644 --- a/media/libstagefright/httplive/Android.mk +++ b/media/libstagefright/httplive/Android.mk @@ -8,7 +8,6 @@ LOCAL_SRC_FILES:= \ M3UParser.cpp \ LOCAL_C_INCLUDES:= \ - $(JNI_H_INCLUDE) \ $(TOP)/frameworks/base/media/libstagefright \ $(TOP)/frameworks/native/include/media/openmax \ $(TOP)/external/openssl/include diff --git a/media/libstagefright/id3/ID3.cpp b/media/libstagefright/id3/ID3.cpp index 2e92926..ca14054 100644 --- a/media/libstagefright/id3/ID3.cpp +++ b/media/libstagefright/id3/ID3.cpp @@ -463,40 +463,65 @@ static void convertISO8859ToString8( tmp = NULL; } -void ID3::Iterator::getString(String8 *id) const { +// the 2nd argument is used to get the data following the \0 in a comment field +void ID3::Iterator::getString(String8 *id, String8 *comment) const { + getstring(id, false); + if (comment != NULL) { + getstring(comment, true); + } +} + +// comment fields (COM/COMM) contain an initial short descriptor, followed by \0, +// followed by more data. The data following the \0 can be retrieved by setting +// "otherdata" to true. +void ID3::Iterator::getstring(String8 *id, bool otherdata) const { id->setTo(""); - if (mFrameData == NULL) { + const uint8_t *frameData = mFrameData; + if (frameData == NULL) { return; } + uint8_t encoding = *frameData; + if (mParent.mVersion == ID3_V1 || mParent.mVersion == ID3_V1_1) { if (mOffset == 126 || mOffset == 127) { // Special treatment for the track number and genre. char tmp[16]; - sprintf(tmp, "%d", (int)*mFrameData); + sprintf(tmp, "%d", (int)*frameData); id->setTo(tmp); return; } - convertISO8859ToString8(mFrameData, mFrameSize, id); + convertISO8859ToString8(frameData, mFrameSize, id); return; } size_t n = mFrameSize - getHeaderLength() - 1; + if (otherdata) { + // skip past the encoding, language, and the 0 separator + frameData += 4; + int32_t i = n - 4; + while(--i >= 0 && *++frameData != 0) ; + int skipped = (frameData - mFrameData); + if (skipped >= n) { + return; + } + n -= skipped; + } - if (*mFrameData == 0x00) { + if (encoding == 0x00) { // ISO 8859-1 - convertISO8859ToString8(mFrameData + 1, n, id); - } else if (*mFrameData == 0x03) { + convertISO8859ToString8(frameData + 1, n, id); + } else if (encoding == 0x03) { // UTF-8 - id->setTo((const char *)(mFrameData + 1), n); - } else if (*mFrameData == 0x02) { + id->setTo((const char *)(frameData + 1), n); + } else if (encoding == 0x02) { // UTF-16 BE, no byte order mark. // API wants number of characters, not number of bytes... int len = n / 2; - const char16_t *framedata = (const char16_t *) (mFrameData + 1); + const char16_t *framedata = (const char16_t *) (frameData + 1); char16_t *framedatacopy = NULL; #if BYTE_ORDER == LITTLE_ENDIAN framedatacopy = new char16_t[len]; @@ -513,7 +538,7 @@ void ID3::Iterator::getString(String8 *id) const { // UCS-2 // API wants number of characters, not number of bytes... int len = n / 2; - const char16_t *framedata = (const char16_t *) (mFrameData + 1); + const char16_t *framedata = (const char16_t *) (frameData + 1); char16_t *framedatacopy = NULL; if (*framedata == 0xfffe) { // endianness marker doesn't match host endianness, convert diff --git a/media/libstagefright/include/ID3.h b/media/libstagefright/include/ID3.h index 98c82a4..8714008 100644 --- a/media/libstagefright/include/ID3.h +++ b/media/libstagefright/include/ID3.h @@ -50,7 +50,7 @@ struct ID3 { bool done() const; void getID(String8 *id) const; - void getString(String8 *s) const; + void getString(String8 *s, String8 *ss = NULL) const; const uint8_t *getData(size_t *length) const; void next(); @@ -65,6 +65,7 @@ struct ID3 { void findFrame(); size_t getHeaderLength() const; + void getstring(String8 *s, bool secondhalf) const; Iterator(const Iterator &); Iterator &operator=(const Iterator &); diff --git a/media/libstagefright/include/MPEG4Extractor.h b/media/libstagefright/include/MPEG4Extractor.h index eae62c6..5c549e0 100644 --- a/media/libstagefright/include/MPEG4Extractor.h +++ b/media/libstagefright/include/MPEG4Extractor.h @@ -20,6 +20,7 @@ #include <media/stagefright/MediaExtractor.h> #include <utils/Vector.h> +#include <utils/String8.h> namespace android { @@ -64,6 +65,9 @@ private: sp<MetaData> mFileMetaData; Vector<uint32_t> mPath; + String8 mLastCommentMean; + String8 mLastCommentName; + String8 mLastCommentData; status_t readMetaData(); status_t parseChunk(off64_t *offset, int depth); diff --git a/media/libstagefright/include/XINGSeeker.h b/media/libstagefright/include/XINGSeeker.h index 8510979..c408576 100644 --- a/media/libstagefright/include/XINGSeeker.h +++ b/media/libstagefright/include/XINGSeeker.h @@ -31,10 +31,15 @@ struct XINGSeeker : public MP3Seeker { virtual bool getDuration(int64_t *durationUs); virtual bool getOffsetForTime(int64_t *timeUs, off64_t *pos); + virtual int32_t getEncoderDelay(); + virtual int32_t getEncoderPadding(); + private: int64_t mFirstFramePos; int64_t mDurationUs; int32_t mSizeBytes; + int32_t mEncoderDelay; + int32_t mEncoderPadding; // TOC entries in XING header. Skip the first one since it's always 0. unsigned char mTOC[99]; diff --git a/media/libstagefright/matroska/Android.mk b/media/libstagefright/matroska/Android.mk index e67da4c..2cccb4f 100644 --- a/media/libstagefright/matroska/Android.mk +++ b/media/libstagefright/matroska/Android.mk @@ -5,7 +5,6 @@ LOCAL_SRC_FILES:= \ MatroskaExtractor.cpp LOCAL_C_INCLUDES:= \ - $(JNI_H_INCLUDE) \ $(TOP)/external/libvpx/mkvparser \ $(TOP)/frameworks/native/include/media/openmax \ diff --git a/media/libstagefright/mpeg2ts/Android.mk b/media/libstagefright/mpeg2ts/Android.mk index ac4c2a1..eaa139d 100644 --- a/media/libstagefright/mpeg2ts/Android.mk +++ b/media/libstagefright/mpeg2ts/Android.mk @@ -10,7 +10,6 @@ LOCAL_SRC_FILES:= \ MPEG2TSExtractor.cpp \ LOCAL_C_INCLUDES:= \ - $(JNI_H_INCLUDE) \ $(TOP)/frameworks/base/media/libstagefright \ $(TOP)/frameworks/native/include/media/openmax diff --git a/media/libstagefright/omx/Android.mk b/media/libstagefright/omx/Android.mk index 083c7ef..d20ecb6 100644 --- a/media/libstagefright/omx/Android.mk +++ b/media/libstagefright/omx/Android.mk @@ -1,8 +1,6 @@ LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) -LOCAL_C_INCLUDES += $(JNI_H_INCLUDE) - LOCAL_SRC_FILES:= \ OMX.cpp \ OMXMaster.cpp \ diff --git a/media/libstagefright/omx/tests/Android.mk b/media/libstagefright/omx/tests/Android.mk index 07d47a8..6aa7470 100644 --- a/media/libstagefright/omx/tests/Android.mk +++ b/media/libstagefright/omx/tests/Android.mk @@ -8,7 +8,6 @@ LOCAL_SHARED_LIBRARIES := \ libstagefright libbinder libmedia libutils libstagefright_foundation LOCAL_C_INCLUDES := \ - $(JNI_H_INCLUDE) \ frameworks/base/media/libstagefright \ $(TOP)/frameworks/native/include/media/openmax diff --git a/media/libstagefright/rtsp/Android.mk b/media/libstagefright/rtsp/Android.mk index b3bc37c..e0fe59b 100644 --- a/media/libstagefright/rtsp/Android.mk +++ b/media/libstagefright/rtsp/Android.mk @@ -18,7 +18,6 @@ LOCAL_SRC_FILES:= \ ASessionDescription.cpp \ LOCAL_C_INCLUDES:= \ - $(JNI_H_INCLUDE) \ $(TOP)/frameworks/base/media/libstagefright/include \ $(TOP)/frameworks/native/include/media/openmax \ $(TOP)/external/openssl/include @@ -45,7 +44,6 @@ LOCAL_STATIC_LIBRARIES := \ libstagefright_rtsp LOCAL_C_INCLUDES:= \ - $(JNI_H_INCLUDE) \ frameworks/base/media/libstagefright \ $(TOP)/frameworks/native/include/media/openmax diff --git a/media/libstagefright/timedtext/Android.mk b/media/libstagefright/timedtext/Android.mk index d2d5f7b..58ef9e3 100644 --- a/media/libstagefright/timedtext/Android.mk +++ b/media/libstagefright/timedtext/Android.mk @@ -11,7 +11,6 @@ LOCAL_SRC_FILES:= \ LOCAL_CFLAGS += -Wno-multichar LOCAL_C_INCLUDES:= \ - $(JNI_H_INCLUDE) \ $(TOP)/frameworks/base/include/media/stagefright/timedtext \ $(TOP)/frameworks/base/media/libstagefright diff --git a/media/libstagefright/timedtext/TimedText3GPPSource.cpp b/media/libstagefright/timedtext/TimedText3GPPSource.cpp index c423ef0..4854121 100644 --- a/media/libstagefright/timedtext/TimedText3GPPSource.cpp +++ b/media/libstagefright/timedtext/TimedText3GPPSource.cpp @@ -39,19 +39,21 @@ TimedText3GPPSource::~TimedText3GPPSource() { } status_t TimedText3GPPSource::read( - int64_t *timeUs, Parcel *parcel, const MediaSource::ReadOptions *options) { + int64_t *startTimeUs, int64_t *endTimeUs, Parcel *parcel, + const MediaSource::ReadOptions *options) { MediaBuffer *textBuffer = NULL; status_t err = mSource->read(&textBuffer, options); if (err != OK) { return err; } CHECK(textBuffer != NULL); - textBuffer->meta_data()->findInt64(kKeyTime, timeUs); - // TODO: this is legacy code. when 'timeUs' can be <= 0? - if (*timeUs > 0) { - extractAndAppendLocalDescriptions(*timeUs, textBuffer, parcel); - } + textBuffer->meta_data()->findInt64(kKeyTime, startTimeUs); + CHECK_GE(*startTimeUs, 0); + extractAndAppendLocalDescriptions(*startTimeUs, textBuffer, parcel); textBuffer->release(); + // endTimeUs is a dummy parameter for 3gpp timed text format. + // Set a negative value to it to mark it is unavailable. + *endTimeUs = -1; return OK; } diff --git a/media/libstagefright/timedtext/TimedText3GPPSource.h b/media/libstagefright/timedtext/TimedText3GPPSource.h index 4ec3d8a..4170940 100644 --- a/media/libstagefright/timedtext/TimedText3GPPSource.h +++ b/media/libstagefright/timedtext/TimedText3GPPSource.h @@ -33,7 +33,8 @@ public: virtual status_t start() { return mSource->start(); } virtual status_t stop() { return mSource->stop(); } virtual status_t read( - int64_t *timeUs, + int64_t *startTimeUs, + int64_t *endTimeUs, Parcel *parcel, const MediaSource::ReadOptions *options = NULL); virtual status_t extractGlobalDescriptions(Parcel *parcel); diff --git a/media/libstagefright/timedtext/TimedTextPlayer.cpp b/media/libstagefright/timedtext/TimedTextPlayer.cpp index 8717914..917c62a 100644 --- a/media/libstagefright/timedtext/TimedTextPlayer.cpp +++ b/media/libstagefright/timedtext/TimedTextPlayer.cpp @@ -31,6 +31,7 @@ namespace android { static const int64_t kAdjustmentProcessingTimeUs = 100000ll; +static const int64_t kWaitTimeUsToRetryRead = 100000ll; TimedTextPlayer::TimedTextPlayer(const wp<MediaPlayerBase> &listener) : mListener(listener), @@ -139,13 +140,25 @@ void TimedTextPlayer::doSeekAndRead(int64_t seekTimeUs) { } void TimedTextPlayer::doRead(MediaSource::ReadOptions* options) { - int64_t timeUs = 0; + int64_t startTimeUs = 0; + int64_t endTimeUs = 0; sp<ParcelEvent> parcelEvent = new ParcelEvent(); - status_t err = mSource->read(&timeUs, &(parcelEvent->parcel), options); - if (err != OK) { + status_t err = mSource->read(&startTimeUs, &endTimeUs, + &(parcelEvent->parcel), options); + if (err == WOULD_BLOCK) { + postTextEventDelayUs(NULL, kWaitTimeUsToRetryRead); + return; + } else if (err != OK) { notifyError(err); - } else { - postTextEvent(parcelEvent, timeUs); + return; + } + + postTextEvent(parcelEvent, startTimeUs); + if (endTimeUs > 0) { + CHECK_GE(endTimeUs, startTimeUs); + // send an empty timed text to clear the subtitle when it reaches to the + // end time. + postTextEvent(NULL, endTimeUs); } } @@ -162,6 +175,13 @@ void TimedTextPlayer::postTextEvent(const sp<ParcelEvent>& parcel, int64_t timeU } else { delayUs = timeUs - positionUs - kAdjustmentProcessingTimeUs; } + postTextEventDelayUs(parcel, delayUs); + } +} + +void TimedTextPlayer::postTextEventDelayUs(const sp<ParcelEvent>& parcel, int64_t delayUs) { + sp<MediaPlayerBase> listener = mListener.promote(); + if (listener != NULL) { sp<AMessage> msg = new AMessage(kWhatSendSubtitle, id()); msg->setInt32("generation", mSendSubtitleGeneration); if (parcel != NULL) { diff --git a/media/libstagefright/timedtext/TimedTextPlayer.h b/media/libstagefright/timedtext/TimedTextPlayer.h index b869f18..47aff03 100644 --- a/media/libstagefright/timedtext/TimedTextPlayer.h +++ b/media/libstagefright/timedtext/TimedTextPlayer.h @@ -67,6 +67,7 @@ private: void doRead(MediaSource::ReadOptions* options = NULL); void onTextEvent(); void postTextEvent(const sp<ParcelEvent>& parcel = NULL, int64_t timeUs = -1); + void postTextEventDelayUs(const sp<ParcelEvent>& parcel = NULL, int64_t delayUs = -1); void notifyError(int error = 0); void notifyListener(const Parcel *parcel = NULL); diff --git a/media/libstagefright/timedtext/TimedTextSRTSource.cpp b/media/libstagefright/timedtext/TimedTextSRTSource.cpp index c44a99b..7b1f7f6 100644 --- a/media/libstagefright/timedtext/TimedTextSRTSource.cpp +++ b/media/libstagefright/timedtext/TimedTextSRTSource.cpp @@ -19,6 +19,7 @@ #include <utils/Log.h> #include <binder/Parcel.h> +#include <media/stagefright/foundation/ADebug.h> // for CHECK_xx #include <media/stagefright/foundation/AString.h> #include <media/stagefright/DataSource.h> #include <media/stagefright/MediaDefs.h> // for MEDIA_MIMETYPE_xxx @@ -63,19 +64,18 @@ status_t TimedTextSRTSource::stop() { } status_t TimedTextSRTSource::read( - int64_t *timeUs, + int64_t *startTimeUs, + int64_t *endTimeUs, Parcel *parcel, const MediaSource::ReadOptions *options) { - int64_t endTimeUs; AString text; - status_t err = getText(options, &text, timeUs, &endTimeUs); + status_t err = getText(options, &text, startTimeUs, endTimeUs); if (err != OK) { return err; } - if (*timeUs > 0) { - extractAndAppendLocalDescriptions(*timeUs, text, parcel); - } + CHECK_GE(*startTimeUs, 0); + extractAndAppendLocalDescriptions(*startTimeUs, text, parcel); return OK; } diff --git a/media/libstagefright/timedtext/TimedTextSRTSource.h b/media/libstagefright/timedtext/TimedTextSRTSource.h index 62710a0..e1371b8 100644 --- a/media/libstagefright/timedtext/TimedTextSRTSource.h +++ b/media/libstagefright/timedtext/TimedTextSRTSource.h @@ -36,7 +36,8 @@ public: virtual status_t start(); virtual status_t stop(); virtual status_t read( - int64_t *timeUs, + int64_t *startTimeUs, + int64_t *endTimeUs, Parcel *parcel, const MediaSource::ReadOptions *options = NULL); virtual sp<MetaData> getFormat(); diff --git a/media/libstagefright/timedtext/TimedTextSource.h b/media/libstagefright/timedtext/TimedTextSource.h index 9349342..756cc31 100644 --- a/media/libstagefright/timedtext/TimedTextSource.h +++ b/media/libstagefright/timedtext/TimedTextSource.h @@ -43,7 +43,8 @@ class TimedTextSource : public RefBase { virtual status_t stop() = 0; // Returns subtitle parcel and its start time. virtual status_t read( - int64_t *timeUs, + int64_t *startTimeUs, + int64_t *endTimeUs, Parcel *parcel, const MediaSource::ReadOptions *options = NULL) = 0; virtual status_t extractGlobalDescriptions(Parcel *parcel) { diff --git a/services/audioflinger/Android.mk b/services/audioflinger/Android.mk index 5164213..257f62c 100644 --- a/services/audioflinger/Android.mk +++ b/services/audioflinger/Android.mk @@ -15,6 +15,7 @@ LOCAL_C_INCLUDES := \ $(call include-path-for, audio-effects) \ $(call include-path-for, audio-utils) +# FIXME keep libmedia_native but remove libmedia after split LOCAL_SHARED_LIBRARIES := \ libaudioutils \ libcommon_time_client \ @@ -22,6 +23,7 @@ LOCAL_SHARED_LIBRARIES := \ libutils \ libbinder \ libmedia \ + libmedia_native \ libhardware \ libhardware_legacy \ libeffects \ diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp index 993192c..c5ad0f5 100644 --- a/services/audioflinger/AudioFlinger.cpp +++ b/services/audioflinger/AudioFlinger.cpp @@ -893,7 +893,8 @@ status_t AudioFlinger::setParameters(audio_io_handle_t ioHandle, const String8& // indicate output device change to all input threads for pre processing AudioParameter param = AudioParameter(keyValuePairs); int value; - if (param.getInt(String8(AudioParameter::keyRouting), value) == NO_ERROR) { + if ((param.getInt(String8(AudioParameter::keyRouting), value) == NO_ERROR) && + (value != 0)) { for (size_t i = 0; i < mRecordThreads.size(); i++) { mRecordThreads.valueAt(i)->setParameters(keyValuePairs); } @@ -1587,7 +1588,7 @@ void AudioFlinger::PlaybackThread::onFirstRef() } // PlaybackThread::createTrack_l() must be called with AudioFlinger::mLock held -sp<AudioFlinger::PlaybackThread::Track> AudioFlinger::PlaybackThread::createTrack_l( +sp<AudioFlinger::PlaybackThread::Track> AudioFlinger::PlaybackThread::createTrack_l( const sp<AudioFlinger::Client>& client, audio_stream_type_t streamType, uint32_t sampleRate, @@ -2336,7 +2337,7 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::MixerThread::prepareTrac size_t tracksWithEffect = 0; float masterVolume = mMasterVolume; - bool masterMute = mMasterMute; + bool masterMute = mMasterMute; if (masterMute) { masterVolume = 0; @@ -2375,7 +2376,7 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::MixerThread::prepareTrac // +1 for rounding and +1 for additional sample needed for interpolation minFrames = (mFrameCount * t->sampleRate()) / mSampleRate + 1 + 1; // add frames already consumed but not yet released by the resampler - // because cblk->framesReady() will include these frames + // because cblk->framesReady() will include these frames minFrames += mAudioMixer->getUnreleasedFrames(track->name()); // the minimum track buffer size is normally twice the number of frames necessary // to fill one buffer and the resampler should not leave more than one buffer worth @@ -2513,6 +2514,7 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::MixerThread::prepareTrac // reset retry count track->mRetryCount = kMaxTrackRetries; + // If one track is ready, set the mixer ready if: // - the mixer was not ready during previous round OR // - no other track is not ready @@ -3371,19 +3373,19 @@ AudioFlinger::ThreadBase::TrackBase::TrackBase( } } else { mCblk = (audio_track_cblk_t *)(new uint8_t[size]); - // construct the shared structure in-place. - new(mCblk) audio_track_cblk_t(); - // clear all buffers - mCblk->frameCount = frameCount; - mCblk->sampleRate = sampleRate; - mChannelCount = channelCount; - mChannelMask = channelMask; - mBuffer = (char*)mCblk + sizeof(audio_track_cblk_t); - memset(mBuffer, 0, frameCount*channelCount*sizeof(int16_t)); - // Force underrun condition to avoid false underrun callback until first data is - // written to buffer (other flags are cleared) - mCblk->flags = CBLK_UNDERRUN_ON; - mBufferEnd = (uint8_t *)mBuffer + bufferSize; + // construct the shared structure in-place. + new(mCblk) audio_track_cblk_t(); + // clear all buffers + mCblk->frameCount = frameCount; + mCblk->sampleRate = sampleRate; + mChannelCount = channelCount; + mChannelMask = channelMask; + mBuffer = (char*)mCblk + sizeof(audio_track_cblk_t); + memset(mBuffer, 0, frameCount*channelCount*sizeof(int16_t)); + // Force underrun condition to avoid false underrun callback until first data is + // written to buffer (other flags are cleared) + mCblk->flags = CBLK_UNDERRUN_ON; + mBufferEnd = (uint8_t *)mBuffer + bufferSize; } } @@ -3478,23 +3480,27 @@ AudioFlinger::PlaybackThread::Track::Track( const sp<IMemory>& sharedBuffer, int sessionId) : TrackBase(thread, client, sampleRate, format, channelMask, frameCount, sharedBuffer, sessionId), - mMute(false), mSharedBuffer(sharedBuffer), mName(-1), mMainBuffer(NULL), mAuxBuffer(NULL), + mMute(false), + // mFillingUpStatus ? + // mRetryCount initialized later when needed + mSharedBuffer(sharedBuffer), + mStreamType(streamType), + mName(-1), // see note below + mMainBuffer(thread->mixBuffer()), + mAuxBuffer(NULL), mAuxEffectId(0), mHasVolumeController(false) { if (mCblk != NULL) { - if (thread != NULL) { - mName = thread->getTrackName_l(); - mMainBuffer = thread->mixBuffer(); - } - ALOGV("Track constructor name %d, calling pid %d", mName, IPCThreadState::self()->getCallingPid()); - if (mName < 0) { - ALOGE("no more track names available"); - } - mStreamType = streamType; // NOTE: audio_track_cblk_t::frameSize for 8 bit PCM data is based on a sample size of // 16 bit because data is converted to 16 bit before being stored in buffer by AudioTrack mCblk->frameSize = audio_is_linear_pcm(format) ? mChannelCount * sizeof(int16_t) : sizeof(uint8_t); + // to avoid leaking a track name, do not allocate one unless there is an mCblk + mName = thread->getTrackName_l(); + if (mName < 0) { + ALOGE("no more track names available"); + } } + ALOGV("Track constructor name %d, calling pid %d", mName, IPCThreadState::self()->getCallingPid()); } AudioFlinger::PlaybackThread::Track::~Track() @@ -3791,16 +3797,9 @@ AudioFlinger::PlaybackThread::TimedTrack::create( if (!client->reserveTimedTrack()) return NULL; - sp<TimedTrack> track = new TimedTrack( + return new TimedTrack( thread, client, streamType, sampleRate, format, channelMask, frameCount, sharedBuffer, sessionId); - - if (track == NULL) { - client->releaseTimedTrack(); - return NULL; - } - - return track; } AudioFlinger::PlaybackThread::TimedTrack::TimedTrack( @@ -4260,7 +4259,7 @@ void AudioFlinger::RecordThread::RecordTrack::stop() RecordThread *recordThread = (RecordThread *)thread.get(); recordThread->stop(this); TrackBase::reset(); - // Force overerrun condition to avoid false overrun callback until first data is + // Force overrun condition to avoid false overrun callback until first data is // read from buffer android_atomic_or(CBLK_UNDERRUN_ON, &mCblk->flags); } diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h index a9afcb3..795807d 100644 --- a/services/audioflinger/AudioFlinger.h +++ b/services/audioflinger/AudioFlinger.h @@ -680,9 +680,9 @@ private: enum {FS_FILLING, FS_FILLED, FS_ACTIVE}; mutable uint8_t mFillingUpStatus; int8_t mRetryCount; - sp<IMemory> mSharedBuffer; + const sp<IMemory> mSharedBuffer; bool mResetDone; - audio_stream_type_t mStreamType; + const audio_stream_type_t mStreamType; int mName; int16_t *mMainBuffer; int32_t *mAuxBuffer; @@ -898,6 +898,7 @@ public: protected: SortedVector< wp<Track> > mActiveTracks; + // Allocate a track name. Returns name >= 0 if successful, -1 on failure. virtual int getTrackName_l() = 0; virtual void deleteTrackName_l(int name) = 0; virtual uint32_t activeSleepTimeUs(); @@ -1558,9 +1559,10 @@ mutable Mutex mLock; // mutex for process, commands and handl uint32_t mNewLeftVolume; // new volume on left channel uint32_t mNewRightVolume; // new volume on right channel uint32_t mStrategy; // strategy for this effect chain - // mSuspendedEffects lists all effect currently suspended in the chain - // use effect type UUID timelow field as key. There is no real risk of identical + // mSuspendedEffects lists all effects currently suspended in the chain. + // Use effect type UUID timelow field as key. There is no real risk of identical // timeLow fields among effect type UUIDs. + // Updated by updateSuspendedSessions_l() only. KeyedVector< int, sp<SuspendedEffectDesc> > mSuspendedEffects; }; diff --git a/services/audioflinger/AudioMixer.cpp b/services/audioflinger/AudioMixer.cpp index 1ec238b..3f4c19a 100644 --- a/services/audioflinger/AudioMixer.cpp +++ b/services/audioflinger/AudioMixer.cpp @@ -42,12 +42,15 @@ namespace android { // ---------------------------------------------------------------------------- -AudioMixer::AudioMixer(size_t frameCount, uint32_t sampleRate) - : mTrackNames(0), mSampleRate(sampleRate) +AudioMixer::AudioMixer(size_t frameCount, uint32_t sampleRate, uint32_t maxNumTracks) + : mTrackNames(0), mConfiguredNames((1 << maxNumTracks) - 1), mSampleRate(sampleRate) { // AudioMixer is not yet capable of multi-channel beyond stereo COMPILE_TIME_ASSERT_FUNCTION_SCOPE(2 == MAX_NUM_CHANNELS); + ALOG_ASSERT(maxNumTracks <= MAX_NUM_TRACKS, "maxNumTracks %u > MAX_NUM_TRACKS %u", + maxNumTracks, MAX_NUM_TRACKS); + LocalClock lc; mState.enabledTracks= 0; @@ -57,6 +60,10 @@ AudioMixer::AudioMixer(size_t frameCount, uint32_t sampleRate) mState.outputTemp = NULL; mState.resampleTemp = NULL; // mState.reserved + + // FIXME Most of the following initialization is probably redundant since + // tracks[i] should only be referenced if (mTrackNames & (1 << i)) != 0 + // and mTrackNames is initially 0. However, leave it here until that's verified. track_t* t = mState.tracks; for (unsigned i=0 ; i < MAX_NUM_TRACKS ; i++) { t->needs = 0; @@ -103,7 +110,7 @@ AudioMixer::~AudioMixer() int AudioMixer::getTrackName() { - uint32_t names = ~mTrackNames; + uint32_t names = (~mTrackNames) & mConfiguredNames; if (names != 0) { int n = __builtin_ctz(names); ALOGV("add track (%d)", n); @@ -132,7 +139,7 @@ void AudioMixer::deleteTrackName(int name) invalidateState(1<<name); } if (track.resampler != NULL) { - // delete the resampler + // delete the resampler delete track.resampler; track.resampler = NULL; track.sampleRate = mSampleRate; diff --git a/services/audioflinger/AudioMixer.h b/services/audioflinger/AudioMixer.h index b210212..856450c 100644 --- a/services/audioflinger/AudioMixer.h +++ b/services/audioflinger/AudioMixer.h @@ -31,7 +31,8 @@ namespace android { class AudioMixer { public: - AudioMixer(size_t frameCount, uint32_t sampleRate); + AudioMixer(size_t frameCount, uint32_t sampleRate, + uint32_t maxNumTracks = MAX_NUM_TRACKS); /*virtual*/ ~AudioMixer(); // non-virtual saves a v-table, restore if sub-classed @@ -70,9 +71,14 @@ public: // For all APIs with "name": TRACK0 <= name < TRACK0 + MAX_NUM_TRACKS + + // Allocate a track name. Returns new track name if successful, -1 on failure. int getTrackName(); + + // Free an allocated track by name void deleteTrackName(int name); + // Enable or disable an allocated track by name void enable(int name); void disable(int name); @@ -184,11 +190,17 @@ private: int32_t *outputTemp; int32_t *resampleTemp; int32_t reserved[2]; + // FIXME allocate dynamically to save some memory when maxNumTracks < MAX_NUM_TRACKS track_t tracks[MAX_NUM_TRACKS]; __attribute__((aligned(32))); }; // bitmask of allocated track names, where bit 0 corresponds to TRACK0 etc. uint32_t mTrackNames; + + // bitmask of configured track names; ~0 if maxNumTracks == MAX_NUM_TRACKS, + // but will have fewer bits set if maxNumTracks < MAX_NUM_TRACKS + const uint32_t mConfiguredNames; + const uint32_t mSampleRate; state_t mState __attribute__((aligned(32))); diff --git a/services/camera/libcameraservice/Android.mk b/services/camera/libcameraservice/Android.mk index e35435e..3cae1f5 100644 --- a/services/camera/libcameraservice/Android.mk +++ b/services/camera/libcameraservice/Android.mk @@ -15,6 +15,7 @@ LOCAL_SHARED_LIBRARIES:= \ libbinder \ libcutils \ libmedia \ + libmedia_native \ libcamera_client \ libgui \ libhardware |